Skip to content
Merged
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
42 changes: 13 additions & 29 deletions ps2xRecomp/include/ps2recomp/instructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,37 +670,21 @@ namespace ps2recomp
// VU0_VLDQ = 0x1F // VU0 Load/Store Quad with Decrement
// };

// VU0 Control Register Numbers (used with CFC2/CTC2)
// VU0 COP2 control register numbers used by CFC2/CTC2.
// Registers 0..15 address VI0..VI15 directly.
enum VU0ControlRegisters
{
VU0_CR_STATUS = 0, // Status/Control register
VU0_CR_MAC = 1, // MAC flags register
VU0_CR_CLIP = 5, // Clipping flags register
VU0_CR_R = 3, // R register (Random number)
VU0_CR_I = 4, // I register (Immediate)

// Add missing registers
VU0_CR_VPU_STAT = 2, // VPU-STAT register
VU0_CR_TPC = 6, // T (program counter) register
VU0_CR_CMSAR0 = 7, // Call/return address 0
VU0_CR_FBRST = 8, // VIF/VU reset register
VU0_CR_VPU_STAT2 = 9, // VPU-STAT register 2
VU0_CR_TPC2 = 10, // T (program counter) register 2
VU0_CR_CMSAR1 = 11, // Call/return address 1
VU0_CR_FBRST2 = 12, // VIF/VU reset register 2
VU0_CR_VPU_STAT3 = 13, // VPU-STAT register 3
VU0_CR_CMSAR2 = 14, // Call/return address 2
VU0_CR_FBRST3 = 15, // VIF/VU reset register 3
VU0_CR_VPU_STAT4 = 16, // VPU-STAT register 4
VU0_CR_CMSAR3 = 17, // Call/return address 3
VU0_CR_FBRST4 = 18, // VIF/VU reset register 4
VU0_CR_ACC = 20, // Accumulator register
VU0_CR_INFO = 21, // Information register
VU0_CR_CLIP2 = 22, // Clipping flags register 2
VU0_CR_P = 26, // P register
VU0_CR_XITOP = 27, // XITOP register
VU0_CR_ITOP = 28, // ITOP register
VU0_CR_TOP = 29 // TOP register
VU0_CR_STATUS = 16,
VU0_CR_MAC = 17,
VU0_CR_CLIP = 18,
VU0_CR_R = 20,
VU0_CR_I = 21,
VU0_CR_Q = 22,
VU0_CR_TPC = 26,
VU0_CR_CMSAR0 = 27,
VU0_CR_FBRST = 28,
VU0_CR_VPU_STAT = 29,
VU0_CR_CMSAR1 = 31
};
enum VU0OPSFunctions
{
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