Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion ps2xIOP/src/builtin_profiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ namespace ps2x::iop::detail
.responseCounterOffset = 4u,
.zeroReceiveBuffer = true,
.signalNowaitCompletion = true,
.suppressedCompletionCallbacks = {0x001FFD70u},
.completeQueuedPlayStreams = true,
.suppressedCompletionCallbacks = {},
};
}

Expand Down
1 change: 1 addition & 0 deletions ps2xIOP/src/module_factories.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ namespace ps2x::iop::detail
uint32_t responseCounterOffset = 0u;
bool zeroReceiveBuffer = true;
bool signalNowaitCompletion = false;
bool completeQueuedPlayStreams = false;
std::vector<uint32_t> suppressedCompletionCallbacks;
};

Expand Down
120 changes: 117 additions & 3 deletions ps2xIOP/src/modules/sound_update_stub.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@
#include <stdexcept>
#include <unordered_set>
#include <utility>
#include <vector>

namespace ps2x::iop::detail
{
namespace
{
constexpr uint16_t kPlayStreamCommand = 1u;
constexpr uint32_t kResponseRecordStride = 0x20u;
constexpr uint32_t kPackedStreamOffset = 4u;
constexpr uint32_t kStreamSlotMask = 0x3Fu;
constexpr uint32_t kStreamSlotCount = 48u;
constexpr uint32_t kCommandStreamSlotShift = 8u;
constexpr uint32_t kResponseStreamSlotShift = 4u;

class SoundUpdateStubService final : public IopService
{
public:
Expand All @@ -34,6 +43,7 @@ namespace ps2x::iop::detail
{
std::lock_guard<std::mutex> lock(m_mutex);
m_updateCounter = 0u;
m_completedStreamCount = 0u;
}

[[nodiscard]] RpcResult handleRpc(const RpcRequest &request) override
Expand Down Expand Up @@ -62,24 +72,44 @@ namespace ps2x::iop::detail
(void)m_host.zeroGuest(request.receive.address, request.receive.size);
}

std::vector<uint32_t> activeStreamSlots;
if (m_bindings.completeQueuedPlayStreams && request.receive.address != 0u)
{
// PlayStream leaves the EE slot in state 2. One active record moves it
// to state 1; the following empty update lets SOUND_CopyIOPBuffer clear it.
activeStreamSlots = findQueuedPlayStreams(request);
trimToReceiveCapacity(activeStreamSlots, request.receive.size);
}

uint32_t counter = 0u;
{
std::lock_guard<std::mutex> lock(m_mutex);
counter = ++m_updateCounter;
m_completedStreamCount += activeStreamSlots.size();
}

constexpr uint32_t activeStreams = 0u;
const uint32_t activeStreams = static_cast<uint32_t>(activeStreamSlots.size());
if (request.receive.address != 0u &&
request.receive.size >= m_bindings.activeStreamCountOffset + sizeof(activeStreams))
{
const uint32_t address = request.receive.address + m_bindings.activeStreamCountOffset;
(void)m_host.writeGuest(address, &activeStreams, sizeof(activeStreams));
}

for (size_t index = 0u; index < activeStreamSlots.size(); ++index)
{
const uint32_t packedStream = activeStreamSlots[index] << kResponseStreamSlotShift;
const uint32_t offset = m_bindings.activeStreamCountOffset + static_cast<uint32_t>(index) * kResponseRecordStride + kPackedStreamOffset;
const uint32_t address = request.receive.address + offset;
(void)m_host.writeGuest(address, &packedStream, sizeof(packedStream));
}

const uint32_t counterOffset = m_bindings.responseCounterOffset +
activeStreams * kResponseRecordStride;
if (request.receive.address != 0u &&
request.receive.size >= m_bindings.responseCounterOffset + sizeof(counter))
request.receive.size >= counterOffset + sizeof(counter))
{
const uint32_t address = request.receive.address + m_bindings.responseCounterOffset;
const uint32_t address = request.receive.address + counterOffset;
(void)m_host.writeGuest(address, &counter, sizeof(counter));
}

Expand All @@ -90,14 +120,98 @@ namespace ps2x::iop::detail
{
std::lock_guard<std::mutex> lock(m_mutex);
metrics.push_back({"update_counter", m_updateCounter, false});
metrics.push_back({"completed_streams", m_completedStreamCount, false});
}

private:
[[nodiscard]] std::vector<uint32_t> findQueuedPlayStreams(const RpcRequest &request) const
{
std::vector<uint32_t> slots;
if (request.send.address == 0u || request.send.size < sizeof(uint16_t))
{
return slots;
}

uint16_t commandCount = 0u;
if (!m_host.readGuest(request.send.address, &commandCount, sizeof(commandCount)))
{
return slots;
}

uint32_t offset = sizeof(commandCount);
for (uint32_t commandIndex = 0u; commandIndex < commandCount; ++commandIndex)
{
constexpr uint32_t headerSize = sizeof(uint16_t) * 2u;
if (offset > request.send.size || request.send.size - offset < headerSize)
{
break;
}

std::array<uint16_t, 2> header{};
if (!m_host.readGuest(request.send.address + offset,
header.data(),
sizeof(header)))
{
break;
}
offset += headerSize;

const uint32_t argumentBytes =
static_cast<uint32_t>(header[1]) * sizeof(uint16_t);
if (argumentBytes > request.send.size - offset)
{
break;
}

if (header[0] == kPlayStreamCommand && header[1] >= 2u)
{
uint16_t encodedSlot = 0u;
if (m_host.readGuest(request.send.address + offset + sizeof(uint16_t),
&encodedSlot,
sizeof(encodedSlot)))
{
const uint32_t slot =
(encodedSlot >> kCommandStreamSlotShift) & kStreamSlotMask;
if (slot < kStreamSlotCount &&
std::find(slots.begin(), slots.end(), slot) == slots.end())
{
slots.push_back(slot);
}
}
}

offset += argumentBytes;
}
return slots;
}

void trimToReceiveCapacity(std::vector<uint32_t> &slots, uint32_t receiveSize) const
{
size_t count = 0u;
for (; count < slots.size(); ++count)
{
const uint64_t recordOffset =
static_cast<uint64_t>(m_bindings.activeStreamCountOffset) +
static_cast<uint64_t>(count) * kResponseRecordStride +
kPackedStreamOffset;
const uint64_t counterOffset =
static_cast<uint64_t>(m_bindings.responseCounterOffset) +
static_cast<uint64_t>(count + 1u) * kResponseRecordStride;
if (recordOffset + sizeof(uint32_t) > receiveSize ||
counterOffset + sizeof(uint32_t) > receiveSize)
{
break;
}
}
slots.resize(count);
}

IopHost &m_host;
SoundUpdateStubBindings m_bindings;
std::array<uint32_t, 1> m_sids;
mutable std::mutex m_mutex;
uint32_t m_updateCounter = 0u;
uint64_t m_completedStreamCount = 0u;
};
}

Expand Down
6 changes: 6 additions & 0 deletions ps2xRecomp/include/ps2recomp/ps2_recompiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ namespace ps2recomp
bool recompile();
void generateOutput();
void printReport() const;
const RecompilerReporter::Counters &reportCounters() const { return m_reporter.counters(); }

static StubTarget resolveStubTarget(const std::string& name);
static bool IsCorrectnessCriticalFunctionName(const std::string &name);
static size_t DiscoverAdditionalEntryPoints(
std::vector<Function> &functions,
std::unordered_map<uint32_t, std::vector<Instruction>> &decodedFunctions,
Expand Down Expand Up @@ -65,6 +67,7 @@ namespace ps2recomp
std::unordered_set<std::string> m_stubFunctions;
std::unordered_set<uint32_t> m_stubFunctionStarts;
std::unordered_map<uint32_t, std::string> m_stubHandlerBindingsByStart;
std::unordered_set<uint32_t> m_correctnessCriticalFunctionStarts;
std::map<uint32_t, std::string> m_generatedStubs;
std::unordered_map<uint32_t, std::string> m_functionRenames;
std::unordered_map<uint32_t, std::vector<uint32_t>> m_resumeEntryTargetsByOwner;
Expand All @@ -74,6 +77,9 @@ namespace ps2recomp
void discoverAdditionalEntryPoints();
bool shouldSkipFunction(const Function &function) const;
bool isStubFunction(const Function &function) const;
bool isCorrectnessCriticalFunction(const Function &function) const;
bool hasResolvedStubHandler(const Function &function) const;
void collectCorrectnessCriticalFunctionStarts();
bool generateFunctionHeader();
bool generateStubHeader();
bool writeToFile(const std::string &path, const std::string &content);
Expand Down
4 changes: 4 additions & 0 deletions ps2xRecomp/include/ps2recomp/recompiler_reporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ namespace ps2recomp
size_t unhandledInstructions = 0;
size_t indirectFallbackPromotions = 0;
size_t indirectFallbackEntries = 0;
size_t correctnessCriticalGuestFallbacks = 0;
size_t correctnessCriticalFailures = 0;
};

void progress(const std::string &message);
Expand All @@ -63,6 +65,8 @@ namespace ps2recomp
void recordDecodeFailure();
void recordAdditionalEntryPoints(size_t count);
void recordGeneratedFunctions(size_t count);
void recordCorrectnessCriticalGuestFallback();
void recordCorrectnessCriticalFailure();
void recordIndirectFallbackPromotion(const std::string &functionName,
const std::vector<uint32_t> &jumpAddresses,
size_t promotedEntryCount);
Expand Down
Loading