mirror of
https://github.com/ran-j/PS2Recomp.git
synced 2026-09-26 08:51:05 -04:00
8c8a97af65
* feat: added ffmepg as dependency * feat: wip decoder video * feat: some perf and cleanup * feat: added generic MPEG stream notification * feat: CMakeLists.txt in ps2xStudio to configure SDL2 build options for static linking. fix: fix ffmpeg setup for linux fix: now MPEG decoder now identify that movie has ended and can play again anytime feat: better audio stub to not block games * feat: fix expansion test * feat: foo * a * feat: finally added a helper to to prevent thread starvation * feat: added basic vu0 code execution * feat: added yield Guest Execution After Wake to prevent deadlock * feat: added options on cmake for logs feat: better input for keyboard pad * feat: small corrections like top and itop vu branches etc * feat: changes * feat: working feature * feat: fatal frame iop * feat: test fix feat: z buffer fix * fix: gix GsPutIMR IMR * feat: added rl imgui * feat: added helper to get snapshot * feat: added debug panel consuming snapshots * feat: added pad snapshot feat: added RCP debug events * feat: final cleanup from old code * feat: added EE timer counter feat: applyed sound driver for Lotr feat: better check for sound driver compat layout feat: enquee and cosumed DMa cause feat: added Pad execCMd feat: update GS vsync signal flag feat: custom IOPs for LotR * feat: small cleanups * fix: fix wrong import
1051 lines
49 KiB
C++
1051 lines
49 KiB
C++
#include "MiniTest.h"
|
|
#include "ps2_runtime.h"
|
|
#include "ps2_syscalls.h"
|
|
#include "ps2_stubs.h"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
namespace ps2_stubs
|
|
{
|
|
void resetSifState();
|
|
}
|
|
|
|
namespace
|
|
{
|
|
constexpr int KE_OK = 0;
|
|
|
|
struct TestEnv
|
|
{
|
|
std::vector<uint8_t> rdram;
|
|
R5900Context ctx{};
|
|
PS2Runtime runtime;
|
|
|
|
TestEnv() : rdram(PS2_RAM_SIZE, 0u)
|
|
{
|
|
ps2_stubs::resetSifState();
|
|
ps2_syscalls::resetSoundDriverRpcState();
|
|
ps2_syscalls::clearSoundDriverCompatLayout();
|
|
ps2_syscalls::clearDtxCompatLayout();
|
|
std::memset(&ctx, 0, sizeof(ctx));
|
|
}
|
|
};
|
|
|
|
void setRecvxDtxCompatLayout()
|
|
{
|
|
PS2DtxCompatLayout layout{};
|
|
layout.rpcSid = 0x7D000000u;
|
|
layout.urpcObjBase = 0x01F18000u;
|
|
layout.urpcObjLimit = 0x01F1FF00u;
|
|
layout.urpcObjStride = 0x20u;
|
|
layout.urpcFnTableBase = 0x0034FED0u;
|
|
layout.urpcObjTableBase = 0x0034FFD0u;
|
|
layout.dispatcherFuncAddr = 0x002FABC0u;
|
|
ps2_syscalls::setDtxCompatLayout(layout);
|
|
}
|
|
|
|
#pragma pack(push, 1)
|
|
struct Ps2SifDmaTransfer
|
|
{
|
|
uint32_t src;
|
|
uint32_t dest;
|
|
int32_t size;
|
|
int32_t attr;
|
|
};
|
|
|
|
struct SifRpcHeader
|
|
{
|
|
uint32_t pkt_addr;
|
|
uint32_t rpc_id;
|
|
int32_t sema_id;
|
|
uint32_t mode;
|
|
};
|
|
|
|
struct SifRpcReceiveData
|
|
{
|
|
SifRpcHeader hdr;
|
|
uint32_t src;
|
|
uint32_t dest;
|
|
int32_t size;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
static_assert(sizeof(Ps2SifDmaTransfer) == 16u, "Unexpected Ps2SifDmaTransfer size.");
|
|
static_assert(sizeof(SifRpcReceiveData) == 28u, "Unexpected SifRpcReceiveData size.");
|
|
|
|
void setRegU32(R5900Context &ctx, int reg, uint32_t value)
|
|
{
|
|
ctx.r[reg] = _mm_set_epi64x(0, static_cast<int64_t>(value));
|
|
}
|
|
|
|
int32_t getRegS32(const R5900Context &ctx, int reg)
|
|
{
|
|
return static_cast<int32_t>(::getRegU32(&ctx, reg));
|
|
}
|
|
|
|
void writeGuestU32(uint8_t *rdram, uint32_t addr, uint32_t value)
|
|
{
|
|
std::memcpy(rdram + addr, &value, sizeof(value));
|
|
}
|
|
|
|
uint32_t readGuestU32(const uint8_t *rdram, uint32_t addr)
|
|
{
|
|
uint32_t value = 0;
|
|
std::memcpy(&value, rdram + addr, sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
void writeGuestS16(uint8_t *rdram, uint32_t addr, int16_t value)
|
|
{
|
|
std::memcpy(rdram + addr, &value, sizeof(value));
|
|
}
|
|
|
|
int16_t readGuestS16(const uint8_t *rdram, uint32_t addr)
|
|
{
|
|
int16_t value = 0;
|
|
std::memcpy(&value, rdram + addr, sizeof(value));
|
|
return value;
|
|
}
|
|
|
|
uint32_t g_dmacHandlerWriteAddr = 0u;
|
|
uint32_t g_dmacHandlerValue = 0u;
|
|
uint32_t g_dmacHandlerLastCause = 0u;
|
|
uint32_t g_dmacHandlerLastArg = 0u;
|
|
|
|
void testDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
|
|
{
|
|
(void)runtime;
|
|
g_dmacHandlerLastCause = ::getRegU32(ctx, 4);
|
|
g_dmacHandlerLastArg = ::getRegU32(ctx, 5);
|
|
if (g_dmacHandlerWriteAddr != 0u)
|
|
{
|
|
writeGuestU32(rdram, g_dmacHandlerWriteAddr, g_dmacHandlerValue);
|
|
}
|
|
ctx->pc = 0u;
|
|
}
|
|
}
|
|
|
|
void register_ps2_sif_dma_tests()
|
|
{
|
|
MiniTest::Case("PS2SifDma", [](TestCase &tc)
|
|
{
|
|
tc.Run("sceSifSetDma copies payload and sceSifDmaStat reports complete", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kDescAddr = 0x00020000u;
|
|
constexpr uint32_t kSrcAddr = 0x00020100u;
|
|
constexpr uint32_t kDstAddr = 0x00020200u;
|
|
|
|
std::array<uint8_t, 16> payload{};
|
|
for (size_t i = 0; i < payload.size(); ++i)
|
|
{
|
|
payload[i] = static_cast<uint8_t>(0x30u + i);
|
|
}
|
|
std::memcpy(env.rdram.data() + kSrcAddr, payload.data(), payload.size());
|
|
std::memset(env.rdram.data() + kDstAddr, 0, payload.size());
|
|
|
|
const Ps2SifDmaTransfer desc{
|
|
kSrcAddr,
|
|
kDstAddr,
|
|
static_cast<int32_t>(payload.size()),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
const int32_t dmaId = getRegS32(env.ctx, 2);
|
|
t.IsTrue(dmaId > 0, "sceSifSetDma should return a positive transfer id on success");
|
|
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kDstAddr, payload.data(), payload.size()) == 0,
|
|
"sceSifSetDma should copy transfer payload to destination");
|
|
|
|
setRegU32(env.ctx, 4, static_cast<uint32_t>(dmaId));
|
|
ps2_stubs::sceSifDmaStat(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) < 0, "sceSifDmaStat should be negative when transfer is complete");
|
|
});
|
|
|
|
tc.Run("isceSifSetDma and isceSifSetDChain alias the SIF DMA helpers", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kDescAddr = 0x00020240u;
|
|
constexpr uint32_t kSrcAddr = 0x00020340u;
|
|
constexpr uint32_t kDstAddr = 0x00020440u;
|
|
|
|
std::array<uint8_t, 12> payload{};
|
|
for (size_t i = 0; i < payload.size(); ++i)
|
|
{
|
|
payload[i] = static_cast<uint8_t>(0x50u + i);
|
|
}
|
|
std::memcpy(env.rdram.data() + kSrcAddr, payload.data(), payload.size());
|
|
std::memset(env.rdram.data() + kDstAddr, 0, payload.size());
|
|
|
|
const Ps2SifDmaTransfer desc{
|
|
kSrcAddr,
|
|
kDstAddr,
|
|
static_cast<int32_t>(payload.size()),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::isceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "isceSifSetDma should report a successful transfer id");
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kDstAddr, payload.data(), payload.size()) == 0,
|
|
"isceSifSetDma should copy transfer payload like sceSifSetDma");
|
|
|
|
ps2_stubs::isceSifSetDChain(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.Equals(getRegS32(env.ctx, 2), 0, "isceSifSetDChain should mirror sceSifSetDChain");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma dispatches enabled DMAC handlers for cause 5", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kDescAddr = 0x00020300u;
|
|
constexpr uint32_t kSrcAddr = 0x00020400u;
|
|
constexpr uint32_t kDstAddr = 0x00020500u;
|
|
constexpr uint32_t kHandlerAddr = 0x00100000u;
|
|
constexpr uint32_t kHandlerWriteAddr = 0x00020600u;
|
|
constexpr uint32_t kHandlerArg = 0x12345678u;
|
|
|
|
g_dmacHandlerWriteAddr = kHandlerWriteAddr;
|
|
g_dmacHandlerValue = 0xCAFEBABEu;
|
|
g_dmacHandlerLastCause = 0u;
|
|
g_dmacHandlerLastArg = 0u;
|
|
env.runtime.registerFunction(kHandlerAddr, &testDmacHandler);
|
|
|
|
setRegU32(env.ctx, 4, 5u);
|
|
setRegU32(env.ctx, 5, kHandlerAddr);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kHandlerArg);
|
|
ps2_syscalls::AddDmacHandler(env.rdram.data(), &env.ctx, &env.runtime);
|
|
const int32_t handlerId = getRegS32(env.ctx, 2);
|
|
t.IsTrue(handlerId > 0, "AddDmacHandler should register a handler");
|
|
|
|
setRegU32(env.ctx, 4, 5u);
|
|
ps2_syscalls::EnableDmac(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.Equals(getRegS32(env.ctx, 2), 0, "EnableDmac should succeed");
|
|
|
|
std::array<uint8_t, 16> payload{};
|
|
for (size_t i = 0; i < payload.size(); ++i)
|
|
{
|
|
payload[i] = static_cast<uint8_t>(0x40u + i);
|
|
}
|
|
std::memcpy(env.rdram.data() + kSrcAddr, payload.data(), payload.size());
|
|
|
|
const Ps2SifDmaTransfer desc{
|
|
kSrcAddr,
|
|
kDstAddr,
|
|
static_cast<int32_t>(payload.size()),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should still report success");
|
|
t.Equals(readGuestU32(env.rdram.data(), kHandlerWriteAddr), g_dmacHandlerValue,
|
|
"sceSifSetDma should invoke registered DMAC handlers");
|
|
t.Equals(g_dmacHandlerLastCause, 5u, "DMAC handler should observe cause 5");
|
|
t.Equals(g_dmacHandlerLastArg, kHandlerArg, "DMAC handler should receive registered argument");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma acknowledges DTX work-buffer transfers by advancing the EE footer ticket", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
setRecvxDtxCompatLayout();
|
|
|
|
constexpr uint32_t kClientAddr = 0x0002D000u;
|
|
constexpr uint32_t kDtxSid = 0x7D000000u;
|
|
constexpr uint32_t kSendAddr = 0x0002D100u;
|
|
constexpr uint32_t kRecvAddr = 0x0002D200u;
|
|
constexpr uint32_t kDescAddr = 0x0002D300u;
|
|
constexpr uint32_t kEeWorkAddr = 0x0002D400u;
|
|
constexpr uint32_t kIopWorkAddr = 0x0002D800u;
|
|
constexpr uint32_t kDtxId = 3u;
|
|
constexpr uint32_t kWorkLen = 0x100u;
|
|
constexpr uint32_t kFooterTicketAddr = kEeWorkAddr + kWorkLen - sizeof(uint32_t);
|
|
|
|
ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, kDtxSid);
|
|
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 the DTX sid");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, kDtxId);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWorkAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWorkAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen);
|
|
writeGuestU32(env.rdram.data(), kRecvAddr + 0x00u, 0u);
|
|
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 2u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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);
|
|
t.Equals(getRegS32(env.ctx, 2), KE_OK, "SifCallRpc should create the DTX transport");
|
|
t.IsTrue(readGuestU32(env.rdram.data(), kRecvAddr) != 0u, "DTX create should return a remote handle");
|
|
|
|
std::memset(env.rdram.data() + kEeWorkAddr, 0x44, kWorkLen);
|
|
std::memset(env.rdram.data() + kIopWorkAddr, 0x00, kWorkLen);
|
|
writeGuestU32(env.rdram.data(), kFooterTicketAddr, 1u);
|
|
|
|
const Ps2SifDmaTransfer desc{
|
|
kEeWorkAddr,
|
|
kIopWorkAddr,
|
|
static_cast<int32_t>(kWorkLen),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the DTX transfer");
|
|
|
|
t.Equals(readGuestU32(env.rdram.data(), kFooterTicketAddr), 2u,
|
|
"sceSifSetDma should advance the EE footer ticket so DTX clears wait_flag");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma applies SJX DTX payloads into the emulated SJRMT data ring", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
setRecvxDtxCompatLayout();
|
|
|
|
constexpr uint32_t kClientAddr = 0x0002E000u;
|
|
constexpr uint32_t kDtxSid = 0x7D000000u;
|
|
constexpr uint32_t kRecvAddr = 0x0002E100u;
|
|
constexpr uint32_t kSendAddr = 0x0002E200u;
|
|
constexpr uint32_t kDescAddr = 0x0002E300u;
|
|
constexpr uint32_t kEeWorkAddr = 0x0002E400u;
|
|
constexpr uint32_t kIopWorkAddr = 0x0002E800u;
|
|
constexpr uint32_t kRingAddr = 0x0002EC00u;
|
|
constexpr uint32_t kChunkDataAddr = 0x0002ED00u;
|
|
constexpr uint32_t kWorkLen = 0x100u;
|
|
constexpr uint32_t kChunkLen = 8u;
|
|
|
|
ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, kDtxSid);
|
|
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 bind the DTX sid");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kRingAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x422u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 12u);
|
|
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 sjrmtHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(sjrmtHandle != 0u, "SJRMT_UNI_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0x12345678u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x400u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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 sjxHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(sjxHandle != 0u, "SJX_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWorkAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWorkAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 2u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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);
|
|
t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed");
|
|
|
|
std::memset(env.rdram.data() + kEeWorkAddr, 0, kWorkLen);
|
|
std::memset(env.rdram.data() + kIopWorkAddr, 0, kWorkLen);
|
|
std::memset(env.rdram.data() + kRingAddr, 0, kWorkLen);
|
|
for (uint32_t i = 0; i < kChunkLen; ++i)
|
|
{
|
|
env.rdram[kChunkDataAddr + i] = static_cast<uint8_t>(0xA0u + i);
|
|
}
|
|
|
|
writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x00u, 1u);
|
|
env.rdram[kEeWorkAddr + 0x10u] = 0u;
|
|
env.rdram[kEeWorkAddr + 0x11u] = 1u;
|
|
std::memcpy(env.rdram.data() + kEeWorkAddr + 0x12u, "\0\0", 2u);
|
|
writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x14u, sjxHandle);
|
|
writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x18u, kChunkDataAddr);
|
|
writeGuestU32(env.rdram.data(), kEeWorkAddr + 0x1Cu, kChunkLen);
|
|
writeGuestU32(env.rdram.data(), kEeWorkAddr + kWorkLen - sizeof(uint32_t), 1u);
|
|
|
|
const Ps2SifDmaTransfer desc{
|
|
kEeWorkAddr,
|
|
kIopWorkAddr,
|
|
static_cast<int32_t>(kWorkLen),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the SJX transport");
|
|
t.Equals(env.rdram[kEeWorkAddr + 0x11u], static_cast<uint8_t>(0u),
|
|
"SJX DMA ack should rewrite the response line to room so EE recycles the chunk");
|
|
t.Equals(readGuestU32(env.rdram.data(), kEeWorkAddr + kWorkLen - sizeof(uint32_t)), 2u,
|
|
"SJX DMA ack should still advance the EE footer ticket");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 1u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x429u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 8u);
|
|
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);
|
|
t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), kChunkLen,
|
|
"SJX DMA should make SJRMT report available data");
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kRingAddr, env.rdram.data() + kChunkDataAddr, kChunkLen) == 0,
|
|
"SJX DMA should copy the chunk payload into the emulated SJRMT ring");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma recognizes SJX DTX payloads from rotated EE work buffers", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
setRecvxDtxCompatLayout();
|
|
|
|
constexpr uint32_t kClientAddr = 0x00031000u;
|
|
constexpr uint32_t kDtxSid = 0x7D000000u;
|
|
constexpr uint32_t kRecvAddr = 0x00031100u;
|
|
constexpr uint32_t kSendAddr = 0x00031200u;
|
|
constexpr uint32_t kDescAddr = 0x00031300u;
|
|
constexpr uint32_t kRegisteredEeWorkAddr = 0x00031400u;
|
|
constexpr uint32_t kRegisteredIopWorkAddr = 0x00031800u;
|
|
constexpr uint32_t kAltEeWorkAddr = 0x00031C00u;
|
|
constexpr uint32_t kAltIopWorkAddr = 0x00032000u;
|
|
constexpr uint32_t kRingAddr = 0x00032400u;
|
|
constexpr uint32_t kChunkDataAddr = 0x00032500u;
|
|
constexpr uint32_t kRegisteredWorkLen = 0x100u;
|
|
constexpr uint32_t kAltWorkLen = 0x180u;
|
|
constexpr uint32_t kChunkLen = 12u;
|
|
|
|
ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, kDtxSid);
|
|
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 bind the DTX sid");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kRingAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kRegisteredWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x422u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 12u);
|
|
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 sjrmtHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(sjrmtHandle != 0u, "SJRMT_UNI_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0x87654321u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x400u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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 sjxHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(sjxHandle != 0u, "SJX_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kRegisteredEeWorkAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kRegisteredIopWorkAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kRegisteredWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 2u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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);
|
|
t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed");
|
|
|
|
std::memset(env.rdram.data() + kRegisteredEeWorkAddr, 0, kRegisteredWorkLen);
|
|
std::memset(env.rdram.data() + kRegisteredIopWorkAddr, 0, kRegisteredWorkLen);
|
|
std::memset(env.rdram.data() + kAltEeWorkAddr, 0, kAltWorkLen);
|
|
std::memset(env.rdram.data() + kAltIopWorkAddr, 0, kAltWorkLen);
|
|
std::memset(env.rdram.data() + kRingAddr, 0, kRegisteredWorkLen);
|
|
for (uint32_t i = 0; i < kChunkLen; ++i)
|
|
{
|
|
env.rdram[kChunkDataAddr + i] = static_cast<uint8_t>(0xC0u + i);
|
|
}
|
|
|
|
writeGuestU32(env.rdram.data(), kAltEeWorkAddr + 0x00u, 1u);
|
|
env.rdram[kAltEeWorkAddr + 0x10u] = 0u;
|
|
env.rdram[kAltEeWorkAddr + 0x11u] = 1u;
|
|
std::memcpy(env.rdram.data() + kAltEeWorkAddr + 0x12u, "\0\0", 2u);
|
|
writeGuestU32(env.rdram.data(), kAltEeWorkAddr + 0x14u, sjxHandle);
|
|
writeGuestU32(env.rdram.data(), kAltEeWorkAddr + 0x18u, kChunkDataAddr);
|
|
writeGuestU32(env.rdram.data(), kAltEeWorkAddr + 0x1Cu, kChunkLen);
|
|
writeGuestU32(env.rdram.data(), kAltEeWorkAddr + kAltWorkLen - sizeof(uint32_t), 9u);
|
|
|
|
const Ps2SifDmaTransfer desc{
|
|
kAltEeWorkAddr,
|
|
kAltIopWorkAddr,
|
|
static_cast<int32_t>(kAltWorkLen),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDescAddr, &desc, sizeof(desc));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the rotated SJX transport");
|
|
t.Equals(env.rdram[kAltEeWorkAddr + 0x11u], static_cast<uint8_t>(0u),
|
|
"rotated SJX DMA ack should rewrite the response line to room");
|
|
t.Equals(readGuestU32(env.rdram.data(), kAltEeWorkAddr + kAltWorkLen - sizeof(uint32_t)), 10u,
|
|
"rotated SJX DMA ack should advance the alternate EE footer ticket");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 1u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x429u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 8u);
|
|
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);
|
|
t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), kChunkLen,
|
|
"rotated SJX DMA should make SJRMT report available data");
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kRingAddr, env.rdram.data() + kChunkDataAddr, kChunkLen) == 0,
|
|
"rotated SJX DMA should copy the chunk payload into the emulated SJRMT ring");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma lets active PS2RNA playback drain emulated SJRMT data", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
setRecvxDtxCompatLayout();
|
|
|
|
constexpr uint32_t kClientAddr = 0x0002F000u;
|
|
constexpr uint32_t kDtxSid = 0x7D000000u;
|
|
constexpr uint32_t kRecvAddr = 0x0002F100u;
|
|
constexpr uint32_t kSendAddr = 0x0002F200u;
|
|
constexpr uint32_t kDesc0Addr = 0x0002F300u;
|
|
constexpr uint32_t kDesc1Addr = 0x0002F320u;
|
|
constexpr uint32_t kEeWork0Addr = 0x0002F400u;
|
|
constexpr uint32_t kIopWork0Addr = 0x0002F800u;
|
|
constexpr uint32_t kEeWork1Addr = 0x0002FC00u;
|
|
constexpr uint32_t kIopWork1Addr = 0x00030000u;
|
|
constexpr uint32_t kRingAddr = 0x00030400u;
|
|
constexpr uint32_t kChunkDataAddr = 0x00030500u;
|
|
constexpr uint32_t kWorkLen = 0x100u;
|
|
constexpr uint32_t kChunkLen = 8u;
|
|
|
|
ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, kDtxSid);
|
|
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 bind the DTX sid");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kRingAddr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x422u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 12u);
|
|
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 sjrmtHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(sjrmtHandle != 0u, "SJRMT_UNI_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0xCAFEBABEu);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x400u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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 sjxHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(sjxHandle != 0u, "SJX_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, 0u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x408u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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 ps2RnaHandle = readGuestU32(env.rdram.data(), kRecvAddr);
|
|
t.IsTrue(ps2RnaHandle != 0u, "PS2RNA_CREATE should return a handle");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 0u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWork0Addr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWork0Addr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 2u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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);
|
|
t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed for SJX transport");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, 1u);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, kEeWork1Addr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x08u, kIopWork1Addr);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x0Cu, kWorkLen);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 2u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 16u);
|
|
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);
|
|
t.Equals(getRegS32(env.ctx, 2), KE_OK, "DTX create should succeed for PS2RNA transport");
|
|
|
|
std::memset(env.rdram.data() + kEeWork0Addr, 0, kWorkLen);
|
|
std::memset(env.rdram.data() + kIopWork0Addr, 0, kWorkLen);
|
|
std::memset(env.rdram.data() + kEeWork1Addr, 0, kWorkLen);
|
|
std::memset(env.rdram.data() + kIopWork1Addr, 0, kWorkLen);
|
|
std::memset(env.rdram.data() + kRingAddr, 0, kWorkLen);
|
|
for (uint32_t i = 0; i < kChunkLen; ++i)
|
|
{
|
|
env.rdram[kChunkDataAddr + i] = static_cast<uint8_t>(0xB0u + i);
|
|
}
|
|
|
|
writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x00u, 1u);
|
|
writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x10u, 2u);
|
|
writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x14u, ps2RnaHandle);
|
|
writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x18u, 1u);
|
|
writeGuestU32(env.rdram.data(), kEeWork1Addr + 0x1Cu, 0u);
|
|
writeGuestU32(env.rdram.data(), kEeWork1Addr + kWorkLen - sizeof(uint32_t), 1u);
|
|
|
|
const Ps2SifDmaTransfer desc1{
|
|
kEeWork1Addr,
|
|
kIopWork1Addr,
|
|
static_cast<int32_t>(kWorkLen),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDesc1Addr, &desc1, sizeof(desc1));
|
|
|
|
setRegU32(env.ctx, 4, kDesc1Addr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the PS2RNA control transport");
|
|
t.Equals(readGuestU32(env.rdram.data(), kEeWork1Addr + kWorkLen - sizeof(uint32_t)), 2u,
|
|
"PS2RNA control DMA should advance the EE footer ticket");
|
|
|
|
writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x00u, 1u);
|
|
env.rdram[kEeWork0Addr + 0x10u] = 0u;
|
|
env.rdram[kEeWork0Addr + 0x11u] = 1u;
|
|
std::memcpy(env.rdram.data() + kEeWork0Addr + 0x12u, "\0\0", 2u);
|
|
writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x14u, sjxHandle);
|
|
writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x18u, kChunkDataAddr);
|
|
writeGuestU32(env.rdram.data(), kEeWork0Addr + 0x1Cu, kChunkLen);
|
|
writeGuestU32(env.rdram.data(), kEeWork0Addr + kWorkLen - sizeof(uint32_t), 1u);
|
|
|
|
const Ps2SifDmaTransfer desc0{
|
|
kEeWork0Addr,
|
|
kIopWork0Addr,
|
|
static_cast<int32_t>(kWorkLen),
|
|
0};
|
|
std::memcpy(env.rdram.data() + kDesc0Addr, &desc0, sizeof(desc0));
|
|
|
|
setRegU32(env.ctx, 4, kDesc0Addr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.IsTrue(getRegS32(env.ctx, 2) > 0, "sceSifSetDma should succeed for the SJX transport");
|
|
t.Equals(env.rdram[kEeWork0Addr + 0x11u], static_cast<uint8_t>(0u),
|
|
"SJX DMA ack should still rewrite the response line to room");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 1u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x429u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 8u);
|
|
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);
|
|
t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), 0u,
|
|
"active PS2RNA playback should drain remote SJRMT data instead of leaving it queued forever");
|
|
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x00u, sjrmtHandle);
|
|
writeGuestU32(env.rdram.data(), kSendAddr + 0x04u, 0u);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 0x429u);
|
|
setRegU32(env.ctx, 6, 0u);
|
|
setRegU32(env.ctx, 7, kSendAddr);
|
|
setRegU32(env.ctx, 8, 8u);
|
|
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);
|
|
t.Equals(readGuestU32(env.rdram.data(), kRecvAddr), kWorkLen,
|
|
"drained PS2RNA playback should return remote SJRMT room to full capacity");
|
|
});
|
|
|
|
tc.Run("resetSifState seeds boot-ready SIF registers", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
auto getReg = [&](uint32_t reg) -> uint32_t
|
|
{
|
|
setRegU32(env.ctx, 4, reg);
|
|
ps2_stubs::sceSifGetReg(env.rdram.data(), &env.ctx, &env.runtime);
|
|
return ::getRegU32(&env.ctx, 2);
|
|
};
|
|
|
|
t.Equals(getReg(0x4u), 0x00020000u, "SIF boot status register should expose ready bit by default");
|
|
t.Equals(getReg(0x80000000u), 0u, "SIF main-address register should default to zero");
|
|
t.Equals(getReg(0x80000001u), 0u, "SIF sub-address register should default to zero");
|
|
t.Equals(getReg(0x80000002u), 0u, "SIF mscom register should default to zero");
|
|
});
|
|
|
|
tc.Run("sceSifExitCmd restores default boot-ready SIF registers", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
setRegU32(env.ctx, 4, 0x4u);
|
|
setRegU32(env.ctx, 5, 0x12340000u);
|
|
ps2_stubs::sceSifSetReg(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
setRegU32(env.ctx, 4, 0x80000002u);
|
|
setRegU32(env.ctx, 5, 0x89ABCDEFu);
|
|
ps2_stubs::sceSifSetReg(env.rdram.data(), &env.ctx, &env.runtime);
|
|
|
|
ps2_stubs::sceSifExitCmd(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.Equals(getRegS32(env.ctx, 2), 0, "sceSifExitCmd should succeed");
|
|
|
|
auto getReg = [&](uint32_t reg) -> uint32_t
|
|
{
|
|
setRegU32(env.ctx, 4, reg);
|
|
ps2_stubs::sceSifGetReg(env.rdram.data(), &env.ctx, &env.runtime);
|
|
return ::getRegU32(&env.ctx, 2);
|
|
};
|
|
|
|
t.Equals(getReg(0x4u), 0x00020000u, "sceSifExitCmd should restore the boot-ready status bit");
|
|
t.Equals(getReg(0x80000002u), 0u, "sceSifExitCmd should clear transient mscom state");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma rejects invalid descriptors without partial writes", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kDescAddr = 0x00021000u;
|
|
constexpr uint32_t kSrcA = 0x00021100u;
|
|
constexpr uint32_t kDstA = 0x00021200u;
|
|
constexpr uint32_t kSrcB = 0x00021300u;
|
|
constexpr uint32_t kInvalidDstB = 0xE0000100u; // unsupported guest segment
|
|
|
|
std::array<uint8_t, 8> payloadA{};
|
|
for (size_t i = 0; i < payloadA.size(); ++i)
|
|
{
|
|
payloadA[i] = static_cast<uint8_t>(0x70u + i);
|
|
}
|
|
std::array<uint8_t, 8> payloadB{};
|
|
for (size_t i = 0; i < payloadB.size(); ++i)
|
|
{
|
|
payloadB[i] = static_cast<uint8_t>(0x90u + i);
|
|
}
|
|
|
|
std::memcpy(env.rdram.data() + kSrcA, payloadA.data(), payloadA.size());
|
|
std::memcpy(env.rdram.data() + kSrcB, payloadB.data(), payloadB.size());
|
|
std::memset(env.rdram.data() + kDstA, 0x5Au, payloadA.size());
|
|
|
|
const Ps2SifDmaTransfer descs[2] = {
|
|
{kSrcA, kDstA, static_cast<int32_t>(payloadA.size()), 0},
|
|
{kSrcB, kInvalidDstB, static_cast<int32_t>(payloadB.size()), 0}};
|
|
std::memcpy(env.rdram.data() + kDescAddr, descs, sizeof(descs));
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 2u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.Equals(getRegS32(env.ctx, 2), 0, "sceSifSetDma should fail when any descriptor is invalid");
|
|
|
|
const std::array<uint8_t, 8> expectedUnchanged{
|
|
0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A};
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kDstA, expectedUnchanged.data(), expectedUnchanged.size()) == 0,
|
|
"failed multi-descriptor sceSifSetDma should not partially write earlier descriptors");
|
|
});
|
|
|
|
tc.Run("sceSifSetDma enforces descriptor count limit", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
constexpr uint32_t kDescAddr = 0x00022000u;
|
|
|
|
setRegU32(env.ctx, 4, kDescAddr);
|
|
setRegU32(env.ctx, 5, 33u);
|
|
ps2_stubs::sceSifSetDma(env.rdram.data(), &env.ctx, &env.runtime);
|
|
t.Equals(getRegS32(env.ctx, 2), 0, "sceSifSetDma should reject count > 32");
|
|
});
|
|
|
|
tc.Run("sceSifGetOtherData copies payload and writes receive metadata", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kRdAddr = 0x00023000u;
|
|
constexpr uint32_t kSrcAddr = 0x00023100u;
|
|
constexpr uint32_t kDstAddr = 0x00023200u;
|
|
constexpr uint32_t kSize = 20u;
|
|
|
|
std::array<uint8_t, kSize> payload{};
|
|
for (size_t i = 0; i < payload.size(); ++i)
|
|
{
|
|
payload[i] = static_cast<uint8_t>((i * 7u) & 0xFFu);
|
|
}
|
|
std::memcpy(env.rdram.data() + kSrcAddr, payload.data(), payload.size());
|
|
std::memset(env.rdram.data() + kDstAddr, 0, payload.size());
|
|
std::memset(env.rdram.data() + kRdAddr, 0, sizeof(SifRpcReceiveData));
|
|
|
|
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 valid transfer");
|
|
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kDstAddr, payload.data(), payload.size()) == 0,
|
|
"sceSifGetOtherData should copy payload");
|
|
|
|
const SifRpcReceiveData rd = *reinterpret_cast<const SifRpcReceiveData *>(env.rdram.data() + kRdAddr);
|
|
t.Equals(rd.src, kSrcAddr, "receive metadata src should be populated");
|
|
t.Equals(rd.dest, kDstAddr, "receive metadata dest should be populated");
|
|
t.Equals(static_cast<uint32_t>(rd.size), kSize, "receive metadata size should be populated");
|
|
});
|
|
|
|
tc.Run("sceSifGetOtherData preserves live sound-status sums when compat backfill is enabled", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kRdAddr = 0x00023300u;
|
|
constexpr uint32_t kDstAddr = 0x00023400u;
|
|
constexpr uint32_t kSize = 0x42u;
|
|
constexpr uint32_t kPrimarySeCheckAddr = 0x01E0EF10u;
|
|
constexpr uint32_t kPrimaryMidiCheckAddr = 0x01E0EF20u;
|
|
constexpr uint32_t kMidiSumOffset = 0x1Eu;
|
|
constexpr uint32_t kSeSumOffset = 0x26u;
|
|
constexpr uint32_t kBank = 1u;
|
|
|
|
PS2SoundDriverCompatLayout compat{};
|
|
compat.primarySeCheckAddr = kPrimarySeCheckAddr;
|
|
compat.primaryMidiCheckAddr = kPrimaryMidiCheckAddr;
|
|
ps2_syscalls::setSoundDriverCompatLayout(compat);
|
|
|
|
constexpr uint32_t kClientAddr = 0x00023500u;
|
|
constexpr uint32_t kRecvAddr = 0x00023600u;
|
|
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);
|
|
|
|
std::memset(env.rdram.data() + kDstAddr, 0, kSize);
|
|
std::memset(env.rdram.data() + kRdAddr, 0, sizeof(SifRpcReceiveData));
|
|
|
|
writeGuestS16(env.rdram.data(), kSrcAddr + kSeSumOffset + (kBank * 2u), static_cast<int16_t>(0x1357));
|
|
writeGuestS16(env.rdram.data(), kSrcAddr + kMidiSumOffset + (kBank * 2u), static_cast<int16_t>(0x2468));
|
|
|
|
writeGuestS16(env.rdram.data(), kPrimarySeCheckAddr + (kBank * 2u), static_cast<int16_t>(0x7B7B));
|
|
writeGuestS16(env.rdram.data(), kPrimaryMidiCheckAddr + (kBank * 2u), static_cast<int16_t>(0x6A6A));
|
|
|
|
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 + (kBank * 2u)),
|
|
static_cast<int16_t>(0x1357),
|
|
"live se_sum for the active bank should not be clobbered by compat check arrays");
|
|
t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kBank * 2u)),
|
|
static_cast<int16_t>(0x2468),
|
|
"live midi_sum for the active bank should not be clobbered by compat check arrays");
|
|
});
|
|
|
|
tc.Run("sceSifGetOtherData backfills zero sound-status sums for later banks", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kRdAddr = 0x00023700u;
|
|
constexpr uint32_t kDstAddr = 0x00023800u;
|
|
constexpr uint32_t kSize = 0x42u;
|
|
constexpr uint32_t kPrimarySeCheckAddr = 0x01E0EF10u;
|
|
constexpr uint32_t kPrimaryMidiCheckAddr = 0x01E0EF20u;
|
|
constexpr uint32_t kMidiSumOffset = 0x1Eu;
|
|
constexpr uint32_t kSeSumOffset = 0x26u;
|
|
constexpr uint32_t kLiveBank = 0u;
|
|
constexpr uint32_t kPendingBank = 1u;
|
|
|
|
PS2SoundDriverCompatLayout compat{};
|
|
compat.primarySeCheckAddr = kPrimarySeCheckAddr;
|
|
compat.primaryMidiCheckAddr = kPrimaryMidiCheckAddr;
|
|
ps2_syscalls::setSoundDriverCompatLayout(compat);
|
|
|
|
constexpr uint32_t kClientAddr = 0x00023900u;
|
|
constexpr uint32_t kRecvAddr = 0x00023A00u;
|
|
|
|
ps2_syscalls::SifInitRpc(env.rdram.data(), &env.ctx, &env.runtime);
|
|
setRegU32(env.ctx, 4, kClientAddr);
|
|
setRegU32(env.ctx, 5, 1u);
|
|
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);
|
|
|
|
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 later-bank 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 compat tables for later banks");
|
|
t.Equals(readGuestS16(env.rdram.data(), kDstAddr + kMidiSumOffset + (kPendingBank * 2u)),
|
|
static_cast<int16_t>(0x4444),
|
|
"zero midi_sum slots should backfill from compat tables for later banks");
|
|
});
|
|
|
|
tc.Run("sceSifGetOtherData rejects unsupported guest segments", [](TestCase &t)
|
|
{
|
|
TestEnv env;
|
|
|
|
constexpr uint32_t kRdAddr = 0x00024000u;
|
|
constexpr uint32_t kDstAddr = 0x00024100u;
|
|
constexpr uint32_t kInvalidSrcAddr = 0xE0000200u;
|
|
constexpr uint32_t kSize = 16u;
|
|
|
|
std::memset(env.rdram.data() + kDstAddr, 0xA5, kSize);
|
|
writeGuestU32(env.rdram.data(), kRdAddr + 0x10u, 0x11111111u);
|
|
writeGuestU32(env.rdram.data(), kRdAddr + 0x14u, 0x22222222u);
|
|
writeGuestU32(env.rdram.data(), kRdAddr + 0x18u, 0x33333333u);
|
|
|
|
setRegU32(env.ctx, 4, kRdAddr);
|
|
setRegU32(env.ctx, 5, kInvalidSrcAddr);
|
|
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), -1, "sceSifGetOtherData should fail for unsupported source segment");
|
|
|
|
std::array<uint8_t, kSize> expected{};
|
|
expected.fill(0xA5u);
|
|
t.IsTrue(std::memcmp(env.rdram.data() + kDstAddr, expected.data(), expected.size()) == 0,
|
|
"failed sceSifGetOtherData should not modify destination");
|
|
t.Equals(readGuestU32(env.rdram.data(), kRdAddr + 0x10u), 0x11111111u,
|
|
"failed sceSifGetOtherData should not overwrite rd metadata");
|
|
});
|
|
});
|
|
}
|