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: 3 additions & 0 deletions ps2xRuntime/src/lib/ps2_syscalls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ namespace ps2_syscalls
case static_cast<uint32_t>(-0x78):
ps2_stubs::sceSifSetDChain(rdram, ctx, runtime);
return true;
case 0x83:
FindAddress(rdram, ctx, runtime);
return true;
case 0x85:
SetMemoryMode(rdram, ctx, runtime);
return true;
Expand Down
53 changes: 53 additions & 0 deletions ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,59 @@ void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
setReturnU32(ctx, PS2_RAM_SIZE);
}

static inline uint32_t normalizeKernelAlias(uint32_t addr)
{
if (addr >= 0x80000000u && addr < 0xC0000000u)
{
return addr & 0x1FFFFFFFu;
}
return addr;
}

// 0x83 FindAddress:
// - a0: table start (inclusive)
// - a1: table end (exclusive)
// - a2: target address to locate inside the table (word entries)
// Returns the guest address of the matching word entry, or 0 if not found.
void FindAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be thread safe ?

{
(void)runtime;

uint32_t start = getRegU32(ctx, 4);
uint32_t end = getRegU32(ctx, 5);
const uint32_t target = getRegU32(ctx, 6);
const uint32_t targetNorm = normalizeKernelAlias(target);

// Word-scan semantics: align the search window to uint32 boundaries.
start = (start + 3u) & ~0x3u;
end &= ~0x3u;

if (start >= end)
{
setReturnU32(ctx, 0u);
return;
}

for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t))
{
const uint8_t *entryPtr = getConstMemPtr(rdram, addr);
if (!entryPtr)
{
break;
}

uint32_t entry = 0;
std::memcpy(&entry, entryPtr, sizeof(entry));
if (entry == target || normalizeKernelAlias(entry) == targetNorm)
{
setReturnU32(ctx, addr);
return;
}
}

setReturnU32(ctx, 0u);
}

void Deci2Call(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime)
{
(void)rdram;
Expand Down
67 changes: 67 additions & 0 deletions ps2xTest/src/ps2_runtime_kernel_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,5 +386,72 @@ void register_ps2_runtime_kernel_tests()
const uint32_t setupSp = static_cast<uint32_t>(getRegS32(env.ctx, 2));
t.Equals(setupSp & 0xFu, 0u, "SetupThread should always return a 16-byte aligned stack pointer");
});

tc.Run("numeric syscall 0x83 finds matching table entry", [](TestCase &t)
{
TestEnv env;
constexpr uint32_t kTableBase = 0x00002000u;
constexpr uint32_t kValues[] = {
0x11111111u,
0x11223344u,
0x55555555u,
0x89ABCDEFu
};

writeGuestWords(env.rdram.data(), kTableBase, kValues, std::size(kValues));
setRegU32(env.ctx, 4, kTableBase);
setRegU32(env.ctx, 5, kTableBase + static_cast<uint32_t>(sizeof(kValues)));
setRegU32(env.ctx, 6, 0x11223344u);

t.IsTrue(callSyscall(0x83u, env.rdram.data(), &env.ctx, &env.runtime),
"syscall 0x83 should dispatch");
t.Equals(static_cast<uint32_t>(getRegS32(env.ctx, 2)),
kTableBase + 4u,
"FindAddress should return address of first matching word");
});

tc.Run("numeric syscall 0x83 supports KSEG aliases", [](TestCase &t)
{
TestEnv env;
constexpr uint32_t kTableBasePhys = 0x00003000u;
constexpr uint32_t kTableBaseKseg = 0x80003000u;
constexpr uint32_t kValues[] = {
0x00123456u,
0x8000AAAAu
};

writeGuestWords(env.rdram.data(), kTableBasePhys, kValues, std::size(kValues));
setRegU32(env.ctx, 4, kTableBaseKseg);
setRegU32(env.ctx, 5, kTableBaseKseg + static_cast<uint32_t>(sizeof(kValues)));
setRegU32(env.ctx, 6, 0x80123456u); // Alias of first table value

t.IsTrue(callSyscall(0x83u, env.rdram.data(), &env.ctx, &env.runtime),
"syscall 0x83 should dispatch");
t.Equals(static_cast<uint32_t>(getRegS32(env.ctx, 2)),
kTableBaseKseg,
"FindAddress should match KSEG aliases and preserve guest segment in return value");
});

tc.Run("numeric syscall 0x83 returns 0 when entry is absent", [](TestCase &t)
{
TestEnv env;
constexpr uint32_t kTableBase = 0x00004000u;
constexpr uint32_t kValues[] = {
0x00000001u,
0x00000002u,
0x00000003u
};

writeGuestWords(env.rdram.data(), kTableBase, kValues, std::size(kValues));
setRegU32(env.ctx, 4, kTableBase);
setRegU32(env.ctx, 5, kTableBase + static_cast<uint32_t>(sizeof(kValues)));
setRegU32(env.ctx, 6, 0xDEADBEEFu);

t.IsTrue(callSyscall(0x83u, env.rdram.data(), &env.ctx, &env.runtime),
"syscall 0x83 should dispatch");
t.Equals(static_cast<uint32_t>(getRegS32(env.ctx, 2)),
0u,
"FindAddress should return 0 when no matching word exists");
});
});
}