diff --git a/ps2xRuntime/src/lib/ps2_syscalls.cpp b/ps2xRuntime/src/lib/ps2_syscalls.cpp index 67647c59..0e3950e5 100644 --- a/ps2xRuntime/src/lib/ps2_syscalls.cpp +++ b/ps2xRuntime/src/lib/ps2_syscalls.cpp @@ -286,6 +286,9 @@ namespace ps2_syscalls case static_cast(-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; diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl index bba0b959..e835807b 100644 --- a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl @@ -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) +{ + (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; diff --git a/ps2xTest/src/ps2_runtime_kernel_tests.cpp b/ps2xTest/src/ps2_runtime_kernel_tests.cpp index e05e6e1c..1ddbfd5f 100644 --- a/ps2xTest/src/ps2_runtime_kernel_tests.cpp +++ b/ps2xTest/src/ps2_runtime_kernel_tests.cpp @@ -386,5 +386,72 @@ void register_ps2_runtime_kernel_tests() const uint32_t setupSp = static_cast(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(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(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(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(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(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(getRegS32(env.ctx, 2)), + 0u, + "FindAddress should return 0 when no matching word exists"); + }); }); }