Skip to content
Open
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
27 changes: 27 additions & 0 deletions ps2xRuntime/include/ps2_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,33 @@ struct PS2SoundDriverCompatLayout
std::array<uint32_t, 4> completionCallbacks{};
std::array<uint32_t, 2> clearBusyCallbacks{};

// Service ids (0 = unused; a driver muxing everything on one SID sets both equal).
// 0 is the "unused" sentinel, so a service or fno that is literally 0 can't be
// expressed -- a deliberate tradeoff (real SIF-RPC services are nonzero).
uint32_t commandSid = 0; // service id carrying the submit-command-buffer subcommand
uint32_t stateSid = 0; // service id carrying the status/addr-table queries

// Subcommand (rpcNum -> semantic) mapping. 0 = that semantic is unused.
uint32_t submitFno = 0; // submit-command-buffer
uint32_t getStatusFno = 0; // return statusAddr
uint32_t getAddrTableFno = 0; // return addrTableAddr
uint32_t streamOpenFno = 0; // write streamReadyValue to streamStateAddr
uint32_t channelConfigFno = 0; // set channelAllocFlagTableAddr[channel]=1 (channel from send word 0 if <16)
uint32_t stopFno = 0; // write 1 to stopCompletionFlagAddr
uint32_t benignStatusValue = 0xffffff9bu; // recv[0] for an unknown fno on a served SID

// Addresses/values the stream/channel/stop subcommands operate on.
uint32_t streamStateAddr = 0;
uint32_t streamReadyValue = 0;
uint32_t channelAllocFlagTableAddr = 0;
uint32_t stopCompletionFlagAddr = 0;

[[nodiscard]] bool servesSid(uint32_t sid) const
{
return (commandSid != 0u && sid == commandSid) ||
(stateSid != 0u && sid == stateSid);
}

[[nodiscard]] bool hasChecksumTables() const
{
return primarySeCheckAddr != 0u || primaryMidiCheckAddr != 0u ||
Expand Down
6 changes: 0 additions & 6 deletions ps2xRuntime/include/runtime/ps2_iop.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@

class PS2Runtime;

constexpr uint32_t IOP_SID_SNDDRV_COMMAND = 0x00000000u;
constexpr uint32_t IOP_SID_SNDDRV_STATE = 0x00000001u;
constexpr uint32_t IOP_SID_LOTR_CLFILE = 0x0000FF01u;
constexpr uint32_t IOP_SID_LOTR_SOUND = 0x00012345u;
constexpr uint32_t IOP_SID_LIBSD = 0x80000701u;
constexpr uint32_t IOP_SID_FATAL_FRAME_SDRDRV = 0x19740512u;

constexpr uint32_t IOP_RPC_SNDDRV_SUBMIT = 0x00000000u;
constexpr uint32_t IOP_RPC_SNDDRV_GET_STATUS_ADDR = 0x00000012u;
constexpr uint32_t IOP_RPC_SNDDRV_GET_ADDR_TABLE = 0x00000013u;

class ps2_iop
{
public:
Expand Down
97 changes: 92 additions & 5 deletions ps2xRuntime/src/lib/Kernel/Syscalls/RPC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,14 +539,30 @@ namespace ps2_syscalls
return false;
}

if (sid == IOP_SID_SNDDRV_COMMAND && rpcNum == IOP_RPC_SNDDRV_SUBMIT)
PS2SoundDriverCompatLayout compat{};
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
compat = g_soundDriverCompatLayout;
}

if (compat.commandSid == 0u && compat.stateSid == 0u)
{
return false;
}

if (!compat.servesSid(sid))
{
return false;
}

if (compat.submitFno != 0u && rpcNum == compat.submitFno)
{
handleSoundDriverCommandBuffer(rdram, runtime, sendBuf, sendSize);
return true;
}

if (sid == IOP_SID_SNDDRV_STATE &&
(rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR || rpcNum == IOP_RPC_SNDDRV_GET_ADDR_TABLE))
if ((compat.getStatusFno != 0u && rpcNum == compat.getStatusFno) ||
(compat.getAddrTableFno != 0u && rpcNum == compat.getAddrTableFno))
{
uint32_t responseWord = 0u;
{
Expand All @@ -556,8 +572,8 @@ namespace ps2_syscalls
return false;
}
responseWord =
(rpcNum == IOP_RPC_SNDDRV_GET_STATUS_ADDR) ? g_soundDriverRpcState.statusAddr
: g_soundDriverRpcState.addrTableAddr;
(rpcNum == compat.getStatusFno) ? g_soundDriverRpcState.statusAddr
: g_soundDriverRpcState.addrTableAddr;
}

if (recvBuf && recvSize >= sizeof(uint32_t))
Expand All @@ -574,6 +590,77 @@ namespace ps2_syscalls
return true;
}

if (compat.streamOpenFno != 0u && rpcNum == compat.streamOpenFno)
{
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (compat.streamStateAddr != 0u)
{
(void)writeGuestU32(rdram, compat.streamStateAddr, compat.streamReadyValue);
}
}

if (recvBuf && recvSize >= sizeof(uint32_t))
{
(void)writeGuestU32(rdram, recvBuf, 0u);
resultPtr = recvBuf;
}

signalNowaitCompletion = true;
return true;
}

if (compat.channelConfigFno != 0u && rpcNum == compat.channelConfigFno)
{
uint32_t channel = 0u;
if (sendBuf != 0u)
{
(void)readGuestU32(rdram, sendBuf, channel);
}

{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (compat.channelAllocFlagTableAddr != 0u && channel < 16u)
{
(void)writeGuestU32(rdram, compat.channelAllocFlagTableAddr + channel * sizeof(uint32_t), 1u);
}
}

if (recvBuf && recvSize >= sizeof(uint32_t))
{
(void)writeGuestU32(rdram, recvBuf, 0u);
resultPtr = recvBuf;
}

signalNowaitCompletion = true;
return true;
}

if (compat.stopFno != 0u && rpcNum == compat.stopFno)
{
{
std::lock_guard<std::mutex> lock(g_rpc_mutex);
if (compat.stopCompletionFlagAddr != 0u)
{
(void)writeGuestU32(rdram, compat.stopCompletionFlagAddr, 1u);
}
}

if (recvBuf && recvSize >= sizeof(uint32_t))
{
(void)writeGuestU32(rdram, recvBuf, 0u);
resultPtr = recvBuf;
}

signalNowaitCompletion = true;
return true;
}

if (recvBuf && recvSize >= sizeof(uint32_t))
{
(void)writeGuestU32(rdram, recvBuf, compat.benignStatusValue);
}

return false;
}

Expand Down
8 changes: 8 additions & 0 deletions ps2xRuntime/src/lib/game_overrides.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,14 @@ namespace
layout.busyFlagAddr = 0x01E212C8u;
layout.completionCallbacks = {0x002EAC20u, 0x002EAC30u, 0x002FAC20u, 0x002FAC30u};
layout.clearBusyCallbacks = {0x002EAC30u, 0x002FAC30u};

// SID + subcommand (fno) numbers RE:CVX's sound driver speaks; carried per-game
// so its getStatus RPC provisions the status/addr-table pool the
// sceSifGetOtherData checksum backfill depends on. The submit path (SID 0 /
// fno 0, never a live service) is intentionally left unconfigured.
layout.stateSid = 1u;
layout.getStatusFno = 0x12u;
layout.getAddrTableFno = 0x13u;
ps2_syscalls::setSoundDriverCompatLayout(layout);
}

Expand Down
6 changes: 0 additions & 6 deletions ps2xRuntime/src/lib/ps2_debug_panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,6 @@ namespace
{
switch (sid)
{
case IOP_SID_SNDDRV_COMMAND:
return "SNDDRV command";
case IOP_SID_SNDDRV_STATE:
return "SNDDRV state";
case IOP_SID_LIBSD:
return "LIBSD";
case IOP_SID_FATAL_FRAME_SDRDRV:
Expand Down Expand Up @@ -1085,8 +1081,6 @@ namespace
bool dynamic;
};
const ServiceRow services[] = {
{"SNDDRV command", IOP_SID_SNDDRV_COMMAND, false},
{"SNDDRV state", IOP_SID_SNDDRV_STATE, false},
{"LIBSD", IOP_SID_LIBSD, false},
{"Fatal Frame SDRDRV", IOP_SID_FATAL_FRAME_SDRDRV, false},
{"LOTR SOUND", IOP_SID_LOTR_SOUND, false},
Expand Down
83 changes: 83 additions & 0 deletions ps2xTest/src/ps2_sif_dma_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "ps2_runtime.h"
#include "ps2_syscalls.h"
#include "ps2_stubs.h"
#include "game_overrides.h"

#include <array>
#include <cstdint>
Expand Down Expand Up @@ -898,6 +899,8 @@ void register_ps2_sif_dma_tests()
PS2SoundDriverCompatLayout compat{};
compat.primarySeCheckAddr = kPrimarySeCheckAddr;
compat.primaryMidiCheckAddr = kPrimaryMidiCheckAddr;
compat.stateSid = 1u;
compat.getStatusFno = 0x12u;
ps2_syscalls::setSoundDriverCompatLayout(compat);

constexpr uint32_t kClientAddr = 0x00023500u;
Expand Down Expand Up @@ -947,6 +950,84 @@ void register_ps2_sif_dma_tests()
"live midi_sum for the active bank should not be clobbered by compat check arrays");
});

tc.Run("RE:CVX sound-driver override provisions status pool for sceSifGetOtherData backfill", [](TestCase &t)
{
// Real-path guard for the RE:CVX override: with the layout supplied only by
// applyMatching, drives the actual SifCallRpc(getStatus) -> sceSifGetOtherData
// path and asserts the check-array -> status backfill ran.
TestEnv env;

constexpr uint32_t kRdAddr = 0x00024300u;
constexpr uint32_t kDstAddr = 0x00024400u;
constexpr uint32_t kSize = 0x42u;
constexpr uint32_t kPrimarySeCheckAddr = 0x01E0EF10u; // matches the RE:CVX override
constexpr uint32_t kPrimaryMidiCheckAddr = 0x01E0EF20u; // matches the RE:CVX override
constexpr uint32_t kMidiSumOffset = 0x1Eu;
constexpr uint32_t kSeSumOffset = 0x26u;
constexpr uint32_t kLiveBank = 0u;
constexpr uint32_t kPendingBank = 1u;

// Provision the layout via the real RE:CVX game override (elf slus_201.84).
ps2_game_overrides::applyMatching(env.runtime, "slus_201.84", 0u);

constexpr uint32_t kClientAddr = 0x00024500u;
constexpr uint32_t kRecvAddr = 0x00024600u;
constexpr uint32_t kSid = 1u;

ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime);
setRegU32(env.ctx, 4, kClientAddr);
setRegU32(env.ctx, 5, kSid);
setRegU32(env.ctx, 6, 0u);
ps2_syscalls::SifBindRpc(env.rdram.data(), &env.ctx, &env.runtime);
t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifBindRpc should succeed for sound-driver sid");

setRegU32(env.ctx, 4, kClientAddr);
setRegU32(env.ctx, 5, 0x12u);
setRegU32(env.ctx, 6, 0u);
setRegU32(env.ctx, 7, 0u);
setRegU32(env.ctx, 8, 0u);
setRegU32(env.ctx, 9, kRecvAddr);
setRegU32(env.ctx, 10, 4u);
setRegU32(env.ctx, 11, 0u);
ps2_syscalls::SifCallRpc(env.rdram.data(), &env.ctx, &env.runtime);
const uint32_t kSrcAddr = readGuestU32(env.rdram.data(), kRecvAddr);

// The override must have provisioned a nonzero status pool address; if
// stateSid/getStatusFno are unset the getStatus RPC is unhandled and this is 0.
t.IsTrue(kSrcAddr != 0u,
"getStatus RPC (served only via the migrated override) must return a nonzero status address");

std::memset(env.rdram.data() + kDstAddr, 0, kSize);
std::memset(env.rdram.data() + kRdAddr, 0, sizeof(SifRpcReceiveData));

writeGuestS16(env.rdram.data(), kSrcAddr + kSeSumOffset + (kLiveBank * 2u), static_cast<int16_t>(0x1111));
writeGuestS16(env.rdram.data(), kSrcAddr + kMidiSumOffset + (kLiveBank * 2u), static_cast<int16_t>(0x2222));

writeGuestS16(env.rdram.data(), kPrimarySeCheckAddr + (kPendingBank * 2u), static_cast<int16_t>(0x3333));
writeGuestS16(env.rdram.data(), kPrimaryMidiCheckAddr + (kPendingBank * 2u), static_cast<int16_t>(0x4444));

setRegU32(env.ctx, 4, kRdAddr);
setRegU32(env.ctx, 5, kSrcAddr);
setRegU32(env.ctx, 6, kDstAddr);
setRegU32(env.ctx, 7, kSize);
ps2_stubs::sceSifGetOtherData(env.rdram.data(), &env.ctx, &env.runtime);

t.Equals(getRegS32(env.ctx, 2), 0,
"sceSifGetOtherData should succeed for sound-status transfer");
t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kSeSumOffset + (kLiveBank * 2u)),
static_cast<int16_t>(0x1111),
"existing live se_sum values should remain intact");
t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kLiveBank * 2u)),
static_cast<int16_t>(0x2222),
"existing live midi_sum values should remain intact");
t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kSeSumOffset + (kPendingBank * 2u)),
static_cast<int16_t>(0x3333),
"zero se_sum slots should backfill from the override's compat tables (proves the status pool was provisioned)");
t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kPendingBank * 2u)),
static_cast<int16_t>(0x4444),
"zero midi_sum slots should backfill from the override's compat tables (proves the status pool was provisioned)");
});

tc.Run("sceSifGetOtherData backfills zero sound-status sums for later banks", [](TestCase &t)
{
TestEnv env;
Expand All @@ -964,6 +1045,8 @@ void register_ps2_sif_dma_tests()
PS2SoundDriverCompatLayout compat{};
compat.primarySeCheckAddr = kPrimarySeCheckAddr;
compat.primaryMidiCheckAddr = kPrimaryMidiCheckAddr;
compat.stateSid = 1u;
compat.getStatusFno = 0x12u;
ps2_syscalls::setSoundDriverCompatLayout(compat);

constexpr uint32_t kClientAddr = 0x00023900u;
Expand Down
Loading