#include "Common.h" #include "System.h" namespace ps2_syscalls { void GsSetCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int interlaced = getRegU32(ctx, 4); // $a0 - 0=non-interlaced, 1=interlaced int videoMode = getRegU32(ctx, 5); // $a1 - 0=NTSC, 1=PAL, 2=VESA, 3=HiVision int frameMode = getRegU32(ctx, 6); // $a2 - 0=field, 1=frame if (runtime) { auto &gs = runtime->memory().gs(); const uint64_t smode2 = (static_cast(interlaced) & 0x1ull) | ((static_cast(frameMode) & 0x1ull) << 1); gs.smode2 = smode2; // Keep CRT1 enabled after the BIOS syscall selects a display mode. if ((gs.pmode & 0x3ull) == 0ull) { gs.pmode |= 0x1ull; } } RUNTIME_LOG("PS2 GsSetCrt: interlaced=" << interlaced << ", videoMode=" << videoMode << ", frameMode=" << frameMode << std::endl); setReturnS32(ctx, 0); } void SetGsCrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { GsSetCrt(rdram, ctx, runtime); } void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint64_t imr = 0; if (runtime) { imr = runtime->memory().gs().imr; } RUNTIME_LOG("PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << " pc=0x" << ctx->pc << " ra=0x" << getRegU32(ctx, 31) << std::dec << std::endl); setReturnU64(ctx, imr); // Return in $v0/$v1 } void iGsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { GsGetIMR(rdram, ctx, runtime); } void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint64_t newImr = GPR_U64(ctx, 4); uint64_t oldImr = 0; if (runtime) { oldImr = runtime->memory().gs().imr; runtime->memory().gs().imr = newImr; } RUNTIME_LOG("PS2 GsPutIMR: " << " new=0x" << newImr << " a0_64=0x" << GPR_U64(ctx, 4) << " a0_32=0x" << getRegU32(ctx, 4) << " a1_32=0x" << getRegU32(ctx, 5) << " pc=0x" << ctx->pc << " ra=0x" << getRegU32(ctx, 31) << std::dec << std::endl); setReturnU64(ctx, oldImr); } void iGsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { GsPutIMR(rdram, ctx, runtime); } void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags) RUNTIME_LOG("PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec); // Do nothing for now. } void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure if (!getMemPtr(rdram, paramAddr)) { std::cerr << "PS2 GetOsdConfigParam error: Invalid parameter address: 0x" << std::hex << paramAddr << std::dec << std::endl; setReturnS32(ctx, -1); return; } uint32_t *param = reinterpret_cast(getMemPtr(rdram, paramAddr)); ensureOsdConfigInitialized(); uint32_t raw; { std::lock_guard lock(g_osd_mutex); raw = g_osd_config_raw; } *param = raw; setReturnS32(ctx, 0); } void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - pointer to parameter structure if (!getConstMemPtr(rdram, paramAddr)) { std::cerr << "PS2 SetOsdConfigParam error: Invalid parameter address: 0x" << std::hex << paramAddr << std::dec << std::endl; setReturnS32(ctx, -1); return; } const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); uint32_t raw = param ? *param : 0; raw = sanitizeOsdConfigRaw(raw); { std::lock_guard lock(g_osd_mutex); g_osd_config_raw = raw; g_osd_config2_raw = makeReadableOsdConfig2RawLocked(); g_osd_config_initialized = true; } setReturnS32(ctx, 0); } void SetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)runtime; const uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - Config2Param* const uint32_t size = getRegU32(ctx, 5); // $a1 - sizeof(Config2Param), normally 4 ensureOsdConfigInitialized(); if (size == 0u) { setReturnS32(ctx, 0); return; } uint32_t raw = 0; { std::lock_guard lock(g_osd_mutex); raw = makeReadableOsdConfig2RawLocked(); } const uint32_t copyBytes = std::min(size, 4u); uint8_t rawBytes[4] = { static_cast(raw & 0xFFu), static_cast((raw >> 8) & 0xFFu), static_cast((raw >> 16) & 0xFFu), static_cast((raw >> 24) & 0xFFu), }; for (uint32_t i = 0; i < copyBytes; ++i) { const uint8_t *src = getConstMemPtr(rdram, paramAddr + i); if (!src) { std::cerr << "PS2 SetOsdConfigParam2 error: Invalid parameter address: 0x" << std::hex << (paramAddr + i) << std::dec << std::endl; setReturnS32(ctx, -1); return; } rawBytes[i] = *src; } raw = static_cast(rawBytes[0]) | (static_cast(rawBytes[1]) << 8) | (static_cast(rawBytes[2]) << 16) | (static_cast(rawBytes[3]) << 24); raw = sanitizeOsdConfig2Raw(raw); { std::lock_guard lock(g_osd_mutex); g_osd_config2_raw = raw; uint32_t version = (g_osd_config_raw >> 13) & 0x7u; uint32_t language = (g_osd_config_raw >> 16) & 0x1Fu; if (copyBytes >= 3u) version = (raw >> 16) & 0xFFu; if (copyBytes >= 4u) language = (raw >> 24) & 0xFFu; g_osd_config_raw = syncOsdConfigRawVersionLanguage(g_osd_config_raw, version, language); g_osd_config_initialized = true; } setReturnS32(ctx, 0); } void GetOsdConfigParam2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)runtime; const uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - Config2Param* const uint32_t size = getRegU32(ctx, 5); // $a1 - sizeof(Config2Param), normally 4 ensureOsdConfigInitialized(); if (size == 0u) { setReturnS32(ctx, 0); return; } uint32_t raw = 0; { std::lock_guard lock(g_osd_mutex); raw = makeReadableOsdConfig2RawLocked(); } const uint8_t rawBytes[4] = { static_cast(raw & 0xFFu), static_cast((raw >> 8) & 0xFFu), static_cast((raw >> 16) & 0xFFu), static_cast((raw >> 24) & 0xFFu), }; const uint32_t copyBytes = std::min(size, 4u); for (uint32_t i = 0; i < copyBytes; ++i) { uint8_t *dst = getMemPtr(rdram, paramAddr + i); if (!dst) { std::cerr << "PS2 GetOsdConfigParam2 error: Invalid parameter address: 0x" << std::hex << (paramAddr + i) << std::dec << std::endl; setReturnS32(ctx, -1); return; } *dst = rawBytes[i]; } setReturnS32(ctx, 0); } void GetRomName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t bufAddr = getRegU32(ctx, 4); // $a0 size_t bufSize = getRegU32(ctx, 5); // $a1 char *hostBuf = reinterpret_cast(getMemPtr(rdram, bufAddr)); const char *romName = "ROMVER 0100"; if (!hostBuf) { std::cerr << "GetRomName error: Invalid buffer address" << std::endl; setReturnS32(ctx, -1); // Error return; } if (bufSize == 0) { setReturnS32(ctx, 0); return; } strncpy(hostBuf, romName, bufSize - 1); hostBuf[bufSize - 1] = '\0'; // returns the length of the string (excluding null?) or error setReturnS32(ctx, (int32_t)strlen(hostBuf)); } void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path const uint32_t secNameAddr = getRegU32(ctx, 5); // $a1 - section name ("all" typically) const uint32_t execDataAddr = getRegU32(ctx, 6); // $a2 - t_ExecData* std::string secName = readGuestCStringBounded(rdram, secNameAddr, kLoadfileArgMaxBytes); if (secName.empty()) { secName = "all"; } const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, secName, execDataAddr); setReturnS32(ctx, ret); } void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - path const uint32_t execDataAddr = getRegU32(ctx, 5); // $a1 - t_ExecData* const int32_t ret = runSifLoadElfPart(rdram, ctx, runtime, pathAddr, "all", execDataAddr); setReturnS32(ctx, ret); } void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { SifLoadElfPart(rdram, ctx, runtime); } void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { // Use the same tracker as SifLoadModule so both APIs return the same module IDs. SifLoadModule(rdram, ctx, runtime); } void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t bufferAddr = getRegU32(ctx, 4); // $a0 const uint32_t argumentSize = getRegU32(ctx, 5); // $a1 const uint32_t argumentAddr = getRegU32(ctx, 6); // $a2 if (!rdram || bufferAddr == 0u) { setReturnS32(ctx, -1); return; } const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr); std::vector arguments; constexpr uint32_t kMaxIopModuleArguments = 64u * 1024u; if (!copyGuestBytesBounded(rdram, argumentAddr, argumentSize, kMaxIopModuleArguments, arguments)) { setReturnS32(ctx, -1); return; } const auto emulated = runtime->loadIopModuleBuffer(bufferAddr, arguments.empty() ? nullptr : arguments.data(), static_cast(arguments.size())); if (emulated.handled) { if (emulated.moduleId <= 0) { setReturnS32(ctx, -1); return; } trackSifModuleLoadExternal(moduleTag, emulated.moduleId); logSifModuleAction("load-buffer-emulated", emulated.moduleId, moduleTag, 1u); setReturnS32(ctx, emulated.moduleId); return; } // Profile mode keeps the existing deterministic synthetic IDs. const int32_t moduleId = trackSifModuleLoad(moduleTag); if (moduleId <= 0) { setReturnS32(ctx, -1); return; } uint32_t refs = 0; { std::lock_guard lock(g_sif_module_mutex); auto it = g_sif_modules_by_id.find(moduleId); if (it != g_sif_modules_by_id.end()) { refs = it->second.refCount; } } logSifModuleAction("load-buffer", moduleId, moduleTag, refs); setReturnS32(ctx, moduleId); } void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId) { // a bit more detail mayber reomve old logic, lets get it more raw std::cerr << "[Syscall TODO]" << " encoded=0x" << std::hex << encodedSyscallId << " v1=0x" << getRegU32(ctx, 3) << " v0=0x" << getRegU32(ctx, 2) << " a0=0x" << getRegU32(ctx, 4) << " a1=0x" << getRegU32(ctx, 5) << " a2=0x" << getRegU32(ctx, 6) << " a3=0x" << getRegU32(ctx, 7) << " pc=0x" << ctx->pc << std::dec << std::endl; const uint32_t v0 = getRegU32(ctx, 2); const uint32_t v1 = getRegU32(ctx, 3); const uint32_t caller_ra = getRegU32(ctx, 31); uint32_t syscallId = encodedSyscallId; if (syscallId == 0u) { syscallId = v1; } std::cerr << "Warning: Unimplemented PS2 syscall called. PC=0x" << std::hex << ctx->pc << ", RA=0x" << caller_ra << ", Encoded=0x" << encodedSyscallId << ", v0=0x" << v0 << ", v1=0x" << v1 << ", Chosen=0x" << syscallId << std::dec << std::endl; std::cerr << " Args: $a0=0x" << std::hex << getRegU32(ctx, 4) << ", $a1=0x" << getRegU32(ctx, 5) << ", $a2=0x" << getRegU32(ctx, 6) << ", $a3=0x" << getRegU32(ctx, 7) << std::dec << std::endl; // Common syscalls: // 0x04: Exit // 0x06: LoadExecPS2 // 0x07: ExecPS2 if (syscallId == 0x04u) { std::cerr << " -> Syscall is Exit(), calling ExitThread stub." << std::endl; ExitThread(rdram, ctx, runtime); return; } static std::mutex s_unknownMutex; static std::unordered_map s_unknownCounts; { std::lock_guard lock(s_unknownMutex); const uint64_t count = ++s_unknownCounts[syscallId]; if (count == 1 || (count % 5000u) == 0u) { std::cerr << " -> Unknown syscallId=0x" << std::hex << syscallId << " hits=" << std::dec << count << std::endl; } } // Bootstrap default: avoid hard-failing loops that probe syscall availability. setReturnS32(ctx, 0); } bool dispatchSyscallOverride(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t handler = 0u; if (!runtime || !ctx || !runtime->findEeSyscallOverride(syscallNumber, handler) || handler == 0u) { return false; } EeScheduler &scheduler = runtime->eeScheduler(); scheduler.bindMainContextForSyscall(*ctx, rdram); if (scheduler.hasInvocation(GuestInvocationKind::SyscallOverride, syscallNumber)) { return false; } if (!runtime->hasFunction(handler)) { setReturnS32(ctx, KE_ERROR); return true; } GuestInvocation invocation{}; invocation.kind = GuestInvocationKind::SyscallOverride; invocation.tag = syscallNumber; invocation.context = *ctx; invocation.context.pc = handler; SET_GPR_U32(&invocation.context, 29, scheduler.invocationStackTop()); SET_GPR_U32(&invocation.context, 31, 0u); invocation.onComplete = [](const R5900Context &completed, R5900Context &parent) { parent.r[2] = completed.r[2]; }; scheduler.invokeCurrent(std::move(invocation)); } static bool tryResolveGuestSyscallMirrorAddr(uint32_t syscallIndex, uint32_t &guestAddr) { const int64_t offsetBytes = static_cast(static_cast(syscallIndex)) * static_cast(sizeof(uint32_t)); const int64_t guestAddr64 = static_cast(kGuestSyscallTablePhysBase) + offsetBytes; if (guestAddr64 < 0 || (guestAddr64 + static_cast(sizeof(uint32_t))) > static_cast(kGuestSyscallMirrorLimit)) { return false; } guestAddr = static_cast(guestAddr64); return true; } static void writeGuestKernelWord(uint8_t *rdram, uint32_t guestAddr, uint32_t value) { if (!rdram) { return; } if (uint8_t *ptr = getMemPtr(rdram, guestAddr)) { std::memcpy(ptr, &value, sizeof(value)); } } void initializeGuestKernelState(uint8_t *rdram, PS2Runtime *runtime) { if (!runtime) { return; } runtime->initializeEeKernelState(rdram); } void SetSyscall(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t syscallIndex = getRegU32(ctx, 4); const uint32_t handler = getRegU32(ctx, 5); runtime->setEeSyscallOverride(rdram, syscallIndex, handler); setReturnS32(ctx, 0); } // 0x3C SetupThread // args: $a0 = gp, $a1 = stack, $a2 = stack_size, $a3 = args, $t0 = root_func void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t gp = getRegU32(ctx, 4); const uint32_t stack = getRegU32(ctx, 5); const int32_t stackSizeSigned = static_cast(getRegU32(ctx, 6)); const uint32_t currentSp = getRegU32(ctx, 29); EeScheduler &scheduler = runtime->eeScheduler(); scheduler.bindMainContextForSyscall(*ctx, rdram); if (gp != 0u) { setRegU32(ctx, 28, gp); } uint32_t sp = currentSp; uint32_t initialStack = 0u; const uint32_t stackSize = stackSizeSigned > 0 ? static_cast(stackSizeSigned) : 0u; if (stack == 0xFFFFFFFFu) { if (stackSizeSigned > 0) { const uint32_t requestedSize = static_cast(stackSizeSigned); if (requestedSize < PS2_RAM_SIZE) { sp = PS2_RAM_SIZE - requestedSize; } else { sp = PS2_RAM_SIZE; } } else { sp = PS2_RAM_SIZE; } } else if (stack != 0u) { if (stackSizeSigned > 0) { sp = stack + static_cast(stackSizeSigned); } else { sp = stack; } } sp &= ~0xFu; if (stack == 0xFFFFFFFFu) { initialStack = sp; } else if (stack != 0u) { initialStack = stack; } scheduler.setupCurrentThread(initialStack, stackSize, getRegU32(ctx, 28)); setReturnU32(ctx, sp); } // 0x3D SetupHeap: returns heap base/start pointer void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t heapBaseRaw = getRegU32(ctx, 4); // $a0 const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) const uint32_t heapBase = (heapBaseRaw + 0xFu) & ~0xFu; // Silent Hill and other games often pass -1 (0xFFFFFFFF) to mean "rest of RAM". static constexpr uint32_t kDefaultGuestHeapEnd = 0x01F00000u; uint32_t heapLimit = kDefaultGuestHeapEnd; if (heapSize != 0u && heapSize != 0xFFFFFFFFu) { const uint64_t candidate = static_cast(heapBase) + static_cast(heapSize); heapLimit = static_cast(std::min(candidate, kDefaultGuestHeapEnd)); } if (heapLimit <= heapBase) { heapLimit = kDefaultGuestHeapEnd; } if (runtime) { runtime->configureGuestHeap(heapBase, heapLimit); PS2_IF_AGRESSIVE_LOGS({ std::cerr << "[SetupHeap]" << " base=0x" << std::hex << heapBaseRaw << " alignedBase=0x" << heapBase << " size=0x" << heapSize << " runtimeBase=0x" << runtime->guestHeapBase() << " runtimeEnd=0x" << runtime->guestHeapEnd() << std::dec << std::endl; }); setReturnU32(ctx, runtime->guestHeapBase()); return; } setReturnU32(ctx, heapBase); } // 0x3E EndOfHeap: commonly returns current heap end; keep it stable for now. void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)rdram; static constexpr uint32_t kDefaultGuestHeapEnd = 0x01F00000u; const uint32_t ret = runtime ? runtime->guestHeapLimit() : kDefaultGuestHeapEnd; setReturnU32(ctx, ret); } void GetMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)rdram; (void)runtime; setReturnU32(ctx, PS2_RAM_SIZE); } void InitTLB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { (void)rdram; // TODO I`m 99% sure we dont need this we could just return ok and should be fine. auto &memory = runtime->memory(); const uint32_t entryCount = static_cast(memory.tlbEntryCount()); for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) { memory.tlbWrite(entryIndex, 0u, 0u, 0u, false); } // Reset basic COP0 TLB bookkeeping to sane post-init values. ctx->cop0_index = 0u; ctx->cop0_random = entryCount > 0u ? (entryCount - 1u) : 0u; ctx->cop0_entrylo0 = 0u; ctx->cop0_entrylo1 = 0u; ctx->cop0_context = 0u; ctx->cop0_pagemask = 0u; ctx->cop0_entryhi = 0u; setReturnS32(ctx, KE_OK); } static inline uint32_t normalizeKernelAlias(uint32_t addr) { if (addr >= 0x80000000u && addr < 0xC0000000u) { return addr & 0x1FFFFFFFu; } return addr; } static uint32_t computeBuiltinFindAddressResult(uint8_t *rdram, uint32_t originalStart, uint32_t originalEnd, uint32_t target) { uint32_t start = (originalStart + 3u) & ~0x3u; uint32_t end = originalEnd & ~0x3u; if (start >= end) { return 0u; } const uint32_t targetNorm = normalizeKernelAlias(target); for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t)) { const uint8_t *entryPtr = getConstMemPtr(rdram, addr); if (!entryPtr) { break; } uint32_t entry = 0u; std::memcpy(&entry, entryPtr, sizeof(entry)); if (entry == target || normalizeKernelAlias(entry) == targetNorm) { return addr; } } return 0u; } struct FindAddressWordSample { uint32_t addr = 0u; uint32_t value = 0u; }; struct FindAddressMatchSample { uint32_t addr = 0u; uint32_t value = 0u; bool aliasOnly = false; }; static void logFindAddressDiagnostics(uint32_t callerPc, uint32_t originalStart, uint32_t originalEnd, uint32_t alignedStart, uint32_t alignedEnd, uint32_t target, uint32_t targetNorm, bool found, uint32_t resultAddr, uint32_t scannedWords, bool allZero, bool aborted, uint32_t abortedAddr, const FindAddressWordSample *firstWords, uint32_t firstWordCount, const FindAddressWordSample *nonZeroWords, uint32_t nonZeroWordCount, const FindAddressMatchSample *matches, uint32_t matchCount) { #if !AGRESSIVE_LOGS return; #else static std::atomic s_findAddressHitLogs{0u}; static std::atomic s_findAddressMissLogs{0u}; constexpr uint32_t kMaxFindAddressHitLogs = 16u; constexpr uint32_t kMaxFindAddressMissLogs = 128u; std::atomic &counter = found ? s_findAddressHitLogs : s_findAddressMissLogs; const uint32_t logIndex = counter.fetch_add(1u, std::memory_order_relaxed); const uint32_t logLimit = found ? kMaxFindAddressHitLogs : kMaxFindAddressMissLogs; if (logIndex >= logLimit) { return; } std::cerr << "[FindAddress:" << (found ? "hit" : "miss") << "]" << " pc=0x" << std::hex << callerPc << " start=0x" << originalStart << " end=0x" << originalEnd << " alignedStart=0x" << alignedStart << " alignedEnd=0x" << alignedEnd << " target=0x" << target << " targetNorm=0x" << targetNorm << " result=0x" << resultAddr << std::dec << " scannedWords=" << scannedWords << " allZero=" << (allZero ? "true" : "false") << " aborted=" << (aborted ? "true" : "false"); if (aborted) { std::cerr << " abortedAddr=0x" << std::hex << abortedAddr << std::dec; } std::cerr << std::endl; std::cerr << " firstWords:"; if (firstWordCount == 0u) { std::cerr << " none"; } else { for (uint32_t i = 0; i < firstWordCount; ++i) { std::cerr << " [0x" << std::hex << firstWords[i].addr << "]=0x" << firstWords[i].value; } std::cerr << std::dec; } std::cerr << std::endl; std::cerr << " nonZeroSample:"; if (nonZeroWordCount == 0u) { std::cerr << " none"; } else { for (uint32_t i = 0; i < nonZeroWordCount; ++i) { std::cerr << " [0x" << std::hex << nonZeroWords[i].addr << "]=0x" << nonZeroWords[i].value; } std::cerr << std::dec; } std::cerr << std::endl; std::cerr << " matches:"; if (matchCount == 0u) { std::cerr << " none"; } else { for (uint32_t i = 0; i < matchCount; ++i) { std::cerr << " [0x" << std::hex << matches[i].addr << "]=0x" << matches[i].value << (matches[i].aliasOnly ? "(alias)" : "(exact)"); } std::cerr << std::dec; } std::cerr << std::endl; #endif } // 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; constexpr uint32_t kFindAddressWordSamples = 8u; constexpr uint32_t kFindAddressMatchSamples = 4u; const uint32_t originalStart = getRegU32(ctx, 4); const uint32_t originalEnd = getRegU32(ctx, 5); const uint32_t target = getRegU32(ctx, 6); const uint32_t targetNorm = normalizeKernelAlias(target); const uint32_t callerPc = ctx->pc; uint32_t start = originalStart; uint32_t end = originalEnd; // Word-scan semantics: align the search window to uint32 boundaries. start = (start + 3u) & ~0x3u; end &= ~0x3u; if (start >= end) { logFindAddressDiagnostics(callerPc, originalStart, originalEnd, start, end, target, targetNorm, false, 0u, 0u, true, false, 0u, nullptr, 0u, nullptr, 0u, nullptr, 0u); setReturnU32(ctx, 0u); return; } FindAddressWordSample firstWords[kFindAddressWordSamples]{}; FindAddressWordSample nonZeroWords[kFindAddressWordSamples]{}; FindAddressMatchSample matches[kFindAddressMatchSamples]{}; uint32_t firstWordCount = 0u; uint32_t nonZeroWordCount = 0u; uint32_t matchCount = 0u; uint32_t scannedWords = 0u; uint32_t resultAddr = 0u; uint32_t abortedAddr = 0u; bool aborted = false; bool allZero = true; bool foundMatch = false; for (uint32_t addr = start; addr < end; addr += sizeof(uint32_t)) { const uint8_t *entryPtr = getConstMemPtr(rdram, addr); if (!entryPtr) { aborted = true; abortedAddr = addr; break; } uint32_t entry = 0; std::memcpy(&entry, entryPtr, sizeof(entry)); ++scannedWords; if (firstWordCount < kFindAddressWordSamples) { firstWords[firstWordCount++] = {addr, entry}; } if (entry != 0u) { allZero = false; if (nonZeroWordCount < kFindAddressWordSamples) { nonZeroWords[nonZeroWordCount++] = {addr, entry}; } } const bool exactMatch = (entry == target); const bool aliasMatch = !exactMatch && (normalizeKernelAlias(entry) == targetNorm); if (exactMatch || aliasMatch) { if (!foundMatch) { resultAddr = addr; foundMatch = true; } if (matchCount < kFindAddressMatchSamples) { matches[matchCount++] = {addr, entry, aliasMatch}; } } } logFindAddressDiagnostics(callerPc, originalStart, originalEnd, start, end, target, targetNorm, foundMatch, resultAddr, scannedWords, allZero, aborted, abortedAddr, firstWords, firstWordCount, nonZeroWords, nonZeroWordCount, matches, matchCount); setReturnU32(ctx, resultAddr); } // QueryBootMode (stub): return 0 for now void QueryBootMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t mode = getRegU32(ctx, 4); ensureBootModeTable(rdram); uint32_t addr = 0; { std::lock_guard lock(g_bootmode_mutex); auto it = g_bootmode_addresses.find(static_cast(mode)); if (it != g_bootmode_addresses.end()) addr = it->second; } setReturnU32(ctx, addr); } // GetThreadTLS (stub): return 0 void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { EeScheduler &ee = runtime->eeScheduler(); ee.bindMainContextForSyscall(*ctx, rdram); GuestThread *info = ee.currentThread(); if (!info) { setReturnU32(ctx, 0); return; } if (info->tlsBase == 0) { info->tlsBase = allocTlsAddr(rdram); } setReturnU32(ctx, info->tlsBase); } void Copy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t dest = getRegU32(ctx, 4); const uint32_t src = getRegU32(ctx, 5); const uint32_t size = getRegU32(ctx, 6); if (rdram && size > 0) { uint8_t *destPtr = getMemPtr(rdram, dest); const uint8_t *srcPtr = getConstMemPtr(rdram, src); if (destPtr && srcPtr) { ps2TraceGuestRangeWrite(rdram, dest, size, "syscallCopy", ctx); std::memcpy(destPtr, srcPtr, size); } } setReturnS32(ctx, 0); } void GetEntryAddress(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { const uint32_t syscallNum = getRegU32(ctx, 4); const uint32_t entryAddr = kGuestSyscallTableGuestBase + (syscallNum * 4u); uint32_t handler = 0; if (const uint8_t *ptr = getConstMemPtr(rdram, entryAddr)) { std::memcpy(&handler, ptr, sizeof(handler)); } setReturnU32(ctx, handler); } // 0x74 RegisterExitHandler (stub): return 0 void RegisterExitHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { uint32_t func = getRegU32(ctx, 4); uint32_t arg = getRegU32(ctx, 5); if (func == 0) { setReturnS32(ctx, -1); return; } EeScheduler &ee = runtime->eeScheduler(); ee.bindMainContextForSyscall(*ctx, rdram); const int tid = ee.currentThreadId(); runtime->addEeExitHandler(tid, func, arg); setReturnS32(ctx, 0); } }