From c4555f67231a724b18661287209fdaab5cbd60a3 Mon Sep 17 00:00:00 2001 From: Ranieri Date: Tue, 17 Feb 2026 03:00:59 -0300 Subject: [PATCH] feat: split runtime code in small files to be easy to develop (#56) * feat: split runtime code in small files to be easy to develop * feat: split stubs in inl files * feat: function auto link function treat functions with underscore as same as without underscore * feat: remove underscore prefix from stubs --- ps2xRecomp/src/lib/ps2_recompiler.cpp | 24 +- ps2xRuntime/CMakeLists.txt | 17 +- ps2xRuntime/include/ps2_call_list.h | 56 +- ps2xRuntime/include/ps2_memory.h | 333 + ps2xRuntime/include/ps2_runtime.h | 316 +- ps2xRuntime/include/ps2_runtime_calls.h | 75 +- ps2xRuntime/include/ps2_syscalls.h | 3 - ps2xRuntime/src/lib/ps2_memory.cpp | 3 +- ps2xRuntime/src/lib/ps2_stubs.cpp | 5908 +---------------- ps2xRuntime/src/lib/ps2_syscalls.cpp | 4960 +------------- .../lib/stubs/helpers/ps2_stubs_helpers.inl | 1528 +++++ ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl | 317 + ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl | 889 +++ ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl | 2363 +++++++ ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl | 60 + .../lib/stubs/ps2_stubs_residentEvilCV.inl | 545 ++ .../helpers/ps2_syscalls_helpers_loader.inl | 422 ++ .../helpers/ps2_syscalls_helpers_path.inl | 80 + .../helpers/ps2_syscalls_helpers_runtime.inl | 577 ++ .../helpers/ps2_syscalls_helpers_state.inl | 449 ++ .../src/lib/syscalls/ps2_syscalls_fileio.inl | 450 ++ .../src/lib/syscalls/ps2_syscalls_flags.inl | 649 ++ .../lib/syscalls/ps2_syscalls_interrupt.inl | 105 + .../src/lib/syscalls/ps2_syscalls_rpc.inl | 1089 +++ .../src/lib/syscalls/ps2_syscalls_system.inl | 347 + .../src/lib/syscalls/ps2_syscalls_thread.inl | 764 +++ ps2xRuntime/src/{runner => }/main.cpp | 0 ps2xTest/src/code_generator_tests.cpp | 11 +- 28 files changed, 11112 insertions(+), 11228 deletions(-) create mode 100644 ps2xRuntime/include/ps2_memory.h create mode 100644 ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl create mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl create mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl create mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl create mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl create mode 100644 ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl create mode 100644 ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl create mode 100644 ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_path.inl create mode 100644 ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl create mode 100644 ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl create mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl create mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl create mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl create mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl create mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl create mode 100644 ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl rename ps2xRuntime/src/{runner => }/main.cpp (100%) diff --git a/ps2xRecomp/src/lib/ps2_recompiler.cpp b/ps2xRecomp/src/lib/ps2_recompiler.cpp index cb0f3ea..aec59da 100644 --- a/ps2xRecomp/src/lib/ps2_recompiler.cpp +++ b/ps2xRecomp/src/lib/ps2_recompiler.cpp @@ -499,17 +499,19 @@ namespace ps2recomp } else { - switch (resolveStubTarget(function.name)) + const std::string_view resolvedSyscallName = ps2_runtime_calls::resolveSyscallName(function.name); + const std::string_view resolvedStubName = ps2_runtime_calls::resolveStubName(function.name); + if (!resolvedSyscallName.empty()) + { + stub << "ps2_syscalls::" << resolvedSyscallName << "(rdram, ctx, runtime); "; + } + else if (!resolvedStubName.empty()) + { + stub << "ps2_stubs::" << resolvedStubName << "(rdram, ctx, runtime); "; + } + else { - case StubTarget::Syscall: - stub << "ps2_syscalls::" << function.name << "(rdram, ctx, runtime); "; - break; - case StubTarget::Stub: - stub << "ps2_stubs::" << function.name << "(rdram, ctx, runtime); "; - break; - default: stub << "ps2_stubs::TODO_NAMED(\"" << escapeCStringLiteral(function.name) << "\", rdram, ctx, runtime); "; - break; } } @@ -1039,11 +1041,11 @@ namespace ps2recomp StubTarget PS2Recompiler::resolveStubTarget(const std::string &name) { - if (ps2_runtime_calls::isSyscallName(name)) + if (!ps2_runtime_calls::resolveSyscallName(name).empty()) { return StubTarget::Syscall; } - if (ps2_runtime_calls::isStubName(name)) + if (!ps2_runtime_calls::resolveStubName(name).empty()) { return StubTarget::Stub; } diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index d2f4412..255a56e 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -29,11 +29,20 @@ file(GLOB RUNNER_SRC_FILES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/runner/*.cpp" ) +set(RUNNER_MAIN_CPP "${CMAKE_CURRENT_SOURCE_DIR}/src/runner/main.cpp") +set(ROOT_MAIN_CPP "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp") + +if(EXISTS "${RUNNER_MAIN_CPP}") + list(APPEND RUNNER_SRC_FILES "${RUNNER_MAIN_CPP}") +elseif(EXISTS "${ROOT_MAIN_CPP}") + list(APPEND RUNNER_SRC_FILES "${ROOT_MAIN_CPP}") +endif() + add_executable(ps2EntryRunner ${RUNNER_SRC_FILES} ) -if (MSVC) +if(MSVC) target_compile_options(ps2EntryRunner PRIVATE /FS /Z7) endif() @@ -42,14 +51,14 @@ target_include_directories(ps2_runtime PUBLIC ) target_link_libraries(ps2_runtime PRIVATE raylib) -target_link_libraries(ps2EntryRunner -PRIVATE +target_link_libraries(ps2EntryRunner + PRIVATE ps2_runtime raylib ) # Work around WinAPI vs raylib symbol clash for CloseWindow on x64 -if (MSVC) +if(MSVC) target_link_options(ps2EntryRunner PRIVATE "/FORCE:MULTIPLE") endif() diff --git a/ps2xRuntime/include/ps2_call_list.h b/ps2xRuntime/include/ps2_call_list.h index 7c17e5e..323cbd1 100644 --- a/ps2xRuntime/include/ps2_call_list.h +++ b/ps2xRuntime/include/ps2_call_list.h @@ -71,7 +71,7 @@ X(SifRemoveRpc) \ X(sceSifCallRpc) \ X(sceSifSendCmd) \ - X(_sceRpcGetPacket) \ + X(sceRpcGetPacket) \ \ X(fioOpen) \ X(fioClose) \ @@ -93,7 +93,10 @@ X(SetOsdConfigParam) \ X(GetRomName) \ X(SifLoadElfPart) \ + X(sceSifLoadElf) \ + X(sceSifLoadElfPart) \ X(sceSifLoadModule) \ + X(sceSifLoadModuleBuffer) \ \ X(SetupThread) \ X(QueryBootMode) \ @@ -103,13 +106,12 @@ // Stubs #define PS2_STUB_LIST(X) \ /* Std/Libc */ \ - X(_calloc_r) \ - X(_free_r) \ - X(_malloc_r) \ - X(_malloc_trim_r) \ - X(_mbtowc_r) \ - X(_printf) \ - X(_printf_r) \ + X(calloc_r) \ + X(free_r) \ + X(malloc_r) \ + X(malloc_trim_r) \ + X(mbtowc_r) \ + X(printf_r) \ X(abs) \ X(__ieee754_rem_pio2f) \ X(__kernel_cosf) \ @@ -176,25 +178,25 @@ X(DmaAddr) \ X(Pad_init) \ X(Pad_set) \ - X(_builtin_set_imask) \ - X(_sceCdRI) \ - X(_sceCdRM) \ - X(_sceFsDbChk) \ - X(_sceFsIntrSigSema) \ - X(_sceFsSemExit) \ - X(_sceFsSemInit) \ - X(_sceFsSigSema) \ - X(_sceIDC) \ - X(_sceMpegFlush) \ - X(_sceRpcFreePacket) \ - X(_sceRpcGetFPacket) \ - X(_sceRpcGetFPacket2) \ - X(_sceSDC) \ - X(_sceSifCmdIntrHdlr) \ - X(_sceSifLoadElfPart) \ - X(_sceSifLoadModule) \ - X(_sceSifSendCmd) \ - X(_sceVu0ecossin) \ + X(builtin_set_imask) \ + X(sceCdRI) \ + X(sceCdRM) \ + X(sceFsDbChk) \ + X(sceFsIntrSigSema) \ + X(sceFsSemExit) \ + X(sceFsSemInit) \ + X(sceFsSigSema) \ + X(sceIDC) \ + X(sceMpegFlush) \ + X(sceRpcFreePacket) \ + X(sceRpcGetFPacket) \ + X(sceRpcGetFPacket2) \ + X(sceSDC) \ + X(sceSifCmdIntrHdlr) \ + X(sceSifLoadElfPart) \ + X(sceSifLoadModule) \ + X(sceSifSendCmd) \ + X(sceVu0ecossin) \ X(iopGetArea) \ X(mcCallMessageTypeSe) \ X(mcCheckReadStartConfigFile) \ diff --git a/ps2xRuntime/include/ps2_memory.h b/ps2xRuntime/include/ps2_memory.h new file mode 100644 index 0000000..db6768e --- /dev/null +++ b/ps2xRuntime/include/ps2_memory.h @@ -0,0 +1,333 @@ +#ifndef PS2_MEMORY_H +#define PS2_MEMORY_H + +#include +#include +#include +#include +#include +#if defined(_MSC_VER) + #include +#elif defined(USE_SSE2NEON) + #include "sse2neon.h" +#else + #include // For SSE/AVX instructions + #include // For SSE4.1 instructions +#endif + +constexpr uint32_t PS2_RAM_SIZE = 32u * 1024u * 1024u; // 32MB +constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1u; // Mask for 32MB alignment +constexpr uint32_t PS2_RAM_BASE = 0x00000000; // Physical base of RDRAM +constexpr uint32_t PS2_SCRATCHPAD_BASE = 0x70000000; +constexpr uint32_t PS2_SCRATCHPAD_SIZE = 16u * 1024u; // 16KB +constexpr uint32_t PS2_IO_BASE = 0x10000000; // Base for many I/O regs (Timers, DMAC, INTC) +constexpr uint32_t PS2_IO_SIZE = 0x10000; // 64KB +constexpr uint32_t PS2_BIOS_BASE = 0x1FC00000; // Or BFC00000 depending on KSEG +constexpr uint32_t PS2_BIOS_SIZE = 4u * 1024u * 1024u; // 4MB + +constexpr uint32_t PS2_VU0_CODE_BASE = 0x11000000; // Base address as seen from EE +constexpr uint32_t PS2_VU0_DATA_BASE = 0x11004000; +constexpr uint32_t PS2_VU0_CODE_SIZE = 4u * 1024u; // 4KB Micro Memory +constexpr uint32_t PS2_VU0_DATA_SIZE = 4u * 1024u; // 4KB Data Memory (VU Mem) + +constexpr uint32_t PS2_VU1_CODE_BASE = 0x11008000; +constexpr uint32_t PS2_VU1_DATA_BASE = 0x1100C000; +constexpr uint32_t PS2_VU1_MEM_BASE = PS2_VU1_CODE_BASE; // Alias used by older code paths +constexpr uint32_t PS2_VU1_CODE_SIZE = 16u * 1024u; // 16KB Micro Memory +constexpr uint32_t PS2_VU1_DATA_SIZE = 16u * 1024u; // 16KB Data Memory (VU Mem) + +constexpr uint32_t PS2_GS_BASE = 0x12000000; +constexpr uint32_t PS2_GS_PRIV_REG_BASE = PS2_GS_BASE; // GS Privileged Registers +constexpr uint32_t PS2_GS_PRIV_REG_SIZE = 0x2000; +constexpr size_t PS2_GS_VRAM_SIZE = 4u * 1024u * 1024u; // 4MB GS VRAM + +inline constexpr uint32_t PS2_FIO_O_RDONLY = 0x0001; +inline constexpr uint32_t PS2_FIO_O_WRONLY = 0x0002; +inline constexpr uint32_t PS2_FIO_O_RDWR = 0x0003; +inline constexpr uint32_t PS2_FIO_O_NBLOCK = 0x0010; +inline constexpr uint32_t PS2_FIO_O_APPEND = 0x0100; +inline constexpr uint32_t PS2_FIO_O_CREAT = 0x0200; +inline constexpr uint32_t PS2_FIO_O_TRUNC = 0x0400; +inline constexpr uint32_t PS2_FIO_O_EXCL = 0x0800; +inline constexpr uint32_t PS2_FIO_O_NOWAIT = 0x8000; + +inline constexpr uint32_t PS2_FIO_SEEK_SET = 0; +inline constexpr uint32_t PS2_FIO_SEEK_CUR = 1; +inline constexpr uint32_t PS2_FIO_SEEK_END = 2; + +inline constexpr uint32_t PS2_FIO_S_IFDIR = 0x1000; +inline constexpr uint32_t PS2_FIO_S_IFREG = 0x2000; + +static_assert((PS2_RAM_SIZE & (PS2_RAM_SIZE - 1u)) == 0u, "PS2_RAM_SIZE must be a power of two"); +static_assert(PS2_RAM_MASK == (PS2_RAM_SIZE - 1u), "PS2_RAM_MASK must match PS2_RAM_SIZE"); + +inline std::atomic &ps2ScratchpadHostPtrStorage() +{ + static std::atomic ptr{nullptr}; + return ptr; +} + +inline void ps2SetScratchpadHostPtr(uint8_t *ptr) +{ + ps2ScratchpadHostPtrStorage().store(ptr, std::memory_order_relaxed); +} + +inline uint8_t *ps2GetScratchpadHostPtr() +{ + return ps2ScratchpadHostPtrStorage().load(std::memory_order_relaxed); +} + +inline bool ps2ResolveGuestPointer(uint32_t addr, uint32_t &offset, bool &scratch) +{ + if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) + { + scratch = true; + offset = addr - PS2_SCRATCHPAD_BASE; + return true; + } + + uint32_t phys = 0; + if (addr < 0x20000000u) + { + phys = addr; + } + else if ((addr >= 0x20000000u && addr < 0x40000000u) || + (addr >= 0x80000000u && addr < 0xC0000000u)) + { + phys = addr & 0x1FFFFFFFu; + } + else + { + // Keep legacy runtime behavior for odd upper-bit aliases used by game code. + phys = addr & PS2_RAM_MASK; + } + + if (phys >= PS2_RAM_SIZE) + { + phys &= PS2_RAM_MASK; + } + + scratch = false; + offset = phys; + return true; +} +inline uint8_t *getMemPtr(uint8_t *rdram, uint32_t addr) +{ + if (rdram == nullptr) + { + return nullptr; + } + + uint32_t offset = 0; + bool scratch = false; + if (!ps2ResolveGuestPointer(addr, offset, scratch)) + { + return nullptr; + } + + if (scratch) + { + uint8_t *scratchpad = ps2GetScratchpadHostPtr(); + return scratchpad ? (scratchpad + offset) : nullptr; + } + return rdram + offset; +} + +inline const uint8_t *getConstMemPtr(const uint8_t *rdram, uint32_t addr) +{ + if (rdram == nullptr) + { + return nullptr; + } + + uint32_t offset = 0; + bool scratch = false; + if (!ps2ResolveGuestPointer(addr, offset, scratch)) + { + return nullptr; + } + + if (scratch) + { + const uint8_t *scratchpad = ps2GetScratchpadHostPtr(); + return scratchpad ? (scratchpad + offset) : nullptr; + } + return rdram + offset; +} + +// PS2 GS (Graphics Synthesizer) registers +struct GSRegisters +{ + uint64_t pmode; // Pixel mode + uint64_t smode1; // Sync mode 1 + uint64_t smode2; // Sync mode 2 + uint64_t srfsh; // Refresh control + uint64_t synch1; // Synchronization control 1 + uint64_t synch2; // Synchronization control 2 + uint64_t syncv; // Synchronization control V + uint64_t dispfb1; // Display buffer 1 + uint64_t display1; // Display area 1 + uint64_t dispfb2; // Display buffer 2 + uint64_t display2; // Display area 2 + uint64_t extbuf; // External buffer + uint64_t extdata; // External data + uint64_t extwrite; // External write + uint64_t bgcolor; // Background color + uint64_t csr; // Status + uint64_t imr; // Interrupt mask + uint64_t busdir; // Bus direction + uint64_t siglblid; // Signal label ID +}; +static_assert(sizeof(GSRegisters) == (19u * sizeof(uint64_t)), "GSRegisters layout changed unexpectedly"); +static_assert(alignof(GSRegisters) == alignof(uint64_t), "GSRegisters alignment must remain 64-bit"); + +// PS2 VIF (VPU Interface) registers +struct VIFRegisters +{ + uint32_t stat; // Status + uint32_t fbrst; // VIF Force Break + uint32_t err; // Error status + uint32_t mark; // Interrupt control + uint32_t cycle; // Transfer mode + uint32_t mode; // Mode control + uint32_t num; // Data amount counter + uint32_t mask; // Data mask + uint32_t code; // VIFcode + uint32_t itops; // ITOP save + uint32_t base; // Base address + uint32_t ofst; // Offset + uint32_t tops; // TOPS + uint32_t itop; // ITOP + uint32_t top; // TOP + uint32_t row[4]; // Transfer row data + uint32_t col[4]; // Transfer column data +}; +static_assert(sizeof(VIFRegisters) == (23u * sizeof(uint32_t)), "VIFRegisters layout changed unexpectedly"); + +// PS2 DMA registers +struct DMARegisters +{ + uint32_t chcr; // Channel control + uint32_t madr; // Memory address + uint32_t qwc; // Quadword count + uint32_t tadr; // Tag address + uint32_t asr0; // Address stack 0 + uint32_t asr1; // Address stack 1 + uint32_t sadr; // Source address +}; +static_assert(sizeof(DMARegisters) == (7u * sizeof(uint32_t)), "DMARegisters layout changed unexpectedly"); + +struct JumpTable +{ + uint32_t address = 0; // Base address of the jump table + uint32_t baseRegister = 0; // Register used for index + std::vector targets; // Jump targets +}; + +class PS2Memory +{ +public: + PS2Memory(); + ~PS2Memory(); + + PS2Memory(const PS2Memory &) = delete; + PS2Memory &operator=(const PS2Memory &) = delete; + PS2Memory(PS2Memory &&) = delete; + PS2Memory &operator=(PS2Memory &&) = delete; + + // Initialize memory + bool initialize(size_t ramSize = PS2_RAM_SIZE); + + // Memory access methods + uint8_t *getRDRAM() { return m_rdram; } + uint8_t *getScratchpad() { return m_scratchpad; } + uint8_t *getIOPRAM() { return iop_ram; } + uint64_t dmaStartCount() const { return m_dmaStartCount.load(std::memory_order_relaxed); } + uint64_t gifCopyCount() const { return m_gifCopyCount.load(std::memory_order_relaxed); } + uint64_t gsWriteCount() const { return m_gsWriteCount.load(std::memory_order_relaxed); } + uint64_t vifWriteCount() const { return m_vifWriteCount.load(std::memory_order_relaxed); } + + // Read/write memory + uint8_t read8(uint32_t address); + uint16_t read16(uint32_t address); + uint32_t read32(uint32_t address); + uint64_t read64(uint32_t address); + __m128i read128(uint32_t address); + + void write8(uint32_t address, uint8_t value); + void write16(uint32_t address, uint16_t value); + void write32(uint32_t address, uint32_t value); + void write64(uint32_t address, uint64_t value); + void write128(uint32_t address, __m128i value); + + // TLB handling + uint32_t translateAddress(uint32_t virtualAddress); + bool tlbRead(uint32_t index, uint32_t &vpn, uint32_t &pfn, uint32_t &mask, bool &valid) const; + bool tlbWrite(uint32_t index, uint32_t vpn, uint32_t pfn, uint32_t mask, bool valid); + int32_t tlbProbe(uint32_t vpn) const; + size_t tlbEntryCount() const { return m_tlbEntries.size(); } + + // Hardware register interface + bool writeIORegister(uint32_t address, uint32_t value); + uint32_t readIORegister(uint32_t address); + + // Track code modifications for self-modifying code + void registerCodeRegion(uint32_t start, uint32_t end); + bool isCodeModified(uint32_t address, uint32_t size); + void clearModifiedFlag(uint32_t address, uint32_t size); + + // GS register accessors + GSRegisters &gs() { return gs_regs; } + const GSRegisters &gs() const { return gs_regs; } + uint8_t *getGSVRAM() { return m_gsVRAM; } + const uint8_t *getGSVRAM() const { return m_gsVRAM; } + bool hasSeenGifCopy() const { return m_seenGifCopy; } + // Main RAM (32MB) + uint8_t *m_rdram; + + // Scratchpad memory (16KB) + uint8_t *m_scratchpad; + + // IOP RAM (2MB) + uint8_t *iop_ram; + + bool m_seenGifCopy; + std::atomic m_dmaStartCount{0}; + std::atomic m_gifCopyCount{0}; + std::atomic m_gsWriteCount{0}; + std::atomic m_vifWriteCount{0}; + // I/O registers + std::unordered_map m_ioRegisters; + + // Registers + GSRegisters gs_regs; + uint8_t *m_gsVRAM; + VIFRegisters vif0_regs; + VIFRegisters vif1_regs; + DMARegisters dma_regs[10]; // 10 DMA channels + + // TLB entries + struct TLBEntry + { + uint32_t vpn; + uint32_t pfn; + uint32_t mask; + bool valid; + }; + + std::vector m_tlbEntries; + + struct CodeRegion + { + uint32_t start; + uint32_t end; + std::vector modified; // Bitmap of modified 4-byte blocks + }; + std::vector m_codeRegions; + + bool isAddressInRegion(uint32_t address, const CodeRegion ®ion); + void markModified(uint32_t address, uint32_t size); + bool isScratchpad(uint32_t address) const; +}; + +#endif // PS2_MEMORY_H diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 443b336..216300c 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -21,51 +21,7 @@ #include #include -constexpr uint32_t PS2_RAM_SIZE = 32u * 1024u * 1024u; // 32MB -constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1u; // Mask for 32MB alignment -constexpr uint32_t PS2_RAM_BASE = 0x00000000; // Physical base of RDRAM -constexpr uint32_t PS2_SCRATCHPAD_BASE = 0x70000000; -constexpr uint32_t PS2_SCRATCHPAD_SIZE = 16u * 1024u; // 16KB -constexpr uint32_t PS2_IO_BASE = 0x10000000; // Base for many I/O regs (Timers, DMAC, INTC) -constexpr uint32_t PS2_IO_SIZE = 0x10000; // 64KB -constexpr uint32_t PS2_BIOS_BASE = 0x1FC00000; // Or BFC00000 depending on KSEG -constexpr uint32_t PS2_BIOS_SIZE = 4u * 1024u * 1024u; // 4MB - -constexpr uint32_t PS2_VU0_CODE_BASE = 0x11000000; // Base address as seen from EE -constexpr uint32_t PS2_VU0_DATA_BASE = 0x11004000; -constexpr uint32_t PS2_VU0_CODE_SIZE = 4u * 1024u; // 4KB Micro Memory -constexpr uint32_t PS2_VU0_DATA_SIZE = 4u * 1024u; // 4KB Data Memory (VU Mem) - -constexpr uint32_t PS2_VU1_CODE_BASE = 0x11008000; -constexpr uint32_t PS2_VU1_DATA_BASE = 0x1100C000; -constexpr uint32_t PS2_VU1_MEM_BASE = PS2_VU1_CODE_BASE; // Alias used by older code paths -constexpr uint32_t PS2_VU1_CODE_SIZE = 16u * 1024u; // 16KB Micro Memory -constexpr uint32_t PS2_VU1_DATA_SIZE = 16u * 1024u; // 16KB Data Memory (VU Mem) - -constexpr uint32_t PS2_GS_BASE = 0x12000000; -constexpr uint32_t PS2_GS_PRIV_REG_BASE = PS2_GS_BASE; // GS Privileged Registers -constexpr uint32_t PS2_GS_PRIV_REG_SIZE = 0x2000; -constexpr size_t PS2_GS_VRAM_SIZE = 4u * 1024u * 1024u; // 4MB GS VRAM - -inline constexpr uint32_t PS2_FIO_O_RDONLY = 0x0001; -inline constexpr uint32_t PS2_FIO_O_WRONLY = 0x0002; -inline constexpr uint32_t PS2_FIO_O_RDWR = 0x0003; -inline constexpr uint32_t PS2_FIO_O_NBLOCK = 0x0010; -inline constexpr uint32_t PS2_FIO_O_APPEND = 0x0100; -inline constexpr uint32_t PS2_FIO_O_CREAT = 0x0200; -inline constexpr uint32_t PS2_FIO_O_TRUNC = 0x0400; -inline constexpr uint32_t PS2_FIO_O_EXCL = 0x0800; -inline constexpr uint32_t PS2_FIO_O_NOWAIT = 0x8000; - -inline constexpr uint32_t PS2_FIO_SEEK_SET = 0; -inline constexpr uint32_t PS2_FIO_SEEK_CUR = 1; -inline constexpr uint32_t PS2_FIO_SEEK_END = 2; - -inline constexpr uint32_t PS2_FIO_S_IFDIR = 0x1000; -inline constexpr uint32_t PS2_FIO_S_IFREG = 0x2000; - -static_assert((PS2_RAM_SIZE & (PS2_RAM_SIZE - 1u)) == 0u, "PS2_RAM_SIZE must be a power of two"); -static_assert(PS2_RAM_MASK == (PS2_RAM_SIZE - 1u), "PS2_RAM_MASK must match PS2_RAM_SIZE"); +#include "ps2_memory.h" enum PS2Exception { @@ -386,275 +342,6 @@ inline void ps2TraceGuestRangeWrite(uint8_t *rdram, std::cout << std::endl; } -inline std::atomic &ps2ScratchpadHostPtrStorage() -{ - static std::atomic ptr{nullptr}; - return ptr; -} - -inline void ps2SetScratchpadHostPtr(uint8_t *ptr) -{ - ps2ScratchpadHostPtrStorage().store(ptr, std::memory_order_relaxed); -} - -inline uint8_t *ps2GetScratchpadHostPtr() -{ - return ps2ScratchpadHostPtrStorage().load(std::memory_order_relaxed); -} - -inline bool ps2ResolveGuestPointer(uint32_t addr, uint32_t &offset, bool &scratch) -{ - if (addr >= PS2_SCRATCHPAD_BASE && addr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) - { - scratch = true; - offset = addr - PS2_SCRATCHPAD_BASE; - return true; - } - - uint32_t phys = 0; - if (addr < 0x20000000u) - { - phys = addr; - } - else if ((addr >= 0x20000000u && addr < 0x40000000u) || - (addr >= 0x80000000u && addr < 0xC0000000u)) - { - phys = addr & 0x1FFFFFFFu; - } - else - { - // Keep legacy runtime behavior for odd upper-bit aliases used by game code. - phys = addr & PS2_RAM_MASK; - } - - if (phys >= PS2_RAM_SIZE) - { - phys &= PS2_RAM_MASK; - } - - scratch = false; - offset = phys; - return true; -} -inline uint8_t *getMemPtr(uint8_t *rdram, uint32_t addr) -{ - if (rdram == nullptr) - { - return nullptr; - } - - uint32_t offset = 0; - bool scratch = false; - if (!ps2ResolveGuestPointer(addr, offset, scratch)) - { - return nullptr; - } - - if (scratch) - { - uint8_t *scratchpad = ps2GetScratchpadHostPtr(); - return scratchpad ? (scratchpad + offset) : nullptr; - } - return rdram + offset; -} - -inline const uint8_t *getConstMemPtr(const uint8_t *rdram, uint32_t addr) -{ - if (rdram == nullptr) - { - return nullptr; - } - - uint32_t offset = 0; - bool scratch = false; - if (!ps2ResolveGuestPointer(addr, offset, scratch)) - { - return nullptr; - } - - if (scratch) - { - const uint8_t *scratchpad = ps2GetScratchpadHostPtr(); - return scratchpad ? (scratchpad + offset) : nullptr; - } - return rdram + offset; -} - -// PS2 GS (Graphics Synthesizer) registers -struct GSRegisters -{ - uint64_t pmode; // Pixel mode - uint64_t smode1; // Sync mode 1 - uint64_t smode2; // Sync mode 2 - uint64_t srfsh; // Refresh control - uint64_t synch1; // Synchronization control 1 - uint64_t synch2; // Synchronization control 2 - uint64_t syncv; // Synchronization control V - uint64_t dispfb1; // Display buffer 1 - uint64_t display1; // Display area 1 - uint64_t dispfb2; // Display buffer 2 - uint64_t display2; // Display area 2 - uint64_t extbuf; // External buffer - uint64_t extdata; // External data - uint64_t extwrite; // External write - uint64_t bgcolor; // Background color - uint64_t csr; // Status - uint64_t imr; // Interrupt mask - uint64_t busdir; // Bus direction - uint64_t siglblid; // Signal label ID -}; -static_assert(sizeof(GSRegisters) == (19u * sizeof(uint64_t)), "GSRegisters layout changed unexpectedly"); -static_assert(alignof(GSRegisters) == alignof(uint64_t), "GSRegisters alignment must remain 64-bit"); - -// PS2 VIF (VPU Interface) registers -struct VIFRegisters -{ - uint32_t stat; // Status - uint32_t fbrst; // VIF Force Break - uint32_t err; // Error status - uint32_t mark; // Interrupt control - uint32_t cycle; // Transfer mode - uint32_t mode; // Mode control - uint32_t num; // Data amount counter - uint32_t mask; // Data mask - uint32_t code; // VIFcode - uint32_t itops; // ITOP save - uint32_t base; // Base address - uint32_t ofst; // Offset - uint32_t tops; // TOPS - uint32_t itop; // ITOP - uint32_t top; // TOP - uint32_t row[4]; // Transfer row data - uint32_t col[4]; // Transfer column data -}; -static_assert(sizeof(VIFRegisters) == (23u * sizeof(uint32_t)), "VIFRegisters layout changed unexpectedly"); - -// PS2 DMA registers -struct DMARegisters -{ - uint32_t chcr; // Channel control - uint32_t madr; // Memory address - uint32_t qwc; // Quadword count - uint32_t tadr; // Tag address - uint32_t asr0; // Address stack 0 - uint32_t asr1; // Address stack 1 - uint32_t sadr; // Source address -}; -static_assert(sizeof(DMARegisters) == (7u * sizeof(uint32_t)), "DMARegisters layout changed unexpectedly"); - -struct JumpTable -{ - uint32_t address = 0; // Base address of the jump table - uint32_t baseRegister = 0; // Register used for index - std::vector targets; // Jump targets -}; - -class PS2Memory -{ -public: - PS2Memory(); - ~PS2Memory(); - - PS2Memory(const PS2Memory &) = delete; - PS2Memory &operator=(const PS2Memory &) = delete; - PS2Memory(PS2Memory &&) = delete; - PS2Memory &operator=(PS2Memory &&) = delete; - - // Initialize memory - bool initialize(size_t ramSize = PS2_RAM_SIZE); - - // Memory access methods - uint8_t *getRDRAM() { return m_rdram; } - uint8_t *getScratchpad() { return m_scratchpad; } - uint8_t *getIOPRAM() { return iop_ram; } - uint64_t dmaStartCount() const { return m_dmaStartCount.load(std::memory_order_relaxed); } - uint64_t gifCopyCount() const { return m_gifCopyCount.load(std::memory_order_relaxed); } - uint64_t gsWriteCount() const { return m_gsWriteCount.load(std::memory_order_relaxed); } - uint64_t vifWriteCount() const { return m_vifWriteCount.load(std::memory_order_relaxed); } - - // Read/write memory - uint8_t read8(uint32_t address); - uint16_t read16(uint32_t address); - uint32_t read32(uint32_t address); - uint64_t read64(uint32_t address); - __m128i read128(uint32_t address); - - void write8(uint32_t address, uint8_t value); - void write16(uint32_t address, uint16_t value); - void write32(uint32_t address, uint32_t value); - void write64(uint32_t address, uint64_t value); - void write128(uint32_t address, __m128i value); - - // TLB handling - uint32_t translateAddress(uint32_t virtualAddress); - bool tlbRead(uint32_t index, uint32_t &vpn, uint32_t &pfn, uint32_t &mask, bool &valid) const; - bool tlbWrite(uint32_t index, uint32_t vpn, uint32_t pfn, uint32_t mask, bool valid); - int32_t tlbProbe(uint32_t vpn) const; - size_t tlbEntryCount() const { return m_tlbEntries.size(); } - - // Hardware register interface - bool writeIORegister(uint32_t address, uint32_t value); - uint32_t readIORegister(uint32_t address); - - // Track code modifications for self-modifying code - void registerCodeRegion(uint32_t start, uint32_t end); - bool isCodeModified(uint32_t address, uint32_t size); - void clearModifiedFlag(uint32_t address, uint32_t size); - - // GS register accessors - GSRegisters &gs() { return gs_regs; } - const GSRegisters &gs() const { return gs_regs; } - uint8_t *getGSVRAM() { return m_gsVRAM; } - const uint8_t *getGSVRAM() const { return m_gsVRAM; } - bool hasSeenGifCopy() const { return m_seenGifCopy; } - // Main RAM (32MB) - uint8_t *m_rdram; - - // Scratchpad memory (16KB) - uint8_t *m_scratchpad; - - // IOP RAM (2MB) - uint8_t *iop_ram; - - bool m_seenGifCopy; - std::atomic m_dmaStartCount{0}; - std::atomic m_gifCopyCount{0}; - std::atomic m_gsWriteCount{0}; - std::atomic m_vifWriteCount{0}; - // I/O registers - std::unordered_map m_ioRegisters; - - // Registers - GSRegisters gs_regs; - uint8_t *m_gsVRAM; - VIFRegisters vif0_regs; - VIFRegisters vif1_regs; - DMARegisters dma_regs[10]; // 10 DMA channels - - // TLB entries - struct TLBEntry - { - uint32_t vpn; - uint32_t pfn; - uint32_t mask; - bool valid; - }; - - std::vector m_tlbEntries; - - struct CodeRegion - { - uint32_t start; - uint32_t end; - std::vector modified; // Bitmap of modified 4-byte blocks - }; - std::vector m_codeRegions; - - bool isAddressInRegion(uint32_t address, const CodeRegion ®ion); - void markModified(uint32_t address, uint32_t size); - bool isScratchpad(uint32_t address) const; -}; - class PS2Runtime { public: @@ -816,3 +503,4 @@ private: }; #endif // PS2_RUNTIME_H + diff --git a/ps2xRuntime/include/ps2_runtime_calls.h b/ps2xRuntime/include/ps2_runtime_calls.h index a1fc6cf..50dc884 100644 --- a/ps2xRuntime/include/ps2_runtime_calls.h +++ b/ps2xRuntime/include/ps2_runtime_calls.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include "ps2_call_list.h" @@ -17,27 +19,68 @@ namespace ps2_runtime_calls #undef PS2_STUB_NAME }; + namespace detail + { + template + inline std::string_view findExact( + std::string_view name, + const std::string_view (&entries)[N]) + { + const auto it = std::ranges::find(entries, name); + return (it == std::end(entries)) ? std::string_view{} : *it; + } + + template + inline std::string_view resolveNameWithOptionalLeadingUnderscoreAlias( + std::string_view name, + const std::string_view (&entries)[N]) + { + if (name.empty()) + { + return {}; + } + + if (const std::string_view exact = findExact(name, entries); !exact.empty()) + { + return exact; + } + + if (name.starts_with('_')) + { + return findExact(name.substr(1), entries); + } + + for (auto entry : entries) + { + if (entry.size() == name.size() + 1 && + entry.starts_with('_') && + entry.substr(1) == name) + { + return entry; + } + } + + return {}; + } + } + + inline std::string_view resolveSyscallName(std::string_view name) + { + return detail::resolveNameWithOptionalLeadingUnderscoreAlias(name, kSyscallNames); + } + + inline std::string_view resolveStubName(std::string_view name) + { + return detail::resolveNameWithOptionalLeadingUnderscoreAlias(name, kStubNames); + } + inline bool isSyscallName(std::string_view name) { - for (auto entry : kSyscallNames) - { - if (entry == name) - { - return true; - } - } - return false; + return !resolveSyscallName(name).empty(); } inline bool isStubName(std::string_view name) { - for (auto entry : kStubNames) - { - if (entry == name) - { - return true; - } - } - return false; + return !resolveStubName(name).empty(); } } diff --git a/ps2xRuntime/include/ps2_syscalls.h b/ps2xRuntime/include/ps2_syscalls.h index cb0fd58..a04454b 100644 --- a/ps2xRuntime/include/ps2_syscalls.h +++ b/ps2xRuntime/include/ps2_syscalls.h @@ -17,9 +17,6 @@ namespace ps2_syscalls PS2_SYSCALL_LIST(PS2_DECLARE_SYSCALL) #undef PS2_DECLARE_SYSCALL - void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, uint32_t encodedSyscallId); } diff --git a/ps2xRuntime/src/lib/ps2_memory.cpp b/ps2xRuntime/src/lib/ps2_memory.cpp index e01c107..25825ba 100644 --- a/ps2xRuntime/src/lib/ps2_memory.cpp +++ b/ps2xRuntime/src/lib/ps2_memory.cpp @@ -1,8 +1,9 @@ -#include "ps2_runtime.h" +#include "ps2_memory.h" #include #include #include #include +#include namespace { diff --git a/ps2xRuntime/src/lib/ps2_stubs.cpp b/ps2xRuntime/src/lib/ps2_stubs.cpp index 170f7d8..6468b1f 100644 --- a/ps2xRuntime/src/lib/ps2_stubs.cpp +++ b/ps2xRuntime/src/lib/ps2_stubs.cpp @@ -17,5911 +17,16 @@ #include #include -#ifndef PS2_CD_REMAP_IDX_TO_AFS -#define PS2_CD_REMAP_IDX_TO_AFS 1 -#endif - -namespace -{ - constexpr uint32_t kCdSectorSize = 2048; - constexpr uint32_t kCdPseudoLbnStart = 0x00100000; - - struct CdFileEntry - { - std::filesystem::path hostPath; - uint32_t sizeBytes = 0; - uint32_t baseLbn = 0; - uint32_t sectors = 0; - }; - - std::unordered_map g_cdFilesByKey; - std::unordered_map g_cdLeafIndex; - std::filesystem::path g_cdLeafIndexRoot; - bool g_cdLeafIndexBuilt = false; - uint32_t g_nextPseudoLbn = kCdPseudoLbnStart; - int32_t g_lastCdError = 0; - uint32_t g_cdMode = 0; - uint32_t g_cdStreamingLbn = 0; - bool g_cdInitialized = false; - - constexpr uint32_t kIopHeapBase = 0x01A00000; - constexpr uint32_t kIopHeapLimit = 0x01F00000; - constexpr uint32_t kIopHeapAlign = 16; - uint32_t g_iopHeapNext = kIopHeapBase; - - std::string toLowerAscii(std::string value) - { - std::transform(value.begin(), value.end(), value.begin(), - [](unsigned char c) - { return static_cast(std::tolower(c)); }); - return value; - } - - std::string stripIsoVersionSuffix(std::string value) - { - const std::size_t semicolon = value.find(';'); - if (semicolon == std::string::npos) - { - return value; - } - - bool numericSuffix = semicolon + 1 < value.size(); - for (std::size_t i = semicolon + 1; i < value.size(); ++i) - { - if (!std::isdigit(static_cast(value[i]))) - { - numericSuffix = false; - break; - } - } - - if (numericSuffix) - { - value.erase(semicolon); - } - return value; - } - - std::string normalizePathSeparators(std::string value) - { - std::replace(value.begin(), value.end(), '\\', '/'); - return value; - } - - void trimLeadingSeparators(std::string &value) - { - while (!value.empty() && (value.front() == '/' || value.front() == '\\')) - { - value.erase(value.begin()); - } - } - - std::string normalizeCdPathNoPrefix(std::string path) - { - path = normalizePathSeparators(std::move(path)); - std::string lower = toLowerAscii(path); - if (lower.rfind("cdrom0:", 0) == 0) - { - path = path.substr(7); - } - else if (lower.rfind("cdrom:", 0) == 0) - { - path = path.substr(6); - } - - trimLeadingSeparators(path); - while (!path.empty() && std::isspace(static_cast(path.front()))) - { - path.erase(path.begin()); - } - while (!path.empty() && std::isspace(static_cast(path.back()))) - { - path.pop_back(); - } - path = stripIsoVersionSuffix(std::move(path)); - return path; - } - - std::filesystem::path getCdRootPath() - { - const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - if (!paths.cdRoot.empty()) - { - return paths.cdRoot; - } - if (!paths.elfDirectory.empty()) - { - return paths.elfDirectory; - } - - std::error_code ec; - const std::filesystem::path cwd = std::filesystem::current_path(ec); - return ec ? std::filesystem::path(".") : cwd.lexically_normal(); - } - - std::filesystem::path getCdImagePath() - { - return PS2Runtime::getIoPaths().cdImage; - } - - uint32_t sectorsForBytes(uint64_t byteCount) - { - const uint64_t sectors = (byteCount + (kCdSectorSize - 1)) / kCdSectorSize; - return sectors > 0 ? static_cast(sectors) : 1; - } - - std::string cdPathKey(const std::string &ps2Path) - { - return toLowerAscii(normalizeCdPathNoPrefix(ps2Path)); - } - - std::filesystem::path cdHostPath(const std::string &ps2Path) - { - const std::string normalized = normalizeCdPathNoPrefix(ps2Path); - std::filesystem::path resolved = getCdRootPath(); - if (!normalized.empty()) - { - resolved /= std::filesystem::path(normalized); - } - return resolved.lexically_normal(); - } - - bool resolveCaseInsensitivePath(const std::filesystem::path &root, - const std::filesystem::path &relative, - std::filesystem::path &resolvedOut) - { - std::filesystem::path current = root; - for (const auto &component : relative) - { - const std::filesystem::path direct = current / component; - std::error_code ec; - if (std::filesystem::exists(direct, ec) && !ec) - { - current = direct; - continue; - } - - bool matched = false; - const std::string needle = toLowerAscii(component.string()); - std::error_code iterEc; - for (const auto &entry : std::filesystem::directory_iterator(current, iterEc)) - { - if (iterEc) - { - break; - } - - const std::string candidate = toLowerAscii(entry.path().filename().string()); - if (candidate == needle) - { - current = entry.path(); - matched = true; - break; - } - } - - if (!matched) - { - return false; - } - } - - std::error_code fileEc; - if (std::filesystem::is_regular_file(current, fileEc) && !fileEc) - { - resolvedOut = current; - return true; - } - return false; - } - - void ensureCdLeafIndex(const std::filesystem::path &root) - { - if (g_cdLeafIndexBuilt && g_cdLeafIndexRoot == root) - { - return; - } - - g_cdLeafIndex.clear(); - g_cdLeafIndexRoot = root; - g_cdLeafIndexBuilt = true; - - std::error_code ec; - if (!std::filesystem::exists(root, ec) || ec) - { - return; - } - - for (const auto &entry : std::filesystem::recursive_directory_iterator( - root, std::filesystem::directory_options::skip_permission_denied, ec)) - { - if (ec) - { - break; - } - if (!entry.is_regular_file()) - { - continue; - } - - const std::string leaf = toLowerAscii(entry.path().filename().string()); - g_cdLeafIndex.emplace(leaf, entry.path()); - } - } - - bool registerCdFile(const std::string &ps2Path, CdFileEntry &entryOut) - { - const std::string key = cdPathKey(ps2Path); - if (key.empty()) - { - g_lastCdError = -1; - return false; - } - - auto existing = g_cdFilesByKey.find(key); - if (existing != g_cdFilesByKey.end()) - { - entryOut = existing->second; - g_lastCdError = 0; - return true; - } - - const std::filesystem::path root = getCdRootPath(); - std::filesystem::path path = cdHostPath(ps2Path); - std::error_code ec; - if (!std::filesystem::exists(path, ec) || ec || !std::filesystem::is_regular_file(path, ec)) - { - const std::filesystem::path relative(normalizeCdPathNoPrefix(ps2Path)); - std::filesystem::path resolvedCasePath; - if (resolveCaseInsensitivePath(root, relative, resolvedCasePath)) - { - path = resolvedCasePath; - ec.clear(); - } - else - { - ensureCdLeafIndex(root); - const std::string leaf = toLowerAscii(relative.filename().string()); - auto it = g_cdLeafIndex.find(leaf); - if (it != g_cdLeafIndex.end()) - { - path = it->second; - ec.clear(); - } - else - { - g_lastCdError = -1; - return false; - } - } - } - - const uint64_t sizeBytes = std::filesystem::file_size(path, ec); - if (ec) - { - g_lastCdError = -1; - return false; - } - - CdFileEntry entry; - entry.hostPath = path; - entry.sizeBytes = static_cast(std::min(sizeBytes, 0xFFFFFFFFu)); - entry.baseLbn = g_nextPseudoLbn; - entry.sectors = sectorsForBytes(sizeBytes); - - g_nextPseudoLbn += entry.sectors + 1; - g_cdFilesByKey.emplace(key, entry); - entryOut = entry; - g_lastCdError = 0; - return true; - } - - bool readHostRange(const std::filesystem::path &path, uint64_t offsetBytes, uint8_t *dst, size_t byteCount) - { - if (!dst) - { - g_lastCdError = -1; - return false; - } - if (byteCount == 0) - { - g_lastCdError = 0; - return true; - } - - std::memset(dst, 0, byteCount); - std::ifstream file(path, std::ios::binary); - if (!file.is_open()) - { - g_lastCdError = -1; - return false; - } - - file.seekg(static_cast(offsetBytes), std::ios::beg); - if (!file.good()) - { - g_lastCdError = -1; - return false; - } - - file.read(reinterpret_cast(dst), static_cast(byteCount)); - g_lastCdError = 0; - return true; - } - - bool readCdSectors(uint32_t lbn, uint32_t sectors, uint8_t *dst, size_t byteCount) - { - for (const auto &[key, entry] : g_cdFilesByKey) - { - const uint32_t endLbn = entry.baseLbn + entry.sectors; - if (lbn < entry.baseLbn || lbn >= endLbn) - { - continue; - } - - const uint64_t relativeLbn = static_cast(lbn - entry.baseLbn); - const uint64_t offset = relativeLbn * kCdSectorSize; - return readHostRange(entry.hostPath, offset, dst, byteCount); - } - - const std::filesystem::path cdImage = getCdImagePath(); - if (!cdImage.empty()) - { - const uint64_t offset = static_cast(lbn) * kCdSectorSize; - return readHostRange(cdImage, offset, dst, byteCount); - } - - std::cerr << "sceCdRead unresolved LBN 0x" << std::hex << lbn - << " sectors=" << std::dec << sectors - << " (no mapped file and no configured CD image)" << std::endl; - g_lastCdError = -1; - return false; - } - - bool writeCdSearchResult(uint8_t *rdram, uint32_t fileAddr, const std::string &ps2Path, const CdFileEntry &entry) - { - // sceCdlFILE layout: u32 lsn, u32 size, char name[16], u8 date[8] - uint8_t *fileStruct = getMemPtr(rdram, fileAddr); - if (!fileStruct) - { - return false; - } - - std::array packed{}; - std::memcpy(packed.data() + 0, &entry.baseLbn, sizeof(entry.baseLbn)); - std::memcpy(packed.data() + 4, &entry.sizeBytes, sizeof(entry.sizeBytes)); - - std::filesystem::path leafPath(normalizeCdPathNoPrefix(ps2Path)); - std::string leaf = leafPath.filename().string(); - leaf = stripIsoVersionSuffix(std::move(leaf)); - std::strncpy(reinterpret_cast(packed.data() + 8), leaf.c_str(), 15); - - std::memcpy(fileStruct, packed.data(), packed.size()); - return true; - } - - bool hostFileHasAfsMagic(const std::filesystem::path &path) - { - std::ifstream file(path, std::ios::binary); - if (!file.is_open()) - { - return false; - } - - char magic[4] = {}; - file.read(magic, sizeof(magic)); - if (file.gcount() < 3) - { - return false; - } - - return magic[0] == 'A' && magic[1] == 'F' && magic[2] == 'S'; - } - - bool tryRemapGdInitSearchToAfs(const std::string &ps2Path, - uint32_t callerRa, - const CdFileEntry &foundEntry, - CdFileEntry &entryOut, - std::string &resolvedPathOut) - { -#if !PS2_CD_REMAP_IDX_TO_AFS - { - return false; - } -#endif - - if (callerRa != 0x2d9444u) - { - return false; - } - - std::filesystem::path relative(normalizeCdPathNoPrefix(ps2Path)); - const std::string ext = toLowerAscii(relative.extension().string()); - const std::string leaf = toLowerAscii(relative.filename().string()); - - if (ext == ".idx") - { - if (foundEntry.sizeBytes > (kCdSectorSize * 8u)) - { - return false; - } - - std::filesystem::path afsRelative = relative; - afsRelative.replace_extension(".AFS"); - - CdFileEntry afsEntry; - if (!registerCdFile(afsRelative.generic_string(), afsEntry)) - { - return false; - } - if (!hostFileHasAfsMagic(afsEntry.hostPath)) - { - return false; - } - - entryOut = afsEntry; - resolvedPathOut = afsRelative.generic_string(); - return true; - } - - return false; - } - - uint8_t toBcd(uint32_t value) - { - const uint32_t clamped = value % 100; - return static_cast(((clamped / 10) << 4) | (clamped % 10)); - } - - uint32_t fromBcd(uint8_t value) - { - return static_cast(((value >> 4) & 0x0F) * 10 + (value & 0x0F)); - } - - std::unordered_map g_file_map; - uint32_t g_next_file_handle = 1; // Start file handles > 0 (0 is NULL) - std::mutex g_file_mutex; - - uint32_t generate_file_handle() - { - uint32_t handle = 0; - do - { - handle = g_next_file_handle++; - if (g_next_file_handle == 0) - g_next_file_handle = 1; - } while (handle == 0 || g_file_map.count(handle)); - return handle; - } - - FILE *get_file_ptr(uint32_t handle) - { - if (handle == 0) - return nullptr; - std::lock_guard lock(g_file_mutex); - auto it = g_file_map.find(handle); - return (it != g_file_map.end()) ? it->second : nullptr; - } -} - -namespace -{ - // convert a host pointer within rdram back to a PS2 address - uint32_t hostPtrToPs2Addr(uint8_t *rdram, const void *hostPtr) - { - if (!hostPtr) - return 0; // Handle NULL pointer case - - const uint8_t *ptr_u8 = static_cast(hostPtr); - std::ptrdiff_t offset = ptr_u8 - rdram; - - // Check if is in rdram range - if (offset >= 0 && static_cast(offset) < PS2_RAM_SIZE) - { - return PS2_RAM_BASE + static_cast(offset); - } - else - { - std::cerr << "Warning: hostPtrToPs2Addr failed - host pointer " << hostPtr << " is outside rdram range [" << static_cast(rdram) << ", " << static_cast(rdram + PS2_RAM_SIZE) << ")" << std::endl; - return 0; - } - } -} - -namespace -{ - bool tryReadWordFromRdram(uint8_t *rdram, uint32_t addr, uint32_t &outWord) - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(&outWord, ptr, sizeof(outWord)); - return true; - } - - bool tryReadWordFromGuest(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint32_t &outWord) - { - if (tryReadWordFromRdram(rdram, addr, outWord)) - { - return true; - } - - if (runtime) - { - try - { - PS2Memory &mem = runtime->memory(); - outWord = static_cast(mem.read8(addr + 0u)) | - (static_cast(mem.read8(addr + 1u)) << 8u) | - (static_cast(mem.read8(addr + 2u)) << 16u) | - (static_cast(mem.read8(addr + 3u)) << 24u); - return true; - } - catch (...) - { - return false; - } - } - return false; - } - - bool tryReadByteFromGuest(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint8_t &outByte) - { - const uint8_t *chPtr = getConstMemPtr(rdram, addr); - if (chPtr) - { - outByte = *chPtr; - return true; - } - - if (runtime) - { - try - { - outByte = runtime->memory().read8(addr); - return true; - } - catch (...) - { - return false; - } - } - return false; - } - - bool writeGuestBytes(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, const uint8_t *src, size_t len) - { - if (!src || len == 0) - { - return true; - } - - bool allViaPtrs = true; - for (size_t i = 0; i < len; ++i) - { - const uint64_t guestAddr = static_cast(addr) + i; - if (guestAddr > 0xFFFFFFFFull) - { - return false; - } - uint8_t *dst = getMemPtr(rdram, static_cast(guestAddr)); - if (!dst) - { - allViaPtrs = false; - break; - } - *dst = src[i]; - } - if (allViaPtrs) - { - return true; - } - - if (runtime) - { - try - { - PS2Memory &mem = runtime->memory(); - for (size_t i = 0; i < len; ++i) - { - const uint64_t guestAddr = static_cast(addr) + i; - if (guestAddr > 0xFFFFFFFFull) - { - return false; - } - mem.write8(static_cast(guestAddr), src[i]); - } - return true; - } - catch (...) - { - return false; - } - } - - return false; - } - - std::string readPs2CStringBounded(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, size_t maxLen = 512) - { - std::string out; - if (addr == 0 || maxLen == 0) - { - return out; - } - - out.reserve(std::min(maxLen, 128)); - for (size_t i = 0; i < maxLen; ++i) - { - const uint64_t guestAddr = static_cast(addr) + i; - if (guestAddr > 0xFFFFFFFFull) - { - break; - } - - uint8_t chByte = 0; - if (!tryReadByteFromGuest(rdram, runtime, static_cast(guestAddr), chByte)) - { - break; - } - - const char ch = static_cast(chByte); - if (ch == '\0') - { - break; - } - out.push_back(ch); - } - - return out; - } - - std::string readPs2CStringBounded(uint8_t *rdram, uint32_t addr, size_t maxLen = 512) - { - return readPs2CStringBounded(rdram, nullptr, addr, maxLen); - } - - std::string sanitizeForLog(const std::string &value) - { - std::string out; - out.reserve(value.size()); - for (unsigned char ch : value) - { - if (ch == '\n' || ch == '\r' || ch == '\t' || (ch >= 0x20 && ch < 0x7F)) - { - out.push_back(static_cast(ch)); - } - else - { - out.push_back('.'); - } - } - return out; - } - - class Ps2VarArgCursor - { - public: - Ps2VarArgCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, int fixedArgs) - : m_rdram(rdram), - m_ctx(ctx), - m_runtime(runtime), - m_fixedArgs(fixedArgs), - m_stackBase(getRegU32(ctx, 29) + 0x10) - { - if (m_fixedArgs < 0) - { - m_fixedArgs = 0; - } - m_slotIndex = static_cast(m_fixedArgs); - } - - uint32_t nextU32() - { - const uint32_t value = readWordAtSlot(m_slotIndex); - ++m_slotIndex; - return value; - } - - uint64_t nextU64() - { - // O32 ABI aligns 64-bit variadic values on even 32-bit slots. - if ((m_slotIndex & 1u) != 0u) - { - ++m_slotIndex; - } - const uint64_t low = readWordAtSlot(m_slotIndex); - const uint64_t high = readWordAtSlot(m_slotIndex + 1u); - m_slotIndex += 2u; - return low | (high << 32); - } - - private: - uint32_t readWordAtSlot(uint32_t slotIndex) const - { - if (slotIndex < 4u) - { - // slot0..slot3 -> a0..a3 (r4..r7) - return getRegU32(m_ctx, 4 + static_cast(slotIndex)); - } - - const uint32_t stackIndex = slotIndex - 4u; - const uint32_t stackAddr = m_stackBase + stackIndex * 4u; - uint32_t value = 0; - (void)tryReadWordFromGuest(m_rdram, m_runtime, stackAddr, value); - return value; - } - - uint8_t *m_rdram; - R5900Context *m_ctx; - PS2Runtime *m_runtime; - int m_fixedArgs; - uint32_t m_stackBase; - uint32_t m_slotIndex = 0; - }; - - class Ps2VaListCursor - { - public: - Ps2VaListCursor(uint8_t *rdram, PS2Runtime *runtime, uint32_t vaListAddr) - : m_rdram(rdram), m_runtime(runtime), m_curr(vaListAddr) - { - } - - uint32_t nextU32() - { - uint32_t value = 0; - (void)tryReadWordFromGuest(m_rdram, m_runtime, m_curr, value); - m_curr += 4; - return value; - } - - uint64_t nextU64() - { - m_curr = (m_curr + 7u) & ~7u; - const uint64_t low = nextU32(); - const uint64_t high = nextU32(); - return low | (high << 32); - } - - private: - uint8_t *m_rdram; - PS2Runtime *m_runtime; - uint32_t m_curr = 0; - }; - - template - std::string formatPs2StringCore(uint8_t *rdram, const char *format, NextU32Fn nextU32, NextU64Fn nextU64, ReadStringFn readString) - { - if (!format) - { - return {}; - } - - std::string out; - out.reserve(std::strlen(format) + 32); - const char *p = format; - - while (*p) - { - if (*p != '%') - { - out.push_back(*p++); - continue; - } - - const char *specStart = p++; - if (*p == '%') - { - out.push_back('%'); - ++p; - continue; - } - - int parsedWidth = -1; - int parsedPrecision = -1; - - while (*p && std::strchr("-+ #0", *p)) - { - ++p; - } - - if (*p == '*') - { - parsedWidth = static_cast(nextU32()); - ++p; - } - else - { - if (*p && std::isdigit(static_cast(*p))) - { - parsedWidth = 0; - } - while (*p && std::isdigit(static_cast(*p))) - { - parsedWidth = (parsedWidth * 10) + (*p - '0'); - ++p; - } - } - - if (*p == '.') - { - ++p; - if (*p == '*') - { - parsedPrecision = static_cast(nextU32()); - ++p; - } - else - { - parsedPrecision = 0; - while (*p && std::isdigit(static_cast(*p))) - { - parsedPrecision = (parsedPrecision * 10) + (*p - '0'); - ++p; - } - } - } - if (parsedPrecision < 0) - { - parsedPrecision = -1; - } - (void)parsedWidth; - - enum class LengthMod - { - None, - H, - HH, - L, - LL, - J, - Z, - T, - BigL - }; - - LengthMod length = LengthMod::None; - if (*p == 'h') - { - ++p; - if (*p == 'h') - { - ++p; - length = LengthMod::HH; - } - else - { - length = LengthMod::H; - } - } - else if (*p == 'l') - { - ++p; - if (*p == 'l') - { - ++p; - length = LengthMod::LL; - } - else - { - length = LengthMod::L; - } - } - else if (*p == 'j') - { - ++p; - length = LengthMod::J; - } - else if (*p == 'z') - { - ++p; - length = LengthMod::Z; - } - else if (*p == 't') - { - ++p; - length = LengthMod::T; - } - else if (*p == 'L') - { - ++p; - length = LengthMod::BigL; - } - - if (*p == '\0') - { - out.append(specStart); - break; - } - - const bool use64Integer = (length == LengthMod::LL || length == LengthMod::J); - auto readUnsignedInteger = [&]() -> uint64_t - { - return use64Integer ? nextU64() : static_cast(nextU32()); - }; - auto readSignedInteger = [&]() -> int64_t - { - if (use64Integer) - { - return static_cast(nextU64()); - } - return static_cast(static_cast(nextU32())); - }; - - const char spec = *p++; - switch (spec) - { - case 's': - { - const uint32_t strAddr = nextU32(); - if (strAddr == 0) - { - out.append("(null)"); - } - else - { - std::string str = readString(strAddr); - if (parsedPrecision >= 0 && - str.size() > static_cast(parsedPrecision)) - { - str.resize(static_cast(parsedPrecision)); - } - out.append(str); - } - break; - } - case 'c': - { - const char ch = static_cast(nextU32() & 0xFF); - out.push_back(ch); - break; - } - case 'd': - case 'i': - out.append(std::to_string(readSignedInteger())); - break; - case 'u': - out.append(std::to_string(readUnsignedInteger())); - break; - case 'x': - case 'X': - { - std::ostringstream ss; - if (spec == 'X') - { - ss.setf(std::ios::uppercase); - } - ss << std::hex << readUnsignedInteger(); - out.append(ss.str()); - break; - } - case 'o': - { - std::ostringstream ss; - ss << std::oct << readUnsignedInteger(); - out.append(ss.str()); - break; - } - case 'p': - { - std::ostringstream ss; - ss << "0x" << std::hex << nextU32(); - out.append(ss.str()); - break; - } - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - case 'a': - case 'A': - { - const uint64_t bits = nextU64(); - double value = 0.0; - std::memcpy(&value, &bits, sizeof(value)); - char numBuf[128]; - std::snprintf(numBuf, sizeof(numBuf), "%g", value); - out.append(numBuf); - break; - } - case 'n': - { - // Avoid arbitrary guest memory mutation through %n in stub formatting. - (void)nextU32(); - break; - } - default: - out.append(specStart, p - specStart); - break; - } - } - - return out; - } - - std::string formatPs2StringWithArgs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, const char *format, int fixedArgs) - { - Ps2VarArgCursor cursor(rdram, ctx, runtime, fixedArgs); - return formatPs2StringCore( - rdram, - format, - [&cursor]() - { return cursor.nextU32(); }, - [&cursor]() - { return cursor.nextU64(); }, - [rdram, runtime](uint32_t addr) - { return readPs2CStringBounded(rdram, runtime, addr); }); - } - - std::string formatPs2StringWithVaList(uint8_t *rdram, PS2Runtime *runtime, const char *format, uint32_t vaListAddr) - { - Ps2VaListCursor cursor(rdram, runtime, vaListAddr); - return formatPs2StringCore( - rdram, - format, - [&cursor]() - { return cursor.nextU32(); }, - [&cursor]() - { return cursor.nextU64(); }, - [rdram, runtime](uint32_t addr) - { return readPs2CStringBounded(rdram, runtime, addr); }); - } - - constexpr uint32_t kMaxStubWarningsPerName = 8; - std::unordered_map g_stubWarningCount; - std::mutex g_stubWarningMutex; - constexpr uint32_t kMaxPrintfLogs = 200; - constexpr size_t kMaxFormattedOutputBytes = 4096; - uint32_t g_printfLogCount = 0; - std::mutex g_printfLogMutex; - - constexpr std::array kDmaChannelBases = { - 0x10008000u, 0x10009000u, 0x1000A000u, 0x1000B000u, 0x1000B400u, - 0x1000C000u, 0x1000C400u, 0x1000C800u, 0x1000D000u, 0x1000D400u}; - std::mutex g_dmaStubMutex; - std::unordered_map g_dmaPendingPolls; - uint32_t g_dmaStubLogCount = 0; - constexpr uint32_t kMaxDmaStubLogs = 64; - - bool isKnownDmaChannelBase(uint32_t value) - { - return std::find(kDmaChannelBases.begin(), kDmaChannelBases.end(), value) != kDmaChannelBases.end(); - } - - uint32_t toDmaPhys(uint32_t addr) - { - return addr & 0x1FFFFFFFu; - } - - uint32_t normalizeQwcFromArg(uint32_t value) - { - if (value == 0) - { - return 0; - } - if (value > 0xFFFFu) - { - return std::min((value + 15u) >> 4u, 0xFFFFu); - } - return value & 0xFFFFu; - } - - struct ParsedDmaTag - { - bool valid = false; - uint32_t qwc = 0; - uint32_t id = 0; - uint32_t addr = 0; - }; - - ParsedDmaTag tryParseDmaTag(uint8_t *rdram, uint32_t guestAddr) - { - ParsedDmaTag out; - if (guestAddr == 0) - { - return out; - } - - const uint8_t *ptr = getConstMemPtr(rdram, guestAddr); - if (!ptr) - { - return out; - } - - uint64_t tag = 0; - std::memcpy(&tag, ptr, sizeof(tag)); - out.valid = true; - out.qwc = static_cast(tag & 0xFFFFu); - out.id = static_cast((tag >> 28) & 0x7u); - out.addr = static_cast((tag >> 32) & 0x7FFFFFFFu); - return out; - } - - uint32_t resolveDmaChannelBase(uint8_t *rdram, uint32_t chanArg) - { - if (isKnownDmaChannelBase(chanArg)) - { - return chanArg; - } - if (chanArg < kDmaChannelBases.size()) - { - return kDmaChannelBases[chanArg]; - } - - const uint32_t masked = chanArg & 0xFFFFFF00u; - if (isKnownDmaChannelBase(masked)) - { - return masked; - } - - uint32_t candidate0 = 0; - if (!tryReadWordFromRdram(rdram, chanArg, candidate0)) - { - return 0; - } - if (isKnownDmaChannelBase(candidate0)) - { - return candidate0; - } - - uint32_t candidate1 = 0; - if (!tryReadWordFromRdram(rdram, chanArg + 4u, candidate1)) - { - return 0; - } - if (isKnownDmaChannelBase(candidate1)) - { - return candidate1; - } - - return 0; - } - - int32_t submitDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, bool preferNormalCount) - { - if (!runtime) - { - return -1; - } - - const uint32_t chanArg = getRegU32(ctx, 4); - const uint32_t payloadArg = getRegU32(ctx, 5); - const uint32_t countArg = getRegU32(ctx, 6); - const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); - if (channelBase == 0) - { - return -1; - } - - const uint32_t payloadPhys = toDmaPhys(payloadArg); - uint32_t madr = 0; - uint32_t qwc = 0; - uint32_t tadr = payloadPhys; - uint32_t chcr = 0x00000181u; // DIR=1, TIE=1, STR=1 (normal mode). - - if (preferNormalCount) - { - qwc = normalizeQwcFromArg(countArg); - madr = payloadPhys; - } - else - { - const ParsedDmaTag tag = tryParseDmaTag(rdram, payloadPhys); - if (tag.valid && tag.qwc != 0) - { - qwc = tag.qwc; - switch (tag.id) - { - case 0: // REFE - case 3: // REF - case 4: // REFS - madr = toDmaPhys(tag.addr); - break; - default: - // CNT/NEXT/CALL/RET-style tags carry payload inline after the tag. - madr = toDmaPhys(payloadPhys + 0x10u); - break; - } - } - else - { - // Fall back to chain mode so the runtime DMA path can walk TADR. - chcr = 0x00000185u; // MODE=1 chain, DIR=1, TIE=1, STR=1. - } - } - - PS2Memory &mem = runtime->memory(); - mem.writeIORegister(channelBase + 0x20u, qwc & 0xFFFFu); - mem.writeIORegister(channelBase + 0x10u, madr); - mem.writeIORegister(channelBase + 0x30u, tadr); - mem.writeIORegister(channelBase + 0x00u, chcr); - - std::lock_guard lock(g_dmaStubMutex); - g_dmaPendingPolls[channelBase] = 1; - if (g_dmaStubLogCount < kMaxDmaStubLogs) - { - std::cout << "[sceDmaSend] ch=0x" << std::hex << channelBase - << " madr=0x" << madr - << " qwc=0x" << qwc - << " tadr=0x" << tadr - << " chcr=0x" << chcr << std::dec << std::endl; - ++g_dmaStubLogCount; - } - - return 0; - } - - int32_t submitDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - if (!runtime) - { - return -1; - } - - const uint32_t chanArg = getRegU32(ctx, 4); - const uint32_t mode = getRegU32(ctx, 5); - const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); - if (channelBase == 0) - { - return -1; - } - - bool modelBusy = false; - { - std::lock_guard lock(g_dmaStubMutex); - auto it = g_dmaPendingPolls.find(channelBase); - if (it != g_dmaPendingPolls.end() && it->second > 0) - { - modelBusy = true; - if (mode != 0) - { - --it->second; - if (it->second == 0) - { - g_dmaPendingPolls.erase(it); - } - } - else - { - // Blocking mode: complete immediately in this runtime. - g_dmaPendingPolls.erase(it); - } - } - } - - const uint32_t chcr = runtime->memory().readIORegister(channelBase + 0x00u); - const bool hwBusy = (chcr & 0x100u) != 0; - return ((modelBusy || hwBusy) && mode != 0) ? 1 : 0; - } - -} - -namespace -{ - struct GsGParam - { - uint8_t interlace; - uint8_t omode; - uint8_t ffmode; - uint8_t version; - }; - - struct GsDispEnvMem - { - uint64_t display; - uint64_t dispfb; - }; - - struct GsImageMem - { - uint16_t x; - uint16_t y; - uint16_t width; - uint16_t height; - uint16_t vram_addr; - uint8_t vram_width; - uint8_t psm; - }; - -#pragma pack(push, 1) - struct GsDrawEnvMem - { - uint16_t offset_x; - uint16_t offset_y; - uint16_t clip_x; - uint16_t clip_y; - uint16_t clip_w; - uint16_t clip_h; - uint16_t vram_addr; - uint8_t fbw; - uint8_t psm; - uint16_t vram_x; - uint16_t vram_y; - uint32_t draw_mask; - uint8_t auto_clear; - uint8_t pad[3]; - uint8_t bg_r; - uint8_t bg_g; - uint8_t bg_b; - uint8_t bg_a; - float bg_q; - }; -#pragma pack(pop) - - static_assert(sizeof(GsImageMem) == 12, "GsImageMem size mismatch"); - static_assert(sizeof(GsDrawEnvMem) == 36, "GsDrawEnvMem size mismatch"); - - constexpr uint32_t kGsParamScratchOffset = 0x100; - GsGParam g_gparam{1, 2, 1, 3}; // Default: interlaced NTSC, frame mode. - - static uint64_t makePmode(uint32_t en1, uint32_t en2, uint32_t mmod, uint32_t amod, uint32_t slbg, uint32_t alp) - { - return (static_cast(en1 & 1) << 0) | - (static_cast(en2 & 1) << 1) | - (static_cast(1) << 2) | - (static_cast(mmod & 1) << 5) | - (static_cast(amod & 1) << 6) | - (static_cast(slbg & 1) << 7) | - (static_cast(alp & 0xFF) << 8); - } - - static uint64_t makeDispFb(uint32_t fbp, uint32_t fbw, uint32_t psm, uint32_t dbx, uint32_t dby) - { - return (static_cast(fbp & 0x1FF) << 0) | - (static_cast(fbw & 0x3F) << 9) | - (static_cast(psm & 0x1F) << 15) | - (static_cast(dbx & 0x7FF) << 32) | - (static_cast(dby & 0x7FF) << 43); - } - - static uint64_t makeDisplay(uint32_t dx, uint32_t dy, uint32_t magh, uint32_t magv, uint32_t dw, uint32_t dh) - { - return (static_cast(dx & 0x0FFF) << 0) | - (static_cast(dy & 0x07FF) << 12) | - (static_cast(magh & 0x0F) << 23) | - (static_cast(magv & 0x03) << 27) | - (static_cast(dw & 0x0FFF) << 32) | - (static_cast(dh & 0x07FF) << 44); - } - - static uint32_t readStackU32(uint8_t *rdram, R5900Context *ctx, uint32_t offset) - { - uint32_t sp = getRegU32(ctx, 29); - const uint8_t *ptr = getConstMemPtr(rdram, sp + offset); - if (!ptr) - return 0; - uint32_t value = 0; - std::memcpy(&value, ptr, sizeof(value)); - return value; - } - - static uint32_t bytesForPixels(uint8_t psm, uint32_t pixelCount) - { - const uint64_t pixels = static_cast(pixelCount); - uint64_t bytes = 0; - switch (psm) - { - case 0: // PSMCT32 - case 1: // PSMCT24 (treat as 32) - case 27: // PSMT8H (packed in 32-bit lanes) - case 36: // PSMT4HL (packed in 32-bit lanes) - case 44: // PSMT4HH (packed in 32-bit lanes) - bytes = pixels * 4ull; - break; - case 2: // PSMCT16 - case 10: // PSMCT16S - bytes = pixels * 2ull; - break; - case 19: // PSMT8 - bytes = pixels; - break; - case 20: // PSMT4 - bytes = (pixels + 1ull) / 2ull; - break; - default: - bytes = pixels * 4ull; - break; - } - if (bytes > 0xFFFFFFFFull) - { - return 0xFFFFFFFFu; - } - return static_cast(bytes); - } - - struct GsSetDefImageArgs - { - uint32_t x = 0; - uint32_t y = 0; - uint32_t width = 0; - uint32_t height = 0; - uint32_t vramAddr = 0; - uint32_t vramWidth = 0; - uint32_t psm = 0; - }; - - static GsSetDefImageArgs decodeGsSetDefImageArgs(uint8_t *rdram, R5900Context *ctx) - { - GsSetDefImageArgs decoded{}; - - const uint32_t reg8 = getRegU32(ctx, 8); - const uint32_t reg9 = getRegU32(ctx, 9); - const uint32_t reg10 = getRegU32(ctx, 10); - const uint32_t reg11 = getRegU32(ctx, 11); - - const uint32_t stack0 = readStackU32(rdram, ctx, 16); - const uint32_t stack1 = readStackU32(rdram, ctx, 20); - const uint32_t stack2 = readStackU32(rdram, ctx, 24); - const uint32_t stack3 = readStackU32(rdram, ctx, 28); - - const bool looksLikeCanonicalRegs = (reg10 != 0u || reg11 != 0u); - const bool looksLikeCanonicalStack = (stack2 != 0u || stack3 != 0u); - - if (looksLikeCanonicalRegs || looksLikeCanonicalStack) - { - decoded.vramAddr = getRegU32(ctx, 5); - decoded.vramWidth = getRegU32(ctx, 6); - decoded.psm = getRegU32(ctx, 7); - - if (looksLikeCanonicalRegs) - { - decoded.x = reg8; - decoded.y = reg9; - decoded.width = reg10; - decoded.height = reg11; - } - else - { - decoded.x = stack0; - decoded.y = stack1; - decoded.width = stack2; - decoded.height = stack3; - } - return decoded; - } - - // Legacy code - // a1=x, a2=y, a3=w, stack/reg extension for h/vram/fbw/psm. - decoded.x = getRegU32(ctx, 5); - decoded.y = getRegU32(ctx, 6); - decoded.width = getRegU32(ctx, 7); - decoded.height = stack0 != 0u ? stack0 : reg8; - decoded.vramAddr = stack1 != 0u ? stack1 : reg9; - decoded.vramWidth = stack2 != 0u ? stack2 : reg10; - decoded.psm = stack3 != 0u ? stack3 : reg11; - return decoded; - } - - static bool readGsImage(uint8_t *rdram, uint32_t addr, GsImageMem &out) - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - return false; - std::memcpy(&out, ptr, sizeof(out)); - return true; - } - - static bool writeGsImage(uint8_t *rdram, uint32_t addr, const GsImageMem &img) - { - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - return false; - std::memcpy(ptr, &img, sizeof(img)); - return true; - } - - static bool writeGsDispEnv(uint8_t *rdram, uint32_t addr, uint64_t display, uint64_t dispfb) - { - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - return false; - GsDispEnvMem env{display, dispfb}; - std::memcpy(ptr, &env, sizeof(env)); - return true; - } - - static bool readGsDispEnv(uint8_t *rdram, uint32_t addr, GsDispEnvMem &out) - { - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - return false; - std::memcpy(&out, ptr, sizeof(out)); - return true; - } - - static uint32_t writeGsGParamToScratch(PS2Runtime *runtime) - { - if (!runtime) - return 0; - uint8_t *scratch = runtime->memory().getScratchpad(); - if (!scratch) - return 0; - std::memcpy(scratch + kGsParamScratchOffset, &g_gparam, sizeof(g_gparam)); - return PS2_SCRATCHPAD_BASE + kGsParamScratchOffset; - } -} +#include "stubs/helpers/ps2_stubs_helpers.inl" namespace ps2_stubs { +#include "stubs/ps2_stubs_libc.inl" +#include "stubs/ps2_stubs_ps2.inl" +#include "stubs/ps2_stubs_misc.inl" - void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t size = getRegU32(ctx, 4); // $a0 - const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; - setReturnU32(ctx, guestAddr); - } - - void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 - if (runtime && guestAddr != 0u) - { - runtime->guestFree(guestAddr); - } - } - - void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t count = getRegU32(ctx, 4); // $a0 - const uint32_t size = getRegU32(ctx, 5); // $a1 - const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; - setReturnU32(ctx, guestAddr); - } - - void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t oldGuestAddr = getRegU32(ctx, 4); // $a0 - const uint32_t newSize = getRegU32(ctx, 5); // $a1 - const uint32_t newGuestAddr = runtime ? runtime->guestRealloc(oldGuestAddr, newSize) : 0u; - setReturnU32(ctx, newGuestAddr); - } - - void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - size_t size = getRegU32(ctx, 6); // $a2 - - uint8_t *hostDest = getMemPtr(rdram, destAddr); - const uint8_t *hostSrc = getConstMemPtr(rdram, srcAddr); - - if (hostDest && hostSrc) - { - ::memcpy(hostDest, hostSrc, size); - ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(size), "memcpy", ctx); - } - else - { - std::cerr << "memcpy error: Attempted copy involving non-RDRAM address (or invalid RDRAM address)." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << ", Size: " << size << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - int value = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) - uint32_t size = getRegU32(ctx, 6); // $a2 - - uint8_t *hostDest = getMemPtr(rdram, destAddr); - - if (hostDest) - { - ::memset(hostDest, value, size); - ps2TraceGuestRangeWrite(rdram, destAddr, size, "memset", ctx); - } - else - { - std::cerr << "memset error: Invalid address provided." << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - size_t size = getRegU32(ctx, 6); // $a2 - - uint8_t *hostDest = getMemPtr(rdram, destAddr); - const uint8_t *hostSrc = getConstMemPtr(rdram, srcAddr); - - if (hostDest && hostSrc) - { - ::memmove(hostDest, hostSrc, size); - ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(size), "memmove", ctx); - } - else - { - std::cerr << "memmove error: Attempted move involving potentially invalid RDRAM address." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << ", Size: " << size << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t ptr1Addr = getRegU32(ctx, 4); // $a0 - uint32_t ptr2Addr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - - const uint8_t *hostPtr1 = getConstMemPtr(rdram, ptr1Addr); - const uint8_t *hostPtr2 = getConstMemPtr(rdram, ptr2Addr); - int result = 0; - - if (hostPtr1 && hostPtr2) - { - result = ::memcmp(hostPtr1, hostPtr2, size); - } - else - { - std::cerr << "memcmp error: Invalid address provided." - << " Ptr1: 0x" << std::hex << ptr1Addr << " (host ptr valid: " << (hostPtr1 != nullptr) << ")" - << ", Ptr2: 0x" << ptr2Addr << " (host ptr valid: " << (hostPtr2 != nullptr) << ")" << std::dec - << std::endl; - - result = (hostPtr1 == nullptr) - (hostPtr2 == nullptr); - if (result == 0) - result = 1; // If both null, still different? Or 0? - } - setReturnS32(ctx, result); - } - - void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - - if (hostDest && hostSrc) - { - ::strcpy(hostDest, hostSrc); - ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(::strlen(hostSrc) + 1u), "strcpy", ctx); - } - else - { - std::cerr << "strcpy error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void strncpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - - if (hostDest && hostSrc) - { - ::strncpy(hostDest, hostSrc, size); - ps2TraceGuestRangeWrite(rdram, destAddr, size, "strncpy", ctx); - } - else - { - std::cerr << "strncpy error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void strlen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - size_t len = 0; - - if (hostStr) - { - len = ::strlen(hostStr); - } - else - { - std::cerr << "strlen error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; - } - setReturnU32(ctx, (uint32_t)len); - } - - void strcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t str1Addr = getRegU32(ctx, 4); // $a0 - uint32_t str2Addr = getRegU32(ctx, 5); // $a1 - - const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); - const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); - int result = 0; - - if (hostStr1 && hostStr2) - { - result = ::strcmp(hostStr1, hostStr2); - } - else - { - std::cerr << "strcmp error: Invalid address provided." - << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" - << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec - << std::endl; - // Return non-zero on error, consistent with memcmp error handling - result = (hostStr1 == nullptr) - (hostStr2 == nullptr); - if (result == 0 && hostStr1 == nullptr) - result = 1; // Both null -> treat as different? Or 0? Let's say different. - } - setReturnS32(ctx, result); - } - - void strncmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t str1Addr = getRegU32(ctx, 4); // $a0 - uint32_t str2Addr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - - const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); - const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); - int result = 0; - - if (hostStr1 && hostStr2) - { - result = ::strncmp(hostStr1, hostStr2, size); - } - else - { - std::cerr << "strncmp error: Invalid address provided." - << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" - << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec - << std::endl; - result = (hostStr1 == nullptr) - (hostStr2 == nullptr); - if (result == 0 && hostStr1 == nullptr) - result = 1; // Both null -> different - } - setReturnS32(ctx, result); - } - - void strcat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - - if (hostDest && hostSrc) - { - ::strcat(hostDest, hostSrc); - } - else - { - std::cerr << "strcat error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void strncat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t destAddr = getRegU32(ctx, 4); // $a0 - uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - uint32_t size = getRegU32(ctx, 6); // $a2 - - char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); - const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - - if (hostDest && hostSrc) - { - ::strncat(hostDest, hostSrc, size); - } - else - { - std::cerr << "strncat error: Invalid address provided." - << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" - << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec - << std::endl; - } - - // returns dest pointer ($v0 = $a0) - ctx->r[2] = ctx->r[4]; - } - - void strchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) - - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - char *foundPtr = nullptr; - uint32_t resultAddr = 0; - - if (hostStr) - { - foundPtr = ::strchr(const_cast(hostStr), char_code); - if (foundPtr) - { - resultAddr = hostPtrToPs2Addr(rdram, foundPtr); - } - } - else - { - std::cerr << "strchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; - } - - // returns PS2 address or 0 (NULL) - setReturnU32(ctx, resultAddr); - } - - void strrchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) - - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - char *foundPtr = nullptr; - uint32_t resultAddr = 0; - - if (hostStr) - { - foundPtr = ::strrchr(const_cast(hostStr), char_code); // Use const_cast carefully - if (foundPtr) - { - resultAddr = hostPtrToPs2Addr(rdram, foundPtr); - } - } - else - { - std::cerr << "strrchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; - } - - // returns PS2 address or 0 (NULL) - setReturnU32(ctx, resultAddr); - } - - void strstr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t haystackAddr = getRegU32(ctx, 4); // $a0 - uint32_t needleAddr = getRegU32(ctx, 5); // $a1 - - const char *hostHaystack = reinterpret_cast(getConstMemPtr(rdram, haystackAddr)); - const char *hostNeedle = reinterpret_cast(getConstMemPtr(rdram, needleAddr)); - char *foundPtr = nullptr; - uint32_t resultAddr = 0; - - if (hostHaystack && hostNeedle) - { - foundPtr = ::strstr(const_cast(hostHaystack), hostNeedle); - if (foundPtr) - { - resultAddr = hostPtrToPs2Addr(rdram, foundPtr); - } - } - else - { - std::cerr << "strstr error: Invalid address provided." - << " Haystack: 0x" << std::hex << haystackAddr << " (host ptr valid: " << (hostHaystack != nullptr) << ")" - << ", Needle: 0x" << needleAddr << " (host ptr valid: " << (hostNeedle != nullptr) << ")" << std::dec - << std::endl; - } - - // returns PS2 address or 0 (NULL) - setReturnU32(ctx, resultAddr); - } - - void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t format_addr = getRegU32(ctx, 4); // $a0 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); - if (rendered.size() > 2048) - { - rendered.resize(2048); - } - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - std::cout << "PS2 printf: " << logLine; - std::cout << std::flush; - } - else if (count == kMaxPrintfLogs + 1) - { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; - } - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "printf error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; - } - - // returns the number of characters written, or negative on error. - setReturnS32(ctx, ret); - } - - void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - const uint32_t watchBase = ps2PathWatchPhysAddr(); - const uint32_t watchEnd = watchBase + PS2_PATH_WATCH_BYTES; - const uint32_t dest = str_addr & PS2_RAM_MASK; - const bool touchesWatch = dest < watchEnd && dest >= watchBase; - static uint32_t watchSprintfLogCount = 0; - if (touchesWatch && watchSprintfLogCount < 64u) - { - const uint32_t arg0 = getRegU32(ctx, 6); - const uint32_t arg1 = getRegU32(ctx, 7); - std::cout << "[watch:sprintf] dest=0x" << std::hex << str_addr - << " fmt@0x" << format_addr - << " arg0=0x" << arg0 - << " arg1=0x" << arg1 - << " fmt=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, format_addr, 64)) << "\"" - << " s0=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg0, 64)) << "\"" - << " s1=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg1, 64)) << "\"" - << std::dec << std::endl; - ++watchSprintfLogCount; - } - - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - if (rendered.size() >= kMaxFormattedOutputBytes) - { - rendered.resize(kMaxFormattedOutputBytes - 1); - } - const size_t writeLen = rendered.size() + 1u; - if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), writeLen)) - { - ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(writeLen), "sprintf", ctx); - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "sprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; - } - } - else - { - std::cerr << "sprintf error: Invalid format address provided." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } - - // returns the number of characters written (excluding null), or negative on error. - setReturnS32(ctx, ret); - } - - void snprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - size_t size = getRegU32(ctx, 5); // $a1 - uint32_t format_addr = getRegU32(ctx, 6); // $a2 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 3); - ret = static_cast(rendered.size()); - - if (size > 0) - { - const size_t copyLen = std::min(size - 1, rendered.size()); - std::vector output(copyLen + 1u, 0u); - if (copyLen > 0u) - { - std::memcpy(output.data(), rendered.data(), copyLen); - } - if (writeGuestBytes(rdram, runtime, str_addr, output.data(), output.size())) - { - ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(output.size()), "snprintf", ctx); - } - else - { - std::cerr << "snprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; - ret = -1; - } - } - } - else - { - std::cerr << "snprintf error: Invalid address provided or size is zero." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << ", Size: " << size << std::endl; - } - - // returns the number of characters that *would* have been written - // if size was large enough (excluding null), or negative on error. - setReturnS32(ctx, ret); - } - - void puts(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t strAddr = getRegU32(ctx, 4); // $a0 - const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); - int result = EOF; - - if (hostStr) - { - result = std::puts(hostStr); // std::puts adds a newline - std::fflush(stdout); // Ensure output appears - } - else - { - std::cerr << "puts error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; - } - - // returns non-negative on success, EOF on error. - setReturnS32(ctx, result >= 0 ? 0 : -1); // PS2 might expect 0/-1 rather than EOF - } - - void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - uint32_t modeAddr = getRegU32(ctx, 5); // $a1 - - const char *hostPath = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - const char *hostMode = reinterpret_cast(getConstMemPtr(rdram, modeAddr)); - uint32_t file_handle = 0; - - if (hostPath && hostMode) - { - // TODO: Add translation for PS2 paths like mc0:, host:, cdrom:, etc. - // treating as direct host path - std::cout << "ps2_stub fopen: path='" << hostPath << "', mode='" << hostMode << "'" << std::endl; - FILE *fp = ::fopen(hostPath, hostMode); - if (fp) - { - std::lock_guard lock(g_file_mutex); - file_handle = generate_file_handle(); - g_file_map[file_handle] = fp; - std::cout << " -> handle=0x" << std::hex << file_handle << std::dec << std::endl; - } - else - { - std::cerr << "ps2_stub fopen error: Failed to open '" << hostPath << "' with mode '" << hostMode << "'. Error: " << strerror(errno) << std::endl; - } - } - else - { - std::cerr << "fopen error: Invalid address provided for path or mode." - << " Path: 0x" << std::hex << pathAddr << " (host ptr valid: " << (hostPath != nullptr) << ")" - << ", Mode: 0x" << modeAddr << " (host ptr valid: " << (hostMode != nullptr) << ")" << std::dec - << std::endl; - } - // returns a file handle (non-zero) on success, or NULL (0) on error. - setReturnU32(ctx, file_handle); - } - - void fclose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - int ret = EOF; // Default to error - - if (file_handle != 0) - { - std::lock_guard lock(g_file_mutex); - auto it = g_file_map.find(file_handle); - if (it != g_file_map.end()) - { - FILE *fp = it->second; - ret = ::fclose(fp); - g_file_map.erase(it); - } - else - { - std::cerr << "ps2_stub fclose error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; - } - } - else - { - // Closing NULL handle in Standard C defines this as no-op - ret = 0; - } - - // returns 0 on success, EOF on error. - setReturnS32(ctx, ret); - } - - void fread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) - uint32_t size = getRegU32(ctx, 5); // $a1 (element size) - uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) - uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) - size_t items_read = 0; - - uint8_t *hostPtr = getMemPtr(rdram, ptrAddr); - FILE *fp = get_file_ptr(file_handle); - - if (hostPtr && fp && size > 0 && count > 0) - { - items_read = ::fread(hostPtr, size, count, fp); - } - else - { - std::cerr << "fread error: Invalid arguments." - << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" - << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec - << ", Size: " << size << ", Count: " << count << std::endl; - } - // returns the number of items successfully read. - setReturnU32(ctx, (uint32_t)items_read); - } - - void fwrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) - uint32_t size = getRegU32(ctx, 5); // $a1 (element size) - uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) - uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) - size_t items_written = 0; - - const uint8_t *hostPtr = getConstMemPtr(rdram, ptrAddr); - FILE *fp = get_file_ptr(file_handle); - - if (hostPtr && fp && size > 0 && count > 0) - { - items_written = ::fwrite(hostPtr, size, count, fp); - } - else - { - std::cerr << "fwrite error: Invalid arguments." - << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" - << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec - << ", Size: " << size << ", Count: " << count << std::endl; - } - // returns the number of items successfully written. - setReturnU32(ctx, (uint32_t)items_written); - } - - void fprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - FILE *fp = get_file_ptr(file_handle); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (fp && format_addr != 0) - { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - ret = std::fprintf(fp, "%s", rendered.c_str()); - } - else - { - std::cerr << "fprintf error: Invalid file handle or format address." - << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } - - // returns the number of characters written, or negative on error. - setReturnS32(ctx, ret); - } - - void fseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - long offset = (long)getRegU32(ctx, 5); // $a1 (Note: might need 64-bit for large files?) - int whence = (int)getRegU32(ctx, 6); // $a2 (SEEK_SET, SEEK_CUR, SEEK_END) - int ret = -1; // Default error - - FILE *fp = get_file_ptr(file_handle); - - if (fp) - { - // Ensure whence is valid (0, 1, 2) - if (whence >= 0 && whence <= 2) - { - ret = ::fseek(fp, offset, whence); - } - else - { - std::cerr << "fseek error: Invalid whence value: " << whence << std::endl; - } - } - else - { - std::cerr << "fseek error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; - } - - // returns 0 on success, non-zero on error. - setReturnS32(ctx, ret); - } - - void ftell(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - long ret = -1L; - - FILE *fp = get_file_ptr(file_handle); - - if (fp) - { - ret = ::ftell(fp); - } - else - { - std::cerr << "ftell error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; - } - - // returns the current position, or -1L on error. - if (ret > 0xFFFFFFFFL || ret < 0) - { - setReturnS32(ctx, -1); - } - else - { - setReturnU32(ctx, (uint32_t)ret); - } - } - - void fflush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - int ret = EOF; // Default error - - // If handle is 0 fflush flushes *all* output streams. - if (file_handle == 0) - { - ret = ::fflush(NULL); - } - else - { - FILE *fp = get_file_ptr(file_handle); - if (fp) - { - ret = ::fflush(fp); - } - else - { - std::cerr << "fflush error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; - } - } - // returns 0 on success, EOF on error. - setReturnS32(ctx, ret); - } - - void sqrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::sqrtf(arg); - } - - void sin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::sinf(arg); - } - - void __kernel_sinf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const float x = ctx->f[12]; - const float y = ctx->f[13]; - const int32_t iy = static_cast(getRegU32(ctx, 4)); - ctx->f[0] = ::sinf(x + (iy != 0 ? y : 0.0f)); - } - - void cos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::cosf(arg); - } - - void __kernel_cosf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const float x = ctx->f[12]; - const float y = ctx->f[13]; - ctx->f[0] = ::cosf(x + y); - } - - void __ieee754_rem_pio2f(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const float x = ctx->f[12]; - constexpr float kPi = 3.14159265358979323846f; - constexpr float kHalfPi = kPi * 0.5f; - constexpr float kInvHalfPi = 2.0f / kPi; - const int32_t n = static_cast(std::nearbyintf(x * kInvHalfPi)); - const float y0 = x - (static_cast(n) * kHalfPi); - const float y1 = 0.0f; - - const uint32_t yOutAddr = getRegU32(ctx, 4); - if (float *yOut0 = reinterpret_cast(getMemPtr(rdram, yOutAddr)); yOut0) - { - *yOut0 = y0; - } - if (float *yOut1 = reinterpret_cast(getMemPtr(rdram, yOutAddr + 4)); yOut1) - { - *yOut1 = y1; - } - - setReturnS32(ctx, n); - } - - void tan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::tanf(arg); - } - - void atan2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float y = ctx->f[12]; - float x = ctx->f[14]; - ctx->f[0] = ::atan2f(y, x); - } - - void pow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float base = ctx->f[12]; - float exp = ctx->f[14]; - ctx->f[0] = ::powf(base, exp); - } - - void exp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::expf(arg); - } - - void log(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::logf(arg); - } - - void log10(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::log10f(arg); - } - - void ceil(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::ceilf(arg); - } - - void floor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::floorf(arg); - } - - void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - float arg = ctx->f[12]; - ctx->f[0] = ::fabsf(arg); - } - - void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t lbn = getRegU32(ctx, 4); // $a0 - logical block number - uint32_t sectors = getRegU32(ctx, 5); // $a1 - sector count - uint32_t buf = getRegU32(ctx, 6); // $a2 - destination buffer in RDRAM - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - if (bytes > 0) - { - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } - } - - uint8_t *dst = rdram + offset; - bool ok = true; - if (bytes > 0) - { - ok = readCdSectors(lbn, sectors, dst, bytes); - if (!ok) - { - std::memset(dst, 0, bytes); - } - } - - if (ok) - { - g_cdStreamingLbn = lbn + sectors; - setReturnS32(ctx, 1); // command accepted/success - } - else - { - setReturnS32(ctx, 0); - } - } - - void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); // 0 = completed/not busy - } - - void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, g_lastCdError); - } - - void njSetBorderColor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njSetBorderColor" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njSetTextureMemorySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njSetTextureMemorySize" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njInitVertexBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njInitVertexBuffer" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njTextureShadingMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njTextureShadingMode" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njInitView(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njInitView" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njSetAspect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njSetAspect" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njInitSystem(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njInitSystem" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njInitPrint(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njInitPrint" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njPolygonCullingMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njPolygonCullingMode" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njSetView(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njSetView" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njGetMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njGetMatrix" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njInitTexture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njInitTexture" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njInitTextureBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njInitTextureBuffer" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njSetPaletteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njSetPaletteMode" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njClipZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njClipZ" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void syRtcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syRtcInit" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void _builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub _builtin_set_imask" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void syFree(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syFree" << std::endl; - ++logCount; - } - - const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 - if (runtime && guestAddr != 0u) - { - runtime->guestFree(guestAddr); - } - - setReturnS32(ctx, 0); - } - - void syMalloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t requestedSize = getRegU32(ctx, 4); // $a0 - uint32_t resultAddr = 0u; - - if (runtime && requestedSize != 0u) - { - // Match game expectation for allocator alignment while keeping pointers in EE RAM. - resultAddr = runtime->guestMalloc(requestedSize, 64u); - } - - static int logCount = 0; - if (logCount < 16) - { - std::cout << "ps2_stub syMalloc" - << " size=0x" << std::hex << requestedSize - << " -> 0x" << resultAddr - << std::dec << std::endl; - ++logCount; - } - - setReturnU32(ctx, resultAddr); - } - - void InitSdcParameter(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitSdcParameter" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void Ps2_pad_actuater(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub Ps2_pad_actuater" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void syMallocInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (runtime) - { - const uint32_t heapBase = getRegU32(ctx, 4); // $a0 - const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) - - constexpr uint32_t kHeapBaseFloor = 0x00100000u; - uint32_t normalizedBase = heapBase; - if (normalizedBase >= 0x80000000u && normalizedBase < 0xC0000000u) - { - normalizedBase &= 0x1FFFFFFFu; - } - else if (normalizedBase >= PS2_RAM_SIZE) - { - normalizedBase &= PS2_RAM_MASK; - } - - const bool suspiciousKsegBase = (heapBase & 0xE0000000u) == 0x80000000u && normalizedBase < kHeapBaseFloor; - if (normalizedBase == 0u || suspiciousKsegBase) - { - // Keep the ELF-driven suggestion instead of collapsing heap to low memory. - normalizedBase = runtime->guestHeapBase(); - } - - // Treat absurd "size" values as unspecified limit. - uint32_t heapLimit = 0u; - if (heapSize != 0u && heapSize <= PS2_RAM_SIZE && normalizedBase < PS2_RAM_SIZE) - { - const uint64_t candidateLimit = static_cast(normalizedBase) + static_cast(heapSize); - heapLimit = static_cast(std::min(candidateLimit, PS2_RAM_SIZE)); - } - runtime->configureGuestHeap(normalizedBase, heapLimit); - if (logCount < 8) - { - std::cout << "ps2_stub syMallocInit" - << " reqBase=0x" << std::hex << heapBase - << " reqSize=0x" << heapSize - << " normBase=0x" << normalizedBase - << " reqLimit=0x" << heapLimit - << " finalBase=0x" << runtime->guestHeapBase() - << " finalEnd=0x" << runtime->guestHeapEnd() - << std::dec << std::endl; - ++logCount; - } - } - else if (logCount < 8) - { - std::cout << "ps2_stub syMallocInit" << std::endl; - ++logCount; - } - - setReturnS32(ctx, 0); - } - - void syHwInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syHwInit" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void syHwInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub syHwInit2" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void InitGdSystemEx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitGdSystemEx" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void pdInitPeripheral(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub pdInitPeripheral" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njSetVertexBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njSetVertexBuffer" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void njPrintSize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub njPrintSize" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void pdGetPeripheral(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub pdGetPeripheral" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub Ps2SwapDBuff" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void InitReadKeyEx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub InitReadKeyEx" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void SetRepeatKeyTimer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub SetRepeatKeyTimer" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void StopFxProgram(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub StopFxProgram" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sndr_trans_func (noop)" << std::endl; - ++logCount; - } - - // For now just clear the snd busy flag used by sdMultiUnitDownload/SysServer loops. - constexpr uint32_t kSndBusyAddr = 0x01E0E170; - if (rdram) - { - uint32_t offset = kSndBusyAddr & PS2_RAM_MASK; - if (offset + sizeof(uint32_t) <= PS2_RAM_SIZE) - { - *reinterpret_cast(rdram + offset) = 0; - } - } - - setReturnS32(ctx, 0); - } - - void sdDrvInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sdDrvInit (noop)" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void ADXF_LoadPartitionNw(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub ADXF_LoadPartitionNw (noop)" << std::endl; - ++logCount; - } - // Return success to keep the ADX partition setup moving. - setReturnS32(ctx, 0); - } - - void sdSndStopAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sdSndStopAll" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void sdSysFinish(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sdSysFinish" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void ADXT_Init(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub ADXT_Init" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void ADXT_SetNumRetry(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub ADXT_SetNumRetry" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub cvFsSetDefDev" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void _calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t count = getRegU32(ctx, 5); // $a1 - const uint32_t size = getRegU32(ctx, 6); // $a2 - const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; - setReturnU32(ctx, guestAddr); - } - - void _free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t guestAddr = getRegU32(ctx, 5); // $a1 - if (runtime && guestAddr != 0u) - { - runtime->guestFree(guestAddr); - } - } - - void _malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t size = getRegU32(ctx, 5); // $a1 - const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; - setReturnU32(ctx, guestAddr); - } - - void _malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void _mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_mbtowc_r", rdram, ctx, runtime); - } - - void _printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - printf(rdram, ctx, runtime); - } - - void _printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); - if (rendered.size() > 2048) - { - rendered.resize(2048); - } - const std::string logLine = sanitizeForLog(rendered); - uint32_t count = 0; - { - std::lock_guard lock(g_printfLogMutex); - count = ++g_printfLogCount; - } - if (count <= kMaxPrintfLogs) - { - std::cout << "PS2 printf: " << logLine; - std::cout << std::flush; - } - else if (count == kMaxPrintfLogs + 1) - { - std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; - } - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "_printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; - } - - setReturnS32(ctx, ret); - } - - void _sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceCdRI", rdram, ctx, runtime); - } - - void _sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceCdRM", rdram, ctx, runtime); - } - - void _sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceFsDbChk", rdram, ctx, runtime); - } - - void _sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceFsIntrSigSema", rdram, ctx, runtime); - } - - void _sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceFsSemExit", rdram, ctx, runtime); - } - - void _sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceFsSemInit", rdram, ctx, runtime); - } - - void _sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceFsSigSema", rdram, ctx, runtime); - } - - void _sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceIDC", rdram, ctx, runtime); - } - - void _sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceMpegFlush", rdram, ctx, runtime); - } - - void _sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceRpcFreePacket", rdram, ctx, runtime); - } - - void _sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceRpcGetFPacket", rdram, ctx, runtime); - } - - void _sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceRpcGetFPacket2", rdram, ctx, runtime); - } - - void _sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceSDC", rdram, ctx, runtime); - } - - void _sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceSifCmdIntrHdlr", rdram, ctx, runtime); - } - - void _sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifLoadElfPart(rdram, ctx, runtime); - } - - void _sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceSifLoadModule", rdram, ctx, runtime); - } - - void _sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceSifSendCmd", rdram, ctx, runtime); - } - - void _sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("_sceVu0ecossin", rdram, ctx, runtime); - } - - void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("abs", rdram, ctx, runtime); - } - - void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("atan", rdram, ctx, runtime); - } - - void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioClose(rdram, ctx, runtime); - } - - void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("DmaAddr", rdram, ctx, runtime); - } - - void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("exit", rdram, ctx, runtime); - } - - void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t statAddr = getRegU32(ctx, 5); - if (uint8_t *statBuf = getMemPtr(rdram, statAddr)) - { - std::memset(statBuf, 0, 128); - setReturnS32(ctx, 0); - return; - } - setReturnS32(ctx, -1); - } - - void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("getpid", rdram, ctx, runtime); - } - - void iopGetArea(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("iopGetArea", rdram, ctx, runtime); - } - - void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioLseek(rdram, ctx, runtime); - } - - void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCallMessageTypeSe", rdram, ctx, runtime); - } - - void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCheckReadStartConfigFile", rdram, ctx, runtime); - } - - void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCheckReadStartSaveFile", rdram, ctx, runtime); - } - - void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCheckWriteStartConfigFile", rdram, ctx, runtime); - } - - void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCheckWriteStartSaveFile", rdram, ctx, runtime); - } - - void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCreateConfigInit", rdram, ctx, runtime); - } - - void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCreateFileSelectWindow", rdram, ctx, runtime); - } - - void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCreateIconInit", rdram, ctx, runtime); - } - - void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcCreateSaveFileInit", rdram, ctx, runtime); - } - - void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDispFileName", rdram, ctx, runtime); - } - - void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDispFileNumber", rdram, ctx, runtime); - } - - void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDisplayFileSelectWindow", rdram, ctx, runtime); - } - - void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDisplaySelectFileInfo", rdram, ctx, runtime); - } - - void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDisplaySelectFileInfoMesCount", rdram, ctx, runtime); - } - - void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDispWindowCurSol", rdram, ctx, runtime); - } - - void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcDispWindowFoundtion", rdram, ctx, runtime); - } - - void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mceGetInfoApdx", rdram, ctx, runtime); - } - - void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mceIntrReadFixAlign", rdram, ctx, runtime); - } - - void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mceStorePwd", rdram, ctx, runtime); - } - - void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetConfigCapacitySize", rdram, ctx, runtime); - } - - void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetFileSelectWindowCursol", rdram, ctx, runtime); - } - - void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetFreeCapacitySize", rdram, ctx, runtime); - } - - void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetIconCapacitySize", rdram, ctx, runtime); - } - - void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetIconFileCapacitySize", rdram, ctx, runtime); - } - - void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetPortSelectDirInfo", rdram, ctx, runtime); - } - - void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetSaveFileCapacitySize", rdram, ctx, runtime); - } - - void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcGetStringEnd", rdram, ctx, runtime); - } - - void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcMoveFileSelectWindowCursor", rdram, ctx, runtime); - } - - void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcNewCreateConfigFile", rdram, ctx, runtime); - } - - void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcNewCreateIcon", rdram, ctx, runtime); - } - - void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcNewCreateSaveFile", rdram, ctx, runtime); - } - - void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcReadIconData", rdram, ctx, runtime); - } - - void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcReadStartConfigFile", rdram, ctx, runtime); - } - - void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcReadStartSaveFile", rdram, ctx, runtime); - } - - void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcSelectFileInfoInit", rdram, ctx, runtime); - } - - void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcSelectSaveFileCheck", rdram, ctx, runtime); - } - - void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcSetFileSelectWindowCursol", rdram, ctx, runtime); - } - - void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcSetFileSelectWindowCursolInit", rdram, ctx, runtime); - } - - void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcSetStringSaveFile", rdram, ctx, runtime); - } - - void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcSetTyepWriteMode", rdram, ctx, runtime); - } - - void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcWriteIconData", rdram, ctx, runtime); - } - - void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcWriteStartConfigFile", rdram, ctx, runtime); - } - - void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("mcWriteStartSaveFile", rdram, ctx, runtime); - } - - void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("memchr", rdram, ctx, runtime); - } - - void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioOpen(rdram, ctx, runtime); - } - - void Pad_init(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void Pad_set(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("Pad_set", rdram, ctx, runtime); - } - - void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("rand", rdram, ctx, runtime); - } - - void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioRead(rdram, ctx, runtime); - } - - void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 2); - } - - void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - // SCECdPS2DVD - setReturnS32(ctx, 0x14); - } - - void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnU32(ctx, g_cdStreamingLbn); - } - - void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t tocAddr = getRegU32(ctx, 4); - if (uint8_t *toc = getMemPtr(rdram, tocAddr)) - { - std::memset(toc, 0, 1024); - } - setReturnS32(ctx, 1); - } - - void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_cdInitialized = true; - g_lastCdError = 0; - setReturnS32(ctx, 1); - } - - void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t lsn = getRegU32(ctx, 4); - uint32_t posAddr = getRegU32(ctx, 5); - uint8_t *pos = getMemPtr(rdram, posAddr); - if (!pos) - { - setReturnS32(ctx, 0); - return; - } - - uint32_t adjusted = lsn + 150; - const uint32_t minutes = adjusted / (60 * 75); - adjusted %= (60 * 75); - const uint32_t seconds = adjusted / 75; - const uint32_t sectors = adjusted % 75; - - pos[0] = toBcd(minutes); - pos[1] = toBcd(seconds); - pos[2] = toBcd(sectors); - pos[3] = 0; - setReturnS32(ctx, 1); - } - - void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_cdMode = getRegU32(ctx, 4); - setReturnS32(ctx, 1); - } - - void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 2); - } - - void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t posAddr = getRegU32(ctx, 4); - const uint8_t *pos = getConstMemPtr(rdram, posAddr); - if (!pos) - { - setReturnS32(ctx, -1); - return; - } - - const uint32_t minutes = fromBcd(pos[0]); - const uint32_t seconds = fromBcd(pos[1]); - const uint32_t sectors = fromBcd(pos[2]); - const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; - const int32_t lsn = static_cast(absolute) - 150; - setReturnS32(ctx, lsn); - } - - void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t chainAddr = getRegU32(ctx, 4); - bool ok = true; - - for (int i = 0; i < 64; ++i) - { - uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); - if (!entry) - { - ok = false; - break; - } - - const uint32_t lbn = entry[0]; - const uint32_t sectors = entry[1]; - const uint32_t buf = entry[2]; - if (lbn == 0xFFFFFFFFu || sectors == 0) - { - break; - } - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } - - if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) - { - ok = false; - break; - } - - g_cdStreamingLbn = lbn + sectors; - } - - setReturnS32(ctx, ok ? 1 : 0); - } - - void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t clockAddr = getRegU32(ctx, 4); - uint8_t *clockData = getMemPtr(rdram, clockAddr); - if (!clockData) - { - setReturnS32(ctx, 0); - return; - } - - std::time_t now = std::time(nullptr); - std::tm localTm{}; -#ifdef _WIN32 - localtime_s(&localTm, &now); -#else - localtime_r(&now, &localTm); -#endif - - // sceCdCLOCK format (BCD fields). - clockData[0] = 0; - clockData[1] = toBcd(static_cast(localTm.tm_sec)); - clockData[2] = toBcd(static_cast(localTm.tm_min)); - clockData[3] = toBcd(static_cast(localTm.tm_hour)); - clockData[4] = 0; - clockData[5] = toBcd(static_cast(localTm.tm_mday)); - clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); - clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); - setReturnS32(ctx, 1); - } - - void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - sceCdRead(rdram, ctx, runtime); - } - - void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t fileAddr = getRegU32(ctx, 4); - uint32_t pathAddr = getRegU32(ctx, 5); - const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); - const std::string normalizedPath = normalizeCdPathNoPrefix(path); - static uint32_t traceCount = 0; - const uint32_t callerRa = getRegU32(ctx, 31); - const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); - if (shouldTrace) - { - std::cout << "[sceCdSearchFile] pc=0x" << std::hex << ctx->pc - << " ra=0x" << callerRa - << " file=0x" << fileAddr - << " pathAddr=0x" << pathAddr - << " path=\"" << sanitizeForLog(path) << "\"" - << std::dec << std::endl; - } - ++traceCount; - - if (path.empty()) - { - static uint32_t emptyPathCount = 0; - if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) - { - std::ostringstream preview; - preview << std::hex; - for (uint32_t i = 0; i < 16; ++i) - { - const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); - preview << (i == 0 ? "" : " ") << static_cast(byte); - } - std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr - << " preview=" << preview.str() - << " ra=0x" << callerRa << std::dec << std::endl; - } - ++emptyPathCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - if (normalizedPath.empty()) - { - static uint32_t emptyNormalizedCount = 0; - if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) - { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (normalized path is empty, root: " << getCdRootPath().string() << ")" - << std::endl; - } - ++emptyNormalizedCount; - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - CdFileEntry entry; - bool found = registerCdFile(path, entry); - CdFileEntry resolvedEntry = entry; - std::string resolvedPath; - bool usedRemapFallback = false; - - // Remap is fallback-only: if the requested .IDX exists, keep it. - // This avoids feeding AFS payload sectors to code that expects IDX metadata. - if (!found) - { - const CdFileEntry missingEntry{}; - if (tryRemapGdInitSearchToAfs(path, callerRa, missingEntry, resolvedEntry, resolvedPath)) - { - found = true; - usedRemapFallback = true; - } - } - - if (!found) - { - static std::string lastFailedPath; - static uint32_t samePathFailCount = 0; - if (path == lastFailedPath) - { - ++samePathFailCount; - } - else - { - lastFailedPath = path; - samePathFailCount = 1; - } - - if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) - { - std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) - << " (root: " << getCdRootPath().string() - << ", repeat=" << samePathFailCount << ")" << std::endl; - } - setReturnS32(ctx, 0); - return; - } - - if (usedRemapFallback) - { - std::cout << "[sceCdSearchFile] remap gd-init search \"" << sanitizeForLog(path) - << "\" -> \"" << sanitizeForLog(resolvedPath) << "\"" << std::endl; - } - - if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) - { - g_lastCdError = -1; - setReturnS32(ctx, 0); - return; - } - - g_cdStreamingLbn = resolvedEntry.baseLbn; - if (shouldTrace) - { - std::cout << "[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) - << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn - << " size=0x" << resolvedEntry.sizeBytes - << " sectors=0x" << resolvedEntry.sectors - << std::dec << std::endl; - } - setReturnS32(ctx, 1); - } - - void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); - } - - void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, g_cdInitialized ? 6 : 0); - } - - void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t sectors = getRegU32(ctx, 4); - uint32_t buf = getRegU32(ctx, 5); - uint32_t errAddr = getRegU32(ctx, 7); - - uint32_t offset = buf & PS2_RAM_MASK; - size_t bytes = static_cast(sectors) * kCdSectorSize; - const size_t maxBytes = PS2_RAM_SIZE - offset; - if (bytes > maxBytes) - { - bytes = maxBytes; - } - - const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); - if (ok) - { - g_cdStreamingLbn += sectors; - } - - if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) - { - *err = ok ? 0 : g_lastCdError; - } - - setReturnS32(ctx, ok ? static_cast(sectors) : 0); - } - - void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); - } - - void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); - } - - void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_cdStreamingLbn = getRegU32(ctx, 4); - setReturnS32(ctx, 1); - } - - void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t statusPtr = getRegU32(ctx, 5); - if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) - { - *status = 0; - } - setReturnS32(ctx, 1); - } - - void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioClose(rdram, ctx, runtime); - } - - void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2Close", rdram, ctx, runtime); - } - - void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime); - } - - void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime); - } - - void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime); - } - - void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime); - } - - void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime); - } - - void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2Open", rdram, ctx, runtime); - } - - void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime); - } - - void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime); - } - - void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaCallback", rdram, ctx, runtime); - } - - void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaDebug", rdram, ctx, runtime); - } - - void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t chanArg = getRegU32(ctx, 4); - const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); - setReturnU32(ctx, channelBase); - } - - void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); - } - - void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime); - } - - void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaPause", rdram, ctx, runtime); - } - - void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); - } - - void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); - } - - void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaRecv", rdram, ctx, runtime); - } - - void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime); - } - - void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime); - } - - void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaRestart", rdram, ctx, runtime); - } - - void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); - } - - void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); - } - - void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); - } - - void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true)); - } - - void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); - } - - void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); - } - - void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceDmaWatch", rdram, ctx, runtime); - } - - void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceFsInit", rdram, ctx, runtime); - } - - void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t imgAddr = getRegU32(ctx, 4); - uint32_t srcAddr = getRegU32(ctx, 5); - - GsImageMem img{}; - if (!runtime || !readGsImage(rdram, imgAddr, img)) - { - setReturnS32(ctx, -1); - return; - } - - const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); - if (rowBytes == 0) - { - setReturnS32(ctx, -1); - return; - } - - uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); - uint32_t base = static_cast(img.vram_addr) * 2048u; - uint32_t stride = bytesForPixels(img.psm, fbw * 64u); - if (stride == 0) - { - setReturnS32(ctx, -1); - return; - } - - uint8_t *gsvram = runtime->memory().getGSVRAM(); - uint8_t *src = getMemPtr(rdram, srcAddr); - if (!gsvram || !src) - { - setReturnS32(ctx, -1); - return; - } - - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sceGsExecLoadImage: x=" << img.x - << " y=" << img.y - << " w=" << img.width - << " h=" << img.height - << " vram=0x" << std::hex << img.vram_addr - << " fbw=" << std::dec << static_cast(fbw) - << " psm=" << static_cast(img.psm) - << " src=0x" << std::hex << srcAddr << std::dec << std::endl; - ++logCount; - } - - for (uint32_t row = 0; row < img.height; ++row) - { - uint32_t dstOff = base + (static_cast(img.y) + row) * stride + bytesForPixels(img.psm, static_cast(img.x)); - uint32_t srcOff = row * rowBytes; - if (dstOff >= PS2_GS_VRAM_SIZE) - break; - uint32_t copyBytes = rowBytes; - if (dstOff + copyBytes > PS2_GS_VRAM_SIZE) - copyBytes = PS2_GS_VRAM_SIZE - dstOff; - std::memcpy(gsvram + dstOff, src + srcOff, copyBytes); - } - - if (img.width >= 320 && img.height >= 200) - { - auto &gs = runtime->memory().gs(); - gs.dispfb1 = makeDispFb(img.vram_addr, fbw, img.psm, 0, 0); - gs.display1 = makeDisplay(0, 0, 0, 0, img.width - 1, img.height - 1); - } - - setReturnS32(ctx, 0); - } - - void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t imgAddr = getRegU32(ctx, 4); - uint32_t dstAddr = getRegU32(ctx, 5); - - GsImageMem img{}; - if (!runtime || !readGsImage(rdram, imgAddr, img)) - { - setReturnS32(ctx, -1); - return; - } - - const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); - if (rowBytes == 0) - { - setReturnS32(ctx, -1); - return; - } - - uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); - uint32_t base = static_cast(img.vram_addr) * 2048u; - uint32_t stride = bytesForPixels(img.psm, fbw * 64u); - if (stride == 0) - { - setReturnS32(ctx, -1); - return; - } - - uint8_t *gsvram = runtime->memory().getGSVRAM(); - uint8_t *dst = getMemPtr(rdram, dstAddr); - if (!gsvram || !dst) - { - setReturnS32(ctx, -1); - return; - } - - static int logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sceGsExecStoreImage: x=" << img.x - << " y=" << img.y - << " w=" << img.width - << " h=" << img.height - << " vram=0x" << std::hex << img.vram_addr - << " fbw=" << std::dec << static_cast(fbw) - << " psm=" << static_cast(img.psm) - << " dst=0x" << std::hex << dstAddr << std::dec << std::endl; - ++logCount; - } - - for (uint32_t row = 0; row < img.height; ++row) - { - uint32_t srcOff = base + (static_cast(img.y) + row) * stride + bytesForPixels(img.psm, static_cast(img.x)); - uint32_t dstOff = row * rowBytes; - if (srcOff >= PS2_GS_VRAM_SIZE) - break; - uint32_t copyBytes = rowBytes; - if (srcOff + copyBytes > PS2_GS_VRAM_SIZE) - copyBytes = PS2_GS_VRAM_SIZE - srcOff; - std::memcpy(dst + dstOff, gsvram + srcOff, copyBytes); - } - - setReturnS32(ctx, 0); - } - - void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t addr = writeGsGParamToScratch(runtime); - setReturnU32(ctx, addr); - } - - void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t envAddr = getRegU32(ctx, 4); - GsDispEnvMem env{}; - if (readGsDispEnv(rdram, envAddr, env)) - { - auto &gs = runtime->memory().gs(); - gs.display1 = env.display; - gs.dispfb1 = env.dispfb; - } - setReturnS32(ctx, 0); - } - - void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t envAddr = getRegU32(ctx, 4); - uint32_t psm = getRegU32(ctx, 5); - uint32_t w = getRegU32(ctx, 6); - uint32_t h = getRegU32(ctx, 7); - - if (w == 0) - w = 640; - if (h == 0) - h = 448; - - GsDrawEnvMem env{}; - env.offset_x = static_cast(2048 - (w / 2)); - env.offset_y = static_cast(2048 - (h / 2)); - env.clip_x = 0; - env.clip_y = 0; - env.clip_w = static_cast(w); - env.clip_h = static_cast(h); - env.vram_addr = 0; - env.fbw = static_cast((w + 63) / 64); - env.psm = static_cast(psm); - env.vram_x = 0; - env.vram_y = 0; - env.draw_mask = 0; - env.auto_clear = 1; - env.bg_r = 1; - env.bg_g = 1; - env.bg_b = 1; - env.bg_a = 0x80; - env.bg_q = 0.0f; - - uint8_t *ptr = getMemPtr(rdram, envAddr); - if (ptr) - { - std::memcpy(ptr, &env, sizeof(env)); - } - setReturnS32(ctx, 0); - } - - void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t mode = getRegU32(ctx, 4); - uint32_t interlace = getRegU32(ctx, 5); - uint32_t omode = getRegU32(ctx, 6); - uint32_t ffmode = getRegU32(ctx, 7); - - if (mode == 0) - { - g_gparam.interlace = static_cast(interlace & 0x1); - g_gparam.omode = static_cast(omode & 0xFF); - g_gparam.ffmode = static_cast(ffmode & 0x1); - writeGsGParamToScratch(runtime); - - auto &gs = runtime->memory().gs(); - gs.pmode = makePmode(1, 0, 0, 0, 0, 0x80); - gs.smode2 = (interlace & 0x1) | ((ffmode & 0x1) << 1); - gs.dispfb1 = makeDispFb(0, 10, 0, 0, 0); - gs.display1 = makeDisplay(0, 0, 0, 0, 639, 447); - } - - setReturnS32(ctx, 0); - } - - void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceGsSetDefClear", rdram, ctx, runtime); - } - - void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t envAddr = getRegU32(ctx, 4); - uint32_t psm = getRegU32(ctx, 5); - uint32_t w = getRegU32(ctx, 6); - uint32_t h = getRegU32(ctx, 7); - uint32_t dx = readStackU32(rdram, ctx, 16); - uint32_t dy = readStackU32(rdram, ctx, 20); - - if (w == 0) - w = 640; - if (h == 0) - h = 448; - - uint32_t fbw = (w + 63) / 64; - uint64_t dispfb = makeDispFb(0, fbw, psm, 0, 0); - uint64_t display = makeDisplay(dx, dy, 0, 0, w - 1, h - 1); - - writeGsDispEnv(rdram, envAddr, display, dispfb); - setReturnS32(ctx, 0); - } - - void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceGsSetDefDrawEnv", rdram, ctx, runtime); - } - - void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceGsSetDefDrawEnv2", rdram, ctx, runtime); - } - - void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t imgAddr = getRegU32(ctx, 4); - const GsSetDefImageArgs args = decodeGsSetDefImageArgs(rdram, ctx); - - GsImageMem img{}; - img.x = static_cast(args.x); - img.y = static_cast(args.y); - img.width = static_cast(args.width); - img.height = static_cast(args.height); - img.vram_addr = static_cast(args.vramAddr); - img.vram_width = static_cast(args.vramWidth); - img.psm = static_cast(args.psm); - - writeGsImage(rdram, imgAddr, img); - setReturnS32(ctx, 0); - } - - void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - sceGsSetDefLoadImage(rdram, ctx, runtime); - } - - void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - // can we get away with that ? kkkk - static int cur = 0; - cur ^= 1; - setReturnS32(ctx, cur); - } - - void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceGszbufaddr", rdram, ctx, runtime); - } - - void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceIoctl", rdram, ctx, runtime); - } - - void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceIpuInit", rdram, ctx, runtime); - } - - void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceIpuRestartDMA", rdram, ctx, runtime); - } - - void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceIpuStopDMA", rdram, ctx, runtime); - } - - void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceIpuSync", rdram, ctx, runtime); - } - - void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioLseek(rdram, ctx, runtime); - } - - void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcChangeThreadPriority", rdram, ctx, runtime); - } - - void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcChdir", rdram, ctx, runtime); - } - - void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcClose", rdram, ctx, runtime); - } - - void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcDelete", rdram, ctx, runtime); - } - - void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcFlush", rdram, ctx, runtime); - } - - void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcFormat", rdram, ctx, runtime); - } - - void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcGetDir", rdram, ctx, runtime); - } - - void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcGetEntSpace", rdram, ctx, runtime); - } - - void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcGetInfo", rdram, ctx, runtime); - } - - void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcGetSlotMax", rdram, ctx, runtime); - } - - void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static uint32_t logCount = 0; - if (logCount < 8) - { - std::cout << "ps2_stub sceMcInit -> 0" << std::endl; - ++logCount; - } - setReturnS32(ctx, 0); - } - - void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcMkdir", rdram, ctx, runtime); - } - - void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcOpen", rdram, ctx, runtime); - } - - void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcRead", rdram, ctx, runtime); - } - - void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcRename", rdram, ctx, runtime); - } - - void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcSeek", rdram, ctx, runtime); - } - - void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcSetFileInfo", rdram, ctx, runtime); - } - - void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcSync", rdram, ctx, runtime); - } - - void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcUnformat", rdram, ctx, runtime); - } - - void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMcWrite", rdram, ctx, runtime); - } - - void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime); - } - - void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegAddCallback", rdram, ctx, runtime); - } - - void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegAddStrCallback", rdram, ctx, runtime); - } - - void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegClearRefBuff", rdram, ctx, runtime); - } - - void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegCreate", rdram, ctx, runtime); - } - - void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDelete", rdram, ctx, runtime); - } - - void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime); - } - - void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDemuxPssRing", rdram, ctx, runtime); - } - - void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime); - } - - void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime); - } - - void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime); - } - - void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime); - } - - void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime); - } - - void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegGetPicture", rdram, ctx, runtime); - } - - void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime); - } - - void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime); - } - - void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegInit", rdram, ctx, runtime); - } - - void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegIsEnd", rdram, ctx, runtime); - } - - void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime); - } - - void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegReset", rdram, ctx, runtime); - } - - void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime); - } - - void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime); - } - - void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime); - } - - void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime); - } - - void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioOpen(rdram, ctx, runtime); - } - - void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadEnd", rdram, ctx, runtime); - } - - void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadEnterPressMode", rdram, ctx, runtime); - } - - void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadExitPressMode", rdram, ctx, runtime); - } - - void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadGetButtonMask", rdram, ctx, runtime); - } - - void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadGetDmaStr", rdram, ctx, runtime); - } - - void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadGetFrameCount", rdram, ctx, runtime); - } - - void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - // Arbitrary non-zero module version. - setReturnS32(ctx, 0x0200); - } - - void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - setReturnS32(ctx, 2); - } - - void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - // 0 = completed/no pending request. - setReturnS32(ctx, 0); - } - - void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - // Most games use one slot unless multitap is active. - setReturnS32(ctx, 1); - } - - void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - // Pad state constants used by libpad: 6 means stable and ready. - setReturnS32(ctx, 6); - } - - void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadInfoAct", rdram, ctx, runtime); - } - - void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadInfoComb", rdram, ctx, runtime); - } - - void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - - const int32_t infoMode = static_cast(getRegU32(ctx, 6)); // a2 - const int32_t index = static_cast(getRegU32(ctx, 7)); // a3 - - // Minimal DualShock-like capabilities to keep game-side pad setup paths alive. - constexpr int32_t kPadTypeDualShock = 7; - switch (infoMode) - { - case 1: // PAD_MODECURID - setReturnS32(ctx, kPadTypeDualShock); - return; - case 2: // PAD_MODECUREXID - setReturnS32(ctx, kPadTypeDualShock); - return; - case 3: // PAD_MODECUROFFS - setReturnS32(ctx, 0); - return; - case 4: // PAD_MODETABLE - if (index == -1) - { - setReturnS32(ctx, 1); // one available mode - } - else if (index == 0) - { - setReturnS32(ctx, kPadTypeDualShock); - } - else - { - setReturnS32(ctx, 0); - } - return; - default: - setReturnS32(ctx, 0); - return; - } - } - - void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - // Pressure mode is disabled in this minimal implementation. - setReturnS32(ctx, 0); - } - - void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); - } - - void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); - } - - void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); - } - - void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)rdram; - (void)runtime; - setReturnS32(ctx, 1); - } - - void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - (void)runtime; - - const uint32_t dataAddr = getRegU32(ctx, 6); // a2 - uint8_t *data = getMemPtr(rdram, dataAddr); - if (!data) - { - setReturnS32(ctx, 0); - return; - } - - // struct padButtonStatus (32 bytes): neutral state, no buttons pressed. - std::memset(data, 0, 32); - data[1] = 0x73; // analog/dualshock mode marker - data[2] = 0xFF; // btns low (active-low) - data[3] = 0xFF; // btns high - data[4] = 0x80; // rjoy_h - data[5] = 0x80; // rjoy_v - data[6] = 0x80; // ljoy_h - data[7] = 0x80; // ljoy_v - - setReturnS32(ctx, 1); - } - - void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadReqIntToStr", rdram, ctx, runtime); - } - - void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetActAlign", rdram, ctx, runtime); - } - - void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetActDirect", rdram, ctx, runtime); - } - - void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetButtonInfo", rdram, ctx, runtime); - } - - void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetMainMode", rdram, ctx, runtime); - } - - void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetReqState", rdram, ctx, runtime); - } - - void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetVrefParam", rdram, ctx, runtime); - } - - void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadSetWarningLevel", rdram, ctx, runtime); - } - - void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePadStateIntToStr", rdram, ctx, runtime); - } - - void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("scePrintf", rdram, ctx, runtime); - } - - void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioRead(rdram, ctx, runtime); - } - - void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceResetttyinit", rdram, ctx, runtime); - } - - void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSdCallBack", rdram, ctx, runtime); - } - - void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSdRemote", rdram, ctx, runtime); - } - - void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSdRemoteInit", rdram, ctx, runtime); - } - - void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime); - } - - void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime); - } - - void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSetPtm", rdram, ctx, runtime); - } - - void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifAddCmdHandler", rdram, ctx, runtime); - } - - void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t reqSize = getRegU32(ctx, 4); - const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); - if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) - { - setReturnS32(ctx, 0); - return; - } - - const uint32_t allocAddr = g_iopHeapNext; - g_iopHeapNext += alignedSize; - setReturnS32(ctx, static_cast(allocAddr)); - } - - void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifBindRpc(rdram, ctx, runtime); - } - - void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime); - } - - void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifDmaStat", rdram, ctx, runtime); - } - - void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifExitCmd", rdram, ctx, runtime); - } - - void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifGetDataTable", rdram, ctx, runtime); - } - - void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifGetIopAddr", rdram, ctx, runtime); - } - - void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifGetReg", rdram, ctx, runtime); - } - - void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifGetSreg", rdram, ctx, runtime); - } - - void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifInitCmd", rdram, ctx, runtime); - } - - void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - g_iopHeapNext = kIopHeapBase; - setReturnS32(ctx, 0); - } - - void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifInitRpc(rdram, ctx, runtime); - } - - void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifIsAliveIop", rdram, ctx, runtime); - } - - void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime); - } - - void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime); - } - - void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifLoadFileReset", rdram, ctx, runtime); - } - - void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime); - } - - void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime); - } - - void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifRemoveCmdHandler", rdram, ctx, runtime); - } - - void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime); - } - - void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime); - } - - void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifResetIop", rdram, ctx, runtime); - } - - void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetCmdBuffer", rdram, ctx, runtime); - } - - void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetDChain", rdram, ctx, runtime); - } - - void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetDma", rdram, ctx, runtime); - } - - void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetIopAddr", rdram, ctx, runtime); - } - - void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetReg", rdram, ctx, runtime); - } - - void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime); - } - - void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetSreg", rdram, ctx, runtime); - } - - void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifSetSysCmdBuffer", rdram, ctx, runtime); - } - - void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifStopDma", rdram, ctx, runtime); - } - - void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 1); - } - - void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSifWriteBackDCache", rdram, ctx, runtime); - } - - void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime); - } - - void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime); - } - - void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime); - } - - void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime); - } - - void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime); - } - - void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime); - } - - void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime); - } - - void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime); - } - - void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime); - } - - void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime); - } - - void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime); - } - - void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime); - } - - void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime); - } - - void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime); - } - - void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime); - } - - void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime); - } - - void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime); - } - - void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime); - } - - void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime); - } - - void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime); - } - - void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime); - } - - void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime); - } - - void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime); - } - - void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime); - } - - void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime); - } - - void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime); - } - - void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime); - } - - void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime); - } - - void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime); - } - - void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime); - } - - void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime); - } - - void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime); - } - - void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime); - } - - void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime); - } - - void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime); - } - - void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime); - } - - void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime); - } - - void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime); - } - - void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime); - } - - void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime); - } - - void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime); - } - - void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime); - } - - void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime); - } - - void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime); - } - - void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime); - } - - void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime); - } - - void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime); - } - - void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime); - } - - void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime); - } - - void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime); - } - - void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime); - } - - void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime); - } - - void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime); - } - - void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime); - } - - void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime); - } - - void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime); - } - - void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime); - } - - void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime); - } - - void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime); - } - - void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime); - } - - void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime); - } - - void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime); - } - - void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime); - } - - void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime); - } - - void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime); - } - - void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime); - } - - void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime); - } - - void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime); - } - - void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime); - } - - void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime); - } - - void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime); - } - - void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime); - } - - void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime); - } - - void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime); - } - - void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime); - } - - void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime); - } - - void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime); - } - - void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime); - } - - void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime); - } - - void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime); - } - - void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime); - } - - void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime); - } - - void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceTtyHandler", rdram, ctx, runtime); - } - - void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceTtyInit", rdram, ctx, runtime); - } - - void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceTtyRead", rdram, ctx, runtime); - } - - void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceTtyWrite", rdram, ctx, runtime); - } - - void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0AddVector", rdram, ctx, runtime); - } - - void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); - } - - void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime); - } - - void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime); - } - - void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime); - } - - void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime); - } - - void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime); - } - - void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); - } - - void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); - } - - void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); - } - - void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime); - } - - void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime); - } - - void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime); - } - - void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0FTOI0Vector", rdram, ctx, runtime); - } - - void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0FTOI4Vector", rdram, ctx, runtime); - } - - void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0InnerProduct", rdram, ctx, runtime); - } - - void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime); - } - - void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime); - } - - void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime); - } - - void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ITOF0Vector", rdram, ctx, runtime); - } - - void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ITOF12Vector", rdram, ctx, runtime); - } - - void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ITOF4Vector", rdram, ctx, runtime); - } - - void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime); - } - - void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime); - } - - void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime); - } - - void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0Normalize", rdram, ctx, runtime); - } - - void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime); - } - - void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0OuterProduct", rdram, ctx, runtime); - } - - void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime); - } - - void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); - } - - void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); - } - - void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); - } - - void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime); - } - - void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime); - } - - void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ScaleVector", rdram, ctx, runtime); - } - - void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime); - } - - void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0SubVector", rdram, ctx, runtime); - } - - void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime); - } - - void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); - } - - void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst - alignas(16) const float identity[16] = { - 1.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 1.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f}; - - if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast(identity), sizeof(identity))) - { - static uint32_t warnCount = 0; - if (warnCount < 8) - { - std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x" - << std::hex << dstAddr << std::dec << std::endl; - ++warnCount; - } - } - - setReturnS32(ctx, 0); - } - - void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime); - } - - void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioWrite(rdram, ctx, runtime); - } - - void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("srand", rdram, ctx, runtime); - } - - void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("stat", rdram, ctx, runtime); - } - - void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - TODO_NAMED("strcasecmp", rdram, ctx, runtime); - } - - void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t file_handle = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 - FILE *fp = get_file_ptr(file_handle); - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (fp && format_addr != 0) - { - std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); - ret = std::fprintf(fp, "%s", rendered.c_str()); - } - else - { - std::cerr << "vfprintf error: Invalid file handle or format address." - << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } - - setReturnS32(ctx, ret); - } - - void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t str_addr = getRegU32(ctx, 4); // $a0 - uint32_t format_addr = getRegU32(ctx, 5); // $a1 - uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 - const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); - int ret = -1; - - if (format_addr != 0) - { - std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); - if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), rendered.size() + 1u)) - { - ret = static_cast(rendered.size()); - } - else - { - std::cerr << "vsprintf error: Failed to write destination buffer at 0x" - << std::hex << str_addr << std::dec << std::endl; - } - } - else - { - std::cerr << "vsprintf error: Invalid address provided." - << " Dest: 0x" << std::hex << str_addr - << ", Format: 0x" << format_addr << std::dec - << std::endl; - } - - setReturnS32(ctx, ret); - } - - void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ps2_syscalls::fioWrite(rdram, ctx, runtime); - } +#include "stubs/ps2_stubs_gs.inl" +#include "stubs/ps2_stubs_residentEvilCV.inl" void TODO(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { @@ -5964,4 +69,5 @@ namespace ps2_stubs setReturnS32(ctx, -1); // Return error } + } diff --git a/ps2xRuntime/src/lib/ps2_syscalls.cpp b/ps2xRuntime/src/lib/ps2_syscalls.cpp index feca94b..2fb1024 100644 --- a/ps2xRuntime/src/lib/ps2_syscalls.cpp +++ b/ps2xRuntime/src/lib/ps2_syscalls.cpp @@ -28,1657 +28,15 @@ std::string translatePs2Path(const char *ps2Path); -namespace -{ - std::string toLowerAscii(std::string value) - { - std::transform(value.begin(), value.end(), value.begin(), - [](unsigned char c) - { return static_cast(std::tolower(c)); }); - return value; - } - - std::string stripIsoVersionSuffix(std::string value) - { - const std::size_t semicolon = value.find(';'); - if (semicolon == std::string::npos) - { - return value; - } - - bool numericSuffix = semicolon + 1 < value.size(); - for (std::size_t i = semicolon + 1; i < value.size(); ++i) - { - if (!std::isdigit(static_cast(value[i]))) - { - numericSuffix = false; - break; - } - } - - if (numericSuffix) - { - value.erase(semicolon); - } - return value; - } - - std::string normalizePs2PathSuffix(std::string suffix) - { - std::replace(suffix.begin(), suffix.end(), '\\', '/'); - suffix = stripIsoVersionSuffix(std::move(suffix)); - while (!suffix.empty() && (suffix.front() == '/' || suffix.front() == '\\')) - { - suffix.erase(suffix.begin()); - } - return suffix; - } - - std::filesystem::path getConfiguredHostRoot() - { - const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - if (!paths.hostRoot.empty()) - { - return paths.hostRoot; - } - if (!paths.elfDirectory.empty()) - { - return paths.elfDirectory; - } - - std::error_code ec; - const std::filesystem::path cwd = std::filesystem::current_path(ec); - return ec ? std::filesystem::path(".") : cwd.lexically_normal(); - } - - std::filesystem::path getConfiguredCdRoot() - { - const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); - if (!paths.cdRoot.empty()) - { - return paths.cdRoot; - } - if (!paths.elfDirectory.empty()) - { - return paths.elfDirectory; - } - - std::error_code ec; - const std::filesystem::path cwd = std::filesystem::current_path(ec); - return ec ? std::filesystem::path(".") : cwd.lexically_normal(); - } -} - -std::unordered_map g_fileDescriptors; -int g_nextFd = 3; // Start after stdin, stdout, stderr - -struct ThreadInfo -{ - uint32_t entry = 0; - uint32_t stack = 0; - uint32_t stackSize = 0; - uint32_t gp = 0; - uint32_t priority = 0; - uint32_t attr = 0; - uint32_t option = 0; - uint32_t arg = 0; - bool started = false; - uint32_t tlsBase = 0; - - // Thread Status - int status = 0x10; // THS_DORMANT - int waitType = 0; // TSW_NONE - int waitId = 0; - int wakeupCount = 0; - int currentPriority = 0; - int suspendCount = 0; - - std::mutex m; - std::condition_variable cv; - std::atomic forceRelease{false}; - std::atomic terminated{false}; -}; - -// Thread status -#define THS_RUN 0x01 -#define THS_READY 0x02 -#define THS_WAIT 0x04 -#define THS_SUSPEND 0x08 -#define THS_WAITSUSPEND 0x0c -#define THS_DORMANT 0x10 - -// Thread WAIT Status -#define TSW_NONE 0 -#define TSW_SLEEP 1 -#define TSW_SEMA 2 -#define TSW_EVENT 3 - -// Common kernel-like error codes used by thread/event/alarm syscalls. -constexpr int KE_OK = 0; -constexpr int KE_ERROR = -1; -constexpr int KE_ILLEGAL_MODE = -405; -constexpr int KE_ILLEGAL_THID = -406; -constexpr int KE_UNKNOWN_THID = -407; -constexpr int KE_UNKNOWN_SEMID = -408; -constexpr int KE_UNKNOWN_EVFID = -409; -constexpr int KE_DORMANT = -413; -constexpr int KE_NOT_WAIT = -416; -constexpr int KE_RELEASE_WAIT = -418; -constexpr int KE_SEMA_ZERO = -419; -constexpr int KE_EVF_COND = -421; -constexpr int KE_EVF_MULTI = -422; -constexpr int KE_EVF_ILPAT = -423; -constexpr int KE_WAIT_DELETE = -425; - -// SIF RPC Structures -struct t_SifRpcHeader -{ - uint32_t pkt_addr; // void* - uint32_t rpc_id; - int sema_id; - uint32_t mode; -}; - -struct t_SifRpcClientData -{ - t_SifRpcHeader hdr; - uint32_t command; - uint32_t buf; // void* - uint32_t cbuf; // void* - uint32_t end_function; // func ptr - uint32_t end_param; // void* - uint32_t server; // t_SifRpcServerData* -}; - -struct t_SifRpcServerData -{ - int sid; - uint32_t func; // func ptr - uint32_t buf; // void* - int size; - uint32_t cfunc; // func ptr - uint32_t cbuf; // void* - int size2; - uint32_t client; // t_SifRpcClientData* - uint32_t pkt_addr; // void* - int rpc_number; - uint32_t recvbuf; // void* - int rsize; - int rmode; - int rid; - uint32_t link; // t_SifRpcServerData* - uint32_t next; // t_SifRpcServerData* - uint32_t base; // t_SifRpcDataQueue* -}; - -struct t_SifRpcDataQueue -{ - int thread_id; - int active; - uint32_t link; // t_SifRpcServerData* - uint32_t start; // t_SifRpcServerData* - uint32_t end; // t_SifRpcServerData* - uint32_t next; // t_SifRpcDataQueue* -}; - -struct ee_thread_status_t -{ - int status; // 0x00 - uint32_t func; // 0x04 - uint32_t stack; // 0x08 - int stack_size; // 0x0C - uint32_t gp_reg; // 0x10 - int initial_priority; // 0x14 - int current_priority; // 0x18 - uint32_t attr; // 0x1C - uint32_t option; // 0x20 - uint32_t waitType; // 0x24 - uint32_t waitId; // 0x28 - uint32_t wakeupCount; // 0x2C -}; - -struct ee_sema_t -{ - int count; - int max_count; - int init_count; - int wait_threads; - uint32_t attr; - uint32_t option; -}; - -struct SemaInfo -{ - int count = 0; - int maxCount = 0; - int initCount = 0; - uint32_t attr = 0; - uint32_t option = 0; - int waiters = 0; - bool deleted = false; - std::mutex m; - std::condition_variable cv; -}; - -struct EventFlagInfo -{ - uint32_t attr = 0; - uint32_t option = 0; - uint32_t initBits = 0; - uint32_t bits = 0; - int waiters = 0; - bool deleted = false; - std::mutex m; - std::condition_variable cv; -}; - -struct AlarmInfo -{ - int id = 0; - uint16_t ticks = 0; - uint32_t handler = 0; - uint32_t commonArg = 0; - uint32_t gp = 0; - uint32_t sp = 0; - uint8_t *rdram = nullptr; - PS2Runtime *runtime = nullptr; - std::chrono::steady_clock::time_point dueAt; -}; - -struct io_stat_t -{ - uint32_t mode; - uint32_t attr; - uint32_t size; - uint8_t ctime[8]; - uint8_t atime[8]; - uint8_t mtime[8]; - uint32_t hisize; -}; - -static constexpr uint32_t kFioSoIfLnk = 0x0008; -static constexpr uint32_t kFioSoIfReg = 0x0010; -static constexpr uint32_t kFioSoIfDir = 0x0020; -static constexpr uint32_t kFioSoIROth = 0x0004; -static constexpr uint32_t kFioSoIWOth = 0x0002; -static constexpr uint32_t kFioSoIXOth = 0x0001; - -static std::unordered_map> g_threads; -static int g_nextThreadId = 2; // Reserve 1 for the main thread -static thread_local int g_currentThreadId = 1; -static std::mutex g_thread_map_mutex; - -static std::unordered_map> g_semas; -static int g_nextSemaId = 1; -static std::mutex g_sema_map_mutex; -static std::unordered_map> g_eventFlags; -static int g_nextEventFlagId = 1; -static std::mutex g_event_flag_map_mutex; -static std::unordered_map> g_alarms; -static int g_nextAlarmId = 1; -static std::mutex g_alarm_mutex; -static std::condition_variable g_alarm_cv; -static std::once_flag g_alarm_worker_once; -std::atomic g_activeThreads{0}; -static std::mutex g_fd_mutex; - -struct RpcServerState -{ - uint32_t sid = 0; - uint32_t sd_ptr = 0; // PS2 address -}; - -struct RpcClientState -{ - bool busy = false; - uint32_t last_rpc = 0; - uint32_t sid = 0; -}; - -static std::unordered_map g_rpc_servers; -static std::unordered_map g_rpc_clients; -static std::mutex g_rpc_mutex; -static bool g_rpc_initialized = false; -static uint32_t g_rpc_next_id = 1; -static uint32_t g_rpc_packet_index = 0; -static uint32_t g_rpc_server_index = 0; -static uint32_t g_rpc_active_queue = 0; -static constexpr uint32_t kDtxRpcSid = 0x7D000000u; -static constexpr uint32_t kDtxUrpcObjBase = 0x01F18000u; -static constexpr uint32_t kDtxUrpcObjLimit = 0x01F1FF00u; -static constexpr uint32_t kDtxUrpcFnTableBase = 0x0034FED0u; -static constexpr uint32_t kDtxUrpcObjTableBase = 0x0034FFD0u; -static std::mutex g_dtx_rpc_mutex; -static std::unordered_map g_dtx_remote_by_id; -static uint32_t g_dtx_next_urpc_obj = kDtxUrpcObjBase; - -struct DtxSjrmtState -{ - uint32_t handle = 0; - uint32_t mode = 0; - uint32_t wkAddr = 0; - uint32_t wkSize = 0; - uint32_t readPos = 0; - uint32_t writePos = 0; - uint32_t roomBytes = 0; - uint32_t dataBytes = 0; - uint32_t uuid0 = 0; - uint32_t uuid1 = 0; - uint32_t uuid2 = 0; - uint32_t uuid3 = 0; -}; - -static std::unordered_map g_dtx_sjrmt_by_handle; - -static uint32_t dtxNormalizeSjrmtCapacity(uint32_t requestedBytes) -{ - if (requestedBytes == 0u || requestedBytes > 0x01000000u) - { - return 0x4000u; - } - return requestedBytes; -} - -static uint32_t dtxAllocUrpcHandleLocked() -{ - for (uint32_t i = 0; i < 4096u; ++i) - { - uint32_t candidate = g_dtx_next_urpc_obj; - g_dtx_next_urpc_obj += 0x20u; - if (g_dtx_next_urpc_obj < kDtxUrpcObjBase || g_dtx_next_urpc_obj >= kDtxUrpcObjLimit) - { - g_dtx_next_urpc_obj = kDtxUrpcObjBase; - } - - if (candidate < kDtxUrpcObjBase || candidate >= kDtxUrpcObjLimit) - { - continue; - } - - if (g_dtx_sjrmt_by_handle.find(candidate) != g_dtx_sjrmt_by_handle.end()) - { - continue; - } - - bool inUseByDtxRemote = false; - for (const auto &entry : g_dtx_remote_by_id) - { - if (entry.second == candidate) - { - inUseByDtxRemote = true; - break; - } - } - - if (!inUseByDtxRemote) - { - return candidate; - } - } - - return kDtxUrpcObjBase; -} - -struct ExitHandlerEntry -{ - uint32_t func = 0; - uint32_t arg = 0; -}; - -static std::mutex g_exit_handler_mutex; -static std::unordered_map> g_exit_handlers; - -static std::mutex g_bootmode_mutex; -static bool g_bootmode_initialized = false; -static uint32_t g_bootmode_pool_offset = 0; -static std::unordered_map g_bootmode_addresses; - -static std::mutex g_tls_mutex; -static uint32_t g_tls_index = 0; - -static std::mutex g_osd_mutex; -static bool g_osd_config_initialized = false; -static uint32_t g_osd_config_raw = 0; - -static std::mutex g_ps2_path_mutex; -static bool g_ps2_paths_initialized = false; -static std::filesystem::path g_host_base; -static std::filesystem::path g_cdrom_base; -static std::filesystem::path g_host_cwd; -static std::filesystem::path g_cdrom_cwd; -static std::string g_ps2_cwd_device = "host0"; - -static constexpr uint32_t kRpcPacketSize = 64; -static constexpr uint32_t kRpcPacketPoolBase = 0x01F00000; -static constexpr uint32_t kRpcPacketPoolBytes = 0x00010000; -static constexpr uint32_t kRpcPacketPoolCount = kRpcPacketPoolBytes / kRpcPacketSize; -static constexpr uint32_t kRpcServerPoolBase = 0x01F10000; -static constexpr uint32_t kRpcServerPoolBytes = 0x00010000; -static constexpr uint32_t kRpcServerStride = 0x80; -static constexpr uint32_t kRpcServerPoolCount = kRpcServerPoolBytes / kRpcServerStride; - -static constexpr uint32_t kTlsPoolBase = 0x01F20000; -static constexpr uint32_t kTlsPoolBytes = 0x00010000; -static constexpr uint32_t kTlsBlockSize = 0x100; -static constexpr uint32_t kTlsPoolCount = kTlsPoolBytes / kTlsBlockSize; - -static constexpr uint32_t kBootModePoolBase = 0x01F30000; -static constexpr uint32_t kBootModePoolBytes = 0x00001000; - -static constexpr uint32_t kSifRpcModeNowait = 0x01; -static constexpr uint32_t kSifRpcModeNoWbDc = 0x02; -static constexpr size_t kMaxSifModulePathBytes = 260; -static constexpr uint32_t kMaxSifModuleLogs = 24; -static constexpr size_t kSifModuleBufferProbeBytes = 2048; -static constexpr size_t kLoadfilePathMaxBytes = 252; -static constexpr size_t kLoadfileArgMaxBytes = 252; -static constexpr uint32_t kElfMagic = 0x464C457Fu; -static constexpr uint16_t kElfMachineMips = 8u; -static constexpr uint16_t kElfTypeExec = 2u; -static constexpr uint32_t kElfPtLoad = 1u; -static constexpr uint32_t kElfPtMipsRegInfo = 0x70000000u; -static constexpr uint32_t kElfShtMipsRegInfo = 0x70000006u; - -#pragma pack(push, 1) -struct Elf32Header -{ - uint32_t magic; - uint8_t elfClass; - uint8_t endianness; - uint8_t version; - uint8_t osAbi; - uint8_t abiVersion; - uint8_t pad[7]; - uint16_t type; - uint16_t machine; - uint32_t version2; - uint32_t entry; - uint32_t phoff; - uint32_t shoff; - uint32_t flags; - uint16_t ehsize; - uint16_t phentsize; - uint16_t phnum; - uint16_t shentsize; - uint16_t shnum; - uint16_t shstrndx; -}; - -struct Elf32ProgramHeader -{ - uint32_t type; - uint32_t offset; - uint32_t vaddr; - uint32_t paddr; - uint32_t filesz; - uint32_t memsz; - uint32_t flags; - uint32_t align; -}; - -struct Elf32SectionHeader -{ - uint32_t name; - uint32_t type; - uint32_t flags; - uint32_t addr; - uint32_t offset; - uint32_t size; - uint32_t link; - uint32_t info; - uint32_t addralign; - uint32_t entsize; -}; - -struct GuestExecData -{ - uint32_t epc; - uint32_t gp; - uint32_t sp; - uint32_t dummy; -}; -#pragma pack(pop) - -static_assert(sizeof(Elf32Header) == 52u, "Unexpected ELF32 header layout."); -static_assert(sizeof(Elf32ProgramHeader) == 32u, "Unexpected ELF32 program header layout."); -static_assert(sizeof(Elf32SectionHeader) == 40u, "Unexpected ELF32 section header layout."); -static_assert(sizeof(GuestExecData) == 16u, "Unexpected GuestExecData layout."); - -struct SifModuleRecord -{ - int32_t id = 0; - std::string path; - std::string pathKey; - uint32_t refCount = 0; - bool loaded = false; -}; - -static std::mutex g_sif_module_mutex; -static std::unordered_map g_sif_modules_by_id; -static std::unordered_map g_sif_module_id_by_path; -static int32_t g_next_sif_module_id = 1; -static uint32_t g_sif_module_log_count = 0; - -namespace -{ - std::string readGuestCStringBounded(const uint8_t *rdram, uint32_t guestAddr, size_t maxBytes) - { - std::string out; - if (!rdram || guestAddr == 0 || maxBytes == 0) - { - return out; - } - - out.reserve(maxBytes); - for (size_t i = 0; i < maxBytes; ++i) - { - const char ch = static_cast(rdram[(guestAddr + static_cast(i)) & PS2_RAM_MASK]); - if (ch == '\0') - { - break; - } - out.push_back(ch); - } - return out; - } - - std::string normalizeSifModulePathKey(const std::string &path) - { - return toLowerAscii(normalizePs2PathSuffix(path)); - } - - uint64_t hashGuestBytesFnv1a64(const uint8_t *rdram, uint32_t guestAddr, size_t byteCount) - { - constexpr uint64_t kOffset = 1469598103934665603ull; - constexpr uint64_t kPrime = 1099511628211ull; - - if (!rdram || guestAddr == 0 || byteCount == 0) - { - return 0ull; - } - - uint64_t hash = kOffset; - for (size_t i = 0; i < byteCount; ++i) - { - const uint8_t b = rdram[(guestAddr + static_cast(i)) & PS2_RAM_MASK]; - hash ^= static_cast(b); - hash *= kPrime; - } - return hash; - } - - std::string makeSifModuleBufferTag(const uint8_t *rdram, uint32_t bufferAddr) - { - char key[96] = {}; - const uint64_t hash = hashGuestBytesFnv1a64(rdram, bufferAddr, kSifModuleBufferProbeBytes); - std::snprintf(key, sizeof(key), "iopbuf:fnv64:%016llx", static_cast(hash)); - return std::string(key); - } - - void logSifModuleAction(const char *op, int32_t moduleId, const std::string &path, uint32_t refCount) - { - if (!op) - { - return; - } - - std::lock_guard lock(g_sif_module_mutex); - if (g_sif_module_log_count >= kMaxSifModuleLogs) - { - return; - } - - std::cout << "[SIF module] " << op - << " id=" << moduleId - << " ref=" << refCount - << " path=\"" << path << "\"" - << std::endl; - ++g_sif_module_log_count; - } - - int32_t trackSifModuleLoad(const std::string &path) - { - if (path.empty()) - { - return -1; - } - - const std::string pathKey = normalizeSifModulePathKey(path); - if (pathKey.empty()) - { - return -1; - } - - std::lock_guard lock(g_sif_module_mutex); - - auto byPathIt = g_sif_module_id_by_path.find(pathKey); - if (byPathIt != g_sif_module_id_by_path.end()) - { - auto byIdIt = g_sif_modules_by_id.find(byPathIt->second); - if (byIdIt != g_sif_modules_by_id.end()) - { - SifModuleRecord &record = byIdIt->second; - record.loaded = true; - ++record.refCount; - return record.id; - } - } - - if (g_next_sif_module_id <= 0) - { - g_next_sif_module_id = 1; - } - - const int32_t moduleId = g_next_sif_module_id++; - SifModuleRecord record; - record.id = moduleId; - record.path = path; - record.pathKey = pathKey; - record.refCount = 1; - record.loaded = true; - - g_sif_module_id_by_path[pathKey] = moduleId; - g_sif_modules_by_id[moduleId] = record; - return moduleId; - } - - bool trackSifModuleStop(int32_t moduleId, uint32_t *remainingRefs = nullptr) - { - if (moduleId <= 0) - { - if (remainingRefs) - { - *remainingRefs = 0; - } - return false; - } - - 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()) - { - if (remainingRefs) - { - *remainingRefs = 0; - } - return false; - } - - SifModuleRecord &record = it->second; - if (record.refCount > 0) - { - --record.refCount; - } - record.loaded = (record.refCount != 0); - - if (remainingRefs) - { - *remainingRefs = record.refCount; - } - return true; - } - - bool readFileBlockAt(std::ifstream &file, uint64_t offset, void *dst, size_t byteCount) - { - if (!dst || byteCount == 0) - { - return false; - } - - file.seekg(static_cast(offset), std::ios::beg); - if (!file) - { - return false; - } - - file.read(reinterpret_cast(dst), static_cast(byteCount)); - return file.gcount() == static_cast(byteCount); - } - - bool tryExtractElfGpValue(std::ifstream &file, const Elf32Header &header, uint32_t &gpOut) - { - uint8_t regInfo[24] = {}; - - for (uint32_t i = 0; i < header.phnum; ++i) - { - Elf32ProgramHeader ph{}; - const uint64_t phOffset = static_cast(header.phoff) + static_cast(i) * header.phentsize; - if (!readFileBlockAt(file, phOffset, &ph, sizeof(ph))) - { - return false; - } - - if (ph.type == kElfPtMipsRegInfo && ph.filesz >= sizeof(regInfo)) - { - if (!readFileBlockAt(file, ph.offset, regInfo, sizeof(regInfo))) - { - return false; - } - std::memcpy(&gpOut, regInfo + 20u, sizeof(gpOut)); - return true; - } - } - - for (uint32_t i = 0; i < header.shnum; ++i) - { - Elf32SectionHeader sh{}; - const uint64_t shOffset = static_cast(header.shoff) + static_cast(i) * header.shentsize; - if (!readFileBlockAt(file, shOffset, &sh, sizeof(sh))) - { - return false; - } - - if (sh.type == kElfShtMipsRegInfo && sh.size >= sizeof(regInfo)) - { - if (!readFileBlockAt(file, sh.offset, regInfo, sizeof(regInfo))) - { - return false; - } - std::memcpy(&gpOut, regInfo + 20u, sizeof(gpOut)); - return true; - } - } - - return false; - } - - bool loadElfIntoGuestMemory(const std::string &hostPath, - uint8_t *rdram, - PS2Runtime *runtime, - const std::string §ionName, - GuestExecData &execDataOut, - std::string &errorOut) - { - if (!rdram || hostPath.empty()) - { - errorOut = "invalid path or RDRAM pointer"; - return false; - } - - std::ifstream file(hostPath, std::ios::binary); - if (!file) - { - errorOut = "failed to open ELF"; - return false; - } - - Elf32Header header{}; - if (!readFileBlockAt(file, 0, &header, sizeof(header))) - { - errorOut = "failed to read ELF header"; - return false; - } - - if (header.magic != kElfMagic || header.machine != kElfMachineMips || header.type != kElfTypeExec) - { - errorOut = "not a MIPS executable ELF"; - return false; - } - - bool loadedAny = false; - const bool loadAll = sectionName.empty() || toLowerAscii(sectionName) == "all"; - static uint32_t secFilterLogCount = 0; - if (!loadAll && secFilterLogCount < 8u) - { - std::cout << "[SifLoadElfPart] section filter \"" << sectionName - << "\" requested; loading PT_LOAD segments only." << std::endl; - ++secFilterLogCount; - } - - for (uint32_t i = 0; i < header.phnum; ++i) - { - Elf32ProgramHeader ph{}; - const uint64_t phOffset = static_cast(header.phoff) + static_cast(i) * header.phentsize; - if (!readFileBlockAt(file, phOffset, &ph, sizeof(ph))) - { - errorOut = "failed to read ELF program headers"; - return false; - } - - if (ph.type != kElfPtLoad || ph.memsz == 0u) - { - continue; - } - if (ph.filesz > ph.memsz) - { - errorOut = "ELF segment filesz > memsz"; - return false; - } - - const uint64_t memSize64 = static_cast(ph.memsz); - if (runtime && ph.vaddr >= PS2_SCRATCHPAD_BASE && ph.vaddr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) - { - const uint32_t scratchOffset = runtime->memory().translateAddress(ph.vaddr); - if (static_cast(scratchOffset) + memSize64 > PS2_SCRATCHPAD_SIZE) - { - errorOut = "ELF scratchpad segment out of range"; - return false; - } - - uint8_t *dest = runtime->memory().getScratchpad() + scratchOffset; - if (ph.filesz > 0u) - { - if (!readFileBlockAt(file, ph.offset, dest, ph.filesz)) - { - errorOut = "failed to read ELF segment payload"; - return false; - } - } - if (ph.memsz > ph.filesz) - { - std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz); - } - } - else - { - const uint32_t physAddr = runtime ? runtime->memory().translateAddress(ph.vaddr) : (ph.vaddr & PS2_RAM_MASK); - if (static_cast(physAddr) + memSize64 > PS2_RAM_SIZE) - { - errorOut = "ELF RDRAM segment out of range"; - return false; - } - - uint8_t *dest = rdram + physAddr; - if (ph.filesz > 0u) - { - if (!readFileBlockAt(file, ph.offset, dest, ph.filesz)) - { - errorOut = "failed to read ELF segment payload"; - return false; - } - } - if (ph.memsz > ph.filesz) - { - std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz); - } - } - - loadedAny = true; - } - - if (!loadedAny) - { - errorOut = "ELF has no loadable segments"; - return false; - } - - execDataOut.epc = header.entry; - execDataOut.gp = 0u; - execDataOut.sp = 0u; - execDataOut.dummy = 0u; - - uint32_t gpValue = 0u; - if (tryExtractElfGpValue(file, header, gpValue)) - { - execDataOut.gp = gpValue; - } - - return true; - } - - int32_t runSifLoadElfPart(uint8_t *rdram, - R5900Context *ctx, - PS2Runtime *runtime, - uint32_t pathAddr, - const std::string §ionName, - uint32_t execDataAddr) - { - if (!rdram || !ctx) - { - return -1; - } - - const std::string ps2Path = readGuestCStringBounded(rdram, pathAddr, kLoadfilePathMaxBytes); - if (ps2Path.empty()) - { - return -1; - } - - const std::string hostPath = translatePs2Path(ps2Path.c_str()); - if (hostPath.empty()) - { - return -1; - } - - GuestExecData execData{}; - std::string loadError; - if (!loadElfIntoGuestMemory(hostPath, rdram, runtime, sectionName, execData, loadError)) - { - static uint32_t logCount = 0; - if (logCount < 16u) - { - std::cerr << "[SifLoadElfPart] failed path=\"" << ps2Path << "\" host=\"" << hostPath - << "\" reason=" << loadError << std::endl; - ++logCount; - } - return -1; - } - - if (execData.gp == 0u) - { - execData.gp = getRegU32(ctx, 28); - } - execData.sp = getRegU32(ctx, 29); - - if (execDataAddr != 0u) - { - GuestExecData *guestExec = reinterpret_cast(getMemPtr(rdram, execDataAddr)); - if (!guestExec) - { - return -1; - } - std::memcpy(guestExec, &execData, sizeof(execData)); - } - - static uint32_t successLogs = 0; - if (successLogs < 16u) - { - std::cout << "[SifLoadElfPart] loaded \"" << ps2Path << "\" epc=0x" - << std::hex << execData.epc << " gp=0x" << execData.gp << std::dec << std::endl; - ++successLogs; - } - - return 0; - } -} - -namespace -{ - struct ThreadExitException final : public std::exception - { - const char *what() const noexcept override - { - return "PS2 Thread Exit"; - } - }; -} - -static void applySuspendStatusLocked(ThreadInfo &info) -{ - if (info.waitType != TSW_NONE) - { - info.status = THS_WAITSUSPEND; - } - else - { - info.status = THS_SUSPEND; - } -} - -static void throwIfTerminated(const std::shared_ptr &info) -{ - if (info && info->terminated.load()) - { - throw ThreadExitException(); - } -} - -static void waitWhileSuspended(const std::shared_ptr &info) -{ - if (!info) - return; - - std::unique_lock lock(info->m); - if (info->suspendCount > 0) - { - info->status = THS_SUSPEND; - info->waitType = TSW_NONE; - info->waitId = 0; - info->cv.wait(lock, [&]() - { return info->suspendCount == 0 || info->terminated.load(); }); - if (info->terminated.load()) - { - throw ThreadExitException(); - } - info->status = THS_RUN; - } -} - -static std::shared_ptr lookupThreadInfo(int tid) -{ - std::lock_guard lock(g_thread_map_mutex); - auto it = g_threads.find(tid); - if (it != g_threads.end()) - { - return it->second; - } - return nullptr; -} - -static std::shared_ptr ensureCurrentThreadInfo(R5900Context *ctx) -{ - const int tid = g_currentThreadId; - std::lock_guard lock(g_thread_map_mutex); - auto it = g_threads.find(tid); - if (it != g_threads.end()) - { - return it->second; - } - - auto info = std::make_shared(); - info->started = true; - info->status = THS_RUN; - info->currentPriority = info->priority; - info->suspendCount = 0; - if (ctx) - { - info->entry = ctx->pc; - info->stack = getRegU32(ctx, 29); - info->gp = getRegU32(ctx, 28); - } - info->waitType = TSW_NONE; - info->waitId = 0; - - g_threads.emplace(tid, info); - return info; -} - -static std::shared_ptr lookupSemaInfo(int sid) -{ - std::lock_guard lock(g_sema_map_mutex); - auto it = g_semas.find(sid); - if (it != g_semas.end()) - { - return it->second; - } - return nullptr; -} - -static std::shared_ptr lookupEventFlagInfo(int eid) -{ - std::lock_guard lock(g_event_flag_map_mutex); - auto it = g_eventFlags.find(eid); - if (it != g_eventFlags.end()) - { - return it->second; - } - return nullptr; -} - -static void setRegU32(R5900Context *ctx, int reg, uint32_t value) -{ - if (reg < 0 || reg > 31) - return; - ctx->r[reg] = _mm_set_epi32(0, 0, 0, value); -} - -static std::chrono::microseconds alarmTicksToDuration(uint16_t ticks) -{ - constexpr uint64_t kAlarmTickUsec = 64u; // Approximate EE H-SYNC tick period. - const uint64_t clampedTicks = (ticks == 0u) ? 1u : static_cast(ticks); - return std::chrono::microseconds(clampedTicks * kAlarmTickUsec); -} - -static void ensureAlarmWorkerRunning() -{ - std::call_once(g_alarm_worker_once, []() - { std::thread([]() - { - for (;;) - { - std::shared_ptr readyAlarm; - { - std::unique_lock lock(g_alarm_mutex); - while (!readyAlarm) - { - if (g_alarms.empty()) - { - g_alarm_cv.wait(lock); - continue; - } - - auto nextIt = std::min_element(g_alarms.begin(), g_alarms.end(), - [](const auto &a, const auto &b) - { - return a.second->dueAt < b.second->dueAt; - }); - if (nextIt == g_alarms.end()) - { - g_alarm_cv.wait(lock); - continue; - } - - const auto now = std::chrono::steady_clock::now(); - if (nextIt->second->dueAt > now) - { - g_alarm_cv.wait_until(lock, nextIt->second->dueAt); - continue; - } - - readyAlarm = nextIt->second; - g_alarms.erase(nextIt); - } - } - - if (!readyAlarm || !readyAlarm->runtime || !readyAlarm->rdram || !readyAlarm->handler) - { - continue; - } - if (!readyAlarm->runtime->hasFunction(readyAlarm->handler)) - { - continue; - } - - try - { - R5900Context callbackCtx{}; - setRegU32(&callbackCtx, 28, readyAlarm->gp); - setRegU32(&callbackCtx, 29, readyAlarm->sp); - setRegU32(&callbackCtx, 31, 0); - setRegU32(&callbackCtx, 4, static_cast(readyAlarm->id)); - setRegU32(&callbackCtx, 5, static_cast(readyAlarm->ticks)); - setRegU32(&callbackCtx, 6, readyAlarm->commonArg); - setRegU32(&callbackCtx, 7, 0); - callbackCtx.pc = readyAlarm->handler; - - PS2Runtime::RecompiledFunction func = readyAlarm->runtime->lookupFunction(readyAlarm->handler); - func(readyAlarm->rdram, &callbackCtx, readyAlarm->runtime); - } - catch (const ThreadExitException &) - { - } - catch (const std::exception &e) - { - static int alarmExceptionLogs = 0; - if (alarmExceptionLogs < 8) - { - std::cerr << "[SetAlarm] callback exception: " << e.what() << std::endl; - ++alarmExceptionLogs; - } - } - } }) - .detach(); }); -} - -static void rpcCopyToRdram(uint8_t *rdram, uint32_t dst, uint32_t src, size_t size) -{ - if (!rdram || size == 0) - return; - - constexpr size_t kMaxRpcTransferBytes = 1u * 1024u * 1024u; - const size_t clampedSize = std::min(size, kMaxRpcTransferBytes); - if (clampedSize != size) - { - static uint32_t warnCount = 0; - if (warnCount < 8) - { - std::cerr << "[SifCallRpc] clamping copy size from " << size - << " to " << clampedSize - << " bytes (dst=0x" << std::hex << dst - << " src=0x" << src << std::dec << ")" << std::endl; - ++warnCount; - } - } - - for (size_t i = 0; i < clampedSize; ++i) - { - const uint32_t dstAddr = dst + static_cast(i); - const uint32_t srcAddr = src + static_cast(i); - uint8_t *dstPtr = getMemPtr(rdram, dstAddr); - const uint8_t *srcPtr = getConstMemPtr(rdram, srcAddr); - if (!dstPtr || !srcPtr) - { - break; - } - *dstPtr = *srcPtr; - } -} - -static void rpcZeroRdram(uint8_t *rdram, uint32_t dst, size_t size) -{ - if (!rdram || size == 0) - return; - - constexpr size_t kMaxRpcTransferBytes = 1u * 1024u * 1024u; - const size_t clampedSize = std::min(size, kMaxRpcTransferBytes); - if (clampedSize != size) - { - static uint32_t warnCount = 0; - if (warnCount < 8) - { - std::cerr << "[SifCallRpc] clamping zero size from " << size - << " to " << clampedSize - << " bytes (dst=0x" << std::hex << dst << std::dec << ")" << std::endl; - ++warnCount; - } - } - - for (size_t i = 0; i < clampedSize; ++i) - { - const uint32_t dstAddr = dst + static_cast(i); - uint8_t *dstPtr = getMemPtr(rdram, dstAddr); - if (!dstPtr) - { - break; - } - *dstPtr = 0; - } -} - -static bool readStackU32(uint8_t *rdram, uint32_t sp, uint32_t offset, uint32_t &out) -{ - uint8_t *ptr = getMemPtr(rdram, sp + offset); - if (!ptr) - return false; - out = *reinterpret_cast(ptr); - return true; -} - -static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, - uint32_t funcAddr, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t *outV0) -{ - if (!runtime || !funcAddr || !runtime->hasFunction(funcAddr)) - return false; - - R5900Context tmp = *ctx; - setRegU32(&tmp, 4, a0); - setRegU32(&tmp, 5, a1); - setRegU32(&tmp, 6, a2); - setRegU32(&tmp, 7, a3); - tmp.pc = funcAddr; - - PS2Runtime::RecompiledFunction func = runtime->lookupFunction(funcAddr); - func(rdram, &tmp, runtime); - - if (outV0) - { - *outV0 = getRegU32(&tmp, 2); - } - return true; -} - -static uint32_t rpcAllocPacketAddr(uint8_t *rdram) -{ - if (kRpcPacketPoolCount == 0) - return 0; - - uint32_t slot = g_rpc_packet_index++ % kRpcPacketPoolCount; - uint32_t addr = kRpcPacketPoolBase + (slot * kRpcPacketSize); - rpcZeroRdram(rdram, addr, kRpcPacketSize); - return addr; -} - -static uint32_t rpcAllocServerAddr(uint8_t *rdram) -{ - if (kRpcServerPoolCount == 0) - return 0; - - uint32_t slot = g_rpc_server_index++ % kRpcServerPoolCount; - uint32_t addr = kRpcServerPoolBase + (slot * kRpcServerStride); - rpcZeroRdram(rdram, addr, kRpcServerStride); - return addr; -} - -struct IrqHandlerInfo -{ - uint32_t cause = 0; - uint32_t handler = 0; - uint32_t arg = 0; - bool enabled = true; -}; - -static std::unordered_map g_intcHandlers; -static std::unordered_map g_dmacHandlers; -static int g_nextIntcHandlerId = 1; -static int g_nextDmacHandlerId = 1; - -int allocatePs2Fd(FILE *file) -{ - if (!file) - return -1; - - std::lock_guard lock(g_fd_mutex); - int fd = g_nextFd++; - g_fileDescriptors[fd] = file; - return fd; -} - -FILE *getHostFile(int ps2Fd) -{ - std::lock_guard lock(g_fd_mutex); - auto it = g_fileDescriptors.find(ps2Fd); - if (it != g_fileDescriptors.end()) - { - return it->second; - } - return nullptr; -} - -void releasePs2Fd(int ps2Fd) -{ - std::lock_guard lock(g_fd_mutex); - g_fileDescriptors.erase(ps2Fd); -} - -const char *translateFioMode(int ps2Flags) -{ - bool read = (ps2Flags & PS2_FIO_O_RDONLY) || (ps2Flags & PS2_FIO_O_RDWR); - bool write = (ps2Flags & PS2_FIO_O_WRONLY) || (ps2Flags & PS2_FIO_O_RDWR); - bool append = (ps2Flags & PS2_FIO_O_APPEND); - bool create = (ps2Flags & PS2_FIO_O_CREAT); - bool truncate = (ps2Flags & PS2_FIO_O_TRUNC); - - if (read && write) - { - if (create && truncate) - return "w+b"; - if (create) - return "a+b"; - return "r+b"; - } - else if (write) - { - if (append) - return "ab"; - if (create && truncate) - return "wb"; - if (create) - return "wx"; - return "r+b"; - } - else if (read) - { - return "rb"; - } - return "rb"; -} - -std::string translatePs2Path(const char *ps2Path) -{ - if (!ps2Path || !*ps2Path) - { - return {}; - } - - std::string pathStr(ps2Path); - std::string lower = toLowerAscii(pathStr); - - auto resolveWithBase = [&](const std::filesystem::path &base, const std::string &suffix) -> std::string - { - const std::string normalizedSuffix = normalizePs2PathSuffix(suffix); - std::filesystem::path resolved = base; - if (!normalizedSuffix.empty()) - { - resolved /= std::filesystem::path(normalizedSuffix); - } - return resolved.lexically_normal().string(); - }; - - if (lower.rfind("host0:", 0) == 0 || lower.rfind("host:", 0) == 0) - { - const std::size_t prefixLength = (lower.rfind("host0:", 0) == 0) ? 6 : 5; - return resolveWithBase(getConfiguredHostRoot(), pathStr.substr(prefixLength)); - } - - if (lower.rfind("cdrom0:", 0) == 0 || lower.rfind("cdrom:", 0) == 0) - { - const std::size_t prefixLength = (lower.rfind("cdrom0:", 0) == 0) ? 7 : 6; - return resolveWithBase(getConfiguredCdRoot(), pathStr.substr(prefixLength)); - } - - if (!pathStr.empty() && (pathStr.front() == '/' || pathStr.front() == '\\')) - { - return resolveWithBase(getConfiguredCdRoot(), pathStr); - } - - if (pathStr.size() > 1 && pathStr[1] == ':') - { - return pathStr; - } - - return resolveWithBase(getConfiguredCdRoot(), pathStr); -} - -static bool localtimeSafe(const std::time_t *t, std::tm *out) -{ -#ifdef _WIN32 - return localtime_s(out, t) == 0; -#else - return localtime_r(t, out) != nullptr; -#endif -} - -static void encodePs2Time(std::time_t t, uint8_t out[8]) -{ - std::tm tm{}; - if (!localtimeSafe(&t, &tm)) - { - std::memset(out, 0, 8); - return; - } - - uint16_t year = static_cast(tm.tm_year + 1900); - out[0] = 0; - out[1] = static_cast(tm.tm_sec); - out[2] = static_cast(tm.tm_min); - out[3] = static_cast(tm.tm_hour); - out[4] = static_cast(tm.tm_mday); - out[5] = static_cast(tm.tm_mon + 1); - out[6] = static_cast(year & 0xFF); - out[7] = static_cast((year >> 8) & 0xFF); -} - -static std::time_t fileTimeToTimeT(std::filesystem::file_time_type ft) -{ - auto sctp = std::chrono::time_point_cast( - ft - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); - return std::chrono::system_clock::to_time_t(sctp); -} - -static bool gmtimeSafe(const std::time_t *t, std::tm *out) -{ -#ifdef _WIN32 - return gmtime_s(out, t) == 0; -#else - return gmtime_r(t, out) != nullptr; -#endif -} - -static int getTimezoneOffsetMinutes() -{ - std::time_t now = std::time(nullptr); - std::tm local{}; - std::tm gmt{}; - if (!localtimeSafe(&now, &local) || !gmtimeSafe(&now, &gmt)) - return 0; - - std::time_t localTime = std::mktime(&local); - std::time_t gmtTime = std::mktime(&gmt); - if (localTime == static_cast(-1) || gmtTime == static_cast(-1)) - return 0; - - double diff = std::difftime(localTime, gmtTime); - return static_cast(diff / 60.0); -} - -static uint32_t packOsdConfig(uint32_t spdifMode, uint32_t screenType, uint32_t videoOutput, - uint32_t japLanguage, uint32_t ps1drvConfig, uint32_t version, - uint32_t language, int timezoneOffset) -{ - uint32_t raw = 0; - raw |= (spdifMode & 0x1) << 0; - raw |= (screenType & 0x3) << 1; - raw |= (videoOutput & 0x1) << 3; - raw |= (japLanguage & 0x1) << 4; - raw |= (ps1drvConfig & 0xFF) << 5; - raw |= (version & 0x7) << 13; - raw |= (language & 0x1F) << 16; - raw |= (static_cast(timezoneOffset) & 0x7FF) << 21; - return raw; -} - -static int decodeTimezoneOffset(uint32_t raw) -{ - int tz = static_cast((raw >> 21) & 0x7FF); - if (tz & 0x400) - tz |= ~0x7FF; - return tz; -} - -static int clampTimezoneOffset(int tz) -{ - if (tz < -1024) - return -1024; - if (tz > 1023) - return 1023; - return tz; -} - -static uint32_t sanitizeOsdConfigRaw(uint32_t raw) -{ - uint32_t spdifMode = raw & 0x1; - uint32_t screenType = (raw >> 1) & 0x3; - if (screenType > 2) - screenType = 0; - uint32_t videoOutput = (raw >> 3) & 0x1; - uint32_t japLanguage = (raw >> 4) & 0x1; - uint32_t ps1drvConfig = (raw >> 5) & 0xFF; - uint32_t version = (raw >> 13) & 0x7; - if (version > 2) - version = 1; - uint32_t language = (raw >> 16) & 0x1F; - int tz = clampTimezoneOffset(decodeTimezoneOffset(raw)); - return packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz); -} - -static void ensureOsdConfigInitialized() -{ - std::lock_guard lock(g_osd_mutex); - if (g_osd_config_initialized) - return; - - int tz = clampTimezoneOffset(getTimezoneOffsetMinutes()); - uint32_t spdifMode = 1; // disabled - uint32_t screenType = 0; // 4:3 - uint32_t videoOutput = 0; // RGB - uint32_t japLanguage = 1; // non-japanese - uint32_t ps1drvConfig = 0; - uint32_t version = 1; // OSD2 - uint32_t language = 1; // English - g_osd_config_raw = packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz); - g_osd_config_initialized = true; -} - -static uint32_t allocTlsAddr(uint8_t *rdram) -{ - if (!rdram || kTlsPoolCount == 0) - return 0; - - std::lock_guard lock(g_tls_mutex); - uint32_t slot = g_tls_index++ % kTlsPoolCount; - uint32_t addr = kTlsPoolBase + (slot * kTlsBlockSize); - rpcZeroRdram(rdram, addr, kTlsBlockSize); - return addr; -} - -static uint32_t allocBootModeAddr(uint8_t *rdram, size_t bytes) -{ - if (!rdram) - return 0; - - size_t aligned = (bytes + 15u) & ~15u; - if (g_bootmode_pool_offset + aligned > kBootModePoolBytes) - return 0; - - uint32_t addr = kBootModePoolBase + g_bootmode_pool_offset; - g_bootmode_pool_offset += static_cast(aligned); - rpcZeroRdram(rdram, addr, aligned); - return addr; -} - -static uint32_t createBootModeEntry(uint8_t *rdram, uint8_t id, uint16_t value, uint8_t lenField, const uint32_t *data, uint8_t dataCount) -{ - uint8_t allocCount = (dataCount == 0) ? 1 : dataCount; - size_t bytes = static_cast(1 + allocCount) * sizeof(uint32_t); - uint32_t addr = allocBootModeAddr(rdram, bytes); - if (!addr) - return 0; - - uint32_t header = (static_cast(lenField) << 24) | - (static_cast(id) << 16) | - (static_cast(value) & 0xFFFFu); - - uint32_t *dst = reinterpret_cast(getMemPtr(rdram, addr)); - if (!dst) - return 0; - - dst[0] = header; - for (uint8_t i = 0; i < allocCount; ++i) - { - dst[1 + i] = (data && i < dataCount) ? data[i] : 0; - } - - return addr; -} - -static void ensureBootModeTable(uint8_t *rdram) -{ - std::lock_guard lock(g_bootmode_mutex); - if (g_bootmode_initialized) - return; - - g_bootmode_pool_offset = 0; - g_bootmode_addresses.clear(); - - const uint32_t boot3Data[1] = {0}; - const uint32_t boot5Data[1] = {0}; - - g_bootmode_addresses[1] = createBootModeEntry(rdram, 1, 0, 0, nullptr, 0); - g_bootmode_addresses[3] = createBootModeEntry(rdram, 3, 0, 1, boot3Data, 1); - g_bootmode_addresses[4] = createBootModeEntry(rdram, 4, 0, 0, nullptr, 0); - g_bootmode_addresses[5] = createBootModeEntry(rdram, 5, 0, 1, boot5Data, 1); - g_bootmode_addresses[6] = createBootModeEntry(rdram, 6, 0, 0, nullptr, 0); - g_bootmode_addresses[7] = createBootModeEntry(rdram, 7, 0, 0, nullptr, 0); - - g_bootmode_initialized = true; -} - -static void runExitHandlersForThread(int tid, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) -{ - if (!runtime || !ctx) - return; - - std::vector handlers; - { - std::lock_guard lock(g_exit_handler_mutex); - auto it = g_exit_handlers.find(tid); - if (it == g_exit_handlers.end()) - return; - handlers = std::move(it->second); - g_exit_handlers.erase(it); - } - - for (const auto &handler : handlers) - { - if (!handler.func) - continue; - try - { - rpcInvokeFunction(rdram, ctx, runtime, handler.func, handler.arg, 0, 0, 0, nullptr); - } - catch (const ThreadExitException &) - { - // ignore - } - catch (const std::exception &) - { - } - } -} +#include "syscalls/helpers/ps2_syscalls_helpers_path.inl" +#include "syscalls/helpers/ps2_syscalls_helpers_state.inl" +#include "syscalls/helpers/ps2_syscalls_helpers_loader.inl" +#include "syscalls/helpers/ps2_syscalls_helpers_runtime.inl" namespace ps2_syscalls { - // for some bizarre case I have to duplicate this here - void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); - void EndOfHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime); +#include "syscalls/ps2_syscalls_interrupt.inl" +#include "syscalls/ps2_syscalls_system.inl" bool dispatchNumericSyscall(uint32_t syscallNumber, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) { @@ -1916,3306 +274,8 @@ namespace ps2_syscalls } } - void FlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - std::cerr << "Syscall: ResetEE - Halting Execution (Not fully implemented)" << std::endl; - exit(0); // Should we exit or just halt the execution? - } - - void SetMemoryMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 points to ThreadParam - const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); - - if (!param) - { - std::cerr << "CreateThread error: invalid ThreadParam address 0x" << std::hex << paramAddr << std::dec << std::endl; - setReturnS32(ctx, -1); - return; - } - - auto info = std::make_shared(); - info->attr = param[0]; - info->entry = param[1]; - info->stack = param[2]; - info->stackSize = param[3]; - - auto looksLikeGuestPtr = [](uint32_t v) -> bool - { - if (v == 0) - { - return true; - } - const uint32_t norm = v & 0x1FFFFFFFu; - return norm < PS2_RAM_SIZE && norm >= 0x10000u; - }; - - auto looksLikePriority = [](uint32_t v) -> bool - { - // Typical EE priorities are very small integers (1..127). - return v <= 0x400u; - }; - - const uint32_t gpA = param[4]; - const uint32_t prioA = param[5]; - const uint32_t gpB = param[5]; - const uint32_t prioB = param[4]; - - // Prefer the standard EE layout (gp at +0x10, priority at +0x14), - // but keep a fallback for callsites that used the swapped decode. - if (looksLikeGuestPtr(gpA) && looksLikePriority(prioA)) - { - info->gp = gpA; - info->priority = prioA; - } - else if (looksLikeGuestPtr(gpB) && looksLikePriority(prioB)) - { - info->gp = gpB; - info->priority = prioB; - } - else - { - info->gp = gpA; - info->priority = prioA; - } - - info->option = param[6]; - info->currentPriority = static_cast(info->priority); - - int id = 0; - { - std::lock_guard lock(g_thread_map_mutex); - id = g_nextThreadId++; - g_threads[id] = info; - } - - std::cout << "[CreateThread] id=" << id - << " entry=0x" << std::hex << info->entry - << " stack=0x" << info->stack - << " size=0x" << info->stackSize - << " gp=0x" << info->gp - << " prio=" << std::dec << info->priority << std::endl; - - setReturnS32(ctx, id); - } - - void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); // $a0 - auto info = lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } - - { - std::lock_guard lock(info->m); - if (info->status != THS_DORMANT) - { - setReturnS32(ctx, KE_NOT_WAIT); // for now - return; - } - } - - { - std::lock_guard lock(g_thread_map_mutex); - g_threads.erase(tid); - } - - setReturnS32(ctx, KE_OK); - } - - void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); // $a0 = thread id - uint32_t arg = getRegU32(ctx, 5); // $a1 = user arg - - auto info = lookupThreadInfo(tid); - if (!info) - { - std::cerr << "StartThread error: unknown thread id " << tid << std::endl; - setReturnS32(ctx, -1); - return; - } - - { - std::lock_guard lock(info->m); - if (info->started) - { - setReturnS32(ctx, tid); // Already started - return; - } - - info->started = true; - info->status = THS_RUN; - info->arg = arg; - } - - if (!runtime->hasFunction(info->entry)) - { - std::cerr << "[StartThread] entry 0x" << std::hex << info->entry << std::dec << " is not registered" << std::endl; - setReturnS32(ctx, -1); - return; - } - - const uint32_t callerSp = getRegU32(ctx, 29); - const uint32_t callerGp = getRegU32(ctx, 28); - - { - std::lock_guard lock(info->m); - if (info->stack == 0 && info->stackSize != 0) - { - const uint32_t autoStack = runtime->guestMalloc(info->stackSize, 16u); - if (autoStack != 0) - { - info->stack = autoStack; - std::cout << "[StartThread] id=" << tid - << " auto-stack=0x" << std::hex << autoStack - << " size=0x" << info->stackSize << std::dec << std::endl; - } - } - - if (info->stack != 0 && info->stackSize == 0) - { - // Some games leave size zero in the thread param even though a stack - // buffer is supplied; use a conservative default instead of caller SP. - info->stackSize = 0x800u; - } - } - - g_activeThreads.fetch_add(1, std::memory_order_relaxed); - std::thread([=]() mutable - { - { - std::string name = "PS2Thread_" + std::to_string(tid); - ThreadNaming::SetCurrentThreadName(name); - } - R5900Context threadCtxCopy{}; - R5900Context *threadCtx = &threadCtxCopy; - - uint32_t threadSp = callerSp; - if (info->stack) - { - const uint32_t stackSize = (info->stackSize != 0) ? info->stackSize : 0x800u; - threadSp = (info->stack + stackSize) & ~0xFu; - } - uint32_t threadGp = info->gp; - const uint32_t normalizedGp = threadGp & 0x1FFFFFFFu; - if (threadGp == 0 || normalizedGp < 0x10000u || normalizedGp >= PS2_RAM_SIZE) - { - threadGp = callerGp; - } - - SET_GPR_U32(threadCtx, 29, threadSp); - SET_GPR_U32(threadCtx, 28, threadGp); - SET_GPR_U32(threadCtx, 4, info->arg); - SET_GPR_U32(threadCtx, 31, 0); - threadCtx->pc = info->entry; - - PS2Runtime::RecompiledFunction func = runtime->lookupFunction(info->entry); - g_currentThreadId = tid; - - std::cout << "[StartThread] id=" << tid - << " entry=0x" << std::hex << info->entry - << " sp=0x" << GPR_U32(threadCtx, 29) - << " gp=0x" << GPR_U32(threadCtx, 28) - << " arg=0x" << info->arg << std::dec << std::endl; - - bool exited = false; - try - { - func(rdram, threadCtx, runtime); - } - catch (const ThreadExitException &) - { - exited = true; - } - catch (const std::exception &e) - { - std::cerr << "[StartThread] id=" << tid << " exception: " << e.what() << std::endl; - } - - if (!exited) - { - std::cout << "[StartThread] id=" << tid << " returned (pc=0x" - << std::hex << threadCtx->pc << std::dec << ")" << std::endl; - } - - runExitHandlersForThread(tid, rdram, threadCtx, runtime); - - { - std::lock_guard lock(info->m); - info->started = false; - info->status = THS_DORMANT; - } - - g_activeThreads.fetch_sub(1, std::memory_order_relaxed); }) - .detach(); - - // for now report success to the caller. - setReturnS32(ctx, 0); - } - - void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - runExitHandlersForThread(g_currentThreadId, rdram, ctx, runtime); - auto info = ensureCurrentThreadInfo(ctx); - if (info) - { - std::lock_guard lock(info->m); - info->terminated = true; - info->forceRelease = true; - info->status = THS_DORMANT; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - } - if (info) - { - info->cv.notify_all(); - } - throw ThreadExitException(); - } - - void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = g_currentThreadId; - runExitHandlersForThread(tid, rdram, ctx, runtime); - auto info = ensureCurrentThreadInfo(ctx); - if (info) - { - std::lock_guard lock(info->m); - info->terminated = true; - info->forceRelease = true; - info->status = THS_DORMANT; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - } - if (info) - { - info->cv.notify_all(); - } - { - std::lock_guard lock(g_thread_map_mutex); - g_threads.erase(tid); - } - throw ThreadExitException(); - } - - void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, -1); - return; - } - - { - std::lock_guard lock(info->m); - info->terminated = true; - info->forceRelease = true; - info->status = THS_DORMANT; - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount = 0; - } - info->cv.notify_all(); - - if (tid == g_currentThreadId) - { - runExitHandlersForThread(tid, rdram, ctx, runtime); - throw ThreadExitException(); - } - setReturnS32(ctx, 0); - } - - void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, -1); - return; - } - - { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) - { - setReturnS32(ctx, -1); - return; - } - info->suspendCount++; - applySuspendStatusLocked(*info); - } - info->cv.notify_all(); - - if (tid == g_currentThreadId) - { - std::unique_lock lock(info->m); - info->cv.wait(lock, [&]() - { return info->suspendCount == 0 || info->terminated.load(); }); - if (info->terminated.load()) - { - throw ThreadExitException(); - } - info->status = THS_RUN; - } - - setReturnS32(ctx, 0); - } - - void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, -1); - return; - } - - { - std::lock_guard lock(info->m); - if (info->suspendCount <= 0) - { - setReturnS32(ctx, -1); - return; - } - info->suspendCount--; - if (info->suspendCount == 0) - { - if (info->waitType != TSW_NONE) - { - info->status = THS_WAIT; - } - else - { - info->status = (tid == g_currentThreadId) ? THS_RUN : THS_READY; - } - } - } - info->cv.notify_all(); - setReturnS32(ctx, 0); - } - - void GetThreadId(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, g_currentThreadId); - } - - void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - uint32_t statusAddr = getRegU32(ctx, 5); - - if (tid == 0) // TH_SELF - { - tid = g_currentThreadId; - } - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, -1); - return; - } - - ee_thread_status_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); - if (!status) - { - setReturnS32(ctx, -1); - return; - } - - std::lock_guard lock(info->m); - status->status = info->status; - status->func = info->entry; - status->stack = info->stack; - status->stack_size = info->stackSize; - status->gp_reg = info->gp; - status->initial_priority = info->priority; - status->current_priority = info->currentPriority; - status->attr = info->attr; - status->option = info->option; - status->waitType = info->waitType; - status->waitId = info->waitId; - status->wakeupCount = info->wakeupCount; - setReturnS32(ctx, 0); - } - - void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - auto info = ensureCurrentThreadInfo(ctx); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } - - throwIfTerminated(info); - - int ret = 0; - std::unique_lock lock(info->m); - - if (info->wakeupCount > 0) - { - info->wakeupCount--; - info->status = THS_RUN; - info->waitType = TSW_NONE; - info->waitId = 0; - ret = 0; - } - else - { - info->status = THS_WAIT; - info->waitType = TSW_SLEEP; - info->waitId = 0; - info->forceRelease = false; - - info->cv.wait(lock, [&]() - { return info->wakeupCount > 0 || info->forceRelease.load() || info->terminated.load(); }); - - if (info->terminated.load()) - { - throw ThreadExitException(); - } - - info->status = THS_RUN; - info->waitType = TSW_NONE; - info->waitId = 0; - - if (info->forceRelease.load()) - { - info->forceRelease = false; - ret = KE_RELEASE_WAIT; - } - else - { - if (info->wakeupCount > 0) - info->wakeupCount--; - ret = 0; - } - } - - lock.unlock(); - waitWhileSuspended(info); - setReturnS32(ctx, ret); - } - - void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } - - { - std::lock_guard lock(info->m); - if (info->status == THS_DORMANT) - { - setReturnS32(ctx, KE_DORMANT); - return; - } - if (info->status == THS_WAIT && info->waitType == TSW_SLEEP) - { - if (info->suspendCount > 0) - { - info->status = THS_SUSPEND; - } - else - { - info->status = THS_READY; - } - info->waitType = TSW_NONE; - info->waitId = 0; - info->wakeupCount++; - info->cv.notify_one(); - } - else - { - info->wakeupCount++; - } - } - setReturnS32(ctx, 0); - } - - void iWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - WakeupThread(rdram, ctx, runtime); - } - - void CancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - tid = g_currentThreadId; - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, -1); - return; - } - - int previous = 0; - { - std::lock_guard lock(info->m); - previous = info->wakeupCount; - info->wakeupCount = 0; - } - setReturnS32(ctx, previous); - } - - void iCancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } - - auto info = lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } - - int previous = 0; - { - std::lock_guard lock(info->m); - previous = info->wakeupCount; - info->wakeupCount = 0; - } - setReturnS32(ctx, previous); - } - - void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - int newPrio = static_cast(getRegU32(ctx, 5)); - - if (tid == 0) - tid = g_currentThreadId; - - auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); - if (info) - { - int oldPrio = info->currentPriority; - info->currentPriority = newPrio; - setReturnS32(ctx, oldPrio); // Return old priority? - } - else - { - setReturnS32(ctx, -1); - } - } - - void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - static int logCount = 0; - int prio = static_cast(getRegU32(ctx, 4)); - if (logCount < 16) - { - std::cout << "[RotateThreadReadyQueue] prio=" << prio << std::endl; - ++logCount; - } - if (prio >= 128) - { - setReturnS32(ctx, -1); - return; - } - setReturnS32(ctx, 0); - } - - void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int tid = static_cast(getRegU32(ctx, 4)); - if (tid == 0) - { - setReturnS32(ctx, KE_ILLEGAL_THID); - return; - } - - auto info = lookupThreadInfo(tid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_THID); - return; - } - - bool wasWaiting = false; - int waitType = 0; - int waitId = 0; - - { - std::lock_guard lock(info->m); - if (info->status == THS_WAIT) - { - wasWaiting = true; - waitType = info->waitType; - waitId = info->waitId; - info->forceRelease = true; - info->waitType = TSW_NONE; - info->waitId = 0; - if (info->suspendCount > 0) - { - info->status = THS_SUSPEND; - } - else - { - info->status = THS_READY; - } - } - } - - if (!wasWaiting) - { - setReturnS32(ctx, KE_NOT_WAIT); - return; - } - - info->cv.notify_all(); - - if (waitType == TSW_SEMA) - { - auto sema = lookupSemaInfo(waitId); - if (sema) - { - sema->cv.notify_all(); - } - } - else if (waitType == TSW_EVENT) - { - auto eventFlag = lookupEventFlagInfo(waitId); - if (eventFlag) - { - eventFlag->cv.notify_all(); - } - } - setReturnS32(ctx, 0); - } - - void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ReleaseWaitThread(rdram, ctx, runtime); - } - - void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); - int init = 0; - int max = 1; - uint32_t attr = 0; - uint32_t option = 0; - - if (param) - { - // sceSemaParam layout commonly: attr(0), option(1), initCount(2), maxCount(3) - attr = param[0]; - option = param[1]; - init = static_cast(param[2]); - max = static_cast(param[3]); - } - if (max <= 0) - { - max = 1; - } - if (init > max) - { - init = max; - } - - int id = 0; - auto info = std::make_shared(); - info->count = init; - info->maxCount = max; - info->initCount = init; - info->attr = attr; - info->option = option; - - { - std::lock_guard lock(g_sema_map_mutex); - id = g_nextSemaId++; - g_semas.emplace(id, info); - } - std::cout << "[CreateSema] id=" << id << " init=" << init << " max=" << max << std::endl; - setReturnS32(ctx, id); - } - - void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int sid = static_cast(getRegU32(ctx, 4)); - std::shared_ptr sema; - - { - std::lock_guard lock(g_sema_map_mutex); - auto it = g_semas.find(sid); - if (it == g_semas.end()) - { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; - } - sema = it->second; - g_semas.erase(it); - } - - { - std::lock_guard lock(sema->m); - sema->deleted = true; - } - sema->cv.notify_all(); - - setReturnS32(ctx, KE_OK); - } - - void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int sid = static_cast(getRegU32(ctx, 4)); - auto sema = lookupSemaInfo(sid); - if (sema) - { - std::lock_guard lock(sema->m); - if (sema->count < sema->maxCount) - { - sema->count++; - } - sema->cv.notify_one(); - } - setReturnS32(ctx, 0); - } - - void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - SignalSema(rdram, ctx, runtime); - } - - void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int sid = static_cast(getRegU32(ctx, 4)); - auto sema = lookupSemaInfo(sid); - if (!sema) - { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; - } - - auto info = ensureCurrentThreadInfo(ctx); - throwIfTerminated(info); - std::unique_lock lock(sema->m); - int ret = 0; - - if (sema->count == 0) - { - if (info) - { - std::lock_guard tLock(info->m); - info->status = THS_WAIT; - info->waitType = TSW_SEMA; - info->waitId = sid; - info->forceRelease = false; - } - - sema->waiters++; - sema->cv.wait(lock, [&]() - { - bool forced = info ? info->forceRelease.load() : false; - bool terminated = info ? info->terminated.load() : false; - return sema->count > 0 || sema->deleted || forced || terminated; // - }); - sema->waiters--; - if (sema->deleted) - { - ret = KE_WAIT_DELETE; - } - - if (info) - { - std::lock_guard tLock(info->m); - info->status = THS_RUN; - info->waitType = TSW_NONE; - info->waitId = 0; - if (info->forceRelease) - { - info->forceRelease = false; - ret = KE_RELEASE_WAIT; - } - } - - if (info && info->terminated.load()) - { - throw ThreadExitException(); - } - } - - if (ret == 0 && sema->count > 0) - { - sema->count--; - } - lock.unlock(); - waitWhileSuspended(info); - setReturnS32(ctx, ret); - } - - void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int sid = static_cast(getRegU32(ctx, 4)); - auto sema = lookupSemaInfo(sid); - if (!sema) - { - setReturnS32(ctx, KE_UNKNOWN_SEMID); - return; - } - - std::lock_guard lock(sema->m); - if (sema->count > 0) - { - sema->count--; - setReturnS32(ctx, KE_OK); - return; - } - - setReturnS32(ctx, KE_SEMA_ZERO); - } - - void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - PollSema(rdram, ctx, runtime); - } - - void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int sid = static_cast(getRegU32(ctx, 4)); - uint32_t statusAddr = getRegU32(ctx, 5); - - auto sema = lookupSemaInfo(sid); - if (!sema) - { - setReturnS32(ctx, -1); - return; - } - - ee_sema_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); - if (!status) - { - setReturnS32(ctx, -1); - return; - } - - std::lock_guard lock(sema->m); - status->count = sema->count; - status->max_count = sema->maxCount; - status->init_count = sema->initCount; - status->wait_threads = sema->waiters; - status->attr = sema->attr; - status->option = sema->option; - setReturnS32(ctx, 0); - } - - void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ReferSemaStatus(rdram, ctx, runtime); - } - - constexpr uint32_t WEF_OR = 1; - constexpr uint32_t WEF_CLEAR = 0x10; - constexpr uint32_t WEF_CLEAR_ALL = 0x20; - constexpr uint32_t WEF_MODE_MASK = WEF_OR | WEF_CLEAR | WEF_CLEAR_ALL; - constexpr uint32_t EA_MULTI = 0x2; - - void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t paramAddr = getRegU32(ctx, 4); // $a0 - const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); - - auto info = std::make_shared(); - if (param) - { - info->attr = param[0]; - info->option = param[1]; - info->initBits = param[2]; - info->bits = info->initBits; - } - - int id = 0; - { - std::lock_guard mapLock(g_event_flag_map_mutex); - id = g_nextEventFlagId++; - g_eventFlags[id] = info; - } - setReturnS32(ctx, id); - } - - void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int eid = static_cast(getRegU32(ctx, 4)); - std::shared_ptr info; - { - std::lock_guard mapLock(g_event_flag_map_mutex); - auto it = g_eventFlags.find(eid); - if (it == g_eventFlags.end()) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - info = it->second; - g_eventFlags.erase(it); - } - - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - { - std::lock_guard lock(info->m); - info->deleted = true; - } - info->cv.notify_all(); - setReturnS32(ctx, 0); - } - - void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t bits = getRegU32(ctx, 5); - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - if (bits == 0) - { - setReturnS32(ctx, KE_OK); - return; - } - - { - std::lock_guard lock(info->m); - info->bits |= bits; - } - info->cv.notify_all(); - setReturnS32(ctx, 0); - } - - void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - SetEventFlag(rdram, ctx, runtime); - } - - void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t bits = getRegU32(ctx, 5); - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - { - std::lock_guard lock(info->m); - info->bits &= bits; - } - info->cv.notify_all(); - setReturnS32(ctx, KE_OK); - } - - void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ClearEventFlag(rdram, ctx, runtime); - } - - void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t waitBits = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t resBitsAddr = getRegU32(ctx, 7); - - if ((mode & ~WEF_MODE_MASK) != 0) - { - setReturnS32(ctx, KE_ILLEGAL_MODE); - return; - } - - if (waitBits == 0) - { - setReturnS32(ctx, KE_EVF_ILPAT); - return; - } - - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; - - std::unique_lock lock(info->m); - if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) - { - setReturnS32(ctx, KE_EVF_MULTI); - return; - } - - auto tInfo = ensureCurrentThreadInfo(ctx); - throwIfTerminated(tInfo); - int ret = KE_OK; - - auto satisfied = [&]() - { - if (tInfo && tInfo->forceRelease.load()) - return true; - if (tInfo && tInfo->terminated.load()) - return true; - if (info->deleted) - { - return true; - } - if (mode & WEF_OR) - { - return (info->bits & waitBits) != 0; - } - return (info->bits & waitBits) == waitBits; - }; - - if (!satisfied()) - { - if (tInfo) - { - std::lock_guard tLock(tInfo->m); - tInfo->status = THS_WAIT; - tInfo->waitType = TSW_EVENT; - tInfo->waitId = eid; - tInfo->forceRelease = false; - } - - info->waiters++; - info->cv.wait(lock, satisfied); - info->waiters--; - - if (tInfo) - { - std::lock_guard tLock(tInfo->m); - tInfo->status = THS_RUN; - tInfo->waitType = TSW_NONE; - tInfo->waitId = 0; - if (tInfo->forceRelease) - { - tInfo->forceRelease = false; - ret = KE_RELEASE_WAIT; - } - } - - if (tInfo && tInfo->terminated.load()) - { - throw ThreadExitException(); - } - } - - if (ret == KE_OK && info->deleted) - { - ret = KE_WAIT_DELETE; - } - - if (ret == KE_OK && resBitsPtr) - { - *resBitsPtr = info->bits; - } - - if (ret == KE_OK) - { - if (resBitsPtr) - { - *resBitsPtr = info->bits; - } - - if (mode & WEF_CLEAR_ALL) - { - info->bits = 0; - } - else if (mode & WEF_CLEAR) - { - info->bits &= ~waitBits; - } - } - - lock.unlock(); - waitWhileSuspended(tInfo); - setReturnS32(ctx, ret); - } - - void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t waitBits = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t resBitsAddr = getRegU32(ctx, 7); - - if ((mode & ~WEF_MODE_MASK) != 0) - { - setReturnS32(ctx, KE_ILLEGAL_MODE); - return; - } - - if (waitBits == 0) - { - setReturnS32(ctx, KE_EVF_ILPAT); - return; - } - - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; - - std::lock_guard lock(info->m); - if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) - { - setReturnS32(ctx, KE_EVF_MULTI); - return; - } - - bool ok = false; - if (mode & WEF_OR) - { - ok = (info->bits & waitBits) != 0; - } - else - { - ok = (info->bits & waitBits) == waitBits; - } - - if (!ok) - { - setReturnS32(ctx, KE_EVF_COND); - return; - } - - if (resBitsPtr) - { - *resBitsPtr = info->bits; - } - - if (mode & (WEF_CLEAR | WEF_CLEAR_ALL)) - { - info->bits = 0; - } - - setReturnS32(ctx, KE_OK); - } - - void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - PollEventFlag(rdram, ctx, runtime); - } - - void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int eid = static_cast(getRegU32(ctx, 4)); - uint32_t infoAddr = getRegU32(ctx, 5); - - struct Ps2EventFlagInfo - { - uint32_t attr; - uint32_t option; - uint32_t initBits; - uint32_t currBits; - int32_t numThreads; - int32_t reserved1; - int32_t reserved2; - }; - - auto info = lookupEventFlagInfo(eid); - if (!info) - { - setReturnS32(ctx, KE_UNKNOWN_EVFID); - return; - } - - Ps2EventFlagInfo *out = infoAddr ? reinterpret_cast(getMemPtr(rdram, infoAddr)) : nullptr; - if (!out) - { - setReturnS32(ctx, -1); - return; - } - - std::lock_guard lock(info->m); - out->attr = info->attr; - out->option = info->option; - out->initBits = info->initBits; - out->currBits = info->bits; - out->numThreads = info->waiters; - out->reserved1 = 0; - out->reserved2 = 0; - setReturnS32(ctx, 0); - } - - void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - ReferEventFlagStatus(rdram, ctx, runtime); - } - - void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint16_t ticks = static_cast(getRegU32(ctx, 4) & 0xFFFFu); - uint32_t handler = getRegU32(ctx, 5); - uint32_t arg = getRegU32(ctx, 6); - - static int logCount = 0; - if (logCount < 5) - { - std::cout << "[SetAlarm] ticks=" << ticks - << " handler=0x" << std::hex << handler - << " arg=0x" << arg << std::dec << std::endl; - ++logCount; - } - - if (!runtime || !handler || !runtime->hasFunction(handler)) - { - setReturnS32(ctx, KE_ERROR); - return; - } - - auto info = std::make_shared(); - info->ticks = ticks; - info->handler = handler; - info->commonArg = arg; - info->gp = getRegU32(ctx, 28); - info->sp = getRegU32(ctx, 29); - info->rdram = rdram; - info->runtime = runtime; - info->dueAt = std::chrono::steady_clock::now() + alarmTicksToDuration(ticks); - - int alarmId = 0; - { - std::lock_guard lock(g_alarm_mutex); - alarmId = g_nextAlarmId++; - if (g_nextAlarmId <= 0) - { - g_nextAlarmId = 1; - } - info->id = alarmId; - g_alarms[alarmId] = info; - } - - ensureAlarmWorkerRunning(); - g_alarm_cv.notify_all(); - setReturnS32(ctx, alarmId); - } - - void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - SetAlarm(rdram, ctx, runtime); - } - - void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int alarmId = static_cast(getRegU32(ctx, 4)); - if (alarmId <= 0) - { - setReturnS32(ctx, KE_ERROR); - return; - } - - bool removed = false; - { - std::lock_guard lock(g_alarm_mutex); - removed = g_alarms.erase(alarmId) != 0; - } - - if (removed) - { - g_alarm_cv.notify_all(); - setReturnS32(ctx, KE_OK); - return; - } - - setReturnS32(ctx, KE_ERROR); - } - - void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - CancelAlarm(rdram, ctx, runtime); - } - - void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - IrqHandlerInfo info{}; - info.cause = getRegU32(ctx, 4); - info.handler = getRegU32(ctx, 5); - info.arg = getRegU32(ctx, 6); - info.enabled = true; - - const int handlerId = g_nextIntcHandlerId++; - g_intcHandlers[handlerId] = info; - setReturnS32(ctx, handlerId); - } - - void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (handlerId > 0) - { - g_intcHandlers.erase(handlerId); - } - setReturnS32(ctx, 0); - } - - void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - IrqHandlerInfo info{}; - info.cause = getRegU32(ctx, 4); - info.handler = getRegU32(ctx, 5); - info.arg = getRegU32(ctx, 6); - info.enabled = true; - - const int handlerId = g_nextDmacHandlerId++; - g_dmacHandlers[handlerId] = info; - setReturnS32(ctx, handlerId); - } - - void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (handlerId > 0) - { - g_dmacHandlers.erase(handlerId); - } - setReturnS32(ctx, 0); - } - - void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) - { - it->second.enabled = true; - } - setReturnS32(ctx, 0); - } - - void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) - { - it->second.enabled = false; - } - setReturnS32(ctx, 0); - } - - void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) - { - it->second.enabled = true; - } - setReturnS32(ctx, 0); - } - - void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int handlerId = static_cast(getRegU32(ctx, 5)); - if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) - { - it->second.enabled = false; - } - setReturnS32(ctx, 0); - } - - void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - setReturnS32(ctx, 0); - } - - void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const int32_t moduleId = static_cast(getRegU32(ctx, 4)); // $a0 - const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional) - - uint32_t refsLeft = 0; - const bool knownModule = trackSifModuleStop(moduleId, &refsLeft); - const int32_t ret = knownModule ? 0 : -1; - - if (resultAddr != 0) - { - int32_t *hostResult = reinterpret_cast(getMemPtr(rdram, resultAddr)); - if (hostResult) - { - *hostResult = knownModule ? 0 : -1; - } - } - - if (knownModule) - { - std::string modulePath; - { - 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()) - { - modulePath = it->second.path; - } - } - logSifModuleAction("stop", moduleId, modulePath, refsLeft); - } - - setReturnS32(ctx, ret); - } - - void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes); - if (modulePath.empty()) - { - setReturnS32(ctx, -1); - return; - } - - const int32_t moduleId = trackSifModuleLoad(modulePath); - 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", moduleId, modulePath, refs); - - setReturnS32(ctx, moduleId); - } - - void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - std::lock_guard lock(g_rpc_mutex); - if (!g_rpc_initialized) - { - g_rpc_servers.clear(); - g_rpc_clients.clear(); - g_rpc_next_id = 1; - g_rpc_packet_index = 0; - g_rpc_server_index = 0; - g_rpc_active_queue = 0; - { - std::lock_guard dtxLock(g_dtx_rpc_mutex); - g_dtx_remote_by_id.clear(); - g_dtx_next_urpc_obj = kDtxUrpcObjBase; - } - g_rpc_initialized = true; - std::cout << "[SifInitRpc] Initialized" << std::endl; - } - setReturnS32(ctx, 0); - } - - void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t clientPtr = getRegU32(ctx, 4); - uint32_t rpcId = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - - t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); - - if (!client) - { - setReturnS32(ctx, -1); - return; - } - - client->command = 0; - client->buf = 0; - client->cbuf = 0; - client->end_function = 0; - client->end_param = 0; - client->server = 0; - client->hdr.pkt_addr = 0; - client->hdr.sema_id = -1; - client->hdr.mode = mode; - - uint32_t serverPtr = 0; - { - std::lock_guard lock(g_rpc_mutex); - client->hdr.rpc_id = g_rpc_next_id++; - auto it = g_rpc_servers.find(rpcId); - if (it != g_rpc_servers.end()) - { - serverPtr = it->second.sd_ptr; - } - g_rpc_clients[clientPtr] = {}; - g_rpc_clients[clientPtr].sid = rpcId; - } - - if (!serverPtr) - { - // Allocate a dummy server so bind loops can proceed. - serverPtr = rpcAllocServerAddr(rdram); - if (serverPtr) - { - t_SifRpcServerData *dummy = reinterpret_cast(getMemPtr(rdram, serverPtr)); - if (dummy) - { - std::memset(dummy, 0, sizeof(*dummy)); - dummy->sid = static_cast(rpcId); - } - std::lock_guard lock(g_rpc_mutex); - g_rpc_servers[rpcId] = {rpcId, serverPtr}; - } - } - - if (serverPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, serverPtr)); - client->server = serverPtr; - client->buf = sd ? sd->buf : 0; - client->cbuf = sd ? sd->cbuf : 0; - } - else - { - client->server = 0; - client->buf = 0; - client->cbuf = 0; - } - - setReturnS32(ctx, 0); - } - - void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t clientPtr = getRegU32(ctx, 4); - uint32_t rpcNum = getRegU32(ctx, 5); - uint32_t mode = getRegU32(ctx, 6); - uint32_t sendBuf = getRegU32(ctx, 7); - uint32_t sendSize = 0; - uint32_t recvBuf = 0; - uint32_t recvSize = 0; - uint32_t endFunc = 0; - uint32_t endParam = 0; - - // EE-side calls use extended arg registers: - // a0-a3 => r4-r7, arg5-arg8 => r8-r11, arg9 => stack + 0x0. - // Keep O32 stack-layout fallback for compatibility with other call sites. - uint32_t sp = getRegU32(ctx, 29); - sendSize = getRegU32(ctx, 8); - recvBuf = getRegU32(ctx, 9); - recvSize = getRegU32(ctx, 10); - endFunc = getRegU32(ctx, 11); - (void)readStackU32(rdram, sp, 0x0, endParam); - - if (sendSize == 0 && recvBuf == 0 && recvSize == 0 && endFunc == 0) - { - readStackU32(rdram, sp, 0x10, sendSize); - readStackU32(rdram, sp, 0x14, recvBuf); - readStackU32(rdram, sp, 0x18, recvSize); - readStackU32(rdram, sp, 0x1C, endFunc); - readStackU32(rdram, sp, 0x20, endParam); - } - - t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); - - if (!client) - { - setReturnS32(ctx, -1); - return; - } - - client->command = rpcNum; - client->end_function = endFunc; - client->end_param = endParam; - client->hdr.mode = mode; - - { - std::lock_guard lock(g_rpc_mutex); - g_rpc_clients[clientPtr].busy = true; - g_rpc_clients[clientPtr].last_rpc = rpcNum; - uint32_t sid = g_rpc_clients[clientPtr].sid; - if (sid) - { - auto it = g_rpc_servers.find(sid); - if (it != g_rpc_servers.end()) - { - uint32_t mappedServer = it->second.sd_ptr; - if (mappedServer && client->server != mappedServer) - { - client->server = mappedServer; - } - } - } - } - - uint32_t sid = 0; - { - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it != g_rpc_clients.end()) - { - sid = it->second.sid; - } - } - - uint32_t serverPtr = client->server; - t_SifRpcServerData *sd = serverPtr ? reinterpret_cast(getMemPtr(rdram, serverPtr)) : nullptr; - - if (sd) - { - sd->client = clientPtr; - sd->pkt_addr = client->hdr.pkt_addr; - sd->rpc_number = rpcNum; - sd->size = static_cast(sendSize); - sd->recvbuf = recvBuf; - sd->rsize = static_cast(recvSize); - sd->rmode = ((mode & kSifRpcModeNowait) && endFunc == 0) ? 0 : 1; - sd->rid = 0; - } - - if (sd && sd->buf && sendBuf && sendSize > 0) - { - rpcCopyToRdram(rdram, sd->buf, sendBuf, sendSize); - } - - uint32_t resultPtr = 0; - bool handled = false; - - auto readRpcU32 = [&](uint32_t addr, uint32_t &out) -> bool - { - if (!addr) - { - return false; - } - const uint8_t *ptr = getConstMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(&out, ptr, sizeof(out)); - return true; - }; - - auto writeRpcU32 = [&](uint32_t addr, uint32_t value) -> bool - { - if (!addr) - { - return false; - } - uint8_t *ptr = getMemPtr(rdram, addr); - if (!ptr) - { - return false; - } - std::memcpy(ptr, &value, sizeof(value)); - return true; - }; - - const bool isDtxUrpc = (sid == kDtxRpcSid) && (rpcNum >= 0x400u) && (rpcNum < 0x500u); - uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; - uint32_t dtxUrpcFn = 0; - uint32_t dtxUrpcObj = 0; - uint32_t dtxUrpcSend0 = 0; - bool dtxUrpcDispatchAttempted = false; - bool dtxUrpcFallbackEmulated = false; - bool dtxUrpcFallbackCreate34 = false; - bool hasUrpcHandler = false; - if (isDtxUrpc) - { - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, dtxUrpcSend0); - } - if (dtxUrpcCommand < 64u) - { - (void)readRpcU32(kDtxUrpcFnTableBase + (dtxUrpcCommand * 4u), dtxUrpcFn); - (void)readRpcU32(kDtxUrpcObjTableBase + (dtxUrpcCommand * 4u), dtxUrpcObj); - } - hasUrpcHandler = (dtxUrpcCommand < 64u) && (dtxUrpcFn != 0u); - } - const bool allowServerDispatch = !isDtxUrpc || hasUrpcHandler; - - if (sd && sd->func && (sid != kDtxRpcSid || isDtxUrpc) && allowServerDispatch) - { - dtxUrpcDispatchAttempted = dtxUrpcDispatchAttempted || isDtxUrpc; - handled = rpcInvokeFunction(rdram, ctx, runtime, sd->func, rpcNum, sd->buf, sendSize, 0, &resultPtr); - if (handled && resultPtr == 0 && sd->buf) - { - resultPtr = sd->buf; - } - if (handled && resultPtr == 0 && recvBuf) - { - resultPtr = recvBuf; - } - } - - if (!handled && isDtxUrpc && sendBuf && sendSize > 0) - { - // Only dispatch through dtx_rpc_func when a URPC handler is registered in the table. - // If the slot is empty, defer to the fallback emulation below. - if (hasUrpcHandler) - { - dtxUrpcDispatchAttempted = true; - handled = rpcInvokeFunction(rdram, ctx, runtime, 0x2fabc0u, rpcNum, sendBuf, sendSize, 0, &resultPtr); - if (handled && resultPtr == 0) - { - resultPtr = sendBuf; - } - } - } - - if (!handled && sid == kDtxRpcSid) - { - if (rpcNum == 2 && recvBuf && recvSize >= sizeof(uint32_t)) - { - uint32_t dtxId = 0; - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, dtxId); - } - - uint32_t remoteHandle = 0; - { - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_remote_by_id.find(dtxId); - if (it != g_dtx_remote_by_id.end()) - { - remoteHandle = it->second; - } - if (!remoteHandle) - { - remoteHandle = rpcAllocServerAddr(rdram); - if (!remoteHandle) - { - remoteHandle = rpcAllocPacketAddr(rdram); - } - if (!remoteHandle) - { - remoteHandle = kRpcServerPoolBase + ((dtxId & 0xFFu) * kRpcServerStride); - } - g_dtx_remote_by_id[dtxId] = remoteHandle; - } - } - - (void)writeRpcU32(recvBuf, remoteHandle); - if (recvSize > sizeof(uint32_t)) - { - rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); - } - handled = true; - resultPtr = recvBuf; - } - else if (rpcNum == 3) - { - uint32_t remoteHandle = 0; - if (sendBuf && sendSize >= sizeof(uint32_t) && readRpcU32(sendBuf, remoteHandle) && remoteHandle) - { - std::lock_guard lock(g_dtx_rpc_mutex); - for (auto it = g_dtx_remote_by_id.begin(); it != g_dtx_remote_by_id.end(); ++it) - { - if (it->second == remoteHandle) - { - g_dtx_remote_by_id.erase(it); - break; - } - } - } - if (recvBuf && recvSize > 0) - { - rpcZeroRdram(rdram, recvBuf, recvSize); - } - handled = true; - resultPtr = recvBuf; - } - else if (rpcNum >= 0x400 && rpcNum < 0x500) - { - dtxUrpcFallbackEmulated = true; - const uint32_t urpcCommand = rpcNum & 0xFFu; - uint32_t outWords[4] = {1u, 0u, 0u, 0u}; - uint32_t outWordCount = 1u; - - auto readSendWord = [&](uint32_t index, uint32_t &out) -> bool - { - const uint64_t byteOffset = static_cast(index) * sizeof(uint32_t); - if (!sendBuf || sendSize < (byteOffset + sizeof(uint32_t))) - { - return false; - } - return readRpcU32(sendBuf + static_cast(byteOffset), out); - }; - - switch (urpcCommand) - { - case 32u: // SJRMT_RBF_CREATE - case 33u: // SJRMT_MEM_CREATE - case 34u: // SJRMT_UNI_CREATE - { - uint32_t arg0 = 0; - uint32_t arg1 = 0; - uint32_t arg2 = 0; - (void)readSendWord(0u, arg0); - (void)readSendWord(1u, arg1); - (void)readSendWord(2u, arg2); - - uint32_t mode = 0; - uint32_t wkAddr = 0; - uint32_t wkSize = 0; - if (urpcCommand == 34u) - { - mode = arg0; - wkAddr = arg1; - wkSize = arg2; - dtxUrpcFallbackCreate34 = true; - } - else if (urpcCommand == 33u) - { - wkAddr = arg0; - wkSize = arg1; - } - else - { - wkAddr = arg0; - wkSize = (arg1 != 0u) ? arg1 : arg2; - } - - wkSize = dtxNormalizeSjrmtCapacity(wkSize); - - std::lock_guard lock(g_dtx_rpc_mutex); - const uint32_t handle = dtxAllocUrpcHandleLocked(); - DtxSjrmtState state{}; - state.handle = handle; - state.mode = mode; - state.wkAddr = wkAddr; - state.wkSize = wkSize; - state.readPos = 0u; - state.writePos = 0u; - state.roomBytes = wkSize; - state.dataBytes = 0u; - state.uuid0 = 0x53524D54u; // "SRMT" - state.uuid1 = handle; - state.uuid2 = wkAddr; - state.uuid3 = wkSize; - g_dtx_sjrmt_by_handle[handle] = state; - - outWords[0] = handle ? handle : 1u; - outWordCount = 1u; - break; - } - case 35u: // SJRMT_DESTROY - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - g_dtx_sjrmt_by_handle.erase(handle); - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 36u: // SJRMT_GET_UUID - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - outWords[0] = it->second.uuid0; - outWords[1] = it->second.uuid1; - outWords[2] = it->second.uuid2; - outWords[3] = it->second.uuid3; - } - else - { - outWords[0] = 0u; - outWords[1] = 0u; - outWords[2] = 0u; - outWords[3] = 0u; - } - outWordCount = 4u; - break; - } - case 37u: // SJRMT_RESET - { - uint32_t handle = 0; - (void)readSendWord(0u, handle); - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - const uint32_t cap = (it->second.wkSize == 0u) ? 0x4000u : it->second.wkSize; - it->second.readPos = 0u; - it->second.writePos = 0u; - it->second.roomBytes = cap; - it->second.dataBytes = 0u; - } - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 38u: // SJRMT_GET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t nbyte = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(2u, nbyte); - - uint32_t ptr = 0u; - uint32_t len = 0u; - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - - if (streamId == 0u) - { - len = std::min(nbyte, state.roomBytes); - ptr = state.wkAddr + (cap ? (state.writePos % cap) : 0u); - if (cap != 0u) - { - state.writePos = (state.writePos + len) % cap; - } - state.roomBytes -= len; - } - else if (streamId == 1u) - { - len = std::min(nbyte, state.dataBytes); - ptr = state.wkAddr + (cap ? (state.readPos % cap) : 0u); - if (cap != 0u) - { - state.readPos = (state.readPos + len) % cap; - } - state.dataBytes -= len; - } - } - - outWords[0] = ptr; - outWords[1] = len; - outWordCount = 2u; - break; - } - case 39u: // SJRMT_UNGET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t len = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(3u, len); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (streamId == 0u) - { - const uint32_t delta = (cap == 0u) ? 0u : (len % cap); - if (cap != 0u) - { - state.writePos = (state.writePos + cap - delta) % cap; - } - state.roomBytes = std::min(cap, state.roomBytes + len); - } - else if (streamId == 1u) - { - const uint32_t delta = (cap == 0u) ? 0u : (len % cap); - if (cap != 0u) - { - state.readPos = (state.readPos + cap - delta) % cap; - } - state.dataBytes = std::min(cap, state.dataBytes + len); - } - } - - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 40u: // SJRMT_PUT_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t len = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(3u, len); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - DtxSjrmtState &state = it->second; - const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; - if (streamId == 0u) - { - state.roomBytes = std::min(cap, state.roomBytes + len); - } - else if (streamId == 1u) - { - state.dataBytes = std::min(cap, state.dataBytes + len); - } - } - - outWords[0] = 1u; - outWordCount = 1u; - break; - } - case 41u: // SJRMT_GET_NUM_DATA - { - uint32_t handle = 0; - uint32_t streamId = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - outWords[0] = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; - } - else - { - outWords[0] = 0u; - } - outWordCount = 1u; - break; - } - case 42u: // SJRMT_IS_GET_CHUNK - { - uint32_t handle = 0; - uint32_t streamId = 0; - uint32_t nbyte = 0; - (void)readSendWord(0u, handle); - (void)readSendWord(1u, streamId); - (void)readSendWord(2u, nbyte); - - uint32_t available = 0u; - std::lock_guard lock(g_dtx_rpc_mutex); - auto it = g_dtx_sjrmt_by_handle.find(handle); - if (it != g_dtx_sjrmt_by_handle.end()) - { - available = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; - } - outWords[0] = (available >= nbyte) ? 1u : 0u; - outWords[1] = available; - outWordCount = 2u; - break; - } - case 43u: // SJRMT_INIT - case 44u: // SJRMT_FINISH - { - outWords[0] = 1u; - outWordCount = 1u; - break; - } - default: - { - uint32_t urpcRet = 1u; - if (sendBuf && sendSize >= sizeof(uint32_t)) - { - (void)readRpcU32(sendBuf, urpcRet); - } - if (urpcCommand == 0u) - { - std::lock_guard lock(g_dtx_rpc_mutex); - urpcRet = dtxAllocUrpcHandleLocked(); - } - if (urpcRet == 0u) - { - urpcRet = 1u; - } - outWords[0] = urpcRet; - outWordCount = 1u; - break; - } - } - - if (recvBuf && recvSize > 0u) - { - const uint32_t recvWordCapacity = static_cast(recvSize / sizeof(uint32_t)); - const uint32_t wordsToWrite = std::min(outWordCount, recvWordCapacity); - for (uint32_t i = 0; i < wordsToWrite; ++i) - { - (void)writeRpcU32(recvBuf + (i * sizeof(uint32_t)), outWords[i]); - } - - // SJRMT_IsGetChunk callers read rbuf[1] even when nout==1. - if (urpcCommand == 42u && outWordCount > 1u) - { - (void)writeRpcU32(recvBuf + sizeof(uint32_t), outWords[1]); - } - - if (recvSize > (wordsToWrite * sizeof(uint32_t))) - { - rpcZeroRdram(rdram, recvBuf + (wordsToWrite * sizeof(uint32_t)), - recvSize - (wordsToWrite * sizeof(uint32_t))); - } - } - - handled = true; - resultPtr = recvBuf; - } - } - - if (recvBuf && recvSize > 0) - { - if (handled && resultPtr) - { - rpcCopyToRdram(rdram, recvBuf, resultPtr, recvSize); - } - else if (!handled && sendBuf && sendSize > 0) - { - size_t copySize = (sendSize < recvSize) ? sendSize : recvSize; - rpcCopyToRdram(rdram, recvBuf, sendBuf, copySize); - } - else if (!handled) - { - rpcZeroRdram(rdram, recvBuf, recvSize); - } - } - - if (isDtxUrpc) - { - static int dtxUrpcLogCount = 0; - if (dtxUrpcLogCount < 64) - { - uint32_t dtxUrpcRecv0 = 0; - if (recvBuf && recvSize >= sizeof(uint32_t)) - { - (void)readRpcU32(recvBuf, dtxUrpcRecv0); - } - std::cout << "[SifCallRpc:DTX] rpcNum=0x" << std::hex << rpcNum - << " cmd=0x" << dtxUrpcCommand - << " fn=0x" << dtxUrpcFn - << " obj=0x" << dtxUrpcObj - << " send0=0x" << dtxUrpcSend0 - << " recv0=0x" << dtxUrpcRecv0 - << " resultPtr=0x" << resultPtr - << " handled=" << std::dec << (handled ? 1 : 0) - << " dispatch=" << (dtxUrpcDispatchAttempted ? 1 : 0) - << " emu=" << (dtxUrpcFallbackEmulated ? 1 : 0) - << " emu34=" << (dtxUrpcFallbackCreate34 ? 1 : 0) - << std::endl; - ++dtxUrpcLogCount; - } - } - - if (endFunc) - { - rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr); - } - - static int logCount = 0; - if (logCount < 10) - { - std::cout << "[SifCallRpc] client=0x" << std::hex << clientPtr - << " sid=0x" << sid - << " rpcNum=0x" << rpcNum - << " mode=0x" << mode - << " sendBuf=0x" << sendBuf - << " recvBuf=0x" << recvBuf - << " recvSize=0x" << recvSize - << " size=" << std::dec << sendSize << std::endl; - ++logCount; - } - - { - std::lock_guard lock(g_rpc_mutex); - g_rpc_clients[clientPtr].busy = false; - } - - setReturnS32(ctx, 0); - } - - void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t sdPtr = getRegU32(ctx, 4); - uint32_t sid = getRegU32(ctx, 5); - uint32_t func = getRegU32(ctx, 6); - uint32_t buf = getRegU32(ctx, 7); - // stack args: cfunc, cbuf, qd... - uint32_t sp = getRegU32(ctx, 29); - uint32_t cfunc = 0; - uint32_t cbuf = 0; - uint32_t qd = 0; - readStackU32(rdram, sp, 0x10, cfunc); - readStackU32(rdram, sp, 0x14, cbuf); - readStackU32(rdram, sp, 0x18, qd); - - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - if (!sd) - { - setReturnS32(ctx, -1); - return; - } - - sd->sid = static_cast(sid); - sd->func = func; - sd->buf = buf; - sd->size = 0; - sd->cfunc = cfunc; - sd->cbuf = cbuf; - sd->size2 = 0; - sd->client = 0; - sd->pkt_addr = 0; - sd->rpc_number = 0; - sd->recvbuf = 0; - sd->rsize = 0; - sd->rmode = 0; - sd->rid = 0; - sd->base = qd; - sd->link = 0; - sd->next = 0; - - if (qd) - { - t_SifRpcDataQueue *queue = reinterpret_cast(getMemPtr(rdram, qd)); - if (queue) - { - if (!queue->link) - { - queue->link = sdPtr; - } - else - { - uint32_t curPtr = queue->link; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (!cur->link) - { - cur->link = sdPtr; - break; - } - if (cur->link == sdPtr) - break; - curPtr = cur->link; - } - } - } - } - - { - std::lock_guard lock(g_rpc_mutex); - g_rpc_servers[sid] = {sid, sdPtr}; - for (auto &entry : g_rpc_clients) - { - if (entry.second.sid == sid) - { - t_SifRpcClientData *cd = reinterpret_cast(getMemPtr(rdram, entry.first)); - if (cd) - { - cd->server = sdPtr; - cd->buf = sd->buf; - cd->cbuf = sd->cbuf; - } - } - } - } - - std::cout << "[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec << std::endl; - setReturnS32(ctx, 0); - } - - void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t clientPtr = getRegU32(ctx, 4); - std::lock_guard lock(g_rpc_mutex); - auto it = g_rpc_clients.find(clientPtr); - if (it == g_rpc_clients.end()) - { - setReturnS32(ctx, 0); - return; - } - setReturnS32(ctx, it->second.busy ? 1 : 0); - } - - void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t qdPtr = getRegU32(ctx, 4); - int threadId = static_cast(getRegU32(ctx, 5)); - - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - if (!qd) - { - setReturnS32(ctx, -1); - return; - } - - qd->thread_id = threadId; - qd->active = 0; - qd->link = 0; - qd->start = 0; - qd->end = 0; - qd->next = 0; - - { - std::lock_guard lock(g_rpc_mutex); - if (!g_rpc_active_queue) - { - g_rpc_active_queue = qdPtr; - } - else - { - uint32_t curPtr = g_rpc_active_queue; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - if (curPtr == qdPtr) - break; - t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (!cur->next) - { - cur->next = qdPtr; - break; - } - curPtr = cur->next; - } - } - } - - setReturnS32(ctx, 0); - } - - void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t qdPtr = getRegU32(ctx, 4); - if (!qdPtr) - { - setReturnU32(ctx, 0); - return; - } - - std::lock_guard lock(g_rpc_mutex); - if (!g_rpc_active_queue) - { - setReturnU32(ctx, 0); - return; - } - - if (g_rpc_active_queue == qdPtr) - { - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - g_rpc_active_queue = qd ? qd->next : 0; - setReturnU32(ctx, qdPtr); - return; - } - - uint32_t curPtr = g_rpc_active_queue; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (cur->next == qdPtr) - { - t_SifRpcDataQueue *rem = reinterpret_cast(getMemPtr(rdram, qdPtr)); - cur->next = rem ? rem->next : 0; - setReturnU32(ctx, qdPtr); - return; - } - curPtr = cur->next; - } - - setReturnU32(ctx, 0); - } - - void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t sdPtr = getRegU32(ctx, 4); - uint32_t qdPtr = getRegU32(ctx, 5); - - t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); - if (!qd || !sdPtr) - { - setReturnU32(ctx, 0); - return; - } - - if (qd->link == sdPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - qd->link = sd ? sd->link : 0; - if (sd) - sd->link = 0; - setReturnU32(ctx, sdPtr); - return; - } - - uint32_t curPtr = qd->link; - for (int guard = 0; guard < 1024 && curPtr; ++guard) - { - t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); - if (!cur) - break; - if (cur->link == sdPtr) - { - t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); - cur->link = sd ? sd->link : 0; - if (sd) - sd->link = 0; - setReturnU32(ctx, sdPtr); - return; - } - curPtr = cur->link; - } - - setReturnU32(ctx, 0); - } - - void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - SifCallRpc(rdram, ctx, runtime); - } - - void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t cid = getRegU32(ctx, 4); - uint32_t packetAddr = getRegU32(ctx, 5); - uint32_t packetSize = getRegU32(ctx, 6); - uint32_t srcExtra = getRegU32(ctx, 7); - - uint32_t sp = getRegU32(ctx, 29); - uint32_t destExtra = 0; - uint32_t sizeExtra = 0; - readStackU32(rdram, sp, 0x10, destExtra); - readStackU32(rdram, sp, 0x14, sizeExtra); - - if (sizeExtra > 0 && srcExtra && destExtra) - { - rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra); - } - - static int logCount = 0; - if (logCount < 5) - { - std::cout << "[sceSifSendCmd] cid=0x" << std::hex << cid - << " packet=0x" << packetAddr - << " psize=0x" << packetSize - << " extra=0x" << destExtra << std::dec << std::endl; - ++logCount; - } - - // Return non-zero on success. - setReturnS32(ctx, 1); - } - - void _sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t queuePtr = getRegU32(ctx, 4); - setReturnS32(ctx, static_cast(queuePtr)); - } - - void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - int flags = (int)getRegU32(ctx, 5); // $a1 (PS2 FIO flags) - - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioOpen error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); - return; - } - - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioOpen error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } - - const char *mode = translateFioMode(flags); - std::cout << "fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'" << std::endl; - - FILE *fp = ::fopen(hostPath.c_str(), mode); - if (!fp) - { - std::cerr << "fioOpen error: fopen failed for '" << hostPath << "': " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); // e.g., -ENOENT, -EACCES - return; - } - - int ps2Fd = allocatePs2Fd(fp); - if (ps2Fd < 0) - { - std::cerr << "fioOpen error: Failed to allocate PS2 file descriptor" << std::endl; - ::fclose(fp); - setReturnS32(ctx, -1); // e.g., -EMFILE - return; - } - - // returns the PS2 file descriptor - setReturnS32(ctx, ps2Fd); - } - - void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - std::cout << "fioClose: fd=" << ps2Fd << std::endl; - - FILE *fp = getHostFile(ps2Fd); - if (!fp) - { - std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // e.g., -EBADF - return; - } - - int ret = ::fclose(fp); - releasePs2Fd(ps2Fd); - - // returns 0 on success, -1 on error - setReturnS32(ctx, ret == 0 ? 0 : -1); - } - - void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - uint32_t bufAddr = getRegU32(ctx, 5); // $a1 - size_t size = getRegU32(ctx, 6); // $a2 - - uint8_t *hostBuf = getMemPtr(rdram, bufAddr); - FILE *fp = getHostFile(ps2Fd); - - if (!hostBuf) - { - std::cerr << "fioRead error: Invalid buffer address for fd " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EFAULT - return; - } - if (!fp) - { - std::cerr << "fioRead error: Invalid file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EBADF - return; - } - if (size == 0) - { - setReturnS32(ctx, 0); // Read 0 bytes - return; - } - - size_t bytesRead = 0; - { - std::lock_guard lock(g_sys_fd_mutex); - bytesRead = fread(hostBuf, 1, size, fp); - } - - if (bytesRead < size && ferror(fp)) - { - std::cerr << "fioRead error: fread failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; - clearerr(fp); - setReturnS32(ctx, -1); // -EIO or other appropriate error - return; - } - - // returns number of bytes read (can be 0 for EOF) - setReturnS32(ctx, (int32_t)bytesRead); - } - - void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - uint32_t bufAddr = getRegU32(ctx, 5); // $a1 - size_t size = getRegU32(ctx, 6); // $a2 - - const uint8_t *hostBuf = getConstMemPtr(rdram, bufAddr); - if (!hostBuf) - { - setReturnS32(ctx, -1); - return; - } - - size_t bytesWritten = 0; - { - std::lock_guard lock(g_fd_mutex); - FILE *fp = getHostFile(ps2Fd); - if (!fp) - { - setReturnS32(ctx, -1); // -EFAULT - return; - } - - if (size == 0) - { - setReturnS32(ctx, 0); // Wrote 0 bytes - return; - } - - bytesWritten = ::fwrite(hostBuf, 1, size, fp); - if (bytesWritten < size && ferror(fp)) - { - clearerr(fp); - setReturnS32(ctx, -1); // -EIO, -ENOSPC etc. - return; - } - } - - // returns number of bytes written - setReturnS32(ctx, (int32_t)bytesWritten); - } - - void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int ps2Fd = (int)getRegU32(ctx, 4); // $a0 - int32_t offset = getRegU32(ctx, 5); // $a1 (PS2 seems to use 32-bit offset here commonly) - int whence = (int)getRegU32(ctx, 6); // $a2 (PS2 FIO_SEEK constants) - - FILE *fp = getHostFile(ps2Fd); - if (!fp) - { - std::cerr << "fioLseek error: Invalid file descriptor " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EBADF - return; - } - - int hostWhence; - switch (whence) - { - case PS2_FIO_SEEK_SET: - hostWhence = SEEK_SET; - break; - case PS2_FIO_SEEK_CUR: - hostWhence = SEEK_CUR; - break; - case PS2_FIO_SEEK_END: - hostWhence = SEEK_END; - break; - default: - std::cerr << "fioLseek error: Invalid whence value " << whence << " for fd " << ps2Fd << std::endl; - setReturnS32(ctx, -1); // -EINVAL - return; - } - - if (::fseek(fp, static_cast(offset), hostWhence) != 0) - { - std::cerr << "fioLseek error: fseek failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); // Return error code - return; - } - - long newPos = ::ftell(fp); - if (newPos < 0) - { - std::cerr << "fioLseek error: ftell failed after fseek for fd " << ps2Fd << ": " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); - } - else - { - if (newPos > 0xFFFFFFFFL) - { - std::cerr << "fioLseek warning: New position exceeds 32-bit for fd " << ps2Fd << std::endl; - setReturnS32(ctx, -1); - } - else - { - setReturnS32(ctx, (int32_t)newPos); - } - } - } - - void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - // int mode = (int)getRegU32(ctx, 5); - - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioMkdir error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); // -EFAULT - return; - } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioMkdir error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } - -#ifdef _WIN32 - int ret = -1; -#else - int ret = ::mkdir(hostPath.c_str(), 0775); -#endif - - if (ret != 0) - { - std::cerr << "fioMkdir error: mkdir failed for '" << hostPath << "': " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); // errno - } - else - { - setReturnS32(ctx, 0); // Success - } - } - - void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioChdir error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); - return; - } - - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioChdir error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } - - std::cerr << "fioChdir: Attempting host chdir to '" << hostPath << "' (Stub - Check side effects)" << std::endl; - -#ifdef _WIN32 - int ret = -1; -#else - int ret = ::chdir(hostPath.c_str()); -#endif - - if (ret != 0) - { - std::cerr << "fioChdir error: chdir failed for '" << hostPath << "': " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); - } - else - { - setReturnS32(ctx, 0); // Success - } - } - - void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioRmdir error: Invalid path address" << std::endl; - setReturnS32(ctx, -1); - return; - } - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioRmdir error: Failed to translate path '" << ps2Path << "'" << std::endl; - setReturnS32(ctx, -1); - return; - } - -#ifdef _WIN32 - int ret = -1; -#else - int ret = ::rmdir(hostPath.c_str()); -#endif - - if (ret != 0) - { - std::cerr << "fioRmdir error: rmdir failed for '" << hostPath << "': " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); - } - else - { - setReturnS32(ctx, 0); // Success - } - } - - void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - // we wont implement this for now. - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - uint32_t statBufAddr = getRegU32(ctx, 5); // $a1 - - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - uint8_t *ps2StatBuf = getMemPtr(rdram, statBufAddr); - - if (!ps2Path) - { - std::cerr << "fioGetstat error: Invalid path addr" << std::endl; - setReturnS32(ctx, -1); - return; - } - if (!ps2StatBuf) - { - std::cerr << "fioGetstat error: Invalid buffer addr" << std::endl; - setReturnS32(ctx, -1); - return; - } - - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioGetstat error: Bad path translate" << std::endl; - setReturnS32(ctx, -1); - return; - } - - setReturnS32(ctx, -1); - } - - void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t pathAddr = getRegU32(ctx, 4); // $a0 - const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); - if (!ps2Path) - { - std::cerr << "fioRemove error: Invalid path" << std::endl; - setReturnS32(ctx, -1); - return; - } - - std::string hostPath = translatePs2Path(ps2Path); - if (hostPath.empty()) - { - std::cerr << "fioRemove error: Path translate fail" << std::endl; - setReturnS32(ctx, -1); - return; - } - -#ifdef _WIN32 - int ret = -1; -#else - int ret = ::unlink(hostPath.c_str()); -#endif - - if (ret != 0) - { - std::cerr << "fioRemove error: unlink failed for '" << hostPath << "': " << strerror(errno) << std::endl; - setReturnS32(ctx, -1); - } - else - { - setReturnS32(ctx, 0); // Success - } - } - - 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 - - std::cout << "PS2 GsSetCrt: interlaced=" << interlaced - << ", videoMode=" << videoMode - << ", frameMode=" << frameMode << std::endl; - } - - void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint64_t imr = 0; - if (runtime) - { - imr = runtime->memory().gs().imr; - } - - std::cout << "PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec << std::endl; - - setReturnU64(ctx, imr); // Return in $v0/$v1 - } - - void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint64_t newImr = getRegU32(ctx, 4) | ((uint64_t)getRegU32(ctx, 5) << 32); // $a0 = lower 32 bits, $a1 = upper 32 bits - uint64_t oldImr = 0; - if (runtime) - { - oldImr = runtime->memory().gs().imr; - runtime->memory().gs().imr = newImr; - } - std::cout << "PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec << std::endl; - setReturnU64(ctx, oldImr); - } - - void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags) - - std::cout << "PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec << std::endl; - - // 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_config_initialized = true; - } - - 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 - if (!rdram || bufferAddr == 0u) - { - setReturnS32(ctx, -1); - return; - } - - // Match buffer-based module loads to stable synthetic tags so module ID lookup remains deterministic. - const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr); - 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 = (v0 != 0u) ? v0 : 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); - } - - // 0x3C SetupThread: returns stack pointer (stack + stack_size) - // args: $a0 = stack base, $a1 = stack size, $a2 = gp, $a3 = entry point - void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - uint32_t stackBase = getRegU32(ctx, 4); - uint32_t stackSize = getRegU32(ctx, 5); - uint32_t sp = stackBase + stackSize; - setReturnS32(ctx, sp); - } - - // 0x3D SetupHeap: returns heap base/start pointer - void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - const uint32_t heapBase = getRegU32(ctx, 4); // $a0 - const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) - - if (runtime) - { - uint32_t heapLimit = PS2_RAM_SIZE; - if (heapSize != 0u && heapBase < PS2_RAM_SIZE) - { - const uint64_t candidateLimit = static_cast(heapBase) + static_cast(heapSize); - heapLimit = static_cast(std::min(candidateLimit, PS2_RAM_SIZE)); - } - runtime->configureGuestHeap(heapBase, heapLimit); - 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) - { - if (runtime) - { - setReturnU32(ctx, runtime->guestHeapEnd()); - return; - } - - setReturnU32(ctx, getRegU32(ctx, 4)); - } - - // 0x5A 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); - } - - // 0x5B GetThreadTLS (stub): return 0 - void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) - { - auto info = ensureCurrentThreadInfo(ctx); - if (!info) - { - setReturnU32(ctx, 0); - return; - } - - if (info->tlsBase == 0) - { - info->tlsBase = allocTlsAddr(rdram); - } - - setReturnU32(ctx, info->tlsBase); - } - - // 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; - } - - int tid = g_currentThreadId; - { - std::lock_guard lock(g_exit_handler_mutex); - g_exit_handlers[tid].push_back({func, arg}); - } - - setReturnS32(ctx, 0); - } +#include "syscalls/ps2_syscalls_thread.inl" +#include "syscalls/ps2_syscalls_flags.inl" +#include "syscalls/ps2_syscalls_rpc.inl" +#include "syscalls/ps2_syscalls_fileio.inl" } diff --git a/ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl b/ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl new file mode 100644 index 0000000..7105de0 --- /dev/null +++ b/ps2xRuntime/src/lib/stubs/helpers/ps2_stubs_helpers.inl @@ -0,0 +1,1528 @@ +#ifndef PS2_CD_REMAP_IDX_TO_AFS +#define PS2_CD_REMAP_IDX_TO_AFS 1 +#endif + +namespace +{ + constexpr uint32_t kCdSectorSize = 2048; + constexpr uint32_t kCdPseudoLbnStart = 0x00100000; + + struct CdFileEntry + { + std::filesystem::path hostPath; + uint32_t sizeBytes = 0; + uint32_t baseLbn = 0; + uint32_t sectors = 0; + }; + + std::unordered_map g_cdFilesByKey; + std::unordered_map g_cdLeafIndex; + std::filesystem::path g_cdLeafIndexRoot; + bool g_cdLeafIndexBuilt = false; + uint32_t g_nextPseudoLbn = kCdPseudoLbnStart; + int32_t g_lastCdError = 0; + uint32_t g_cdMode = 0; + uint32_t g_cdStreamingLbn = 0; + bool g_cdInitialized = false; + + constexpr uint32_t kIopHeapBase = 0x01A00000; + constexpr uint32_t kIopHeapLimit = 0x01F00000; + constexpr uint32_t kIopHeapAlign = 16; + uint32_t g_iopHeapNext = kIopHeapBase; + + std::string toLowerAscii(std::string value) + { + std::transform(value.begin(), value.end(), value.begin(), + [](unsigned char c) + { return static_cast(std::tolower(c)); }); + return value; + } + + std::string stripIsoVersionSuffix(std::string value) + { + const std::size_t semicolon = value.find(';'); + if (semicolon == std::string::npos) + { + return value; + } + + bool numericSuffix = semicolon + 1 < value.size(); + for (std::size_t i = semicolon + 1; i < value.size(); ++i) + { + if (!std::isdigit(static_cast(value[i]))) + { + numericSuffix = false; + break; + } + } + + if (numericSuffix) + { + value.erase(semicolon); + } + return value; + } + + std::string normalizePathSeparators(std::string value) + { + std::replace(value.begin(), value.end(), '\\', '/'); + return value; + } + + void trimLeadingSeparators(std::string &value) + { + while (!value.empty() && (value.front() == '/' || value.front() == '\\')) + { + value.erase(value.begin()); + } + } + + std::string normalizeCdPathNoPrefix(std::string path) + { + path = normalizePathSeparators(std::move(path)); + std::string lower = toLowerAscii(path); + if (lower.rfind("cdrom0:", 0) == 0) + { + path = path.substr(7); + } + else if (lower.rfind("cdrom:", 0) == 0) + { + path = path.substr(6); + } + + trimLeadingSeparators(path); + while (!path.empty() && std::isspace(static_cast(path.front()))) + { + path.erase(path.begin()); + } + while (!path.empty() && std::isspace(static_cast(path.back()))) + { + path.pop_back(); + } + path = stripIsoVersionSuffix(std::move(path)); + return path; + } + + std::filesystem::path getCdRootPath() + { + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + if (!paths.cdRoot.empty()) + { + return paths.cdRoot; + } + if (!paths.elfDirectory.empty()) + { + return paths.elfDirectory; + } + + std::error_code ec; + const std::filesystem::path cwd = std::filesystem::current_path(ec); + return ec ? std::filesystem::path(".") : cwd.lexically_normal(); + } + + std::filesystem::path getCdImagePath() + { + return PS2Runtime::getIoPaths().cdImage; + } + + uint32_t sectorsForBytes(uint64_t byteCount) + { + const uint64_t sectors = (byteCount + (kCdSectorSize - 1)) / kCdSectorSize; + return sectors > 0 ? static_cast(sectors) : 1; + } + + std::string cdPathKey(const std::string &ps2Path) + { + return toLowerAscii(normalizeCdPathNoPrefix(ps2Path)); + } + + std::filesystem::path cdHostPath(const std::string &ps2Path) + { + const std::string normalized = normalizeCdPathNoPrefix(ps2Path); + std::filesystem::path resolved = getCdRootPath(); + if (!normalized.empty()) + { + resolved /= std::filesystem::path(normalized); + } + return resolved.lexically_normal(); + } + + bool resolveCaseInsensitivePath(const std::filesystem::path &root, + const std::filesystem::path &relative, + std::filesystem::path &resolvedOut) + { + std::filesystem::path current = root; + for (const auto &component : relative) + { + const std::filesystem::path direct = current / component; + std::error_code ec; + if (std::filesystem::exists(direct, ec) && !ec) + { + current = direct; + continue; + } + + bool matched = false; + const std::string needle = toLowerAscii(component.string()); + std::error_code iterEc; + for (const auto &entry : std::filesystem::directory_iterator(current, iterEc)) + { + if (iterEc) + { + break; + } + + const std::string candidate = toLowerAscii(entry.path().filename().string()); + if (candidate == needle) + { + current = entry.path(); + matched = true; + break; + } + } + + if (!matched) + { + return false; + } + } + + std::error_code fileEc; + if (std::filesystem::is_regular_file(current, fileEc) && !fileEc) + { + resolvedOut = current; + return true; + } + return false; + } + + void ensureCdLeafIndex(const std::filesystem::path &root) + { + if (g_cdLeafIndexBuilt && g_cdLeafIndexRoot == root) + { + return; + } + + g_cdLeafIndex.clear(); + g_cdLeafIndexRoot = root; + g_cdLeafIndexBuilt = true; + + std::error_code ec; + if (!std::filesystem::exists(root, ec) || ec) + { + return; + } + + for (const auto &entry : std::filesystem::recursive_directory_iterator( + root, std::filesystem::directory_options::skip_permission_denied, ec)) + { + if (ec) + { + break; + } + if (!entry.is_regular_file()) + { + continue; + } + + const std::string leaf = toLowerAscii(entry.path().filename().string()); + g_cdLeafIndex.emplace(leaf, entry.path()); + } + } + + bool registerCdFile(const std::string &ps2Path, CdFileEntry &entryOut) + { + const std::string key = cdPathKey(ps2Path); + if (key.empty()) + { + g_lastCdError = -1; + return false; + } + + auto existing = g_cdFilesByKey.find(key); + if (existing != g_cdFilesByKey.end()) + { + entryOut = existing->second; + g_lastCdError = 0; + return true; + } + + const std::filesystem::path root = getCdRootPath(); + std::filesystem::path path = cdHostPath(ps2Path); + std::error_code ec; + if (!std::filesystem::exists(path, ec) || ec || !std::filesystem::is_regular_file(path, ec)) + { + const std::filesystem::path relative(normalizeCdPathNoPrefix(ps2Path)); + std::filesystem::path resolvedCasePath; + if (resolveCaseInsensitivePath(root, relative, resolvedCasePath)) + { + path = resolvedCasePath; + ec.clear(); + } + else + { + ensureCdLeafIndex(root); + const std::string leaf = toLowerAscii(relative.filename().string()); + auto it = g_cdLeafIndex.find(leaf); + if (it != g_cdLeafIndex.end()) + { + path = it->second; + ec.clear(); + } + else + { + g_lastCdError = -1; + return false; + } + } + } + + const uint64_t sizeBytes = std::filesystem::file_size(path, ec); + if (ec) + { + g_lastCdError = -1; + return false; + } + + CdFileEntry entry; + entry.hostPath = path; + entry.sizeBytes = static_cast(std::min(sizeBytes, 0xFFFFFFFFu)); + entry.baseLbn = g_nextPseudoLbn; + entry.sectors = sectorsForBytes(sizeBytes); + + g_nextPseudoLbn += entry.sectors + 1; + g_cdFilesByKey.emplace(key, entry); + entryOut = entry; + g_lastCdError = 0; + return true; + } + + bool readHostRange(const std::filesystem::path &path, uint64_t offsetBytes, uint8_t *dst, size_t byteCount) + { + if (!dst) + { + g_lastCdError = -1; + return false; + } + if (byteCount == 0) + { + g_lastCdError = 0; + return true; + } + + std::memset(dst, 0, byteCount); + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) + { + g_lastCdError = -1; + return false; + } + + file.seekg(static_cast(offsetBytes), std::ios::beg); + if (!file.good()) + { + g_lastCdError = -1; + return false; + } + + file.read(reinterpret_cast(dst), static_cast(byteCount)); + g_lastCdError = 0; + return true; + } + + bool readCdSectors(uint32_t lbn, uint32_t sectors, uint8_t *dst, size_t byteCount) + { + for (const auto &[key, entry] : g_cdFilesByKey) + { + const uint32_t endLbn = entry.baseLbn + entry.sectors; + if (lbn < entry.baseLbn || lbn >= endLbn) + { + continue; + } + + const uint64_t relativeLbn = static_cast(lbn - entry.baseLbn); + const uint64_t offset = relativeLbn * kCdSectorSize; + return readHostRange(entry.hostPath, offset, dst, byteCount); + } + + const std::filesystem::path cdImage = getCdImagePath(); + if (!cdImage.empty()) + { + const uint64_t offset = static_cast(lbn) * kCdSectorSize; + return readHostRange(cdImage, offset, dst, byteCount); + } + + std::cerr << "sceCdRead unresolved LBN 0x" << std::hex << lbn + << " sectors=" << std::dec << sectors + << " (no mapped file and no configured CD image)" << std::endl; + g_lastCdError = -1; + return false; + } + + bool writeCdSearchResult(uint8_t *rdram, uint32_t fileAddr, const std::string &ps2Path, const CdFileEntry &entry) + { + // sceCdlFILE layout: u32 lsn, u32 size, char name[16], u8 date[8] + uint8_t *fileStruct = getMemPtr(rdram, fileAddr); + if (!fileStruct) + { + return false; + } + + std::array packed{}; + std::memcpy(packed.data() + 0, &entry.baseLbn, sizeof(entry.baseLbn)); + std::memcpy(packed.data() + 4, &entry.sizeBytes, sizeof(entry.sizeBytes)); + + std::filesystem::path leafPath(normalizeCdPathNoPrefix(ps2Path)); + std::string leaf = leafPath.filename().string(); + leaf = stripIsoVersionSuffix(std::move(leaf)); + std::strncpy(reinterpret_cast(packed.data() + 8), leaf.c_str(), 15); + + std::memcpy(fileStruct, packed.data(), packed.size()); + return true; + } + + bool hostFileHasAfsMagic(const std::filesystem::path &path) + { + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) + { + return false; + } + + char magic[4] = {}; + file.read(magic, sizeof(magic)); + if (file.gcount() < 3) + { + return false; + } + + return magic[0] == 'A' && magic[1] == 'F' && magic[2] == 'S'; + } + + bool tryRemapGdInitSearchToAfs(const std::string &ps2Path, + uint32_t callerRa, + const CdFileEntry &foundEntry, + CdFileEntry &entryOut, + std::string &resolvedPathOut) + { +#if !PS2_CD_REMAP_IDX_TO_AFS + { + return false; + } +#endif + + if (callerRa != 0x2d9444u) + { + return false; + } + + std::filesystem::path relative(normalizeCdPathNoPrefix(ps2Path)); + const std::string ext = toLowerAscii(relative.extension().string()); + const std::string leaf = toLowerAscii(relative.filename().string()); + + if (ext == ".idx") + { + if (foundEntry.sizeBytes > (kCdSectorSize * 8u)) + { + return false; + } + + std::filesystem::path afsRelative = relative; + afsRelative.replace_extension(".AFS"); + + CdFileEntry afsEntry; + if (!registerCdFile(afsRelative.generic_string(), afsEntry)) + { + return false; + } + if (!hostFileHasAfsMagic(afsEntry.hostPath)) + { + return false; + } + + entryOut = afsEntry; + resolvedPathOut = afsRelative.generic_string(); + return true; + } + + return false; + } + + uint8_t toBcd(uint32_t value) + { + const uint32_t clamped = value % 100; + return static_cast(((clamped / 10) << 4) | (clamped % 10)); + } + + uint32_t fromBcd(uint8_t value) + { + return static_cast(((value >> 4) & 0x0F) * 10 + (value & 0x0F)); + } + + std::unordered_map g_file_map; + uint32_t g_next_file_handle = 1; // Start file handles > 0 (0 is NULL) + std::mutex g_file_mutex; + + uint32_t generate_file_handle() + { + uint32_t handle = 0; + do + { + handle = g_next_file_handle++; + if (g_next_file_handle == 0) + g_next_file_handle = 1; + } while (handle == 0 || g_file_map.count(handle)); + return handle; + } + + FILE *get_file_ptr(uint32_t handle) + { + if (handle == 0) + return nullptr; + std::lock_guard lock(g_file_mutex); + auto it = g_file_map.find(handle); + return (it != g_file_map.end()) ? it->second : nullptr; + } +} + +namespace +{ + // convert a host pointer within rdram back to a PS2 address + uint32_t hostPtrToPs2Addr(uint8_t *rdram, const void *hostPtr) + { + if (!hostPtr) + return 0; // Handle NULL pointer case + + const uint8_t *ptr_u8 = static_cast(hostPtr); + std::ptrdiff_t offset = ptr_u8 - rdram; + + // Check if is in rdram range + if (offset >= 0 && static_cast(offset) < PS2_RAM_SIZE) + { + return PS2_RAM_BASE + static_cast(offset); + } + else + { + std::cerr << "Warning: hostPtrToPs2Addr failed - host pointer " << hostPtr << " is outside rdram range [" << static_cast(rdram) << ", " << static_cast(rdram + PS2_RAM_SIZE) << ")" << std::endl; + return 0; + } + } +} + +namespace +{ + bool tryReadWordFromRdram(uint8_t *rdram, uint32_t addr, uint32_t &outWord) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(&outWord, ptr, sizeof(outWord)); + return true; + } + + bool tryReadWordFromGuest(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint32_t &outWord) + { + if (tryReadWordFromRdram(rdram, addr, outWord)) + { + return true; + } + + if (runtime) + { + try + { + PS2Memory &mem = runtime->memory(); + outWord = static_cast(mem.read8(addr + 0u)) | + (static_cast(mem.read8(addr + 1u)) << 8u) | + (static_cast(mem.read8(addr + 2u)) << 16u) | + (static_cast(mem.read8(addr + 3u)) << 24u); + return true; + } + catch (...) + { + return false; + } + } + return false; + } + + bool tryReadByteFromGuest(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, uint8_t &outByte) + { + const uint8_t *chPtr = getConstMemPtr(rdram, addr); + if (chPtr) + { + outByte = *chPtr; + return true; + } + + if (runtime) + { + try + { + outByte = runtime->memory().read8(addr); + return true; + } + catch (...) + { + return false; + } + } + return false; + } + + bool writeGuestBytes(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, const uint8_t *src, size_t len) + { + if (!src || len == 0) + { + return true; + } + + bool allViaPtrs = true; + for (size_t i = 0; i < len; ++i) + { + const uint64_t guestAddr = static_cast(addr) + i; + if (guestAddr > 0xFFFFFFFFull) + { + return false; + } + uint8_t *dst = getMemPtr(rdram, static_cast(guestAddr)); + if (!dst) + { + allViaPtrs = false; + break; + } + *dst = src[i]; + } + if (allViaPtrs) + { + return true; + } + + if (runtime) + { + try + { + PS2Memory &mem = runtime->memory(); + for (size_t i = 0; i < len; ++i) + { + const uint64_t guestAddr = static_cast(addr) + i; + if (guestAddr > 0xFFFFFFFFull) + { + return false; + } + mem.write8(static_cast(guestAddr), src[i]); + } + return true; + } + catch (...) + { + return false; + } + } + + return false; + } + + std::string readPs2CStringBounded(uint8_t *rdram, PS2Runtime *runtime, uint32_t addr, size_t maxLen = 512) + { + std::string out; + if (addr == 0 || maxLen == 0) + { + return out; + } + + out.reserve(std::min(maxLen, 128)); + for (size_t i = 0; i < maxLen; ++i) + { + const uint64_t guestAddr = static_cast(addr) + i; + if (guestAddr > 0xFFFFFFFFull) + { + break; + } + + uint8_t chByte = 0; + if (!tryReadByteFromGuest(rdram, runtime, static_cast(guestAddr), chByte)) + { + break; + } + + const char ch = static_cast(chByte); + if (ch == '\0') + { + break; + } + out.push_back(ch); + } + + return out; + } + + std::string readPs2CStringBounded(uint8_t *rdram, uint32_t addr, size_t maxLen = 512) + { + return readPs2CStringBounded(rdram, nullptr, addr, maxLen); + } + + std::string sanitizeForLog(const std::string &value) + { + std::string out; + out.reserve(value.size()); + for (unsigned char ch : value) + { + if (ch == '\n' || ch == '\r' || ch == '\t' || (ch >= 0x20 && ch < 0x7F)) + { + out.push_back(static_cast(ch)); + } + else + { + out.push_back('.'); + } + } + return out; + } + + class Ps2VarArgCursor + { + public: + Ps2VarArgCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, int fixedArgs) + : m_rdram(rdram), + m_ctx(ctx), + m_runtime(runtime), + m_fixedArgs(fixedArgs), + m_stackBase(getRegU32(ctx, 29) + 0x10) + { + if (m_fixedArgs < 0) + { + m_fixedArgs = 0; + } + m_slotIndex = static_cast(m_fixedArgs); + } + + uint32_t nextU32() + { + const uint32_t value = readWordAtSlot(m_slotIndex); + ++m_slotIndex; + return value; + } + + uint64_t nextU64() + { + // O32 ABI aligns 64-bit variadic values on even 32-bit slots. + if ((m_slotIndex & 1u) != 0u) + { + ++m_slotIndex; + } + const uint64_t low = readWordAtSlot(m_slotIndex); + const uint64_t high = readWordAtSlot(m_slotIndex + 1u); + m_slotIndex += 2u; + return low | (high << 32); + } + + private: + uint32_t readWordAtSlot(uint32_t slotIndex) const + { + if (slotIndex < 4u) + { + // slot0..slot3 -> a0..a3 (r4..r7) + return getRegU32(m_ctx, 4 + static_cast(slotIndex)); + } + + const uint32_t stackIndex = slotIndex - 4u; + const uint32_t stackAddr = m_stackBase + stackIndex * 4u; + uint32_t value = 0; + (void)tryReadWordFromGuest(m_rdram, m_runtime, stackAddr, value); + return value; + } + + uint8_t *m_rdram; + R5900Context *m_ctx; + PS2Runtime *m_runtime; + int m_fixedArgs; + uint32_t m_stackBase; + uint32_t m_slotIndex = 0; + }; + + class Ps2VaListCursor + { + public: + Ps2VaListCursor(uint8_t *rdram, PS2Runtime *runtime, uint32_t vaListAddr) + : m_rdram(rdram), m_runtime(runtime), m_curr(vaListAddr) + { + } + + uint32_t nextU32() + { + uint32_t value = 0; + (void)tryReadWordFromGuest(m_rdram, m_runtime, m_curr, value); + m_curr += 4; + return value; + } + + uint64_t nextU64() + { + m_curr = (m_curr + 7u) & ~7u; + const uint64_t low = nextU32(); + const uint64_t high = nextU32(); + return low | (high << 32); + } + + private: + uint8_t *m_rdram; + PS2Runtime *m_runtime; + uint32_t m_curr = 0; + }; + + template + std::string formatPs2StringCore(uint8_t *rdram, const char *format, NextU32Fn nextU32, NextU64Fn nextU64, ReadStringFn readString) + { + if (!format) + { + return {}; + } + + std::string out; + out.reserve(std::strlen(format) + 32); + const char *p = format; + + while (*p) + { + if (*p != '%') + { + out.push_back(*p++); + continue; + } + + const char *specStart = p++; + if (*p == '%') + { + out.push_back('%'); + ++p; + continue; + } + + int parsedWidth = -1; + int parsedPrecision = -1; + + while (*p && std::strchr("-+ #0", *p)) + { + ++p; + } + + if (*p == '*') + { + parsedWidth = static_cast(nextU32()); + ++p; + } + else + { + if (*p && std::isdigit(static_cast(*p))) + { + parsedWidth = 0; + } + while (*p && std::isdigit(static_cast(*p))) + { + parsedWidth = (parsedWidth * 10) + (*p - '0'); + ++p; + } + } + + if (*p == '.') + { + ++p; + if (*p == '*') + { + parsedPrecision = static_cast(nextU32()); + ++p; + } + else + { + parsedPrecision = 0; + while (*p && std::isdigit(static_cast(*p))) + { + parsedPrecision = (parsedPrecision * 10) + (*p - '0'); + ++p; + } + } + } + if (parsedPrecision < 0) + { + parsedPrecision = -1; + } + (void)parsedWidth; + + enum class LengthMod + { + None, + H, + HH, + L, + LL, + J, + Z, + T, + BigL + }; + + LengthMod length = LengthMod::None; + if (*p == 'h') + { + ++p; + if (*p == 'h') + { + ++p; + length = LengthMod::HH; + } + else + { + length = LengthMod::H; + } + } + else if (*p == 'l') + { + ++p; + if (*p == 'l') + { + ++p; + length = LengthMod::LL; + } + else + { + length = LengthMod::L; + } + } + else if (*p == 'j') + { + ++p; + length = LengthMod::J; + } + else if (*p == 'z') + { + ++p; + length = LengthMod::Z; + } + else if (*p == 't') + { + ++p; + length = LengthMod::T; + } + else if (*p == 'L') + { + ++p; + length = LengthMod::BigL; + } + + if (*p == '\0') + { + out.append(specStart); + break; + } + + const bool use64Integer = (length == LengthMod::LL || length == LengthMod::J); + auto readUnsignedInteger = [&]() -> uint64_t + { + return use64Integer ? nextU64() : static_cast(nextU32()); + }; + auto readSignedInteger = [&]() -> int64_t + { + if (use64Integer) + { + return static_cast(nextU64()); + } + return static_cast(static_cast(nextU32())); + }; + + const char spec = *p++; + switch (spec) + { + case 's': + { + const uint32_t strAddr = nextU32(); + if (strAddr == 0) + { + out.append("(null)"); + } + else + { + std::string str = readString(strAddr); + if (parsedPrecision >= 0 && + str.size() > static_cast(parsedPrecision)) + { + str.resize(static_cast(parsedPrecision)); + } + out.append(str); + } + break; + } + case 'c': + { + const char ch = static_cast(nextU32() & 0xFF); + out.push_back(ch); + break; + } + case 'd': + case 'i': + out.append(std::to_string(readSignedInteger())); + break; + case 'u': + out.append(std::to_string(readUnsignedInteger())); + break; + case 'x': + case 'X': + { + std::ostringstream ss; + if (spec == 'X') + { + ss.setf(std::ios::uppercase); + } + ss << std::hex << readUnsignedInteger(); + out.append(ss.str()); + break; + } + case 'o': + { + std::ostringstream ss; + ss << std::oct << readUnsignedInteger(); + out.append(ss.str()); + break; + } + case 'p': + { + std::ostringstream ss; + ss << "0x" << std::hex << nextU32(); + out.append(ss.str()); + break; + } + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': + case 'a': + case 'A': + { + const uint64_t bits = nextU64(); + double value = 0.0; + std::memcpy(&value, &bits, sizeof(value)); + char numBuf[128]; + std::snprintf(numBuf, sizeof(numBuf), "%g", value); + out.append(numBuf); + break; + } + case 'n': + { + // Avoid arbitrary guest memory mutation through %n in stub formatting. + (void)nextU32(); + break; + } + default: + out.append(specStart, p - specStart); + break; + } + } + + return out; + } + + std::string formatPs2StringWithArgs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, const char *format, int fixedArgs) + { + Ps2VarArgCursor cursor(rdram, ctx, runtime, fixedArgs); + return formatPs2StringCore( + rdram, + format, + [&cursor]() + { return cursor.nextU32(); }, + [&cursor]() + { return cursor.nextU64(); }, + [rdram, runtime](uint32_t addr) + { return readPs2CStringBounded(rdram, runtime, addr); }); + } + + std::string formatPs2StringWithVaList(uint8_t *rdram, PS2Runtime *runtime, const char *format, uint32_t vaListAddr) + { + Ps2VaListCursor cursor(rdram, runtime, vaListAddr); + return formatPs2StringCore( + rdram, + format, + [&cursor]() + { return cursor.nextU32(); }, + [&cursor]() + { return cursor.nextU64(); }, + [rdram, runtime](uint32_t addr) + { return readPs2CStringBounded(rdram, runtime, addr); }); + } + + constexpr uint32_t kMaxStubWarningsPerName = 8; + std::unordered_map g_stubWarningCount; + std::mutex g_stubWarningMutex; + constexpr uint32_t kMaxPrintfLogs = 200; + constexpr size_t kMaxFormattedOutputBytes = 4096; + uint32_t g_printfLogCount = 0; + std::mutex g_printfLogMutex; + + constexpr std::array kDmaChannelBases = { + 0x10008000u, 0x10009000u, 0x1000A000u, 0x1000B000u, 0x1000B400u, + 0x1000C000u, 0x1000C400u, 0x1000C800u, 0x1000D000u, 0x1000D400u}; + std::mutex g_dmaStubMutex; + std::unordered_map g_dmaPendingPolls; + uint32_t g_dmaStubLogCount = 0; + constexpr uint32_t kMaxDmaStubLogs = 64; + + bool isKnownDmaChannelBase(uint32_t value) + { + return std::find(kDmaChannelBases.begin(), kDmaChannelBases.end(), value) != kDmaChannelBases.end(); + } + + uint32_t toDmaPhys(uint32_t addr) + { + return addr & 0x1FFFFFFFu; + } + + uint32_t normalizeQwcFromArg(uint32_t value) + { + if (value == 0) + { + return 0; + } + if (value > 0xFFFFu) + { + return std::min((value + 15u) >> 4u, 0xFFFFu); + } + return value & 0xFFFFu; + } + + struct ParsedDmaTag + { + bool valid = false; + uint32_t qwc = 0; + uint32_t id = 0; + uint32_t addr = 0; + }; + + ParsedDmaTag tryParseDmaTag(uint8_t *rdram, uint32_t guestAddr) + { + ParsedDmaTag out; + if (guestAddr == 0) + { + return out; + } + + const uint8_t *ptr = getConstMemPtr(rdram, guestAddr); + if (!ptr) + { + return out; + } + + uint64_t tag = 0; + std::memcpy(&tag, ptr, sizeof(tag)); + out.valid = true; + out.qwc = static_cast(tag & 0xFFFFu); + out.id = static_cast((tag >> 28) & 0x7u); + out.addr = static_cast((tag >> 32) & 0x7FFFFFFFu); + return out; + } + + uint32_t resolveDmaChannelBase(uint8_t *rdram, uint32_t chanArg) + { + if (isKnownDmaChannelBase(chanArg)) + { + return chanArg; + } + if (chanArg < kDmaChannelBases.size()) + { + return kDmaChannelBases[chanArg]; + } + + const uint32_t masked = chanArg & 0xFFFFFF00u; + if (isKnownDmaChannelBase(masked)) + { + return masked; + } + + uint32_t candidate0 = 0; + if (!tryReadWordFromRdram(rdram, chanArg, candidate0)) + { + return 0; + } + if (isKnownDmaChannelBase(candidate0)) + { + return candidate0; + } + + uint32_t candidate1 = 0; + if (!tryReadWordFromRdram(rdram, chanArg + 4u, candidate1)) + { + return 0; + } + if (isKnownDmaChannelBase(candidate1)) + { + return candidate1; + } + + return 0; + } + + int32_t submitDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, bool preferNormalCount) + { + if (!runtime) + { + return -1; + } + + const uint32_t chanArg = getRegU32(ctx, 4); + const uint32_t payloadArg = getRegU32(ctx, 5); + const uint32_t countArg = getRegU32(ctx, 6); + const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); + if (channelBase == 0) + { + return -1; + } + + const uint32_t payloadPhys = toDmaPhys(payloadArg); + uint32_t madr = 0; + uint32_t qwc = 0; + uint32_t tadr = payloadPhys; + uint32_t chcr = 0x00000181u; // DIR=1, TIE=1, STR=1 (normal mode). + + if (preferNormalCount) + { + qwc = normalizeQwcFromArg(countArg); + madr = payloadPhys; + } + else + { + const ParsedDmaTag tag = tryParseDmaTag(rdram, payloadPhys); + if (tag.valid && tag.qwc != 0) + { + qwc = tag.qwc; + switch (tag.id) + { + case 0: // REFE + case 3: // REF + case 4: // REFS + madr = toDmaPhys(tag.addr); + break; + default: + // CNT/NEXT/CALL/RET-style tags carry payload inline after the tag. + madr = toDmaPhys(payloadPhys + 0x10u); + break; + } + } + else + { + // Fall back to chain mode so the runtime DMA path can walk TADR. + chcr = 0x00000185u; // MODE=1 chain, DIR=1, TIE=1, STR=1. + } + } + + PS2Memory &mem = runtime->memory(); + mem.writeIORegister(channelBase + 0x20u, qwc & 0xFFFFu); + mem.writeIORegister(channelBase + 0x10u, madr); + mem.writeIORegister(channelBase + 0x30u, tadr); + mem.writeIORegister(channelBase + 0x00u, chcr); + + std::lock_guard lock(g_dmaStubMutex); + g_dmaPendingPolls[channelBase] = 1; + if (g_dmaStubLogCount < kMaxDmaStubLogs) + { + std::cout << "[sceDmaSend] ch=0x" << std::hex << channelBase + << " madr=0x" << madr + << " qwc=0x" << qwc + << " tadr=0x" << tadr + << " chcr=0x" << chcr << std::dec << std::endl; + ++g_dmaStubLogCount; + } + + return 0; + } + + int32_t submitDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) + { + if (!runtime) + { + return -1; + } + + const uint32_t chanArg = getRegU32(ctx, 4); + const uint32_t mode = getRegU32(ctx, 5); + const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); + if (channelBase == 0) + { + return -1; + } + + bool modelBusy = false; + { + std::lock_guard lock(g_dmaStubMutex); + auto it = g_dmaPendingPolls.find(channelBase); + if (it != g_dmaPendingPolls.end() && it->second > 0) + { + modelBusy = true; + if (mode != 0) + { + --it->second; + if (it->second == 0) + { + g_dmaPendingPolls.erase(it); + } + } + else + { + // Blocking mode: complete immediately in this runtime. + g_dmaPendingPolls.erase(it); + } + } + } + + const uint32_t chcr = runtime->memory().readIORegister(channelBase + 0x00u); + const bool hwBusy = (chcr & 0x100u) != 0; + return ((modelBusy || hwBusy) && mode != 0) ? 1 : 0; + } + +} + +namespace +{ + struct GsGParam + { + uint8_t interlace; + uint8_t omode; + uint8_t ffmode; + uint8_t version; + }; + + struct GsDispEnvMem + { + uint64_t display; + uint64_t dispfb; + }; + + struct GsImageMem + { + uint16_t x; + uint16_t y; + uint16_t width; + uint16_t height; + uint16_t vram_addr; + uint8_t vram_width; + uint8_t psm; + }; + +#pragma pack(push, 1) + struct GsDrawEnvMem + { + uint16_t offset_x; + uint16_t offset_y; + uint16_t clip_x; + uint16_t clip_y; + uint16_t clip_w; + uint16_t clip_h; + uint16_t vram_addr; + uint8_t fbw; + uint8_t psm; + uint16_t vram_x; + uint16_t vram_y; + uint32_t draw_mask; + uint8_t auto_clear; + uint8_t pad[3]; + uint8_t bg_r; + uint8_t bg_g; + uint8_t bg_b; + uint8_t bg_a; + float bg_q; + }; +#pragma pack(pop) + + static_assert(sizeof(GsImageMem) == 12, "GsImageMem size mismatch"); + static_assert(sizeof(GsDrawEnvMem) == 36, "GsDrawEnvMem size mismatch"); + + constexpr uint32_t kGsParamScratchOffset = 0x100; + GsGParam g_gparam{1, 2, 1, 3}; // Default: interlaced NTSC, frame mode. + + static uint64_t makePmode(uint32_t en1, uint32_t en2, uint32_t mmod, uint32_t amod, uint32_t slbg, uint32_t alp) + { + return (static_cast(en1 & 1) << 0) | + (static_cast(en2 & 1) << 1) | + (static_cast(1) << 2) | + (static_cast(mmod & 1) << 5) | + (static_cast(amod & 1) << 6) | + (static_cast(slbg & 1) << 7) | + (static_cast(alp & 0xFF) << 8); + } + + static uint64_t makeDispFb(uint32_t fbp, uint32_t fbw, uint32_t psm, uint32_t dbx, uint32_t dby) + { + return (static_cast(fbp & 0x1FF) << 0) | + (static_cast(fbw & 0x3F) << 9) | + (static_cast(psm & 0x1F) << 15) | + (static_cast(dbx & 0x7FF) << 32) | + (static_cast(dby & 0x7FF) << 43); + } + + static uint64_t makeDisplay(uint32_t dx, uint32_t dy, uint32_t magh, uint32_t magv, uint32_t dw, uint32_t dh) + { + return (static_cast(dx & 0x0FFF) << 0) | + (static_cast(dy & 0x07FF) << 12) | + (static_cast(magh & 0x0F) << 23) | + (static_cast(magv & 0x03) << 27) | + (static_cast(dw & 0x0FFF) << 32) | + (static_cast(dh & 0x07FF) << 44); + } + + static uint32_t readStackU32(uint8_t *rdram, R5900Context *ctx, uint32_t offset) + { + uint32_t sp = getRegU32(ctx, 29); + const uint8_t *ptr = getConstMemPtr(rdram, sp + offset); + if (!ptr) + return 0; + uint32_t value = 0; + std::memcpy(&value, ptr, sizeof(value)); + return value; + } + + static uint32_t bytesForPixels(uint8_t psm, uint32_t pixelCount) + { + const uint64_t pixels = static_cast(pixelCount); + uint64_t bytes = 0; + switch (psm) + { + case 0: // PSMCT32 + case 1: // PSMCT24 (treat as 32) + case 27: // PSMT8H (packed in 32-bit lanes) + case 36: // PSMT4HL (packed in 32-bit lanes) + case 44: // PSMT4HH (packed in 32-bit lanes) + bytes = pixels * 4ull; + break; + case 2: // PSMCT16 + case 10: // PSMCT16S + bytes = pixels * 2ull; + break; + case 19: // PSMT8 + bytes = pixels; + break; + case 20: // PSMT4 + bytes = (pixels + 1ull) / 2ull; + break; + default: + bytes = pixels * 4ull; + break; + } + if (bytes > 0xFFFFFFFFull) + { + return 0xFFFFFFFFu; + } + return static_cast(bytes); + } + + struct GsSetDefImageArgs + { + uint32_t x = 0; + uint32_t y = 0; + uint32_t width = 0; + uint32_t height = 0; + uint32_t vramAddr = 0; + uint32_t vramWidth = 0; + uint32_t psm = 0; + }; + + static GsSetDefImageArgs decodeGsSetDefImageArgs(uint8_t *rdram, R5900Context *ctx) + { + GsSetDefImageArgs decoded{}; + + const uint32_t reg8 = getRegU32(ctx, 8); + const uint32_t reg9 = getRegU32(ctx, 9); + const uint32_t reg10 = getRegU32(ctx, 10); + const uint32_t reg11 = getRegU32(ctx, 11); + + const uint32_t stack0 = readStackU32(rdram, ctx, 16); + const uint32_t stack1 = readStackU32(rdram, ctx, 20); + const uint32_t stack2 = readStackU32(rdram, ctx, 24); + const uint32_t stack3 = readStackU32(rdram, ctx, 28); + + const bool looksLikeCanonicalRegs = (reg10 != 0u || reg11 != 0u); + const bool looksLikeCanonicalStack = (stack2 != 0u || stack3 != 0u); + + if (looksLikeCanonicalRegs || looksLikeCanonicalStack) + { + decoded.vramAddr = getRegU32(ctx, 5); + decoded.vramWidth = getRegU32(ctx, 6); + decoded.psm = getRegU32(ctx, 7); + + if (looksLikeCanonicalRegs) + { + decoded.x = reg8; + decoded.y = reg9; + decoded.width = reg10; + decoded.height = reg11; + } + else + { + decoded.x = stack0; + decoded.y = stack1; + decoded.width = stack2; + decoded.height = stack3; + } + return decoded; + } + + // Legacy code + // a1=x, a2=y, a3=w, stack/reg extension for h/vram/fbw/psm. + decoded.x = getRegU32(ctx, 5); + decoded.y = getRegU32(ctx, 6); + decoded.width = getRegU32(ctx, 7); + decoded.height = stack0 != 0u ? stack0 : reg8; + decoded.vramAddr = stack1 != 0u ? stack1 : reg9; + decoded.vramWidth = stack2 != 0u ? stack2 : reg10; + decoded.psm = stack3 != 0u ? stack3 : reg11; + return decoded; + } + + static bool readGsImage(uint8_t *rdram, uint32_t addr, GsImageMem &out) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + return false; + std::memcpy(&out, ptr, sizeof(out)); + return true; + } + + static bool writeGsImage(uint8_t *rdram, uint32_t addr, const GsImageMem &img) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + return false; + std::memcpy(ptr, &img, sizeof(img)); + return true; + } + + static bool writeGsDispEnv(uint8_t *rdram, uint32_t addr, uint64_t display, uint64_t dispfb) + { + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + return false; + GsDispEnvMem env{display, dispfb}; + std::memcpy(ptr, &env, sizeof(env)); + return true; + } + + static bool readGsDispEnv(uint8_t *rdram, uint32_t addr, GsDispEnvMem &out) + { + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + return false; + std::memcpy(&out, ptr, sizeof(out)); + return true; + } + + static uint32_t writeGsGParamToScratch(PS2Runtime *runtime) + { + if (!runtime) + return 0; + uint8_t *scratch = runtime->memory().getScratchpad(); + if (!scratch) + return 0; + std::memcpy(scratch + kGsParamScratchOffset, &g_gparam, sizeof(g_gparam)); + return PS2_SCRATCHPAD_BASE + kGsParamScratchOffset; + } +} diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl new file mode 100644 index 0000000..7a3eaae --- /dev/null +++ b/ps2xRuntime/src/lib/stubs/ps2_stubs_gs.inl @@ -0,0 +1,317 @@ +void sceGsExecLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t imgAddr = getRegU32(ctx, 4); + uint32_t srcAddr = getRegU32(ctx, 5); + + GsImageMem img{}; + if (!runtime || !readGsImage(rdram, imgAddr, img)) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); + if (rowBytes == 0) + { + setReturnS32(ctx, -1); + return; + } + + uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); + uint32_t base = static_cast(img.vram_addr) * 2048u; + uint32_t stride = bytesForPixels(img.psm, fbw * 64u); + if (stride == 0) + { + setReturnS32(ctx, -1); + return; + } + + uint8_t *gsvram = runtime->memory().getGSVRAM(); + uint8_t *src = getMemPtr(rdram, srcAddr); + if (!gsvram || !src) + { + setReturnS32(ctx, -1); + return; + } + + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sceGsExecLoadImage: x=" << img.x + << " y=" << img.y + << " w=" << img.width + << " h=" << img.height + << " vram=0x" << std::hex << img.vram_addr + << " fbw=" << std::dec << static_cast(fbw) + << " psm=" << static_cast(img.psm) + << " src=0x" << std::hex << srcAddr << std::dec << std::endl; + ++logCount; + } + + for (uint32_t row = 0; row < img.height; ++row) + { + uint32_t dstOff = base + (static_cast(img.y) + row) * stride + bytesForPixels(img.psm, static_cast(img.x)); + uint32_t srcOff = row * rowBytes; + if (dstOff >= PS2_GS_VRAM_SIZE) + break; + uint32_t copyBytes = rowBytes; + if (dstOff + copyBytes > PS2_GS_VRAM_SIZE) + copyBytes = PS2_GS_VRAM_SIZE - dstOff; + std::memcpy(gsvram + dstOff, src + srcOff, copyBytes); + } + + if (img.width >= 320 && img.height >= 200) + { + auto &gs = runtime->memory().gs(); + gs.dispfb1 = makeDispFb(img.vram_addr, fbw, img.psm, 0, 0); + gs.display1 = makeDisplay(0, 0, 0, 0, img.width - 1, img.height - 1); + } + + setReturnS32(ctx, 0); +} + +void sceGsExecStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t imgAddr = getRegU32(ctx, 4); + uint32_t dstAddr = getRegU32(ctx, 5); + + GsImageMem img{}; + if (!runtime || !readGsImage(rdram, imgAddr, img)) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t rowBytes = bytesForPixels(img.psm, static_cast(img.width)); + if (rowBytes == 0) + { + setReturnS32(ctx, -1); + return; + } + + uint32_t fbw = img.vram_width ? img.vram_width : std::max(1, (img.width + 63) / 64); + uint32_t base = static_cast(img.vram_addr) * 2048u; + uint32_t stride = bytesForPixels(img.psm, fbw * 64u); + if (stride == 0) + { + setReturnS32(ctx, -1); + return; + } + + uint8_t *gsvram = runtime->memory().getGSVRAM(); + uint8_t *dst = getMemPtr(rdram, dstAddr); + if (!gsvram || !dst) + { + setReturnS32(ctx, -1); + return; + } + + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sceGsExecStoreImage: x=" << img.x + << " y=" << img.y + << " w=" << img.width + << " h=" << img.height + << " vram=0x" << std::hex << img.vram_addr + << " fbw=" << std::dec << static_cast(fbw) + << " psm=" << static_cast(img.psm) + << " dst=0x" << std::hex << dstAddr << std::dec << std::endl; + ++logCount; + } + + for (uint32_t row = 0; row < img.height; ++row) + { + uint32_t srcOff = base + (static_cast(img.y) + row) * stride + bytesForPixels(img.psm, static_cast(img.x)); + uint32_t dstOff = row * rowBytes; + if (srcOff >= PS2_GS_VRAM_SIZE) + break; + uint32_t copyBytes = rowBytes; + if (srcOff + copyBytes > PS2_GS_VRAM_SIZE) + copyBytes = PS2_GS_VRAM_SIZE - srcOff; + std::memcpy(dst + dstOff, gsvram + srcOff, copyBytes); + } + + setReturnS32(ctx, 0); +} + +void sceGsGetGParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t addr = writeGsGParamToScratch(runtime); + setReturnU32(ctx, addr); +} + +void sceGsPutDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t envAddr = getRegU32(ctx, 4); + GsDispEnvMem env{}; + if (readGsDispEnv(rdram, envAddr, env)) + { + auto &gs = runtime->memory().gs(); + gs.display1 = env.display; + gs.dispfb1 = env.dispfb; + } + setReturnS32(ctx, 0); +} + +void sceGsPutDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t envAddr = getRegU32(ctx, 4); + uint32_t psm = getRegU32(ctx, 5); + uint32_t w = getRegU32(ctx, 6); + uint32_t h = getRegU32(ctx, 7); + + if (w == 0) + w = 640; + if (h == 0) + h = 448; + + GsDrawEnvMem env{}; + env.offset_x = static_cast(2048 - (w / 2)); + env.offset_y = static_cast(2048 - (h / 2)); + env.clip_x = 0; + env.clip_y = 0; + env.clip_w = static_cast(w); + env.clip_h = static_cast(h); + env.vram_addr = 0; + env.fbw = static_cast((w + 63) / 64); + env.psm = static_cast(psm); + env.vram_x = 0; + env.vram_y = 0; + env.draw_mask = 0; + env.auto_clear = 1; + env.bg_r = 1; + env.bg_g = 1; + env.bg_b = 1; + env.bg_a = 0x80; + env.bg_q = 0.0f; + + uint8_t *ptr = getMemPtr(rdram, envAddr); + if (ptr) + { + std::memcpy(ptr, &env, sizeof(env)); + } + setReturnS32(ctx, 0); +} + +void sceGsResetGraph(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t mode = getRegU32(ctx, 4); + uint32_t interlace = getRegU32(ctx, 5); + uint32_t omode = getRegU32(ctx, 6); + uint32_t ffmode = getRegU32(ctx, 7); + + if (mode == 0) + { + g_gparam.interlace = static_cast(interlace & 0x1); + g_gparam.omode = static_cast(omode & 0xFF); + g_gparam.ffmode = static_cast(ffmode & 0x1); + writeGsGParamToScratch(runtime); + + auto &gs = runtime->memory().gs(); + gs.pmode = makePmode(1, 0, 0, 0, 0, 0x80); + gs.smode2 = (interlace & 0x1) | ((ffmode & 0x1) << 1); + gs.dispfb1 = makeDispFb(0, 10, 0, 0, 0); + gs.display1 = makeDisplay(0, 0, 0, 0, 639, 447); + } + + setReturnS32(ctx, 0); +} + +void sceGsResetPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceGsSetDefClear(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceGsSetDefClear", rdram, ctx, runtime); +} + +void sceGsSetDefDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceGsSetDefDispEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t envAddr = getRegU32(ctx, 4); + uint32_t psm = getRegU32(ctx, 5); + uint32_t w = getRegU32(ctx, 6); + uint32_t h = getRegU32(ctx, 7); + uint32_t dx = readStackU32(rdram, ctx, 16); + uint32_t dy = readStackU32(rdram, ctx, 20); + + if (w == 0) + w = 640; + if (h == 0) + h = 448; + + uint32_t fbw = (w + 63) / 64; + uint64_t dispfb = makeDispFb(0, fbw, psm, 0, 0); + uint64_t display = makeDisplay(dx, dy, 0, 0, w - 1, h - 1); + + writeGsDispEnv(rdram, envAddr, display, dispfb); + setReturnS32(ctx, 0); +} + +void sceGsSetDefDrawEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceGsSetDefDrawEnv", rdram, ctx, runtime); +} + +void sceGsSetDefDrawEnv2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceGsSetDefDrawEnv2", rdram, ctx, runtime); +} + +void sceGsSetDefLoadImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t imgAddr = getRegU32(ctx, 4); + const GsSetDefImageArgs args = decodeGsSetDefImageArgs(rdram, ctx); + + GsImageMem img{}; + img.x = static_cast(args.x); + img.y = static_cast(args.y); + img.width = static_cast(args.width); + img.height = static_cast(args.height); + img.vram_addr = static_cast(args.vramAddr); + img.vram_width = static_cast(args.vramWidth); + img.psm = static_cast(args.psm); + + writeGsImage(rdram, imgAddr, img); + setReturnS32(ctx, 0); +} + +void sceGsSetDefStoreImage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + sceGsSetDefLoadImage(rdram, ctx, runtime); +} + +void sceGsSwapDBuffDc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + // can we get away with that ? kkkk + static int cur = 0; + cur ^= 1; + setReturnS32(ctx, cur); +} + +void sceGsSyncPath(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceGsSyncV(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceGsSyncVCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceGszbufaddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceGszbufaddr", rdram, ctx, runtime); +} diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl new file mode 100644 index 0000000..d5d0cea --- /dev/null +++ b/ps2xRuntime/src/lib/stubs/ps2_stubs_libc.inl @@ -0,0 +1,889 @@ +void malloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t size = getRegU32(ctx, 4); // $a0 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; + setReturnU32(ctx, guestAddr); +} + +void free(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 + if (runtime && guestAddr != 0u) + { + runtime->guestFree(guestAddr); + } +} + +void calloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t count = getRegU32(ctx, 4); // $a0 + const uint32_t size = getRegU32(ctx, 5); // $a1 + const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; + setReturnU32(ctx, guestAddr); +} + +void realloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t oldGuestAddr = getRegU32(ctx, 4); // $a0 + const uint32_t newSize = getRegU32(ctx, 5); // $a1 + const uint32_t newGuestAddr = runtime ? runtime->guestRealloc(oldGuestAddr, newSize) : 0u; + setReturnU32(ctx, newGuestAddr); +} + +void memcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 + + uint8_t *hostDest = getMemPtr(rdram, destAddr); + const uint8_t *hostSrc = getConstMemPtr(rdram, srcAddr); + + if (hostDest && hostSrc) + { + ::memcpy(hostDest, hostSrc, size); + ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(size), "memcpy", ctx); + } + else + { + std::cerr << "memcpy error: Attempted copy involving non-RDRAM address (or invalid RDRAM address)." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << ", Size: " << size << std::endl; + } + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void memset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + int value = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + uint32_t size = getRegU32(ctx, 6); // $a2 + + uint8_t *hostDest = getMemPtr(rdram, destAddr); + + if (hostDest) + { + ::memset(hostDest, value, size); + ps2TraceGuestRangeWrite(rdram, destAddr, size, "memset", ctx); + } + else + { + std::cerr << "memset error: Invalid address provided." << std::endl; + } + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void memmove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 + + uint8_t *hostDest = getMemPtr(rdram, destAddr); + const uint8_t *hostSrc = getConstMemPtr(rdram, srcAddr); + + if (hostDest && hostSrc) + { + ::memmove(hostDest, hostSrc, size); + ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(size), "memmove", ctx); + } + else + { + std::cerr << "memmove error: Attempted move involving potentially invalid RDRAM address." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << ", Size: " << size << std::endl; + } + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void memcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t ptr1Addr = getRegU32(ctx, 4); // $a0 + uint32_t ptr2Addr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + + const uint8_t *hostPtr1 = getConstMemPtr(rdram, ptr1Addr); + const uint8_t *hostPtr2 = getConstMemPtr(rdram, ptr2Addr); + int result = 0; + + if (hostPtr1 && hostPtr2) + { + result = ::memcmp(hostPtr1, hostPtr2, size); + } + else + { + std::cerr << "memcmp error: Invalid address provided." + << " Ptr1: 0x" << std::hex << ptr1Addr << " (host ptr valid: " << (hostPtr1 != nullptr) << ")" + << ", Ptr2: 0x" << ptr2Addr << " (host ptr valid: " << (hostPtr2 != nullptr) << ")" << std::dec + << std::endl; + + result = (hostPtr1 == nullptr) - (hostPtr2 == nullptr); + if (result == 0) + result = 1; // If both null, still different? Or 0? + } + setReturnS32(ctx, result); +} + +void strcpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); + + if (hostDest && hostSrc) + { + ::strcpy(hostDest, hostSrc); + ps2TraceGuestRangeWrite(rdram, destAddr, static_cast(::strlen(hostSrc) + 1u), "strcpy", ctx); + } + else + { + std::cerr << "strcpy error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void strncpy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); + + if (hostDest && hostSrc) + { + ::strncpy(hostDest, hostSrc, size); + ps2TraceGuestRangeWrite(rdram, destAddr, size, "strncpy", ctx); + } + else + { + std::cerr << "strncpy error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void strlen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + size_t len = 0; + + if (hostStr) + { + len = ::strlen(hostStr); + } + else + { + std::cerr << "strlen error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + } + setReturnU32(ctx, (uint32_t)len); +} + +void strcmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t str1Addr = getRegU32(ctx, 4); // $a0 + uint32_t str2Addr = getRegU32(ctx, 5); // $a1 + + const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); + const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); + int result = 0; + + if (hostStr1 && hostStr2) + { + result = ::strcmp(hostStr1, hostStr2); + } + else + { + std::cerr << "strcmp error: Invalid address provided." + << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" + << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec + << std::endl; + // Return non-zero on error, consistent with memcmp error handling + result = (hostStr1 == nullptr) - (hostStr2 == nullptr); + if (result == 0 && hostStr1 == nullptr) + result = 1; // Both null -> treat as different? Or 0? Let's say different. + } + setReturnS32(ctx, result); +} + +void strncmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t str1Addr = getRegU32(ctx, 4); // $a0 + uint32_t str2Addr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + + const char *hostStr1 = reinterpret_cast(getConstMemPtr(rdram, str1Addr)); + const char *hostStr2 = reinterpret_cast(getConstMemPtr(rdram, str2Addr)); + int result = 0; + + if (hostStr1 && hostStr2) + { + result = ::strncmp(hostStr1, hostStr2, size); + } + else + { + std::cerr << "strncmp error: Invalid address provided." + << " Str1: 0x" << std::hex << str1Addr << " (host ptr valid: " << (hostStr1 != nullptr) << ")" + << ", Str2: 0x" << str2Addr << " (host ptr valid: " << (hostStr2 != nullptr) << ")" << std::dec + << std::endl; + result = (hostStr1 == nullptr) - (hostStr2 == nullptr); + if (result == 0 && hostStr1 == nullptr) + result = 1; // Both null -> different + } + setReturnS32(ctx, result); +} + +void strcat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); + + if (hostDest && hostSrc) + { + ::strcat(hostDest, hostSrc); + } + else + { + std::cerr << "strcat error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void strncat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 + + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); + + if (hostDest && hostSrc) + { + ::strncat(hostDest, hostSrc, size); + } + else + { + std::cerr << "strncat error: Invalid address provided." + << " Dest: 0x" << std::hex << destAddr << " (host ptr valid: " << (hostDest != nullptr) << ")" + << ", Src: 0x" << srcAddr << " (host ptr valid: " << (hostSrc != nullptr) << ")" << std::dec + << std::endl; + } + + // returns dest pointer ($v0 = $a0) + ctx->r[2] = ctx->r[4]; +} + +void strchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + char *foundPtr = nullptr; + uint32_t resultAddr = 0; + + if (hostStr) + { + foundPtr = ::strchr(const_cast(hostStr), char_code); + if (foundPtr) + { + resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + } + } + else + { + std::cerr << "strchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + } + + // returns PS2 address or 0 (NULL) + setReturnU32(ctx, resultAddr); +} + +void strrchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + int char_code = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + char *foundPtr = nullptr; + uint32_t resultAddr = 0; + + if (hostStr) + { + foundPtr = ::strrchr(const_cast(hostStr), char_code); // Use const_cast carefully + if (foundPtr) + { + resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + } + } + else + { + std::cerr << "strrchr error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + } + + // returns PS2 address or 0 (NULL) + setReturnU32(ctx, resultAddr); +} + +void strstr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t haystackAddr = getRegU32(ctx, 4); // $a0 + uint32_t needleAddr = getRegU32(ctx, 5); // $a1 + + const char *hostHaystack = reinterpret_cast(getConstMemPtr(rdram, haystackAddr)); + const char *hostNeedle = reinterpret_cast(getConstMemPtr(rdram, needleAddr)); + char *foundPtr = nullptr; + uint32_t resultAddr = 0; + + if (hostHaystack && hostNeedle) + { + foundPtr = ::strstr(const_cast(hostHaystack), hostNeedle); + if (foundPtr) + { + resultAddr = hostPtrToPs2Addr(rdram, foundPtr); + } + } + else + { + std::cerr << "strstr error: Invalid address provided." + << " Haystack: 0x" << std::hex << haystackAddr << " (host ptr valid: " << (hostHaystack != nullptr) << ")" + << ", Needle: 0x" << needleAddr << " (host ptr valid: " << (hostNeedle != nullptr) << ")" << std::dec + << std::endl; + } + + // returns PS2 address or 0 (NULL) + setReturnU32(ctx, resultAddr); +} + +void printf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t format_addr = getRegU32(ctx, 4); // $a0 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 1); + if (rendered.size() > 2048) + { + rendered.resize(2048); + } + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + std::cout << "PS2 printf: " << logLine; + std::cout << std::flush; + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "printf error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; + } + + // returns the number of characters written, or negative on error. + setReturnS32(ctx, ret); +} + +void sprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + const uint32_t watchBase = ps2PathWatchPhysAddr(); + const uint32_t watchEnd = watchBase + PS2_PATH_WATCH_BYTES; + const uint32_t dest = str_addr & PS2_RAM_MASK; + const bool touchesWatch = dest < watchEnd && dest >= watchBase; + static uint32_t watchSprintfLogCount = 0; + if (touchesWatch && watchSprintfLogCount < 64u) + { + const uint32_t arg0 = getRegU32(ctx, 6); + const uint32_t arg1 = getRegU32(ctx, 7); + std::cout << "[watch:sprintf] dest=0x" << std::hex << str_addr + << " fmt@0x" << format_addr + << " arg0=0x" << arg0 + << " arg1=0x" << arg1 + << " fmt=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, format_addr, 64)) << "\"" + << " s0=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg0, 64)) << "\"" + << " s1=\"" << sanitizeForLog(readPs2CStringBounded(rdram, runtime, arg1, 64)) << "\"" + << std::dec << std::endl; + ++watchSprintfLogCount; + } + + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + if (rendered.size() >= kMaxFormattedOutputBytes) + { + rendered.resize(kMaxFormattedOutputBytes - 1); + } + const size_t writeLen = rendered.size() + 1u; + if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), writeLen)) + { + ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(writeLen), "sprintf", ctx); + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "sprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + } + } + else + { + std::cerr << "sprintf error: Invalid format address provided." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + // returns the number of characters written (excluding null), or negative on error. + setReturnS32(ctx, ret); +} + +void snprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + size_t size = getRegU32(ctx, 5); // $a1 + uint32_t format_addr = getRegU32(ctx, 6); // $a2 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 3); + ret = static_cast(rendered.size()); + + if (size > 0) + { + const size_t copyLen = std::min(size - 1, rendered.size()); + std::vector output(copyLen + 1u, 0u); + if (copyLen > 0u) + { + std::memcpy(output.data(), rendered.data(), copyLen); + } + if (writeGuestBytes(rdram, runtime, str_addr, output.data(), output.size())) + { + ps2TraceGuestRangeWrite(rdram, str_addr, static_cast(output.size()), "snprintf", ctx); + } + else + { + std::cerr << "snprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + ret = -1; + } + } + } + else + { + std::cerr << "snprintf error: Invalid address provided or size is zero." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << ", Size: " << size << std::endl; + } + + // returns the number of characters that *would* have been written + // if size was large enough (excluding null), or negative on error. + setReturnS32(ctx, ret); +} + +void puts(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + int result = EOF; + + if (hostStr) + { + result = std::puts(hostStr); // std::puts adds a newline + std::fflush(stdout); // Ensure output appears + } + else + { + std::cerr << "puts error: Invalid address provided: 0x" << std::hex << strAddr << std::dec << std::endl; + } + + // returns non-negative on success, EOF on error. + setReturnS32(ctx, result >= 0 ? 0 : -1); // PS2 might expect 0/-1 rather than EOF +} + +void fopen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + uint32_t modeAddr = getRegU32(ctx, 5); // $a1 + + const char *hostPath = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + const char *hostMode = reinterpret_cast(getConstMemPtr(rdram, modeAddr)); + uint32_t file_handle = 0; + + if (hostPath && hostMode) + { + // TODO: Add translation for PS2 paths like mc0:, host:, cdrom:, etc. + // treating as direct host path + std::cout << "ps2_stub fopen: path='" << hostPath << "', mode='" << hostMode << "'" << std::endl; + FILE *fp = ::fopen(hostPath, hostMode); + if (fp) + { + std::lock_guard lock(g_file_mutex); + file_handle = generate_file_handle(); + g_file_map[file_handle] = fp; + std::cout << " -> handle=0x" << std::hex << file_handle << std::dec << std::endl; + } + else + { + std::cerr << "ps2_stub fopen error: Failed to open '" << hostPath << "' with mode '" << hostMode << "'. Error: " << strerror(errno) << std::endl; + } + } + else + { + std::cerr << "fopen error: Invalid address provided for path or mode." + << " Path: 0x" << std::hex << pathAddr << " (host ptr valid: " << (hostPath != nullptr) << ")" + << ", Mode: 0x" << modeAddr << " (host ptr valid: " << (hostMode != nullptr) << ")" << std::dec + << std::endl; + } + // returns a file handle (non-zero) on success, or NULL (0) on error. + setReturnU32(ctx, file_handle); +} + +void fclose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + int ret = EOF; // Default to error + + if (file_handle != 0) + { + std::lock_guard lock(g_file_mutex); + auto it = g_file_map.find(file_handle); + if (it != g_file_map.end()) + { + FILE *fp = it->second; + ret = ::fclose(fp); + g_file_map.erase(it); + } + else + { + std::cerr << "ps2_stub fclose error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } + } + else + { + // Closing NULL handle in Standard C defines this as no-op + ret = 0; + } + + // returns 0 on success, EOF on error. + setReturnS32(ctx, ret); +} + +void fread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) + uint32_t size = getRegU32(ctx, 5); // $a1 (element size) + uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) + uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) + size_t items_read = 0; + + uint8_t *hostPtr = getMemPtr(rdram, ptrAddr); + FILE *fp = get_file_ptr(file_handle); + + if (hostPtr && fp && size > 0 && count > 0) + { + items_read = ::fread(hostPtr, size, count, fp); + } + else + { + std::cerr << "fread error: Invalid arguments." + << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" + << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec + << ", Size: " << size << ", Count: " << count << std::endl; + } + // returns the number of items successfully read. + setReturnU32(ctx, (uint32_t)items_read); +} + +void fwrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t ptrAddr = getRegU32(ctx, 4); // $a0 (buffer) + uint32_t size = getRegU32(ctx, 5); // $a1 (element size) + uint32_t count = getRegU32(ctx, 6); // $a2 (number of elements) + uint32_t file_handle = getRegU32(ctx, 7); // $a3 (file handle) + size_t items_written = 0; + + const uint8_t *hostPtr = getConstMemPtr(rdram, ptrAddr); + FILE *fp = get_file_ptr(file_handle); + + if (hostPtr && fp && size > 0 && count > 0) + { + items_written = ::fwrite(hostPtr, size, count, fp); + } + else + { + std::cerr << "fwrite error: Invalid arguments." + << " Ptr: 0x" << std::hex << ptrAddr << " (host ptr valid: " << (hostPtr != nullptr) << ")" + << ", Handle: 0x" << file_handle << " (file valid: " << (fp != nullptr) << ")" << std::dec + << ", Size: " << size << ", Count: " << count << std::endl; + } + // returns the number of items successfully written. + setReturnU32(ctx, (uint32_t)items_written); +} + +void fprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + FILE *fp = get_file_ptr(file_handle); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (fp && format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + ret = std::fprintf(fp, "%s", rendered.c_str()); + } + else + { + std::cerr << "fprintf error: Invalid file handle or format address." + << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + // returns the number of characters written, or negative on error. + setReturnS32(ctx, ret); +} + +void fseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + long offset = (long)getRegU32(ctx, 5); // $a1 (Note: might need 64-bit for large files?) + int whence = (int)getRegU32(ctx, 6); // $a2 (SEEK_SET, SEEK_CUR, SEEK_END) + int ret = -1; // Default error + + FILE *fp = get_file_ptr(file_handle); + + if (fp) + { + // Ensure whence is valid (0, 1, 2) + if (whence >= 0 && whence <= 2) + { + ret = ::fseek(fp, offset, whence); + } + else + { + std::cerr << "fseek error: Invalid whence value: " << whence << std::endl; + } + } + else + { + std::cerr << "fseek error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } + + // returns 0 on success, non-zero on error. + setReturnS32(ctx, ret); +} + +void ftell(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + long ret = -1L; + + FILE *fp = get_file_ptr(file_handle); + + if (fp) + { + ret = ::ftell(fp); + } + else + { + std::cerr << "ftell error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } + + // returns the current position, or -1L on error. + if (ret > 0xFFFFFFFFL || ret < 0) + { + setReturnS32(ctx, -1); + } + else + { + setReturnU32(ctx, (uint32_t)ret); + } +} + +void fflush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + int ret = EOF; // Default error + + // If handle is 0 fflush flushes *all* output streams. + if (file_handle == 0) + { + ret = ::fflush(NULL); + } + else + { + FILE *fp = get_file_ptr(file_handle); + if (fp) + { + ret = ::fflush(fp); + } + else + { + std::cerr << "fflush error: Invalid file handle 0x" << std::hex << file_handle << std::dec << std::endl; + } + } + // returns 0 on success, EOF on error. + setReturnS32(ctx, ret); +} + +void sqrt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::sqrtf(arg); +} + +void sin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::sinf(arg); +} + +void __kernel_sinf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const float x = ctx->f[12]; + const float y = ctx->f[13]; + const int32_t iy = static_cast(getRegU32(ctx, 4)); + ctx->f[0] = ::sinf(x + (iy != 0 ? y : 0.0f)); +} + +void cos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::cosf(arg); +} + +void __kernel_cosf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const float x = ctx->f[12]; + const float y = ctx->f[13]; + ctx->f[0] = ::cosf(x + y); +} + +void __ieee754_rem_pio2f(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const float x = ctx->f[12]; + constexpr float kPi = 3.14159265358979323846f; + constexpr float kHalfPi = kPi * 0.5f; + constexpr float kInvHalfPi = 2.0f / kPi; + const int32_t n = static_cast(std::nearbyintf(x * kInvHalfPi)); + const float y0 = x - (static_cast(n) * kHalfPi); + const float y1 = 0.0f; + + const uint32_t yOutAddr = getRegU32(ctx, 4); + if (float *yOut0 = reinterpret_cast(getMemPtr(rdram, yOutAddr)); yOut0) + { + *yOut0 = y0; + } + if (float *yOut1 = reinterpret_cast(getMemPtr(rdram, yOutAddr + 4)); yOut1) + { + *yOut1 = y1; + } + + setReturnS32(ctx, n); +} + +void tan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::tanf(arg); +} + +void atan2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float y = ctx->f[12]; + float x = ctx->f[14]; + ctx->f[0] = ::atan2f(y, x); +} + +void pow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float base = ctx->f[12]; + float exp = ctx->f[14]; + ctx->f[0] = ::powf(base, exp); +} + +void exp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::expf(arg); +} + +void log(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::logf(arg); +} + +void log10(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::log10f(arg); +} + +void ceil(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::ceilf(arg); +} + +void floor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::floorf(arg); +} + +void fabs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + float arg = ctx->f[12]; + ctx->f[0] = ::fabsf(arg); +} diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl new file mode 100644 index 0000000..e1b2d9b --- /dev/null +++ b/ps2xRuntime/src/lib/stubs/ps2_stubs_misc.inl @@ -0,0 +1,2363 @@ +void calloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t count = getRegU32(ctx, 5); // $a1 + const uint32_t size = getRegU32(ctx, 6); // $a2 + const uint32_t guestAddr = runtime ? runtime->guestCalloc(count, size) : 0u; + setReturnU32(ctx, guestAddr); +} + +void free_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t guestAddr = getRegU32(ctx, 5); // $a1 + if (runtime && guestAddr != 0u) + { + runtime->guestFree(guestAddr); + } +} + +void malloc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t size = getRegU32(ctx, 5); // $a1 + const uint32_t guestAddr = runtime ? runtime->guestMalloc(size) : 0u; + setReturnU32(ctx, guestAddr); +} + +void malloc_trim_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void mbtowc_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mbtowc_r", rdram, ctx, runtime); +} + +void printf_r(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithArgs(rdram, ctx, runtime, formatOwned.c_str(), 2); + if (rendered.size() > 2048) + { + rendered.resize(2048); + } + const std::string logLine = sanitizeForLog(rendered); + uint32_t count = 0; + { + std::lock_guard lock(g_printfLogMutex); + count = ++g_printfLogCount; + } + if (count <= kMaxPrintfLogs) + { + std::cout << "PS2 printf: " << logLine; + std::cout << std::flush; + } + else if (count == kMaxPrintfLogs + 1) + { + std::cerr << "PS2 printf logging suppressed after " << kMaxPrintfLogs << " lines" << std::endl; + } + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "printf_r error: Invalid format string address provided: 0x" << std::hex << format_addr << std::dec << std::endl; + } + + setReturnS32(ctx, ret); +} + +void sceCdRI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceCdRI", rdram, ctx, runtime); +} + +void sceCdRM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceCdRM", rdram, ctx, runtime); +} + +void sceFsDbChk(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsDbChk", rdram, ctx, runtime); +} + +void sceFsIntrSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsIntrSigSema", rdram, ctx, runtime); +} + +void sceFsSemExit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsSemExit", rdram, ctx, runtime); +} + +void sceFsSemInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsSemInit", rdram, ctx, runtime); +} + +void sceFsSigSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsSigSema", rdram, ctx, runtime); +} + +void sceIDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceIDC", rdram, ctx, runtime); +} + +void sceMpegFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegFlush", rdram, ctx, runtime); +} + +void sceRpcFreePacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceRpcFreePacket", rdram, ctx, runtime); +} + +void sceRpcGetFPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceRpcGetFPacket", rdram, ctx, runtime); +} + +void sceRpcGetFPacket2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceRpcGetFPacket2", rdram, ctx, runtime); +} + +void sceSDC(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSDC", rdram, ctx, runtime); +} + +void sceSifCmdIntrHdlr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifCmdIntrHdlr", rdram, ctx, runtime); +} + +void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifLoadModule", rdram, ctx, runtime); +} + +void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSendCmd", rdram, ctx, runtime); +} + +void sceVu0ecossin(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ecossin", rdram, ctx, runtime); +} + +void abs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("abs", rdram, ctx, runtime); +} + +void atan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("atan", rdram, ctx, runtime); +} + +void close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioClose(rdram, ctx, runtime); +} + +void DmaAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("DmaAddr", rdram, ctx, runtime); +} + +void exit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("exit", rdram, ctx, runtime); +} + +void fstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t statAddr = getRegU32(ctx, 5); + if (uint8_t *statBuf = getMemPtr(rdram, statAddr)) + { + std::memset(statBuf, 0, 128); + setReturnS32(ctx, 0); + return; + } + setReturnS32(ctx, -1); +} + +void getpid(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("getpid", rdram, ctx, runtime); +} + +void iopGetArea(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("iopGetArea", rdram, ctx, runtime); +} + +void lseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioLseek(rdram, ctx, runtime); +} + +void memchr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("memchr", rdram, ctx, runtime); +} + +void open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioOpen(rdram, ctx, runtime); +} + +void Pad_init(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void Pad_set(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("Pad_set", rdram, ctx, runtime); +} + +void rand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("rand", rdram, ctx, runtime); +} + +void read(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioRead(rdram, ctx, runtime); +} + +void sceCdApplyNCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdBreak(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdDelayThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 2); +} + +void sceCdGetDiskType(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + // SCECdPS2DVD + setReturnS32(ctx, 0x14); +} + +void sceCdGetReadPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnU32(ctx, g_cdStreamingLbn); +} + +void sceCdGetToc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t tocAddr = getRegU32(ctx, 4); + if (uint8_t *toc = getMemPtr(rdram, tocAddr)) + { + std::memset(toc, 0, 1024); + } + setReturnS32(ctx, 1); +} + +void sceCdInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdInitialized = true; + g_lastCdError = 0; + setReturnS32(ctx, 1); +} + +void sceCdInitEeCB(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdIntToPos(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t lsn = getRegU32(ctx, 4); + uint32_t posAddr = getRegU32(ctx, 5); + uint8_t *pos = getMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, 0); + return; + } + + uint32_t adjusted = lsn + 150; + const uint32_t minutes = adjusted / (60 * 75); + adjusted %= (60 * 75); + const uint32_t seconds = adjusted / 75; + const uint32_t sectors = adjusted % 75; + + pos[0] = toBcd(minutes); + pos[1] = toBcd(seconds); + pos[2] = toBcd(sectors); + pos[3] = 0; + setReturnS32(ctx, 1); +} + +void sceCdMmode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdMode = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdNcmdDiskReady(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 2); +} + +void sceCdPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdPosToInt(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t posAddr = getRegU32(ctx, 4); + const uint8_t *pos = getConstMemPtr(rdram, posAddr); + if (!pos) + { + setReturnS32(ctx, -1); + return; + } + + const uint32_t minutes = fromBcd(pos[0]); + const uint32_t seconds = fromBcd(pos[1]); + const uint32_t sectors = fromBcd(pos[2]); + const uint32_t absolute = (minutes * 60 * 75) + (seconds * 75) + sectors; + const int32_t lsn = static_cast(absolute) - 150; + setReturnS32(ctx, lsn); +} + +void sceCdReadChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t chainAddr = getRegU32(ctx, 4); + bool ok = true; + + for (int i = 0; i < 64; ++i) + { + uint32_t *entry = reinterpret_cast(getMemPtr(rdram, chainAddr + (i * 16))); + if (!entry) + { + ok = false; + break; + } + + const uint32_t lbn = entry[0]; + const uint32_t sectors = entry[1]; + const uint32_t buf = entry[2]; + if (lbn == 0xFFFFFFFFu || sectors == 0) + { + break; + } + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + if (!readCdSectors(lbn, sectors, rdram + offset, bytes)) + { + ok = false; + break; + } + + g_cdStreamingLbn = lbn + sectors; + } + + setReturnS32(ctx, ok ? 1 : 0); +} + +void sceCdReadClock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clockAddr = getRegU32(ctx, 4); + uint8_t *clockData = getMemPtr(rdram, clockAddr); + if (!clockData) + { + setReturnS32(ctx, 0); + return; + } + + std::time_t now = std::time(nullptr); + std::tm localTm{}; +#ifdef _WIN32 + localtime_s(&localTm, &now); +#else + localtime_r(&now, &localTm); +#endif + + // sceCdCLOCK format (BCD fields). + clockData[0] = 0; + clockData[1] = toBcd(static_cast(localTm.tm_sec)); + clockData[2] = toBcd(static_cast(localTm.tm_min)); + clockData[3] = toBcd(static_cast(localTm.tm_hour)); + clockData[4] = 0; + clockData[5] = toBcd(static_cast(localTm.tm_mday)); + clockData[6] = toBcd(static_cast(localTm.tm_mon + 1)); + clockData[7] = toBcd(static_cast((localTm.tm_year + 1900) % 100)); + setReturnS32(ctx, 1); +} + +void sceCdReadIOPm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + sceCdRead(rdram, ctx, runtime); +} + +void sceCdSearchFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t fileAddr = getRegU32(ctx, 4); + uint32_t pathAddr = getRegU32(ctx, 5); + const std::string path = readPs2CStringBounded(rdram, pathAddr, 260); + const std::string normalizedPath = normalizeCdPathNoPrefix(path); + static uint32_t traceCount = 0; + const uint32_t callerRa = getRegU32(ctx, 31); + const bool shouldTrace = (traceCount < 128u) || ((traceCount % 512u) == 0u); + if (shouldTrace) + { + std::cout << "[sceCdSearchFile] pc=0x" << std::hex << ctx->pc + << " ra=0x" << callerRa + << " file=0x" << fileAddr + << " pathAddr=0x" << pathAddr + << " path=\"" << sanitizeForLog(path) << "\"" + << std::dec << std::endl; + } + ++traceCount; + + if (path.empty()) + { + static uint32_t emptyPathCount = 0; + if (emptyPathCount < 64 || (emptyPathCount % 512u) == 0u) + { + std::ostringstream preview; + preview << std::hex; + for (uint32_t i = 0; i < 16; ++i) + { + const uint8_t byte = *getConstMemPtr(rdram, pathAddr + i); + preview << (i == 0 ? "" : " ") << static_cast(byte); + } + std::cerr << "[sceCdSearchFile] empty path at 0x" << std::hex << pathAddr + << " preview=" << preview.str() + << " ra=0x" << callerRa << std::dec << std::endl; + } + ++emptyPathCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + if (normalizedPath.empty()) + { + static uint32_t emptyNormalizedCount = 0; + if (emptyNormalizedCount < 64u || (emptyNormalizedCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (normalized path is empty, root: " << getCdRootPath().string() << ")" + << std::endl; + } + ++emptyNormalizedCount; + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + CdFileEntry entry; + bool found = registerCdFile(path, entry); + CdFileEntry resolvedEntry = entry; + std::string resolvedPath; + bool usedRemapFallback = false; + + // Remap is fallback-only: if the requested .IDX exists, keep it. + // This avoids feeding AFS payload sectors to code that expects IDX metadata. + if (!found) + { + const CdFileEntry missingEntry{}; + if (tryRemapGdInitSearchToAfs(path, callerRa, missingEntry, resolvedEntry, resolvedPath)) + { + found = true; + usedRemapFallback = true; + } + } + + if (!found) + { + static std::string lastFailedPath; + static uint32_t samePathFailCount = 0; + if (path == lastFailedPath) + { + ++samePathFailCount; + } + else + { + lastFailedPath = path; + samePathFailCount = 1; + } + + if (samePathFailCount <= 16u || (samePathFailCount % 512u) == 0u) + { + std::cerr << "sceCdSearchFile failed: " << sanitizeForLog(path) + << " (root: " << getCdRootPath().string() + << ", repeat=" << samePathFailCount << ")" << std::endl; + } + setReturnS32(ctx, 0); + return; + } + + if (usedRemapFallback) + { + std::cout << "[sceCdSearchFile] remap gd-init search \"" << sanitizeForLog(path) + << "\" -> \"" << sanitizeForLog(resolvedPath) << "\"" << std::endl; + } + + if (!writeCdSearchResult(rdram, fileAddr, path, resolvedEntry)) + { + g_lastCdError = -1; + setReturnS32(ctx, 0); + return; + } + + g_cdStreamingLbn = resolvedEntry.baseLbn; + if (shouldTrace) + { + std::cout << "[sceCdSearchFile:ok] path=\"" << sanitizeForLog(path) + << "\" lsn=0x" << std::hex << resolvedEntry.baseLbn + << " size=0x" << resolvedEntry.sizeBytes + << " sectors=0x" << resolvedEntry.sectors + << std::dec << std::endl; + } + setReturnS32(ctx, 1); +} + +void sceCdSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStandby(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_cdInitialized ? 6 : 0); +} + +void sceCdStInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sectors = getRegU32(ctx, 4); + uint32_t buf = getRegU32(ctx, 5); + uint32_t errAddr = getRegU32(ctx, 7); + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + + const bool ok = readCdSectors(g_cdStreamingLbn, sectors, rdram + offset, bytes); + if (ok) + { + g_cdStreamingLbn += sectors; + } + + if (int32_t *err = reinterpret_cast(getMemPtr(rdram, errAddr)); err) + { + *err = ok ? 0 : g_lastCdError; + } + + setReturnS32(ctx, ok ? static_cast(sectors) : 0); +} + +void sceCdStream(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStResume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdStSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStSeekF(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStStart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_cdStreamingLbn = getRegU32(ctx, 4); + setReturnS32(ctx, 1); +} + +void sceCdStStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdStStop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceCdSyncS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceCdTrayReq(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t statusPtr = getRegU32(ctx, 5); + if (uint32_t *status = reinterpret_cast(getMemPtr(rdram, statusPtr)); status) + { + *status = 0; + } + setReturnS32(ctx, 1); +} + +void sceClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioClose(rdram, ctx, runtime); +} + +void sceDeci2Close(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2Close", rdram, ctx, runtime); +} + +void sceDeci2ExLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExLock", rdram, ctx, runtime); +} + +void sceDeci2ExRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExRecv", rdram, ctx, runtime); +} + +void sceDeci2ExReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExReqSend", rdram, ctx, runtime); +} + +void sceDeci2ExSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExSend", rdram, ctx, runtime); +} + +void sceDeci2ExUnLock(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ExUnLock", rdram, ctx, runtime); +} + +void sceDeci2Open(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2Open", rdram, ctx, runtime); +} + +void sceDeci2Poll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2Poll", rdram, ctx, runtime); +} + +void sceDeci2ReqSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDeci2ReqSend", rdram, ctx, runtime); +} + +void sceDmaCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaCallback", rdram, ctx, runtime); +} + +void sceDmaDebug(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaDebug", rdram, ctx, runtime); +} + +void sceDmaGetChan(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t chanArg = getRegU32(ctx, 4); + const uint32_t channelBase = resolveDmaChannelBase(rdram, chanArg); + setReturnU32(ctx, channelBase); +} + +void sceDmaGetEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaGetEnv", rdram, ctx, runtime); +} + +void sceDmaLastSyncTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaLastSyncTime", rdram, ctx, runtime); +} + +void sceDmaPause(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaPause", rdram, ctx, runtime); +} + +void sceDmaPutEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaPutEnv", rdram, ctx, runtime); +} + +void sceDmaPutStallAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaPutStallAddr", rdram, ctx, runtime); +} + +void sceDmaRecv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRecv", rdram, ctx, runtime); +} + +void sceDmaRecvI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRecvI", rdram, ctx, runtime); +} + +void sceDmaRecvN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRecvN", rdram, ctx, runtime); +} + +void sceDmaReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceDmaRestart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaRestart", rdram, ctx, runtime); +} + +void sceDmaSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); +} + +void sceDmaSendI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); +} + +void sceDmaSendM(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, false)); +} + +void sceDmaSendN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSend(rdram, ctx, runtime, true)); +} + +void sceDmaSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); +} + +void sceDmaSyncN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, submitDmaSync(rdram, ctx, runtime)); +} + +void sceDmaWatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceDmaWatch", rdram, ctx, runtime); +} + +void sceFsInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceFsInit", rdram, ctx, runtime); +} + +void sceFsReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceIoctl(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceIoctl", rdram, ctx, runtime); +} + +void sceIpuInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceIpuInit", rdram, ctx, runtime); +} + +void sceIpuRestartDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceIpuRestartDMA", rdram, ctx, runtime); +} + +void sceIpuStopDMA(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceIpuStopDMA", rdram, ctx, runtime); +} + +void sceIpuSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceIpuSync", rdram, ctx, runtime); +} + +void sceLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioLseek(rdram, ctx, runtime); +} + +void sceMcChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcChangeThreadPriority", rdram, ctx, runtime); +} + +void sceMcChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcChdir", rdram, ctx, runtime); +} + +void sceMcClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcClose", rdram, ctx, runtime); +} + +void sceMcDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcDelete", rdram, ctx, runtime); +} + +void sceMcFlush(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcFlush", rdram, ctx, runtime); +} + +void sceMcFormat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcFormat", rdram, ctx, runtime); +} + +void sceMcGetDir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcGetDir", rdram, ctx, runtime); +} + +void sceMcGetEntSpace(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcGetEntSpace", rdram, ctx, runtime); +} + +void sceMcGetInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcGetInfo", rdram, ctx, runtime); +} + +void sceMcGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcGetSlotMax", rdram, ctx, runtime); +} + +void sceMcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static uint32_t logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sceMcInit -> 0" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void sceMcMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcMkdir", rdram, ctx, runtime); +} + +void sceMcOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcOpen", rdram, ctx, runtime); +} + +void sceMcRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcRead", rdram, ctx, runtime); +} + +void sceMcRename(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcRename", rdram, ctx, runtime); +} + +void sceMcSeek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcSeek", rdram, ctx, runtime); +} + +void sceMcSetFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcSetFileInfo", rdram, ctx, runtime); +} + +void sceMcSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcSync", rdram, ctx, runtime); +} + +void sceMcUnformat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcUnformat", rdram, ctx, runtime); +} + +void sceMcWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMcWrite", rdram, ctx, runtime); +} + +void sceMpegAddBs(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegAddBs", rdram, ctx, runtime); +} + +void sceMpegAddCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegAddCallback", rdram, ctx, runtime); +} + +void sceMpegAddStrCallback(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegAddStrCallback", rdram, ctx, runtime); +} + +void sceMpegClearRefBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegClearRefBuff", rdram, ctx, runtime); +} + +void sceMpegCreate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegCreate", rdram, ctx, runtime); +} + +void sceMpegDelete(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDelete", rdram, ctx, runtime); +} + +void sceMpegDemuxPss(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDemuxPss", rdram, ctx, runtime); +} + +void sceMpegDemuxPssRing(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDemuxPssRing", rdram, ctx, runtime); +} + +void sceMpegDispCenterOffX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispCenterOffX", rdram, ctx, runtime); +} + +void sceMpegDispCenterOffY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispCenterOffY", rdram, ctx, runtime); +} + +void sceMpegDispHeight(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispHeight", rdram, ctx, runtime); +} + +void sceMpegDispWidth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegDispWidth", rdram, ctx, runtime); +} + +void sceMpegGetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetDecodeMode", rdram, ctx, runtime); +} + +void sceMpegGetPicture(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetPicture", rdram, ctx, runtime); +} + +void sceMpegGetPictureRAW8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetPictureRAW8", rdram, ctx, runtime); +} + +void sceMpegGetPictureRAW8xy(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegGetPictureRAW8xy", rdram, ctx, runtime); +} + +void sceMpegInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegInit", rdram, ctx, runtime); +} + +void sceMpegIsEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegIsEnd", rdram, ctx, runtime); +} + +void sceMpegIsRefBuffEmpty(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegIsRefBuffEmpty", rdram, ctx, runtime); +} + +void sceMpegReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegReset", rdram, ctx, runtime); +} + +void sceMpegResetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegResetDefaultPtsGap", rdram, ctx, runtime); +} + +void sceMpegSetDecodeMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegSetDecodeMode", rdram, ctx, runtime); +} + +void sceMpegSetDefaultPtsGap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegSetDefaultPtsGap", rdram, ctx, runtime); +} + +void sceMpegSetImageBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceMpegSetImageBuff", rdram, ctx, runtime); +} + +void sceOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioOpen(rdram, ctx, runtime); +} + +void scePadEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadEnd", rdram, ctx, runtime); +} + +void scePadEnterPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadEnterPressMode", rdram, ctx, runtime); +} + +void scePadExitPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadExitPressMode", rdram, ctx, runtime); +} + +void scePadGetButtonMask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadGetButtonMask", rdram, ctx, runtime); +} + +void scePadGetDmaStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadGetDmaStr", rdram, ctx, runtime); +} + +void scePadGetFrameCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadGetFrameCount", rdram, ctx, runtime); +} + +void scePadGetModVersion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // Arbitrary non-zero module version. + setReturnS32(ctx, 0x0200); +} + +void scePadGetPortMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 2); +} + +void scePadGetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // 0 = completed/no pending request. + setReturnS32(ctx, 0); +} + +void scePadGetSlotMax(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // Most games use one slot unless multitap is active. + setReturnS32(ctx, 1); +} + +void scePadGetState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // Pad state constants used by libpad: 6 means stable and ready. + setReturnS32(ctx, 6); +} + +void scePadInfoAct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadInfoAct", rdram, ctx, runtime); +} + +void scePadInfoComb(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadInfoComb", rdram, ctx, runtime); +} + +void scePadInfoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + + const int32_t infoMode = static_cast(getRegU32(ctx, 6)); // a2 + const int32_t index = static_cast(getRegU32(ctx, 7)); // a3 + + // Minimal DualShock-like capabilities to keep game-side pad setup paths alive. + constexpr int32_t kPadTypeDualShock = 7; + switch (infoMode) + { + case 1: // PAD_MODECURID + setReturnS32(ctx, kPadTypeDualShock); + return; + case 2: // PAD_MODECUREXID + setReturnS32(ctx, kPadTypeDualShock); + return; + case 3: // PAD_MODECUROFFS + setReturnS32(ctx, 0); + return; + case 4: // PAD_MODETABLE + if (index == -1) + { + setReturnS32(ctx, 1); // one available mode + } + else if (index == 0) + { + setReturnS32(ctx, kPadTypeDualShock); + } + else + { + setReturnS32(ctx, 0); + } + return; + default: + setReturnS32(ctx, 0); + return; + } +} + +void scePadInfoPressMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + // Pressure mode is disabled in this minimal implementation. + setReturnS32(ctx, 0); +} + +void scePadInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadPortClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadPortOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)rdram; + (void)runtime; + setReturnS32(ctx, 1); +} + +void scePadRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + (void)runtime; + + const uint32_t dataAddr = getRegU32(ctx, 6); // a2 + uint8_t *data = getMemPtr(rdram, dataAddr); + if (!data) + { + setReturnS32(ctx, 0); + return; + } + + // struct padButtonStatus (32 bytes): neutral state, no buttons pressed. + std::memset(data, 0, 32); + data[1] = 0x73; // analog/dualshock mode marker + data[2] = 0xFF; // btns low (active-low) + data[3] = 0xFF; // btns high + data[4] = 0x80; // rjoy_h + data[5] = 0x80; // rjoy_v + data[6] = 0x80; // ljoy_h + data[7] = 0x80; // ljoy_v + + setReturnS32(ctx, 1); +} + +void scePadReqIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadReqIntToStr", rdram, ctx, runtime); +} + +void scePadSetActAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetActAlign", rdram, ctx, runtime); +} + +void scePadSetActDirect(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetActDirect", rdram, ctx, runtime); +} + +void scePadSetButtonInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetButtonInfo", rdram, ctx, runtime); +} + +void scePadSetMainMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetMainMode", rdram, ctx, runtime); +} + +void scePadSetReqState(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetReqState", rdram, ctx, runtime); +} + +void scePadSetVrefParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetVrefParam", rdram, ctx, runtime); +} + +void scePadSetWarningLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadSetWarningLevel", rdram, ctx, runtime); +} + +void scePadStateIntToStr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePadStateIntToStr", rdram, ctx, runtime); +} + +void scePrintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("scePrintf", rdram, ctx, runtime); +} + +void sceRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioRead(rdram, ctx, runtime); +} + +void sceResetttyinit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceResetttyinit", rdram, ctx, runtime); +} + +void sceSdCallBack(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSdCallBack", rdram, ctx, runtime); +} + +void sceSdRemote(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSdRemote", rdram, ctx, runtime); +} + +void sceSdRemoteInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSdRemoteInit", rdram, ctx, runtime); +} + +void sceSdTransToIOP(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSdTransToIOP", rdram, ctx, runtime); +} + +void sceSetBrokenLink(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSetBrokenLink", rdram, ctx, runtime); +} + +void sceSetPtm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSetPtm", rdram, ctx, runtime); +} + +void sceSifAddCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifAddCmdHandler", rdram, ctx, runtime); +} + +void sceSifAllocIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t reqSize = getRegU32(ctx, 4); + const uint32_t alignedSize = (reqSize + (kIopHeapAlign - 1)) & ~(kIopHeapAlign - 1); + if (alignedSize == 0 || g_iopHeapNext + alignedSize > kIopHeapLimit) + { + setReturnS32(ctx, 0); + return; + } + + const uint32_t allocAddr = g_iopHeapNext; + g_iopHeapNext += alignedSize; + setReturnS32(ctx, static_cast(allocAddr)); +} + +void sceSifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifBindRpc(rdram, ctx, runtime); +} + +void sceSifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifCheckStatRpc(rdram, ctx, runtime); +} + +void sceSifDmaStat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifDmaStat", rdram, ctx, runtime); +} + +void sceSifExecRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifExitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifExitCmd", rdram, ctx, runtime); +} + +void sceSifExitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifFreeIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifGetDataTable(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifGetDataTable", rdram, ctx, runtime); +} + +void sceSifGetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifGetIopAddr", rdram, ctx, runtime); +} + +void sceSifGetNextRequest(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifGetOtherData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifGetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifGetReg", rdram, ctx, runtime); +} + +void sceSifGetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifGetSreg", rdram, ctx, runtime); +} + +void sceSifInitCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifInitCmd", rdram, ctx, runtime); +} + +void sceSifInitIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + g_iopHeapNext = kIopHeapBase; + setReturnS32(ctx, 0); +} + +void sceSifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifInitRpc(rdram, ctx, runtime); +} + +void sceSifIsAliveIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifIsAliveIop", rdram, ctx, runtime); +} + +void sceSifLoadElf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::sceSifLoadElf(rdram, ctx, runtime); +} + +void sceSifLoadElfPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::sceSifLoadElfPart(rdram, ctx, runtime); +} + +void sceSifLoadFileReset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifLoadFileReset", rdram, ctx, runtime); +} + +void sceSifLoadIopHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifLoadModuleBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::sceSifLoadModuleBuffer(rdram, ctx, runtime); +} + +void sceSifRebootIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceSifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifRegisterRpc(rdram, ctx, runtime); +} + +void sceSifRemoveCmdHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifRemoveCmdHandler", rdram, ctx, runtime); +} + +void sceSifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifRemoveRpc(rdram, ctx, runtime); +} + +void sceSifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifRemoveRpcQueue(rdram, ctx, runtime); +} + +void sceSifResetIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifResetIop", rdram, ctx, runtime); +} + +void sceSifRpcLoop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSifSetCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetCmdBuffer", rdram, ctx, runtime); +} + +void sceSifSetDChain(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetDChain", rdram, ctx, runtime); +} + +void sceSifSetDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetDma", rdram, ctx, runtime); +} + +void sceSifSetIopAddr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetIopAddr", rdram, ctx, runtime); +} + +void sceSifSetReg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetReg", rdram, ctx, runtime); +} + +void sceSifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::SifSetRpcQueue(rdram, ctx, runtime); +} + +void sceSifSetSreg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetSreg", rdram, ctx, runtime); +} + +void sceSifSetSysCmdBuffer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifSetSysCmdBuffer", rdram, ctx, runtime); +} + +void sceSifStopDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifStopDma", rdram, ctx, runtime); +} + +void sceSifSyncIop(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 1); +} + +void sceSifWriteBackDCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSifWriteBackDCache", rdram, ctx, runtime); +} + +void sceSSyn_BreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_BreakAtick", rdram, ctx, runtime); +} + +void sceSSyn_ClearBreakAtick(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_ClearBreakAtick", rdram, ctx, runtime); +} + +void sceSSyn_SendExcMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendExcMsg", rdram, ctx, runtime); +} + +void sceSSyn_SendNrpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendNrpnMsg", rdram, ctx, runtime); +} + +void sceSSyn_SendRpnMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendRpnMsg", rdram, ctx, runtime); +} + +void sceSSyn_SendShortMsg(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SendShortMsg", rdram, ctx, runtime); +} + +void sceSSyn_SetChPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetChPriority", rdram, ctx, runtime); +} + +void sceSSyn_SetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetMasterVolume", rdram, ctx, runtime); +} + +void sceSSyn_SetOutPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetOutPortVolume", rdram, ctx, runtime); +} + +void sceSSyn_SetOutputAssign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetOutputAssign", rdram, ctx, runtime); +} + +void sceSSyn_SetOutputMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceSSyn_SetPortMaxPoly(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetPortMaxPoly", rdram, ctx, runtime); +} + +void sceSSyn_SetPortVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetPortVolume", rdram, ctx, runtime); +} + +void sceSSyn_SetTvaEnvMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSSyn_SetTvaEnvMode", rdram, ctx, runtime); +} + +void sceSynthesizerAmpProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAmpProcI", rdram, ctx, runtime); +} + +void sceSynthesizerAmpProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAmpProcNI", rdram, ctx, runtime); +} + +void sceSynthesizerAssignAllNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignAllNoteOff", rdram, ctx, runtime); +} + +void sceSynthesizerAssignAllSoundOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignAllSoundOff", rdram, ctx, runtime); +} + +void sceSynthesizerAssignHoldChange(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignHoldChange", rdram, ctx, runtime); +} + +void sceSynthesizerAssignNoteOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignNoteOff", rdram, ctx, runtime); +} + +void sceSynthesizerAssignNoteOn(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerAssignNoteOn", rdram, ctx, runtime); +} + +void sceSynthesizerCalcEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcEnv", rdram, ctx, runtime); +} + +void sceSynthesizerCalcPortamentPitch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcPortamentPitch", rdram, ctx, runtime); +} + +void sceSynthesizerCalcTvfCoefAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcTvfCoefAll", rdram, ctx, runtime); +} + +void sceSynthesizerCalcTvfCoefF0(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCalcTvfCoefF0", rdram, ctx, runtime); +} + +void sceSynthesizerCent2PhaseInc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCent2PhaseInc", rdram, ctx, runtime); +} + +void sceSynthesizerChangeEffectSend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeEffectSend", rdram, ctx, runtime); +} + +void sceSynthesizerChangeHsPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeHsPanpot", rdram, ctx, runtime); +} + +void sceSynthesizerChangeNrpnCutOff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeNrpnCutOff", rdram, ctx, runtime); +} + +void sceSynthesizerChangeNrpnLfoDepth(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeNrpnLfoDepth", rdram, ctx, runtime); +} + +void sceSynthesizerChangeNrpnLfoRate(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeNrpnLfoRate", rdram, ctx, runtime); +} + +void sceSynthesizerChangeOutAttrib(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeOutAttrib", rdram, ctx, runtime); +} + +void sceSynthesizerChangeOutVol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangeOutVol", rdram, ctx, runtime); +} + +void sceSynthesizerChangePanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePanpot", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartBendSens(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartBendSens", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartExpression", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartHsExpression(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartHsExpression", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartHsPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartHsPitchBend", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartModuration", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartPitchBend(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartPitchBend", rdram, ctx, runtime); +} + +void sceSynthesizerChangePartVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePartVolume", rdram, ctx, runtime); +} + +void sceSynthesizerChangePortamento(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePortamento", rdram, ctx, runtime); +} + +void sceSynthesizerChangePortamentoTime(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerChangePortamentoTime", rdram, ctx, runtime); +} + +void sceSynthesizerClearKeyMap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerClearKeyMap", rdram, ctx, runtime); +} + +void sceSynthesizerClearSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerClearSpr", rdram, ctx, runtime); +} + +void sceSynthesizerCopyOutput(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerCopyOutput", rdram, ctx, runtime); +} + +void sceSynthesizerDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerDmaFromSPR", rdram, ctx, runtime); +} + +void sceSynthesizerDmaSpr(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerDmaSpr", rdram, ctx, runtime); +} + +void sceSynthesizerDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerDmaToSPR", rdram, ctx, runtime); +} + +void sceSynthesizerGetPartial(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerGetPartial", rdram, ctx, runtime); +} + +void sceSynthesizerGetPartOutLevel(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerGetPartOutLevel", rdram, ctx, runtime); +} + +void sceSynthesizerGetSampleParam(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerGetSampleParam", rdram, ctx, runtime); +} + +void sceSynthesizerHsMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerHsMessage", rdram, ctx, runtime); +} + +void sceSynthesizerLfoNone(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoNone", rdram, ctx, runtime); +} + +void sceSynthesizerLfoProc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoProc", rdram, ctx, runtime); +} + +void sceSynthesizerLfoSawDown(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoSawDown", rdram, ctx, runtime); +} + +void sceSynthesizerLfoSawUp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoSawUp", rdram, ctx, runtime); +} + +void sceSynthesizerLfoSquare(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerLfoSquare", rdram, ctx, runtime); +} + +void sceSynthesizerReadNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadNoise", rdram, ctx, runtime); +} + +void sceSynthesizerReadNoiseAdd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadNoiseAdd", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample16(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample16", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample16Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample16Add", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample8(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample8", rdram, ctx, runtime); +} + +void sceSynthesizerReadSample8Add(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerReadSample8Add", rdram, ctx, runtime); +} + +void sceSynthesizerResetPart(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerResetPart", rdram, ctx, runtime); +} + +void sceSynthesizerRestorDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerRestorDma", rdram, ctx, runtime); +} + +void sceSynthesizerSelectPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSelectPatch", rdram, ctx, runtime); +} + +void sceSynthesizerSendShortMessage(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSendShortMessage", rdram, ctx, runtime); +} + +void sceSynthesizerSetMasterVolume(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetMasterVolume", rdram, ctx, runtime); +} + +void sceSynthesizerSetRVoice(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetRVoice", rdram, ctx, runtime); +} + +void sceSynthesizerSetupDma(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupDma", rdram, ctx, runtime); +} + +void sceSynthesizerSetupLfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupLfo", rdram, ctx, runtime); +} + +void sceSynthesizerSetupMidiModuration(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupMidiModuration", rdram, ctx, runtime); +} + +void sceSynthesizerSetupMidiPanpot(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupMidiPanpot", rdram, ctx, runtime); +} + +void sceSynthesizerSetupNewNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupNewNoise", rdram, ctx, runtime); +} + +void sceSynthesizerSetupReleaseEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupReleaseEnv", rdram, ctx, runtime); +} + +void sceSynthesizerSetuptEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetuptEnv", rdram, ctx, runtime); +} + +void sceSynthesizerSetupTruncateTvaEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupTruncateTvaEnv", rdram, ctx, runtime); +} + +void sceSynthesizerSetupTruncateTvfPitchEnv(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerSetupTruncateTvfPitchEnv", rdram, ctx, runtime); +} + +void sceSynthesizerTonegenerator(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTonegenerator", rdram, ctx, runtime); +} + +void sceSynthesizerTransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTransposeMatrix", rdram, ctx, runtime); +} + +void sceSynthesizerTvfProcI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTvfProcI", rdram, ctx, runtime); +} + +void sceSynthesizerTvfProcNI(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerTvfProcNI", rdram, ctx, runtime); +} + +void sceSynthesizerWaitDmaFromSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerWaitDmaFromSPR", rdram, ctx, runtime); +} + +void sceSynthesizerWaitDmaToSPR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthesizerWaitDmaToSPR", rdram, ctx, runtime); +} + +void sceSynthsizerGetDrumPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthsizerGetDrumPatch", rdram, ctx, runtime); +} + +void sceSynthsizerGetMeloPatch(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthsizerGetMeloPatch", rdram, ctx, runtime); +} + +void sceSynthsizerLfoNoise(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthsizerLfoNoise", rdram, ctx, runtime); +} + +void sceSynthSizerLfoTriangle(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceSynthSizerLfoTriangle", rdram, ctx, runtime); +} + +void sceTtyHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyHandler", rdram, ctx, runtime); +} + +void sceTtyInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyInit", rdram, ctx, runtime); +} + +void sceTtyRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyRead", rdram, ctx, runtime); +} + +void sceTtyWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceTtyWrite", rdram, ctx, runtime); +} + +void sceVpu0Reset(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void sceVu0AddVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0AddVector", rdram, ctx, runtime); +} + +void sceVu0ApplyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ApplyMatrix", rdram, ctx, runtime); +} + +void sceVu0CameraMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CameraMatrix", rdram, ctx, runtime); +} + +void sceVu0ClampVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClampVector", rdram, ctx, runtime); +} + +void sceVu0ClipAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClipAll", rdram, ctx, runtime); +} + +void sceVu0ClipScreen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClipScreen", rdram, ctx, runtime); +} + +void sceVu0ClipScreen3(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ClipScreen3", rdram, ctx, runtime); +} + +void sceVu0CopyMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CopyMatrix", rdram, ctx, runtime); +} + +void sceVu0CopyVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CopyVector", rdram, ctx, runtime); +} + +void sceVu0CopyVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0CopyVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0DivVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0DivVector", rdram, ctx, runtime); +} + +void sceVu0DivVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0DivVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0DropShadowMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0DropShadowMatrix", rdram, ctx, runtime); +} + +void sceVu0FTOI0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0FTOI0Vector", rdram, ctx, runtime); +} + +void sceVu0FTOI4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0FTOI4Vector", rdram, ctx, runtime); +} + +void sceVu0InnerProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InnerProduct", rdram, ctx, runtime); +} + +void sceVu0InterVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InterVector", rdram, ctx, runtime); +} + +void sceVu0InterVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InterVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0InversMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0InversMatrix", rdram, ctx, runtime); +} + +void sceVu0ITOF0Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ITOF0Vector", rdram, ctx, runtime); +} + +void sceVu0ITOF12Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ITOF12Vector", rdram, ctx, runtime); +} + +void sceVu0ITOF4Vector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ITOF4Vector", rdram, ctx, runtime); +} + +void sceVu0LightColorMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0LightColorMatrix", rdram, ctx, runtime); +} + +void sceVu0MulMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0MulMatrix", rdram, ctx, runtime); +} + +void sceVu0MulVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0MulVector", rdram, ctx, runtime); +} + +void sceVu0Normalize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0Normalize", rdram, ctx, runtime); +} + +void sceVu0NormalLightMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0NormalLightMatrix", rdram, ctx, runtime); +} + +void sceVu0OuterProduct(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0OuterProduct", rdram, ctx, runtime); +} + +void sceVu0RotMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrix", rdram, ctx, runtime); +} + +void sceVu0RotMatrixX(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrixX", rdram, ctx, runtime); +} + +void sceVu0RotMatrixY(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrixY", rdram, ctx, runtime); +} + +void sceVu0RotMatrixZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotMatrixZ", rdram, ctx, runtime); +} + +void sceVu0RotTransPers(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotTransPers", rdram, ctx, runtime); +} + +void sceVu0RotTransPersN(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0RotTransPersN", rdram, ctx, runtime); +} + +void sceVu0ScaleVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ScaleVector", rdram, ctx, runtime); +} + +void sceVu0ScaleVectorXYZ(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ScaleVectorXYZ", rdram, ctx, runtime); +} + +void sceVu0SubVector(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0SubVector", rdram, ctx, runtime); +} + +void sceVu0TransMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0TransMatrix", rdram, ctx, runtime); +} + +void sceVu0TransposeMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0TransposeMatrix", rdram, ctx, runtime); +} + +void sceVu0UnitMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t dstAddr = getRegU32(ctx, 4); // sceVu0FMATRIX dst + alignas(16) const float identity[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + + if (!writeGuestBytes(rdram, runtime, dstAddr, reinterpret_cast(identity), sizeof(identity))) + { + static uint32_t warnCount = 0; + if (warnCount < 8) + { + std::cerr << "sceVu0UnitMatrix: failed to write matrix at 0x" + << std::hex << dstAddr << std::dec << std::endl; + ++warnCount; + } + } + + setReturnS32(ctx, 0); +} + +void sceVu0ViewScreenMatrix(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("sceVu0ViewScreenMatrix", rdram, ctx, runtime); +} + +void sceWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioWrite(rdram, ctx, runtime); +} + +void srand(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("srand", rdram, ctx, runtime); +} + +void stat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("stat", rdram, ctx, runtime); +} + +void strcasecmp(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("strcasecmp", rdram, ctx, runtime); +} + +void vfprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t file_handle = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 + FILE *fp = get_file_ptr(file_handle); + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (fp && format_addr != 0) + { + std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); + ret = std::fprintf(fp, "%s", rendered.c_str()); + } + else + { + std::cerr << "vfprintf error: Invalid file handle or format address." + << " Handle: 0x" << std::hex << file_handle << " (file valid: " << (fp != nullptr) << ")" + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + setReturnS32(ctx, ret); +} + +void vsprintf(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + uint32_t va_list_addr = getRegU32(ctx, 6); // $a2 + const std::string formatOwned = readPs2CStringBounded(rdram, runtime, format_addr, 1024); + int ret = -1; + + if (format_addr != 0) + { + std::string rendered = formatPs2StringWithVaList(rdram, runtime, formatOwned.c_str(), va_list_addr); + if (writeGuestBytes(rdram, runtime, str_addr, reinterpret_cast(rendered.c_str()), rendered.size() + 1u)) + { + ret = static_cast(rendered.size()); + } + else + { + std::cerr << "vsprintf error: Failed to write destination buffer at 0x" + << std::hex << str_addr << std::dec << std::endl; + } + } + else + { + std::cerr << "vsprintf error: Invalid address provided." + << " Dest: 0x" << std::hex << str_addr + << ", Format: 0x" << format_addr << std::dec + << std::endl; + } + + setReturnS32(ctx, ret); +} + +void write(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ps2_syscalls::fioWrite(rdram, ctx, runtime); +} + diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl new file mode 100644 index 0000000..5371f2f --- /dev/null +++ b/ps2xRuntime/src/lib/stubs/ps2_stubs_ps2.inl @@ -0,0 +1,60 @@ +void sceCdRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t lbn = getRegU32(ctx, 4); // $a0 - logical block number + uint32_t sectors = getRegU32(ctx, 5); // $a1 - sector count + uint32_t buf = getRegU32(ctx, 6); // $a2 - destination buffer in RDRAM + + uint32_t offset = buf & PS2_RAM_MASK; + size_t bytes = static_cast(sectors) * kCdSectorSize; + if (bytes > 0) + { + const size_t maxBytes = PS2_RAM_SIZE - offset; + if (bytes > maxBytes) + { + bytes = maxBytes; + } + } + + uint8_t *dst = rdram + offset; + bool ok = true; + if (bytes > 0) + { + ok = readCdSectors(lbn, sectors, dst, bytes); + if (!ok) + { + std::memset(dst, 0, bytes); + } + } + + if (ok) + { + g_cdStreamingLbn = lbn + sectors; + setReturnS32(ctx, 1); // command accepted/success + } + else + { + setReturnS32(ctx, 0); + } +} + +void sceCdSync(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); // 0 = completed/not busy +} + +void sceCdGetError(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_lastCdError); +} + +void builtin_set_imask(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub builtin_set_imask" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + diff --git a/ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl b/ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl new file mode 100644 index 0000000..dd89d41 --- /dev/null +++ b/ps2xRuntime/src/lib/stubs/ps2_stubs_residentEvilCV.inl @@ -0,0 +1,545 @@ +void syRtcInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub syRtcInit" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void syFree(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub syFree" << std::endl; + ++logCount; + } + + const uint32_t guestAddr = getRegU32(ctx, 4); // $a0 + if (runtime && guestAddr != 0u) + { + runtime->guestFree(guestAddr); + } + + setReturnS32(ctx, 0); +} + +void syMalloc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t requestedSize = getRegU32(ctx, 4); // $a0 + uint32_t resultAddr = 0u; + + if (runtime && requestedSize != 0u) + { + // Match game expectation for allocator alignment while keeping pointers in EE RAM. + resultAddr = runtime->guestMalloc(requestedSize, 64u); + } + + static int logCount = 0; + if (logCount < 16) + { + std::cout << "ps2_stub syMalloc" + << " size=0x" << std::hex << requestedSize + << " -> 0x" << resultAddr + << std::dec << std::endl; + ++logCount; + } + + setReturnU32(ctx, resultAddr); +} + +void InitSdcParameter(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub InitSdcParameter" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void Ps2_pad_actuater(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub Ps2_pad_actuater" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void syMallocInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (runtime) + { + const uint32_t heapBase = getRegU32(ctx, 4); // $a0 + const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) + + constexpr uint32_t kHeapBaseFloor = 0x00100000u; + uint32_t normalizedBase = heapBase; + if (normalizedBase >= 0x80000000u && normalizedBase < 0xC0000000u) + { + normalizedBase &= 0x1FFFFFFFu; + } + else if (normalizedBase >= PS2_RAM_SIZE) + { + normalizedBase &= PS2_RAM_MASK; + } + + const bool suspiciousKsegBase = (heapBase & 0xE0000000u) == 0x80000000u && normalizedBase < kHeapBaseFloor; + if (normalizedBase == 0u || suspiciousKsegBase) + { + // Keep the ELF-driven suggestion instead of collapsing heap to low memory. + normalizedBase = runtime->guestHeapBase(); + } + + // Treat absurd "size" values as unspecified limit. + uint32_t heapLimit = 0u; + if (heapSize != 0u && heapSize <= PS2_RAM_SIZE && normalizedBase < PS2_RAM_SIZE) + { + const uint64_t candidateLimit = static_cast(normalizedBase) + static_cast(heapSize); + heapLimit = static_cast(std::min(candidateLimit, PS2_RAM_SIZE)); + } + runtime->configureGuestHeap(normalizedBase, heapLimit); + if (logCount < 8) + { + std::cout << "ps2_stub syMallocInit" + << " reqBase=0x" << std::hex << heapBase + << " reqSize=0x" << heapSize + << " normBase=0x" << normalizedBase + << " reqLimit=0x" << heapLimit + << " finalBase=0x" << runtime->guestHeapBase() + << " finalEnd=0x" << runtime->guestHeapEnd() + << std::dec << std::endl; + ++logCount; + } + } + else if (logCount < 8) + { + std::cout << "ps2_stub syMallocInit" << std::endl; + ++logCount; + } + + setReturnS32(ctx, 0); +} + +void syHwInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub syHwInit" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void syHwInit2(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub syHwInit2" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void InitGdSystemEx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub InitGdSystemEx" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void pdInitPeripheral(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub pdInitPeripheral" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void pdGetPeripheral(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub pdGetPeripheral" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void Ps2SwapDBuff(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub Ps2SwapDBuff" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void InitReadKeyEx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub InitReadKeyEx" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void SetRepeatKeyTimer(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub SetRepeatKeyTimer" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void StopFxProgram(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub StopFxProgram" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void sndr_trans_func(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sndr_trans_func (noop)" << std::endl; + ++logCount; + } + + // For now just clear the snd busy flag used by sdMultiUnitDownload/SysServer loops. + constexpr uint32_t kSndBusyAddr = 0x01E0E170; + if (rdram) + { + uint32_t offset = kSndBusyAddr & PS2_RAM_MASK; + if (offset + sizeof(uint32_t) <= PS2_RAM_SIZE) + { + *reinterpret_cast(rdram + offset) = 0; + } + } + + setReturnS32(ctx, 0); +} + +void sdDrvInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sdDrvInit (noop)" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void ADXF_LoadPartitionNw(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub ADXF_LoadPartitionNw (noop)" << std::endl; + ++logCount; + } + // Return success to keep the ADX partition setup moving. + setReturnS32(ctx, 0); +} + +void sdSndStopAll(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sdSndStopAll" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void sdSysFinish(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub sdSysFinish" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void ADXT_Init(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub ADXT_Init" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void ADXT_SetNumRetry(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub ADXT_SetNumRetry" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void cvFsSetDefDev(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + if (logCount < 8) + { + std::cout << "ps2_stub cvFsSetDefDev" << std::endl; + ++logCount; + } + setReturnS32(ctx, 0); +} + +void mcCallMessageTypeSe(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCallMessageTypeSe", rdram, ctx, runtime); +} + +void mcCheckReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCheckReadStartConfigFile", rdram, ctx, runtime); +} + +void mcCheckReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCheckReadStartSaveFile", rdram, ctx, runtime); +} + +void mcCheckWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCheckWriteStartConfigFile", rdram, ctx, runtime); +} + +void mcCheckWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCheckWriteStartSaveFile", rdram, ctx, runtime); +} + +void mcCreateConfigInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCreateConfigInit", rdram, ctx, runtime); +} + +void mcCreateFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCreateFileSelectWindow", rdram, ctx, runtime); +} + +void mcCreateIconInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCreateIconInit", rdram, ctx, runtime); +} + +void mcCreateSaveFileInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcCreateSaveFileInit", rdram, ctx, runtime); +} + +void mcDispFileName(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDispFileName", rdram, ctx, runtime); +} + +void mcDispFileNumber(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDispFileNumber", rdram, ctx, runtime); +} + +void mcDisplayFileSelectWindow(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDisplayFileSelectWindow", rdram, ctx, runtime); +} + +void mcDisplaySelectFileInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDisplaySelectFileInfo", rdram, ctx, runtime); +} + +void mcDisplaySelectFileInfoMesCount(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDisplaySelectFileInfoMesCount", rdram, ctx, runtime); +} + +void mcDispWindowCurSol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDispWindowCurSol", rdram, ctx, runtime); +} + +void mcDispWindowFoundtion(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcDispWindowFoundtion", rdram, ctx, runtime); +} + +void mceGetInfoApdx(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mceGetInfoApdx", rdram, ctx, runtime); +} + +void mceIntrReadFixAlign(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mceIntrReadFixAlign", rdram, ctx, runtime); +} + +void mceStorePwd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mceStorePwd", rdram, ctx, runtime); +} + +void mcGetConfigCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetConfigCapacitySize", rdram, ctx, runtime); +} + +void mcGetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetFileSelectWindowCursol", rdram, ctx, runtime); +} + +void mcGetFreeCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetFreeCapacitySize", rdram, ctx, runtime); +} + +void mcGetIconCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetIconCapacitySize", rdram, ctx, runtime); +} + +void mcGetIconFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetIconFileCapacitySize", rdram, ctx, runtime); +} + +void mcGetPortSelectDirInfo(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetPortSelectDirInfo", rdram, ctx, runtime); +} + +void mcGetSaveFileCapacitySize(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetSaveFileCapacitySize", rdram, ctx, runtime); +} + +void mcGetStringEnd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcGetStringEnd", rdram, ctx, runtime); +} + +void mcMoveFileSelectWindowCursor(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcMoveFileSelectWindowCursor", rdram, ctx, runtime); +} + +void mcNewCreateConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcNewCreateConfigFile", rdram, ctx, runtime); +} + +void mcNewCreateIcon(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcNewCreateIcon", rdram, ctx, runtime); +} + +void mcNewCreateSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcNewCreateSaveFile", rdram, ctx, runtime); +} + +void mcReadIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcReadIconData", rdram, ctx, runtime); +} + +void mcReadStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcReadStartConfigFile", rdram, ctx, runtime); +} + +void mcReadStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcReadStartSaveFile", rdram, ctx, runtime); +} + +void mcSelectFileInfoInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcSelectFileInfoInit", rdram, ctx, runtime); +} + +void mcSelectSaveFileCheck(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcSelectSaveFileCheck", rdram, ctx, runtime); +} + +void mcSetFileSelectWindowCursol(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcSetFileSelectWindowCursol", rdram, ctx, runtime); +} + +void mcSetFileSelectWindowCursolInit(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcSetFileSelectWindowCursolInit", rdram, ctx, runtime); +} + +void mcSetStringSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcSetStringSaveFile", rdram, ctx, runtime); +} + +void mcSetTyepWriteMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcSetTyepWriteMode", rdram, ctx, runtime); +} + +void mcWriteIconData(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcWriteIconData", rdram, ctx, runtime); +} + +void mcWriteStartConfigFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcWriteStartConfigFile", rdram, ctx, runtime); +} + +void mcWriteStartSaveFile(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + TODO_NAMED("mcWriteStartSaveFile", rdram, ctx, runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl new file mode 100644 index 0000000..0c50928 --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_loader.inl @@ -0,0 +1,422 @@ +namespace +{ + std::string readGuestCStringBounded(const uint8_t *rdram, uint32_t guestAddr, size_t maxBytes) + { + std::string out; + if (!rdram || guestAddr == 0 || maxBytes == 0) + { + return out; + } + + out.reserve(maxBytes); + for (size_t i = 0; i < maxBytes; ++i) + { + const char ch = static_cast(rdram[(guestAddr + static_cast(i)) & PS2_RAM_MASK]); + if (ch == '\0') + { + break; + } + out.push_back(ch); + } + return out; + } + + std::string normalizeSifModulePathKey(const std::string &path) + { + return toLowerAscii(normalizePs2PathSuffix(path)); + } + + uint64_t hashGuestBytesFnv1a64(const uint8_t *rdram, uint32_t guestAddr, size_t byteCount) + { + constexpr uint64_t kOffset = 1469598103934665603ull; + constexpr uint64_t kPrime = 1099511628211ull; + + if (!rdram || guestAddr == 0 || byteCount == 0) + { + return 0ull; + } + + uint64_t hash = kOffset; + for (size_t i = 0; i < byteCount; ++i) + { + const uint8_t b = rdram[(guestAddr + static_cast(i)) & PS2_RAM_MASK]; + hash ^= static_cast(b); + hash *= kPrime; + } + return hash; + } + + std::string makeSifModuleBufferTag(const uint8_t *rdram, uint32_t bufferAddr) + { + char key[96] = {}; + const uint64_t hash = hashGuestBytesFnv1a64(rdram, bufferAddr, kSifModuleBufferProbeBytes); + std::snprintf(key, sizeof(key), "iopbuf:fnv64:%016llx", static_cast(hash)); + return std::string(key); + } + + void logSifModuleAction(const char *op, int32_t moduleId, const std::string &path, uint32_t refCount) + { + if (!op) + { + return; + } + + std::lock_guard lock(g_sif_module_mutex); + if (g_sif_module_log_count >= kMaxSifModuleLogs) + { + return; + } + + std::cout << "[SIF module] " << op + << " id=" << moduleId + << " ref=" << refCount + << " path=\"" << path << "\"" + << std::endl; + ++g_sif_module_log_count; + } + + int32_t trackSifModuleLoad(const std::string &path) + { + if (path.empty()) + { + return -1; + } + + const std::string pathKey = normalizeSifModulePathKey(path); + if (pathKey.empty()) + { + return -1; + } + + std::lock_guard lock(g_sif_module_mutex); + + auto byPathIt = g_sif_module_id_by_path.find(pathKey); + if (byPathIt != g_sif_module_id_by_path.end()) + { + auto byIdIt = g_sif_modules_by_id.find(byPathIt->second); + if (byIdIt != g_sif_modules_by_id.end()) + { + SifModuleRecord &record = byIdIt->second; + record.loaded = true; + ++record.refCount; + return record.id; + } + } + + if (g_next_sif_module_id <= 0) + { + g_next_sif_module_id = 1; + } + + const int32_t moduleId = g_next_sif_module_id++; + SifModuleRecord record; + record.id = moduleId; + record.path = path; + record.pathKey = pathKey; + record.refCount = 1; + record.loaded = true; + + g_sif_module_id_by_path[pathKey] = moduleId; + g_sif_modules_by_id[moduleId] = record; + return moduleId; + } + + bool trackSifModuleStop(int32_t moduleId, uint32_t *remainingRefs = nullptr) + { + if (moduleId <= 0) + { + if (remainingRefs) + { + *remainingRefs = 0; + } + return false; + } + + 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()) + { + if (remainingRefs) + { + *remainingRefs = 0; + } + return false; + } + + SifModuleRecord &record = it->second; + if (record.refCount > 0) + { + --record.refCount; + } + record.loaded = (record.refCount != 0); + + if (remainingRefs) + { + *remainingRefs = record.refCount; + } + return true; + } + + bool readFileBlockAt(std::ifstream &file, uint64_t offset, void *dst, size_t byteCount) + { + if (!dst || byteCount == 0) + { + return false; + } + + file.seekg(static_cast(offset), std::ios::beg); + if (!file) + { + return false; + } + + file.read(reinterpret_cast(dst), static_cast(byteCount)); + return file.gcount() == static_cast(byteCount); + } + + bool tryExtractElfGpValue(std::ifstream &file, const Elf32Header &header, uint32_t &gpOut) + { + uint8_t regInfo[24] = {}; + + for (uint32_t i = 0; i < header.phnum; ++i) + { + Elf32ProgramHeader ph{}; + const uint64_t phOffset = static_cast(header.phoff) + static_cast(i) * header.phentsize; + if (!readFileBlockAt(file, phOffset, &ph, sizeof(ph))) + { + return false; + } + + if (ph.type == kElfPtMipsRegInfo && ph.filesz >= sizeof(regInfo)) + { + if (!readFileBlockAt(file, ph.offset, regInfo, sizeof(regInfo))) + { + return false; + } + std::memcpy(&gpOut, regInfo + 20u, sizeof(gpOut)); + return true; + } + } + + for (uint32_t i = 0; i < header.shnum; ++i) + { + Elf32SectionHeader sh{}; + const uint64_t shOffset = static_cast(header.shoff) + static_cast(i) * header.shentsize; + if (!readFileBlockAt(file, shOffset, &sh, sizeof(sh))) + { + return false; + } + + if (sh.type == kElfShtMipsRegInfo && sh.size >= sizeof(regInfo)) + { + if (!readFileBlockAt(file, sh.offset, regInfo, sizeof(regInfo))) + { + return false; + } + std::memcpy(&gpOut, regInfo + 20u, sizeof(gpOut)); + return true; + } + } + + return false; + } + + bool loadElfIntoGuestMemory(const std::string &hostPath, + uint8_t *rdram, + PS2Runtime *runtime, + const std::string §ionName, + GuestExecData &execDataOut, + std::string &errorOut) + { + if (!rdram || hostPath.empty()) + { + errorOut = "invalid path or RDRAM pointer"; + return false; + } + + std::ifstream file(hostPath, std::ios::binary); + if (!file) + { + errorOut = "failed to open ELF"; + return false; + } + + Elf32Header header{}; + if (!readFileBlockAt(file, 0, &header, sizeof(header))) + { + errorOut = "failed to read ELF header"; + return false; + } + + if (header.magic != kElfMagic || header.machine != kElfMachineMips || header.type != kElfTypeExec) + { + errorOut = "not a MIPS executable ELF"; + return false; + } + + bool loadedAny = false; + const bool loadAll = sectionName.empty() || toLowerAscii(sectionName) == "all"; + static uint32_t secFilterLogCount = 0; + if (!loadAll && secFilterLogCount < 8u) + { + std::cout << "[SifLoadElfPart] section filter \"" << sectionName + << "\" requested; loading PT_LOAD segments only." << std::endl; + ++secFilterLogCount; + } + + for (uint32_t i = 0; i < header.phnum; ++i) + { + Elf32ProgramHeader ph{}; + const uint64_t phOffset = static_cast(header.phoff) + static_cast(i) * header.phentsize; + if (!readFileBlockAt(file, phOffset, &ph, sizeof(ph))) + { + errorOut = "failed to read ELF program headers"; + return false; + } + + if (ph.type != kElfPtLoad || ph.memsz == 0u) + { + continue; + } + if (ph.filesz > ph.memsz) + { + errorOut = "ELF segment filesz > memsz"; + return false; + } + + const uint64_t memSize64 = static_cast(ph.memsz); + if (runtime && ph.vaddr >= PS2_SCRATCHPAD_BASE && ph.vaddr < (PS2_SCRATCHPAD_BASE + PS2_SCRATCHPAD_SIZE)) + { + const uint32_t scratchOffset = runtime->memory().translateAddress(ph.vaddr); + if (static_cast(scratchOffset) + memSize64 > PS2_SCRATCHPAD_SIZE) + { + errorOut = "ELF scratchpad segment out of range"; + return false; + } + + uint8_t *dest = runtime->memory().getScratchpad() + scratchOffset; + if (ph.filesz > 0u) + { + if (!readFileBlockAt(file, ph.offset, dest, ph.filesz)) + { + errorOut = "failed to read ELF segment payload"; + return false; + } + } + if (ph.memsz > ph.filesz) + { + std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz); + } + } + else + { + const uint32_t physAddr = runtime ? runtime->memory().translateAddress(ph.vaddr) : (ph.vaddr & PS2_RAM_MASK); + if (static_cast(physAddr) + memSize64 > PS2_RAM_SIZE) + { + errorOut = "ELF RDRAM segment out of range"; + return false; + } + + uint8_t *dest = rdram + physAddr; + if (ph.filesz > 0u) + { + if (!readFileBlockAt(file, ph.offset, dest, ph.filesz)) + { + errorOut = "failed to read ELF segment payload"; + return false; + } + } + if (ph.memsz > ph.filesz) + { + std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz); + } + } + + loadedAny = true; + } + + if (!loadedAny) + { + errorOut = "ELF has no loadable segments"; + return false; + } + + execDataOut.epc = header.entry; + execDataOut.gp = 0u; + execDataOut.sp = 0u; + execDataOut.dummy = 0u; + + uint32_t gpValue = 0u; + if (tryExtractElfGpValue(file, header, gpValue)) + { + execDataOut.gp = gpValue; + } + + return true; + } + + int32_t runSifLoadElfPart(uint8_t *rdram, + R5900Context *ctx, + PS2Runtime *runtime, + uint32_t pathAddr, + const std::string §ionName, + uint32_t execDataAddr) + { + if (!rdram || !ctx) + { + return -1; + } + + const std::string ps2Path = readGuestCStringBounded(rdram, pathAddr, kLoadfilePathMaxBytes); + if (ps2Path.empty()) + { + return -1; + } + + const std::string hostPath = translatePs2Path(ps2Path.c_str()); + if (hostPath.empty()) + { + return -1; + } + + GuestExecData execData{}; + std::string loadError; + if (!loadElfIntoGuestMemory(hostPath, rdram, runtime, sectionName, execData, loadError)) + { + static uint32_t logCount = 0; + if (logCount < 16u) + { + std::cerr << "[SifLoadElfPart] failed path=\"" << ps2Path << "\" host=\"" << hostPath + << "\" reason=" << loadError << std::endl; + ++logCount; + } + return -1; + } + + if (execData.gp == 0u) + { + execData.gp = getRegU32(ctx, 28); + } + execData.sp = getRegU32(ctx, 29); + + if (execDataAddr != 0u) + { + GuestExecData *guestExec = reinterpret_cast(getMemPtr(rdram, execDataAddr)); + if (!guestExec) + { + return -1; + } + std::memcpy(guestExec, &execData, sizeof(execData)); + } + + static uint32_t successLogs = 0; + if (successLogs < 16u) + { + std::cout << "[SifLoadElfPart] loaded \"" << ps2Path << "\" epc=0x" + << std::hex << execData.epc << " gp=0x" << execData.gp << std::dec << std::endl; + ++successLogs; + } + + return 0; + } +} diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_path.inl b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_path.inl new file mode 100644 index 0000000..528ae90 --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_path.inl @@ -0,0 +1,80 @@ +namespace +{ + std::string toLowerAscii(std::string value) + { + std::transform(value.begin(), value.end(), value.begin(), + [](unsigned char c) + { return static_cast(std::tolower(c)); }); + return value; + } + + std::string stripIsoVersionSuffix(std::string value) + { + const std::size_t semicolon = value.find(';'); + if (semicolon == std::string::npos) + { + return value; + } + + bool numericSuffix = semicolon + 1 < value.size(); + for (std::size_t i = semicolon + 1; i < value.size(); ++i) + { + if (!std::isdigit(static_cast(value[i]))) + { + numericSuffix = false; + break; + } + } + + if (numericSuffix) + { + value.erase(semicolon); + } + return value; + } + + std::string normalizePs2PathSuffix(std::string suffix) + { + std::replace(suffix.begin(), suffix.end(), '\\', '/'); + suffix = stripIsoVersionSuffix(std::move(suffix)); + while (!suffix.empty() && (suffix.front() == '/' || suffix.front() == '\\')) + { + suffix.erase(suffix.begin()); + } + return suffix; + } + + std::filesystem::path getConfiguredHostRoot() + { + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + if (!paths.hostRoot.empty()) + { + return paths.hostRoot; + } + if (!paths.elfDirectory.empty()) + { + return paths.elfDirectory; + } + + std::error_code ec; + const std::filesystem::path cwd = std::filesystem::current_path(ec); + return ec ? std::filesystem::path(".") : cwd.lexically_normal(); + } + + std::filesystem::path getConfiguredCdRoot() + { + const PS2Runtime::IoPaths &paths = PS2Runtime::getIoPaths(); + if (!paths.cdRoot.empty()) + { + return paths.cdRoot; + } + if (!paths.elfDirectory.empty()) + { + return paths.elfDirectory; + } + + std::error_code ec; + const std::filesystem::path cwd = std::filesystem::current_path(ec); + return ec ? std::filesystem::path(".") : cwd.lexically_normal(); + } +} diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl new file mode 100644 index 0000000..184d91f --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_runtime.inl @@ -0,0 +1,577 @@ +namespace +{ + struct ThreadExitException final : public std::exception + { + const char *what() const noexcept override + { + return "PS2 Thread Exit"; + } + }; +} + +static void throwIfTerminated(const std::shared_ptr &info) +{ + if (info && info->terminated.load()) + { + throw ThreadExitException(); + } +} + +static void waitWhileSuspended(const std::shared_ptr &info) +{ + if (!info) + return; + + std::unique_lock lock(info->m); + if (info->suspendCount > 0) + { + info->status = THS_SUSPEND; + info->waitType = TSW_NONE; + info->waitId = 0; + info->cv.wait(lock, [&]() + { return info->suspendCount == 0 || info->terminated.load(); }); + if (info->terminated.load()) + { + throw ThreadExitException(); + } + info->status = THS_RUN; + } +} + +static std::shared_ptr lookupThreadInfo(int tid) +{ + std::lock_guard lock(g_thread_map_mutex); + auto it = g_threads.find(tid); + if (it != g_threads.end()) + { + return it->second; + } + return nullptr; +} + +static std::shared_ptr ensureCurrentThreadInfo(R5900Context *ctx) +{ + const int tid = g_currentThreadId; + std::lock_guard lock(g_thread_map_mutex); + auto it = g_threads.find(tid); + if (it != g_threads.end()) + { + return it->second; + } + + auto info = std::make_shared(); + info->started = true; + info->status = THS_RUN; + info->currentPriority = info->priority; + info->suspendCount = 0; + if (ctx) + { + info->entry = ctx->pc; + info->stack = getRegU32(ctx, 29); + info->gp = getRegU32(ctx, 28); + } + info->waitType = TSW_NONE; + info->waitId = 0; + + g_threads.emplace(tid, info); + return info; +} + +static std::shared_ptr lookupSemaInfo(int sid) +{ + std::lock_guard lock(g_sema_map_mutex); + auto it = g_semas.find(sid); + if (it != g_semas.end()) + { + return it->second; + } + return nullptr; +} + +static std::shared_ptr lookupEventFlagInfo(int eid) +{ + std::lock_guard lock(g_event_flag_map_mutex); + auto it = g_eventFlags.find(eid); + if (it != g_eventFlags.end()) + { + return it->second; + } + return nullptr; +} + +static void setRegU32(R5900Context *ctx, int reg, uint32_t value) +{ + if (reg < 0 || reg > 31) + return; + ctx->r[reg] = _mm_set_epi32(0, 0, 0, value); +} + +static std::chrono::microseconds alarmTicksToDuration(uint16_t ticks) +{ + constexpr uint64_t kAlarmTickUsec = 64u; // Approximate EE H-SYNC tick period. + const uint64_t clampedTicks = (ticks == 0u) ? 1u : static_cast(ticks); + return std::chrono::microseconds(clampedTicks * kAlarmTickUsec); +} + +static void ensureAlarmWorkerRunning() +{ + std::call_once(g_alarm_worker_once, []() + { std::thread([]() + { + for (;;) + { + std::shared_ptr readyAlarm; + { + std::unique_lock lock(g_alarm_mutex); + while (!readyAlarm) + { + if (g_alarms.empty()) + { + g_alarm_cv.wait(lock); + continue; + } + + auto nextIt = std::min_element(g_alarms.begin(), g_alarms.end(), + [](const auto &a, const auto &b) + { + return a.second->dueAt < b.second->dueAt; + }); + if (nextIt == g_alarms.end()) + { + g_alarm_cv.wait(lock); + continue; + } + + const auto now = std::chrono::steady_clock::now(); + if (nextIt->second->dueAt > now) + { + g_alarm_cv.wait_until(lock, nextIt->second->dueAt); + continue; + } + + readyAlarm = nextIt->second; + g_alarms.erase(nextIt); + } + } + + if (!readyAlarm || !readyAlarm->runtime || !readyAlarm->rdram || !readyAlarm->handler) + { + continue; + } + if (!readyAlarm->runtime->hasFunction(readyAlarm->handler)) + { + continue; + } + + try + { + R5900Context callbackCtx{}; + setRegU32(&callbackCtx, 28, readyAlarm->gp); + setRegU32(&callbackCtx, 29, readyAlarm->sp); + setRegU32(&callbackCtx, 31, 0); + setRegU32(&callbackCtx, 4, static_cast(readyAlarm->id)); + setRegU32(&callbackCtx, 5, static_cast(readyAlarm->ticks)); + setRegU32(&callbackCtx, 6, readyAlarm->commonArg); + setRegU32(&callbackCtx, 7, 0); + callbackCtx.pc = readyAlarm->handler; + + PS2Runtime::RecompiledFunction func = readyAlarm->runtime->lookupFunction(readyAlarm->handler); + func(readyAlarm->rdram, &callbackCtx, readyAlarm->runtime); + } + catch (const ThreadExitException &) + { + } + catch (const std::exception &e) + { + static int alarmExceptionLogs = 0; + if (alarmExceptionLogs < 8) + { + std::cerr << "[SetAlarm] callback exception: " << e.what() << std::endl; + ++alarmExceptionLogs; + } + } + } }) + .detach(); }); +} + +static void rpcCopyToRdram(uint8_t *rdram, uint32_t dst, uint32_t src, size_t size) +{ + if (!rdram || size == 0) + return; + + constexpr size_t kMaxRpcTransferBytes = 1u * 1024u * 1024u; + const size_t clampedSize = std::min(size, kMaxRpcTransferBytes); + if (clampedSize != size) + { + static uint32_t warnCount = 0; + if (warnCount < 8) + { + std::cerr << "[SifCallRpc] clamping copy size from " << size + << " to " << clampedSize + << " bytes (dst=0x" << std::hex << dst + << " src=0x" << src << std::dec << ")" << std::endl; + ++warnCount; + } + } + + for (size_t i = 0; i < clampedSize; ++i) + { + const uint32_t dstAddr = dst + static_cast(i); + const uint32_t srcAddr = src + static_cast(i); + uint8_t *dstPtr = getMemPtr(rdram, dstAddr); + const uint8_t *srcPtr = getConstMemPtr(rdram, srcAddr); + if (!dstPtr || !srcPtr) + { + break; + } + *dstPtr = *srcPtr; + } +} + +static void rpcZeroRdram(uint8_t *rdram, uint32_t dst, size_t size) +{ + if (!rdram || size == 0) + return; + + constexpr size_t kMaxRpcTransferBytes = 1u * 1024u * 1024u; + const size_t clampedSize = std::min(size, kMaxRpcTransferBytes); + if (clampedSize != size) + { + static uint32_t warnCount = 0; + if (warnCount < 8) + { + std::cerr << "[SifCallRpc] clamping zero size from " << size + << " to " << clampedSize + << " bytes (dst=0x" << std::hex << dst << std::dec << ")" << std::endl; + ++warnCount; + } + } + + for (size_t i = 0; i < clampedSize; ++i) + { + const uint32_t dstAddr = dst + static_cast(i); + uint8_t *dstPtr = getMemPtr(rdram, dstAddr); + if (!dstPtr) + { + break; + } + *dstPtr = 0; + } +} + +static bool readStackU32(uint8_t *rdram, uint32_t sp, uint32_t offset, uint32_t &out) +{ + uint8_t *ptr = getMemPtr(rdram, sp + offset); + if (!ptr) + return false; + out = *reinterpret_cast(ptr); + return true; +} + +static bool rpcInvokeFunction(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime, + uint32_t funcAddr, uint32_t a0, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t *outV0) +{ + if (!runtime || !funcAddr || !runtime->hasFunction(funcAddr)) + return false; + + R5900Context tmp = *ctx; + setRegU32(&tmp, 4, a0); + setRegU32(&tmp, 5, a1); + setRegU32(&tmp, 6, a2); + setRegU32(&tmp, 7, a3); + tmp.pc = funcAddr; + + PS2Runtime::RecompiledFunction func = runtime->lookupFunction(funcAddr); + func(rdram, &tmp, runtime); + + if (outV0) + { + *outV0 = getRegU32(&tmp, 2); + } + return true; +} + +static uint32_t rpcAllocPacketAddr(uint8_t *rdram) +{ + if (kRpcPacketPoolCount == 0) + return 0; + + uint32_t slot = g_rpc_packet_index++ % kRpcPacketPoolCount; + uint32_t addr = kRpcPacketPoolBase + (slot * kRpcPacketSize); + rpcZeroRdram(rdram, addr, kRpcPacketSize); + return addr; +} + +static uint32_t rpcAllocServerAddr(uint8_t *rdram) +{ + if (kRpcServerPoolCount == 0) + return 0; + + uint32_t slot = g_rpc_server_index++ % kRpcServerPoolCount; + uint32_t addr = kRpcServerPoolBase + (slot * kRpcServerStride); + rpcZeroRdram(rdram, addr, kRpcServerStride); + return addr; +} + +struct IrqHandlerInfo +{ + uint32_t cause = 0; + uint32_t handler = 0; + uint32_t arg = 0; + bool enabled = true; +}; + +static std::unordered_map g_intcHandlers; +static std::unordered_map g_dmacHandlers; +static int g_nextIntcHandlerId = 1; +static int g_nextDmacHandlerId = 1; + +std::string translatePs2Path(const char *ps2Path) +{ + if (!ps2Path || !*ps2Path) + { + return {}; + } + + std::string pathStr(ps2Path); + std::string lower = toLowerAscii(pathStr); + + auto resolveWithBase = [&](const std::filesystem::path &base, const std::string &suffix) -> std::string + { + const std::string normalizedSuffix = normalizePs2PathSuffix(suffix); + std::filesystem::path resolved = base; + if (!normalizedSuffix.empty()) + { + resolved /= std::filesystem::path(normalizedSuffix); + } + return resolved.lexically_normal().string(); + }; + + if (lower.rfind("host0:", 0) == 0 || lower.rfind("host:", 0) == 0) + { + const std::size_t prefixLength = (lower.rfind("host0:", 0) == 0) ? 6 : 5; + return resolveWithBase(getConfiguredHostRoot(), pathStr.substr(prefixLength)); + } + + if (lower.rfind("cdrom0:", 0) == 0 || lower.rfind("cdrom:", 0) == 0) + { + const std::size_t prefixLength = (lower.rfind("cdrom0:", 0) == 0) ? 7 : 6; + return resolveWithBase(getConfiguredCdRoot(), pathStr.substr(prefixLength)); + } + + if (!pathStr.empty() && (pathStr.front() == '/' || pathStr.front() == '\\')) + { + return resolveWithBase(getConfiguredCdRoot(), pathStr); + } + + if (pathStr.size() > 1 && pathStr[1] == ':') + { + return pathStr; + } + + return resolveWithBase(getConfiguredCdRoot(), pathStr); +} + +static bool localtimeSafe(const std::time_t *t, std::tm *out) +{ +#ifdef _WIN32 + return localtime_s(out, t) == 0; +#else + return localtime_r(t, out) != nullptr; +#endif +} + +static void encodePs2Time(std::time_t t, uint8_t out[8]) +{ + std::tm tm{}; + if (!localtimeSafe(&t, &tm)) + { + std::memset(out, 0, 8); + return; + } + + uint16_t year = static_cast(tm.tm_year + 1900); + out[0] = 0; + out[1] = static_cast(tm.tm_sec); + out[2] = static_cast(tm.tm_min); + out[3] = static_cast(tm.tm_hour); + out[4] = static_cast(tm.tm_mday); + out[5] = static_cast(tm.tm_mon + 1); + out[6] = static_cast(year & 0xFF); + out[7] = static_cast((year >> 8) & 0xFF); +} + +static std::time_t fileTimeToTimeT(std::filesystem::file_time_type ft) +{ + auto sctp = std::chrono::time_point_cast( + ft - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); + return std::chrono::system_clock::to_time_t(sctp); +} + +static bool gmtimeSafe(const std::time_t *t, std::tm *out) +{ +#ifdef _WIN32 + return gmtime_s(out, t) == 0; +#else + return gmtime_r(t, out) != nullptr; +#endif +} + +static int getTimezoneOffsetMinutes() +{ + std::time_t now = std::time(nullptr); + std::tm local{}; + std::tm gmt{}; + if (!localtimeSafe(&now, &local) || !gmtimeSafe(&now, &gmt)) + return 0; + + std::time_t localTime = std::mktime(&local); + std::time_t gmtTime = std::mktime(&gmt); + if (localTime == static_cast(-1) || gmtTime == static_cast(-1)) + return 0; + + double diff = std::difftime(localTime, gmtTime); + return static_cast(diff / 60.0); +} + +static uint32_t packOsdConfig(uint32_t spdifMode, uint32_t screenType, uint32_t videoOutput, + uint32_t japLanguage, uint32_t ps1drvConfig, uint32_t version, + uint32_t language, int timezoneOffset) +{ + uint32_t raw = 0; + raw |= (spdifMode & 0x1) << 0; + raw |= (screenType & 0x3) << 1; + raw |= (videoOutput & 0x1) << 3; + raw |= (japLanguage & 0x1) << 4; + raw |= (ps1drvConfig & 0xFF) << 5; + raw |= (version & 0x7) << 13; + raw |= (language & 0x1F) << 16; + raw |= (static_cast(timezoneOffset) & 0x7FF) << 21; + return raw; +} + +static int decodeTimezoneOffset(uint32_t raw) +{ + int tz = static_cast((raw >> 21) & 0x7FF); + if (tz & 0x400) + tz |= ~0x7FF; + return tz; +} + +static int clampTimezoneOffset(int tz) +{ + if (tz < -1024) + return -1024; + if (tz > 1023) + return 1023; + return tz; +} + +static uint32_t sanitizeOsdConfigRaw(uint32_t raw) +{ + uint32_t spdifMode = raw & 0x1; + uint32_t screenType = (raw >> 1) & 0x3; + if (screenType > 2) + screenType = 0; + uint32_t videoOutput = (raw >> 3) & 0x1; + uint32_t japLanguage = (raw >> 4) & 0x1; + uint32_t ps1drvConfig = (raw >> 5) & 0xFF; + uint32_t version = (raw >> 13) & 0x7; + if (version > 2) + version = 1; + uint32_t language = (raw >> 16) & 0x1F; + int tz = clampTimezoneOffset(decodeTimezoneOffset(raw)); + return packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz); +} + +static void ensureOsdConfigInitialized() +{ + std::lock_guard lock(g_osd_mutex); + if (g_osd_config_initialized) + return; + + int tz = clampTimezoneOffset(getTimezoneOffsetMinutes()); + uint32_t spdifMode = 1; // disabled + uint32_t screenType = 0; // 4:3 + uint32_t videoOutput = 0; // RGB + uint32_t japLanguage = 1; // non-japanese + uint32_t ps1drvConfig = 0; + uint32_t version = 1; // OSD2 + uint32_t language = 1; // English + g_osd_config_raw = packOsdConfig(spdifMode, screenType, videoOutput, japLanguage, ps1drvConfig, version, language, tz); + g_osd_config_initialized = true; +} + +static uint32_t allocTlsAddr(uint8_t *rdram) +{ + if (!rdram || kTlsPoolCount == 0) + return 0; + + std::lock_guard lock(g_tls_mutex); + uint32_t slot = g_tls_index++ % kTlsPoolCount; + uint32_t addr = kTlsPoolBase + (slot * kTlsBlockSize); + rpcZeroRdram(rdram, addr, kTlsBlockSize); + return addr; +} + +static uint32_t allocBootModeAddr(uint8_t *rdram, size_t bytes) +{ + if (!rdram) + return 0; + + size_t aligned = (bytes + 15u) & ~15u; + if (g_bootmode_pool_offset + aligned > kBootModePoolBytes) + return 0; + + uint32_t addr = kBootModePoolBase + g_bootmode_pool_offset; + g_bootmode_pool_offset += static_cast(aligned); + rpcZeroRdram(rdram, addr, aligned); + return addr; +} + +static uint32_t createBootModeEntry(uint8_t *rdram, uint8_t id, uint16_t value, uint8_t lenField, const uint32_t *data, uint8_t dataCount) +{ + uint8_t allocCount = (dataCount == 0) ? 1 : dataCount; + size_t bytes = static_cast(1 + allocCount) * sizeof(uint32_t); + uint32_t addr = allocBootModeAddr(rdram, bytes); + if (!addr) + return 0; + + uint32_t header = (static_cast(lenField) << 24) | + (static_cast(id) << 16) | + (static_cast(value) & 0xFFFFu); + + uint32_t *dst = reinterpret_cast(getMemPtr(rdram, addr)); + if (!dst) + return 0; + + dst[0] = header; + for (uint8_t i = 0; i < allocCount; ++i) + { + dst[1 + i] = (data && i < dataCount) ? data[i] : 0; + } + + return addr; +} + +static void ensureBootModeTable(uint8_t *rdram) +{ + std::lock_guard lock(g_bootmode_mutex); + if (g_bootmode_initialized) + return; + + g_bootmode_pool_offset = 0; + g_bootmode_addresses.clear(); + + const uint32_t boot3Data[1] = {0}; + const uint32_t boot5Data[1] = {0}; + + g_bootmode_addresses[1] = createBootModeEntry(rdram, 1, 0, 0, nullptr, 0); + g_bootmode_addresses[3] = createBootModeEntry(rdram, 3, 0, 1, boot3Data, 1); + g_bootmode_addresses[4] = createBootModeEntry(rdram, 4, 0, 0, nullptr, 0); + g_bootmode_addresses[5] = createBootModeEntry(rdram, 5, 0, 1, boot5Data, 1); + g_bootmode_addresses[6] = createBootModeEntry(rdram, 6, 0, 0, nullptr, 0); + g_bootmode_addresses[7] = createBootModeEntry(rdram, 7, 0, 0, nullptr, 0); + + g_bootmode_initialized = true; +} diff --git a/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl new file mode 100644 index 0000000..212bdba --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/helpers/ps2_syscalls_helpers_state.inl @@ -0,0 +1,449 @@ +std::unordered_map g_fileDescriptors; +int g_nextFd = 3; // Start after stdin, stdout, stderr + +struct ThreadInfo +{ + uint32_t entry = 0; + uint32_t stack = 0; + uint32_t stackSize = 0; + uint32_t gp = 0; + uint32_t priority = 0; + uint32_t attr = 0; + uint32_t option = 0; + uint32_t arg = 0; + bool started = false; + uint32_t tlsBase = 0; + + // Thread Status + int status = 0x10; // THS_DORMANT + int waitType = 0; // TSW_NONE + int waitId = 0; + int wakeupCount = 0; + int currentPriority = 0; + int suspendCount = 0; + + std::mutex m; + std::condition_variable cv; + std::atomic forceRelease{false}; + std::atomic terminated{false}; +}; + +// Thread status +#define THS_RUN 0x01 +#define THS_READY 0x02 +#define THS_WAIT 0x04 +#define THS_SUSPEND 0x08 +#define THS_WAITSUSPEND 0x0c +#define THS_DORMANT 0x10 + +// Thread WAIT Status +#define TSW_NONE 0 +#define TSW_SLEEP 1 +#define TSW_SEMA 2 +#define TSW_EVENT 3 + +// Common kernel-like error codes used by thread/event/alarm syscalls. +constexpr int KE_OK = 0; +constexpr int KE_ERROR = -1; +constexpr int KE_ILLEGAL_MODE = -405; +constexpr int KE_ILLEGAL_THID = -406; +constexpr int KE_UNKNOWN_THID = -407; +constexpr int KE_UNKNOWN_SEMID = -408; +constexpr int KE_UNKNOWN_EVFID = -409; +constexpr int KE_DORMANT = -413; +constexpr int KE_NOT_WAIT = -416; +constexpr int KE_RELEASE_WAIT = -418; +constexpr int KE_SEMA_ZERO = -419; +constexpr int KE_EVF_COND = -421; +constexpr int KE_EVF_MULTI = -422; +constexpr int KE_EVF_ILPAT = -423; +constexpr int KE_WAIT_DELETE = -425; + +// SIF RPC Structures +struct t_SifRpcHeader +{ + uint32_t pkt_addr; // void* + uint32_t rpc_id; + int sema_id; + uint32_t mode; +}; + +struct t_SifRpcClientData +{ + t_SifRpcHeader hdr; + uint32_t command; + uint32_t buf; // void* + uint32_t cbuf; // void* + uint32_t end_function; // func ptr + uint32_t end_param; // void* + uint32_t server; // t_SifRpcServerData* +}; + +struct t_SifRpcServerData +{ + int sid; + uint32_t func; // func ptr + uint32_t buf; // void* + int size; + uint32_t cfunc; // func ptr + uint32_t cbuf; // void* + int size2; + uint32_t client; // t_SifRpcClientData* + uint32_t pkt_addr; // void* + int rpc_number; + uint32_t recvbuf; // void* + int rsize; + int rmode; + int rid; + uint32_t link; // t_SifRpcServerData* + uint32_t next; // t_SifRpcServerData* + uint32_t base; // t_SifRpcDataQueue* +}; + +struct t_SifRpcDataQueue +{ + int thread_id; + int active; + uint32_t link; // t_SifRpcServerData* + uint32_t start; // t_SifRpcServerData* + uint32_t end; // t_SifRpcServerData* + uint32_t next; // t_SifRpcDataQueue* +}; + +struct ee_thread_status_t +{ + int status; // 0x00 + uint32_t func; // 0x04 + uint32_t stack; // 0x08 + int stack_size; // 0x0C + uint32_t gp_reg; // 0x10 + int initial_priority; // 0x14 + int current_priority; // 0x18 + uint32_t attr; // 0x1C + uint32_t option; // 0x20 + uint32_t waitType; // 0x24 + uint32_t waitId; // 0x28 + uint32_t wakeupCount; // 0x2C +}; + +struct ee_sema_t +{ + int count; + int max_count; + int init_count; + int wait_threads; + uint32_t attr; + uint32_t option; +}; + +struct SemaInfo +{ + int count = 0; + int maxCount = 0; + int initCount = 0; + uint32_t attr = 0; + uint32_t option = 0; + int waiters = 0; + bool deleted = false; + std::mutex m; + std::condition_variable cv; +}; + +struct EventFlagInfo +{ + uint32_t attr = 0; + uint32_t option = 0; + uint32_t initBits = 0; + uint32_t bits = 0; + int waiters = 0; + bool deleted = false; + std::mutex m; + std::condition_variable cv; +}; + +struct AlarmInfo +{ + int id = 0; + uint16_t ticks = 0; + uint32_t handler = 0; + uint32_t commonArg = 0; + uint32_t gp = 0; + uint32_t sp = 0; + uint8_t *rdram = nullptr; + PS2Runtime *runtime = nullptr; + std::chrono::steady_clock::time_point dueAt; +}; + +struct io_stat_t +{ + uint32_t mode; + uint32_t attr; + uint32_t size; + uint8_t ctime[8]; + uint8_t atime[8]; + uint8_t mtime[8]; + uint32_t hisize; +}; + +static constexpr uint32_t kFioSoIfLnk = 0x0008; +static constexpr uint32_t kFioSoIfReg = 0x0010; +static constexpr uint32_t kFioSoIfDir = 0x0020; +static constexpr uint32_t kFioSoIROth = 0x0004; +static constexpr uint32_t kFioSoIWOth = 0x0002; +static constexpr uint32_t kFioSoIXOth = 0x0001; + +static std::unordered_map> g_threads; +static int g_nextThreadId = 2; // Reserve 1 for the main thread +static thread_local int g_currentThreadId = 1; +static std::mutex g_thread_map_mutex; + +static std::unordered_map> g_semas; +static int g_nextSemaId = 1; +static std::mutex g_sema_map_mutex; +static std::unordered_map> g_eventFlags; +static int g_nextEventFlagId = 1; +static std::mutex g_event_flag_map_mutex; +static std::unordered_map> g_alarms; +static int g_nextAlarmId = 1; +static std::mutex g_alarm_mutex; +static std::condition_variable g_alarm_cv; +static std::once_flag g_alarm_worker_once; +std::atomic g_activeThreads{0}; +static std::mutex g_fd_mutex; + +struct RpcServerState +{ + uint32_t sid = 0; + uint32_t sd_ptr = 0; // PS2 address +}; + +struct RpcClientState +{ + bool busy = false; + uint32_t last_rpc = 0; + uint32_t sid = 0; +}; + +static std::unordered_map g_rpc_servers; +static std::unordered_map g_rpc_clients; +static std::mutex g_rpc_mutex; +static bool g_rpc_initialized = false; +static uint32_t g_rpc_next_id = 1; +static uint32_t g_rpc_packet_index = 0; +static uint32_t g_rpc_server_index = 0; +static uint32_t g_rpc_active_queue = 0; +static constexpr uint32_t kDtxRpcSid = 0x7D000000u; +static constexpr uint32_t kDtxUrpcObjBase = 0x01F18000u; +static constexpr uint32_t kDtxUrpcObjLimit = 0x01F1FF00u; +static constexpr uint32_t kDtxUrpcFnTableBase = 0x0034FED0u; +static constexpr uint32_t kDtxUrpcObjTableBase = 0x0034FFD0u; +static std::mutex g_dtx_rpc_mutex; +static std::unordered_map g_dtx_remote_by_id; +static uint32_t g_dtx_next_urpc_obj = kDtxUrpcObjBase; + +struct DtxSjrmtState +{ + uint32_t handle = 0; + uint32_t mode = 0; + uint32_t wkAddr = 0; + uint32_t wkSize = 0; + uint32_t readPos = 0; + uint32_t writePos = 0; + uint32_t roomBytes = 0; + uint32_t dataBytes = 0; + uint32_t uuid0 = 0; + uint32_t uuid1 = 0; + uint32_t uuid2 = 0; + uint32_t uuid3 = 0; +}; + +static std::unordered_map g_dtx_sjrmt_by_handle; + +static uint32_t dtxNormalizeSjrmtCapacity(uint32_t requestedBytes) +{ + if (requestedBytes == 0u || requestedBytes > 0x01000000u) + { + return 0x4000u; + } + return requestedBytes; +} + +static uint32_t dtxAllocUrpcHandleLocked() +{ + for (uint32_t i = 0; i < 4096u; ++i) + { + uint32_t candidate = g_dtx_next_urpc_obj; + g_dtx_next_urpc_obj += 0x20u; + if (g_dtx_next_urpc_obj < kDtxUrpcObjBase || g_dtx_next_urpc_obj >= kDtxUrpcObjLimit) + { + g_dtx_next_urpc_obj = kDtxUrpcObjBase; + } + + if (candidate < kDtxUrpcObjBase || candidate >= kDtxUrpcObjLimit) + { + continue; + } + + if (g_dtx_sjrmt_by_handle.find(candidate) != g_dtx_sjrmt_by_handle.end()) + { + continue; + } + + bool inUseByDtxRemote = false; + for (const auto &entry : g_dtx_remote_by_id) + { + if (entry.second == candidate) + { + inUseByDtxRemote = true; + break; + } + } + + if (!inUseByDtxRemote) + { + return candidate; + } + } + + return kDtxUrpcObjBase; +} + +struct ExitHandlerEntry +{ + uint32_t func = 0; + uint32_t arg = 0; +}; + +static std::mutex g_exit_handler_mutex; +static std::unordered_map> g_exit_handlers; + +static std::mutex g_bootmode_mutex; +static bool g_bootmode_initialized = false; +static uint32_t g_bootmode_pool_offset = 0; +static std::unordered_map g_bootmode_addresses; + +static std::mutex g_tls_mutex; +static uint32_t g_tls_index = 0; + +static std::mutex g_osd_mutex; +static bool g_osd_config_initialized = false; +static uint32_t g_osd_config_raw = 0; + +static std::mutex g_ps2_path_mutex; +static bool g_ps2_paths_initialized = false; +static std::filesystem::path g_host_base; +static std::filesystem::path g_cdrom_base; +static std::filesystem::path g_host_cwd; +static std::filesystem::path g_cdrom_cwd; +static std::string g_ps2_cwd_device = "host0"; + +static constexpr uint32_t kRpcPacketSize = 64; +static constexpr uint32_t kRpcPacketPoolBase = 0x01F00000; +static constexpr uint32_t kRpcPacketPoolBytes = 0x00010000; +static constexpr uint32_t kRpcPacketPoolCount = kRpcPacketPoolBytes / kRpcPacketSize; +static constexpr uint32_t kRpcServerPoolBase = 0x01F10000; +static constexpr uint32_t kRpcServerPoolBytes = 0x00010000; +static constexpr uint32_t kRpcServerStride = 0x80; +static constexpr uint32_t kRpcServerPoolCount = kRpcServerPoolBytes / kRpcServerStride; + +static constexpr uint32_t kTlsPoolBase = 0x01F20000; +static constexpr uint32_t kTlsPoolBytes = 0x00010000; +static constexpr uint32_t kTlsBlockSize = 0x100; +static constexpr uint32_t kTlsPoolCount = kTlsPoolBytes / kTlsBlockSize; + +static constexpr uint32_t kBootModePoolBase = 0x01F30000; +static constexpr uint32_t kBootModePoolBytes = 0x00001000; + +static constexpr uint32_t kSifRpcModeNowait = 0x01; +static constexpr uint32_t kSifRpcModeNoWbDc = 0x02; +static constexpr size_t kMaxSifModulePathBytes = 260; +static constexpr uint32_t kMaxSifModuleLogs = 24; +static constexpr size_t kSifModuleBufferProbeBytes = 2048; +static constexpr size_t kLoadfilePathMaxBytes = 252; +static constexpr size_t kLoadfileArgMaxBytes = 252; +static constexpr uint32_t kElfMagic = 0x464C457Fu; +static constexpr uint16_t kElfMachineMips = 8u; +static constexpr uint16_t kElfTypeExec = 2u; +static constexpr uint32_t kElfPtLoad = 1u; +static constexpr uint32_t kElfPtMipsRegInfo = 0x70000000u; +static constexpr uint32_t kElfShtMipsRegInfo = 0x70000006u; + +#pragma pack(push, 1) +struct Elf32Header +{ + uint32_t magic; + uint8_t elfClass; + uint8_t endianness; + uint8_t version; + uint8_t osAbi; + uint8_t abiVersion; + uint8_t pad[7]; + uint16_t type; + uint16_t machine; + uint32_t version2; + uint32_t entry; + uint32_t phoff; + uint32_t shoff; + uint32_t flags; + uint16_t ehsize; + uint16_t phentsize; + uint16_t phnum; + uint16_t shentsize; + uint16_t shnum; + uint16_t shstrndx; +}; + +struct Elf32ProgramHeader +{ + uint32_t type; + uint32_t offset; + uint32_t vaddr; + uint32_t paddr; + uint32_t filesz; + uint32_t memsz; + uint32_t flags; + uint32_t align; +}; + +struct Elf32SectionHeader +{ + uint32_t name; + uint32_t type; + uint32_t flags; + uint32_t addr; + uint32_t offset; + uint32_t size; + uint32_t link; + uint32_t info; + uint32_t addralign; + uint32_t entsize; +}; + +struct GuestExecData +{ + uint32_t epc; + uint32_t gp; + uint32_t sp; + uint32_t dummy; +}; +#pragma pack(pop) + +static_assert(sizeof(Elf32Header) == 52u, "Unexpected ELF32 header layout."); +static_assert(sizeof(Elf32ProgramHeader) == 32u, "Unexpected ELF32 program header layout."); +static_assert(sizeof(Elf32SectionHeader) == 40u, "Unexpected ELF32 section header layout."); +static_assert(sizeof(GuestExecData) == 16u, "Unexpected GuestExecData layout."); + +struct SifModuleRecord +{ + int32_t id = 0; + std::string path; + std::string pathKey; + uint32_t refCount = 0; + bool loaded = false; +}; + +static std::mutex g_sif_module_mutex; +static std::unordered_map g_sif_modules_by_id; +static std::unordered_map g_sif_module_id_by_path; +static int32_t g_next_sif_module_id = 1; +static uint32_t g_sif_module_log_count = 0; diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl new file mode 100644 index 0000000..7f51d9f --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_fileio.inl @@ -0,0 +1,450 @@ +static int allocatePs2Fd(FILE *file) +{ + if (!file) + return -1; + + std::lock_guard lock(g_fd_mutex); + int fd = g_nextFd++; + g_fileDescriptors[fd] = file; + return fd; +} + +static FILE *getHostFile(int ps2Fd) +{ + std::lock_guard lock(g_fd_mutex); + auto it = g_fileDescriptors.find(ps2Fd); + if (it != g_fileDescriptors.end()) + { + return it->second; + } + return nullptr; +} + +static void releasePs2Fd(int ps2Fd) +{ + std::lock_guard lock(g_fd_mutex); + g_fileDescriptors.erase(ps2Fd); +} + +static const char *translateFioMode(int ps2Flags) +{ + bool read = (ps2Flags & PS2_FIO_O_RDONLY) || (ps2Flags & PS2_FIO_O_RDWR); + bool write = (ps2Flags & PS2_FIO_O_WRONLY) || (ps2Flags & PS2_FIO_O_RDWR); + bool append = (ps2Flags & PS2_FIO_O_APPEND); + bool create = (ps2Flags & PS2_FIO_O_CREAT); + bool truncate = (ps2Flags & PS2_FIO_O_TRUNC); + + if (read && write) + { + if (create && truncate) + return "w+b"; + if (create) + return "a+b"; + return "r+b"; + } + else if (write) + { + if (append) + return "ab"; + if (create && truncate) + return "wb"; + if (create) + return "wx"; + return "r+b"; + } + else if (read) + { + return "rb"; + } + return "rb"; +} + +void fioOpen(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + int flags = (int)getRegU32(ctx, 5); // $a1 (PS2 FIO flags) + + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioOpen error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); + return; + } + + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioOpen error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } + + const char *mode = translateFioMode(flags); + std::cout << "fioOpen: '" << hostPath << "' flags=0x" << std::hex << flags << std::dec << " mode='" << mode << "'" << std::endl; + + FILE *fp = ::fopen(hostPath.c_str(), mode); + if (!fp) + { + std::cerr << "fioOpen error: fopen failed for '" << hostPath << "': " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); // e.g., -ENOENT, -EACCES + return; + } + + int ps2Fd = allocatePs2Fd(fp); + if (ps2Fd < 0) + { + std::cerr << "fioOpen error: Failed to allocate PS2 file descriptor" << std::endl; + ::fclose(fp); + setReturnS32(ctx, -1); // e.g., -EMFILE + return; + } + + // returns the PS2 file descriptor + setReturnS32(ctx, ps2Fd); +} + +void fioClose(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + std::cout << "fioClose: fd=" << ps2Fd << std::endl; + + FILE *fp = getHostFile(ps2Fd); + if (!fp) + { + std::cerr << "fioClose warning: Invalid PS2 file descriptor " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // e.g., -EBADF + return; + } + + int ret = ::fclose(fp); + releasePs2Fd(ps2Fd); + + // returns 0 on success, -1 on error + setReturnS32(ctx, ret == 0 ? 0 : -1); +} + +void fioRead(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + uint32_t bufAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 + + uint8_t *hostBuf = getMemPtr(rdram, bufAddr); + FILE *fp = getHostFile(ps2Fd); + + if (!hostBuf) + { + std::cerr << "fioRead error: Invalid buffer address for fd " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EFAULT + return; + } + if (!fp) + { + std::cerr << "fioRead error: Invalid file descriptor " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EBADF + return; + } + if (size == 0) + { + setReturnS32(ctx, 0); // Read 0 bytes + return; + } + + size_t bytesRead = 0; + { + std::lock_guard lock(g_sys_fd_mutex); + bytesRead = fread(hostBuf, 1, size, fp); + } + + if (bytesRead < size && ferror(fp)) + { + std::cerr << "fioRead error: fread failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; + clearerr(fp); + setReturnS32(ctx, -1); // -EIO or other appropriate error + return; + } + + // returns number of bytes read (can be 0 for EOF) + setReturnS32(ctx, (int32_t)bytesRead); +} + +void fioWrite(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + uint32_t bufAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 + + const uint8_t *hostBuf = getConstMemPtr(rdram, bufAddr); + if (!hostBuf) + { + setReturnS32(ctx, -1); + return; + } + + size_t bytesWritten = 0; + { + std::lock_guard lock(g_fd_mutex); + FILE *fp = getHostFile(ps2Fd); + if (!fp) + { + setReturnS32(ctx, -1); // -EFAULT + return; + } + + if (size == 0) + { + setReturnS32(ctx, 0); // Wrote 0 bytes + return; + } + + bytesWritten = ::fwrite(hostBuf, 1, size, fp); + if (bytesWritten < size && ferror(fp)) + { + clearerr(fp); + setReturnS32(ctx, -1); // -EIO, -ENOSPC etc. + return; + } + } + + // returns number of bytes written + setReturnS32(ctx, (int32_t)bytesWritten); +} + +void fioLseek(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int ps2Fd = (int)getRegU32(ctx, 4); // $a0 + int32_t offset = getRegU32(ctx, 5); // $a1 (PS2 seems to use 32-bit offset here commonly) + int whence = (int)getRegU32(ctx, 6); // $a2 (PS2 FIO_SEEK constants) + + FILE *fp = getHostFile(ps2Fd); + if (!fp) + { + std::cerr << "fioLseek error: Invalid file descriptor " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EBADF + return; + } + + int hostWhence; + switch (whence) + { + case PS2_FIO_SEEK_SET: + hostWhence = SEEK_SET; + break; + case PS2_FIO_SEEK_CUR: + hostWhence = SEEK_CUR; + break; + case PS2_FIO_SEEK_END: + hostWhence = SEEK_END; + break; + default: + std::cerr << "fioLseek error: Invalid whence value " << whence << " for fd " << ps2Fd << std::endl; + setReturnS32(ctx, -1); // -EINVAL + return; + } + + if (::fseek(fp, static_cast(offset), hostWhence) != 0) + { + std::cerr << "fioLseek error: fseek failed for fd " << ps2Fd << ": " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); // Return error code + return; + } + + long newPos = ::ftell(fp); + if (newPos < 0) + { + std::cerr << "fioLseek error: ftell failed after fseek for fd " << ps2Fd << ": " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); + } + else + { + if (newPos > 0xFFFFFFFFL) + { + std::cerr << "fioLseek warning: New position exceeds 32-bit for fd " << ps2Fd << std::endl; + setReturnS32(ctx, -1); + } + else + { + setReturnS32(ctx, (int32_t)newPos); + } + } +} + +void fioMkdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + // int mode = (int)getRegU32(ctx, 5); + + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioMkdir error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); // -EFAULT + return; + } + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioMkdir error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } + +#ifdef _WIN32 + int ret = -1; +#else + int ret = ::mkdir(hostPath.c_str(), 0775); +#endif + + if (ret != 0) + { + std::cerr << "fioMkdir error: mkdir failed for '" << hostPath << "': " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); // errno + } + else + { + setReturnS32(ctx, 0); // Success + } +} + +void fioChdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioChdir error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); + return; + } + + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioChdir error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } + + std::cerr << "fioChdir: Attempting host chdir to '" << hostPath << "' (Stub - Check side effects)" << std::endl; + +#ifdef _WIN32 + int ret = -1; +#else + int ret = ::chdir(hostPath.c_str()); +#endif + + if (ret != 0) + { + std::cerr << "fioChdir error: chdir failed for '" << hostPath << "': " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); + } + else + { + setReturnS32(ctx, 0); // Success + } +} + +void fioRmdir(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioRmdir error: Invalid path address" << std::endl; + setReturnS32(ctx, -1); + return; + } + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioRmdir error: Failed to translate path '" << ps2Path << "'" << std::endl; + setReturnS32(ctx, -1); + return; + } + +#ifdef _WIN32 + int ret = -1; +#else + int ret = ::rmdir(hostPath.c_str()); +#endif + + if (ret != 0) + { + std::cerr << "fioRmdir error: rmdir failed for '" << hostPath << "': " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); + } + else + { + setReturnS32(ctx, 0); // Success + } +} + +void fioGetstat(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + // we wont implement this for now. + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + uint32_t statBufAddr = getRegU32(ctx, 5); // $a1 + + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + uint8_t *ps2StatBuf = getMemPtr(rdram, statBufAddr); + + if (!ps2Path) + { + std::cerr << "fioGetstat error: Invalid path addr" << std::endl; + setReturnS32(ctx, -1); + return; + } + if (!ps2StatBuf) + { + std::cerr << "fioGetstat error: Invalid buffer addr" << std::endl; + setReturnS32(ctx, -1); + return; + } + + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioGetstat error: Bad path translate" << std::endl; + setReturnS32(ctx, -1); + return; + } + + setReturnS32(ctx, -1); +} + +void fioRemove(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const char *ps2Path = reinterpret_cast(getConstMemPtr(rdram, pathAddr)); + if (!ps2Path) + { + std::cerr << "fioRemove error: Invalid path" << std::endl; + setReturnS32(ctx, -1); + return; + } + + std::string hostPath = translatePs2Path(ps2Path); + if (hostPath.empty()) + { + std::cerr << "fioRemove error: Path translate fail" << std::endl; + setReturnS32(ctx, -1); + return; + } + +#ifdef _WIN32 + int ret = -1; +#else + int ret = ::unlink(hostPath.c_str()); +#endif + + if (ret != 0) + { + std::cerr << "fioRemove error: unlink failed for '" << hostPath << "': " << strerror(errno) << std::endl; + setReturnS32(ctx, -1); + } + else + { + setReturnS32(ctx, 0); // Success + } +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl new file mode 100644 index 0000000..7e4f191 --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_flags.inl @@ -0,0 +1,649 @@ +void CreateSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 + const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); + int init = 0; + int max = 1; + uint32_t attr = 0; + uint32_t option = 0; + + if (param) + { + // sceSemaParam layout commonly: attr(0), option(1), initCount(2), maxCount(3) + attr = param[0]; + option = param[1]; + init = static_cast(param[2]); + max = static_cast(param[3]); + } + if (max <= 0) + { + max = 1; + } + if (init > max) + { + init = max; + } + + int id = 0; + auto info = std::make_shared(); + info->count = init; + info->maxCount = max; + info->initCount = init; + info->attr = attr; + info->option = option; + + { + std::lock_guard lock(g_sema_map_mutex); + id = g_nextSemaId++; + g_semas.emplace(id, info); + } + std::cout << "[CreateSema] id=" << id << " init=" << init << " max=" << max << std::endl; + setReturnS32(ctx, id); +} + +void DeleteSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int sid = static_cast(getRegU32(ctx, 4)); + std::shared_ptr sema; + + { + std::lock_guard lock(g_sema_map_mutex); + auto it = g_semas.find(sid); + if (it == g_semas.end()) + { + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; + } + sema = it->second; + g_semas.erase(it); + } + + { + std::lock_guard lock(sema->m); + sema->deleted = true; + } + sema->cv.notify_all(); + + setReturnS32(ctx, KE_OK); +} + +void SignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int sid = static_cast(getRegU32(ctx, 4)); + auto sema = lookupSemaInfo(sid); + if (sema) + { + std::lock_guard lock(sema->m); + if (sema->count < sema->maxCount) + { + sema->count++; + } + sema->cv.notify_one(); + } + setReturnS32(ctx, 0); +} + +void iSignalSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + SignalSema(rdram, ctx, runtime); +} + +void WaitSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int sid = static_cast(getRegU32(ctx, 4)); + auto sema = lookupSemaInfo(sid); + if (!sema) + { + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; + } + + auto info = ensureCurrentThreadInfo(ctx); + throwIfTerminated(info); + std::unique_lock lock(sema->m); + int ret = 0; + + if (sema->count == 0) + { + if (info) + { + std::lock_guard tLock(info->m); + info->status = THS_WAIT; + info->waitType = TSW_SEMA; + info->waitId = sid; + info->forceRelease = false; + } + + sema->waiters++; + sema->cv.wait(lock, [&]() + { + bool forced = info ? info->forceRelease.load() : false; + bool terminated = info ? info->terminated.load() : false; + return sema->count > 0 || sema->deleted || forced || terminated; // + }); + sema->waiters--; + if (sema->deleted) + { + ret = KE_WAIT_DELETE; + } + + if (info) + { + std::lock_guard tLock(info->m); + info->status = THS_RUN; + info->waitType = TSW_NONE; + info->waitId = 0; + if (info->forceRelease) + { + info->forceRelease = false; + ret = KE_RELEASE_WAIT; + } + } + + if (info && info->terminated.load()) + { + throw ThreadExitException(); + } + } + + if (ret == 0 && sema->count > 0) + { + sema->count--; + } + lock.unlock(); + waitWhileSuspended(info); + setReturnS32(ctx, ret); +} + +void PollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int sid = static_cast(getRegU32(ctx, 4)); + auto sema = lookupSemaInfo(sid); + if (!sema) + { + setReturnS32(ctx, KE_UNKNOWN_SEMID); + return; + } + + std::lock_guard lock(sema->m); + if (sema->count > 0) + { + sema->count--; + setReturnS32(ctx, KE_OK); + return; + } + + setReturnS32(ctx, KE_SEMA_ZERO); +} + +void iPollSema(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + PollSema(rdram, ctx, runtime); +} + +void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int sid = static_cast(getRegU32(ctx, 4)); + uint32_t statusAddr = getRegU32(ctx, 5); + + auto sema = lookupSemaInfo(sid); + if (!sema) + { + setReturnS32(ctx, -1); + return; + } + + ee_sema_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); + if (!status) + { + setReturnS32(ctx, -1); + return; + } + + std::lock_guard lock(sema->m); + status->count = sema->count; + status->max_count = sema->maxCount; + status->init_count = sema->initCount; + status->wait_threads = sema->waiters; + status->attr = sema->attr; + status->option = sema->option; + setReturnS32(ctx, 0); +} + +void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ReferSemaStatus(rdram, ctx, runtime); +} + +constexpr uint32_t WEF_OR = 1; +constexpr uint32_t WEF_CLEAR = 0x10; +constexpr uint32_t WEF_CLEAR_ALL = 0x20; +constexpr uint32_t WEF_MODE_MASK = WEF_OR | WEF_CLEAR | WEF_CLEAR_ALL; +constexpr uint32_t EA_MULTI = 0x2; + +void CreateEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 + const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); + + auto info = std::make_shared(); + if (param) + { + info->attr = param[0]; + info->option = param[1]; + info->initBits = param[2]; + info->bits = info->initBits; + } + + int id = 0; + { + std::lock_guard mapLock(g_event_flag_map_mutex); + id = g_nextEventFlagId++; + g_eventFlags[id] = info; + } + setReturnS32(ctx, id); +} + +void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int eid = static_cast(getRegU32(ctx, 4)); + std::shared_ptr info; + { + std::lock_guard mapLock(g_event_flag_map_mutex); + auto it = g_eventFlags.find(eid); + if (it == g_eventFlags.end()) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + info = it->second; + g_eventFlags.erase(it); + } + + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + { + std::lock_guard lock(info->m); + info->deleted = true; + } + info->cv.notify_all(); + setReturnS32(ctx, 0); +} + +void SetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t bits = getRegU32(ctx, 5); + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + if (bits == 0) + { + setReturnS32(ctx, KE_OK); + return; + } + + { + std::lock_guard lock(info->m); + info->bits |= bits; + } + info->cv.notify_all(); + setReturnS32(ctx, 0); +} + +void iSetEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + SetEventFlag(rdram, ctx, runtime); +} + +void ClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t bits = getRegU32(ctx, 5); + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + { + std::lock_guard lock(info->m); + info->bits &= bits; + } + info->cv.notify_all(); + setReturnS32(ctx, KE_OK); +} + +void iClearEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ClearEventFlag(rdram, ctx, runtime); +} + +void WaitEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t waitBits = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t resBitsAddr = getRegU32(ctx, 7); + + if ((mode & ~WEF_MODE_MASK) != 0) + { + setReturnS32(ctx, KE_ILLEGAL_MODE); + return; + } + + if (waitBits == 0) + { + setReturnS32(ctx, KE_EVF_ILPAT); + return; + } + + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; + + std::unique_lock lock(info->m); + if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) + { + setReturnS32(ctx, KE_EVF_MULTI); + return; + } + + auto tInfo = ensureCurrentThreadInfo(ctx); + throwIfTerminated(tInfo); + int ret = KE_OK; + + auto satisfied = [&]() + { + if (tInfo && tInfo->forceRelease.load()) + return true; + if (tInfo && tInfo->terminated.load()) + return true; + if (info->deleted) + { + return true; + } + if (mode & WEF_OR) + { + return (info->bits & waitBits) != 0; + } + return (info->bits & waitBits) == waitBits; + }; + + if (!satisfied()) + { + if (tInfo) + { + std::lock_guard tLock(tInfo->m); + tInfo->status = THS_WAIT; + tInfo->waitType = TSW_EVENT; + tInfo->waitId = eid; + tInfo->forceRelease = false; + } + + info->waiters++; + info->cv.wait(lock, satisfied); + info->waiters--; + + if (tInfo) + { + std::lock_guard tLock(tInfo->m); + tInfo->status = THS_RUN; + tInfo->waitType = TSW_NONE; + tInfo->waitId = 0; + if (tInfo->forceRelease) + { + tInfo->forceRelease = false; + ret = KE_RELEASE_WAIT; + } + } + + if (tInfo && tInfo->terminated.load()) + { + throw ThreadExitException(); + } + } + + if (ret == KE_OK && info->deleted) + { + ret = KE_WAIT_DELETE; + } + + if (ret == KE_OK && resBitsPtr) + { + *resBitsPtr = info->bits; + } + + if (ret == KE_OK) + { + if (resBitsPtr) + { + *resBitsPtr = info->bits; + } + + if (mode & WEF_CLEAR_ALL) + { + info->bits = 0; + } + else if (mode & WEF_CLEAR) + { + info->bits &= ~waitBits; + } + } + + lock.unlock(); + waitWhileSuspended(tInfo); + setReturnS32(ctx, ret); +} + +void PollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t waitBits = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t resBitsAddr = getRegU32(ctx, 7); + + if ((mode & ~WEF_MODE_MASK) != 0) + { + setReturnS32(ctx, KE_ILLEGAL_MODE); + return; + } + + if (waitBits == 0) + { + setReturnS32(ctx, KE_EVF_ILPAT); + return; + } + + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + uint32_t *resBitsPtr = resBitsAddr ? reinterpret_cast(getMemPtr(rdram, resBitsAddr)) : nullptr; + + std::lock_guard lock(info->m); + if ((info->attr & EA_MULTI) == 0 && info->waiters > 0) + { + setReturnS32(ctx, KE_EVF_MULTI); + return; + } + + bool ok = false; + if (mode & WEF_OR) + { + ok = (info->bits & waitBits) != 0; + } + else + { + ok = (info->bits & waitBits) == waitBits; + } + + if (!ok) + { + setReturnS32(ctx, KE_EVF_COND); + return; + } + + if (resBitsPtr) + { + *resBitsPtr = info->bits; + } + + if (mode & (WEF_CLEAR | WEF_CLEAR_ALL)) + { + info->bits = 0; + } + + setReturnS32(ctx, KE_OK); +} + +void iPollEventFlag(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + PollEventFlag(rdram, ctx, runtime); +} + +void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int eid = static_cast(getRegU32(ctx, 4)); + uint32_t infoAddr = getRegU32(ctx, 5); + + struct Ps2EventFlagInfo + { + uint32_t attr; + uint32_t option; + uint32_t initBits; + uint32_t currBits; + int32_t numThreads; + int32_t reserved1; + int32_t reserved2; + }; + + auto info = lookupEventFlagInfo(eid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_EVFID); + return; + } + + Ps2EventFlagInfo *out = infoAddr ? reinterpret_cast(getMemPtr(rdram, infoAddr)) : nullptr; + if (!out) + { + setReturnS32(ctx, -1); + return; + } + + std::lock_guard lock(info->m); + out->attr = info->attr; + out->option = info->option; + out->initBits = info->initBits; + out->currBits = info->bits; + out->numThreads = info->waiters; + out->reserved1 = 0; + out->reserved2 = 0; + setReturnS32(ctx, 0); +} + +void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ReferEventFlagStatus(rdram, ctx, runtime); +} + +void SetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint16_t ticks = static_cast(getRegU32(ctx, 4) & 0xFFFFu); + uint32_t handler = getRegU32(ctx, 5); + uint32_t arg = getRegU32(ctx, 6); + + static int logCount = 0; + if (logCount < 5) + { + std::cout << "[SetAlarm] ticks=" << ticks + << " handler=0x" << std::hex << handler + << " arg=0x" << arg << std::dec << std::endl; + ++logCount; + } + + if (!runtime || !handler || !runtime->hasFunction(handler)) + { + setReturnS32(ctx, KE_ERROR); + return; + } + + auto info = std::make_shared(); + info->ticks = ticks; + info->handler = handler; + info->commonArg = arg; + info->gp = getRegU32(ctx, 28); + info->sp = getRegU32(ctx, 29); + info->rdram = rdram; + info->runtime = runtime; + info->dueAt = std::chrono::steady_clock::now() + alarmTicksToDuration(ticks); + + int alarmId = 0; + { + std::lock_guard lock(g_alarm_mutex); + alarmId = g_nextAlarmId++; + if (g_nextAlarmId <= 0) + { + g_nextAlarmId = 1; + } + info->id = alarmId; + g_alarms[alarmId] = info; + } + + ensureAlarmWorkerRunning(); + g_alarm_cv.notify_all(); + setReturnS32(ctx, alarmId); +} + +void iSetAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + SetAlarm(rdram, ctx, runtime); +} + +void CancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int alarmId = static_cast(getRegU32(ctx, 4)); + if (alarmId <= 0) + { + setReturnS32(ctx, KE_ERROR); + return; + } + + bool removed = false; + { + std::lock_guard lock(g_alarm_mutex); + removed = g_alarms.erase(alarmId) != 0; + } + + if (removed) + { + g_alarm_cv.notify_all(); + setReturnS32(ctx, KE_OK); + return; + } + + setReturnS32(ctx, KE_ERROR); +} + +void iCancelAlarm(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + CancelAlarm(rdram, ctx, runtime); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl new file mode 100644 index 0000000..d9b1243 --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_interrupt.inl @@ -0,0 +1,105 @@ +void EnableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void DisableIntc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void AddIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + IrqHandlerInfo info{}; + info.cause = getRegU32(ctx, 4); + info.handler = getRegU32(ctx, 5); + info.arg = getRegU32(ctx, 6); + info.enabled = true; + + const int handlerId = g_nextIntcHandlerId++; + g_intcHandlers[handlerId] = info; + setReturnS32(ctx, handlerId); +} + +void RemoveIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (handlerId > 0) + { + g_intcHandlers.erase(handlerId); + } + setReturnS32(ctx, 0); +} + +void AddDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + IrqHandlerInfo info{}; + info.cause = getRegU32(ctx, 4); + info.handler = getRegU32(ctx, 5); + info.arg = getRegU32(ctx, 6); + info.enabled = true; + + const int handlerId = g_nextDmacHandlerId++; + g_dmacHandlers[handlerId] = info; + setReturnS32(ctx, handlerId); +} + +void RemoveDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (handlerId > 0) + { + g_dmacHandlers.erase(handlerId); + } + setReturnS32(ctx, 0); +} + +void EnableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) + { + it->second.enabled = true; + } + setReturnS32(ctx, 0); +} + +void DisableIntcHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (auto it = g_intcHandlers.find(handlerId); it != g_intcHandlers.end()) + { + it->second.enabled = false; + } + setReturnS32(ctx, 0); +} + +void EnableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) + { + it->second.enabled = true; + } + setReturnS32(ctx, 0); +} + +void DisableDmacHandler(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int handlerId = static_cast(getRegU32(ctx, 5)); + if (auto it = g_dmacHandlers.find(handlerId); it != g_dmacHandlers.end()) + { + it->second.enabled = false; + } + setReturnS32(ctx, 0); +} + +void EnableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void DisableDmac(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl new file mode 100644 index 0000000..cefab11 --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_rpc.inl @@ -0,0 +1,1089 @@ +void SifStopModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const int32_t moduleId = static_cast(getRegU32(ctx, 4)); // $a0 + const uint32_t resultAddr = getRegU32(ctx, 7); // $a3 (int* result, optional) + + uint32_t refsLeft = 0; + const bool knownModule = trackSifModuleStop(moduleId, &refsLeft); + const int32_t ret = knownModule ? 0 : -1; + + if (resultAddr != 0) + { + int32_t *hostResult = reinterpret_cast(getMemPtr(rdram, resultAddr)); + if (hostResult) + { + *hostResult = knownModule ? 0 : -1; + } + } + + if (knownModule) + { + std::string modulePath; + { + 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()) + { + modulePath = it->second.path; + } + } + logSifModuleAction("stop", moduleId, modulePath, refsLeft); + } + + setReturnS32(ctx, ret); +} + +void SifLoadModule(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t pathAddr = getRegU32(ctx, 4); // $a0 + const std::string modulePath = readGuestCStringBounded(rdram, pathAddr, kMaxSifModulePathBytes); + if (modulePath.empty()) + { + setReturnS32(ctx, -1); + return; + } + + const int32_t moduleId = trackSifModuleLoad(modulePath); + 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", moduleId, modulePath, refs); + + setReturnS32(ctx, moduleId); +} + +void SifInitRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::lock_guard lock(g_rpc_mutex); + if (!g_rpc_initialized) + { + g_rpc_servers.clear(); + g_rpc_clients.clear(); + g_rpc_next_id = 1; + g_rpc_packet_index = 0; + g_rpc_server_index = 0; + g_rpc_active_queue = 0; + { + std::lock_guard dtxLock(g_dtx_rpc_mutex); + g_dtx_remote_by_id.clear(); + g_dtx_next_urpc_obj = kDtxUrpcObjBase; + } + g_rpc_initialized = true; + std::cout << "[SifInitRpc] Initialized" << std::endl; + } + setReturnS32(ctx, 0); +} + +void SifBindRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clientPtr = getRegU32(ctx, 4); + uint32_t rpcId = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + + t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + + if (!client) + { + setReturnS32(ctx, -1); + return; + } + + client->command = 0; + client->buf = 0; + client->cbuf = 0; + client->end_function = 0; + client->end_param = 0; + client->server = 0; + client->hdr.pkt_addr = 0; + client->hdr.sema_id = -1; + client->hdr.mode = mode; + + uint32_t serverPtr = 0; + { + std::lock_guard lock(g_rpc_mutex); + client->hdr.rpc_id = g_rpc_next_id++; + auto it = g_rpc_servers.find(rpcId); + if (it != g_rpc_servers.end()) + { + serverPtr = it->second.sd_ptr; + } + g_rpc_clients[clientPtr] = {}; + g_rpc_clients[clientPtr].sid = rpcId; + } + + if (!serverPtr) + { + // Allocate a dummy server so bind loops can proceed. + serverPtr = rpcAllocServerAddr(rdram); + if (serverPtr) + { + t_SifRpcServerData *dummy = reinterpret_cast(getMemPtr(rdram, serverPtr)); + if (dummy) + { + std::memset(dummy, 0, sizeof(*dummy)); + dummy->sid = static_cast(rpcId); + } + std::lock_guard lock(g_rpc_mutex); + g_rpc_servers[rpcId] = {rpcId, serverPtr}; + } + } + + if (serverPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, serverPtr)); + client->server = serverPtr; + client->buf = sd ? sd->buf : 0; + client->cbuf = sd ? sd->cbuf : 0; + } + else + { + client->server = 0; + client->buf = 0; + client->cbuf = 0; + } + + setReturnS32(ctx, 0); +} + +void SifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clientPtr = getRegU32(ctx, 4); + uint32_t rpcNum = getRegU32(ctx, 5); + uint32_t mode = getRegU32(ctx, 6); + uint32_t sendBuf = getRegU32(ctx, 7); + uint32_t sendSize = 0; + uint32_t recvBuf = 0; + uint32_t recvSize = 0; + uint32_t endFunc = 0; + uint32_t endParam = 0; + + // EE-side calls use extended arg registers: + // a0-a3 => r4-r7, arg5-arg8 => r8-r11, arg9 => stack + 0x0. + // Keep O32 stack-layout fallback for compatibility with other call sites. + uint32_t sp = getRegU32(ctx, 29); + sendSize = getRegU32(ctx, 8); + recvBuf = getRegU32(ctx, 9); + recvSize = getRegU32(ctx, 10); + endFunc = getRegU32(ctx, 11); + (void)readStackU32(rdram, sp, 0x0, endParam); + + if (sendSize == 0 && recvBuf == 0 && recvSize == 0 && endFunc == 0) + { + readStackU32(rdram, sp, 0x10, sendSize); + readStackU32(rdram, sp, 0x14, recvBuf); + readStackU32(rdram, sp, 0x18, recvSize); + readStackU32(rdram, sp, 0x1C, endFunc); + readStackU32(rdram, sp, 0x20, endParam); + } + + t_SifRpcClientData *client = reinterpret_cast(getMemPtr(rdram, clientPtr)); + + if (!client) + { + setReturnS32(ctx, -1); + return; + } + + client->command = rpcNum; + client->end_function = endFunc; + client->end_param = endParam; + client->hdr.mode = mode; + + { + std::lock_guard lock(g_rpc_mutex); + g_rpc_clients[clientPtr].busy = true; + g_rpc_clients[clientPtr].last_rpc = rpcNum; + uint32_t sid = g_rpc_clients[clientPtr].sid; + if (sid) + { + auto it = g_rpc_servers.find(sid); + if (it != g_rpc_servers.end()) + { + uint32_t mappedServer = it->second.sd_ptr; + if (mappedServer && client->server != mappedServer) + { + client->server = mappedServer; + } + } + } + } + + uint32_t sid = 0; + { + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it != g_rpc_clients.end()) + { + sid = it->second.sid; + } + } + + uint32_t serverPtr = client->server; + t_SifRpcServerData *sd = serverPtr ? reinterpret_cast(getMemPtr(rdram, serverPtr)) : nullptr; + + if (sd) + { + sd->client = clientPtr; + sd->pkt_addr = client->hdr.pkt_addr; + sd->rpc_number = rpcNum; + sd->size = static_cast(sendSize); + sd->recvbuf = recvBuf; + sd->rsize = static_cast(recvSize); + sd->rmode = ((mode & kSifRpcModeNowait) && endFunc == 0) ? 0 : 1; + sd->rid = 0; + } + + if (sd && sd->buf && sendBuf && sendSize > 0) + { + rpcCopyToRdram(rdram, sd->buf, sendBuf, sendSize); + } + + uint32_t resultPtr = 0; + bool handled = false; + + auto readRpcU32 = [&](uint32_t addr, uint32_t &out) -> bool + { + if (!addr) + { + return false; + } + const uint8_t *ptr = getConstMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(&out, ptr, sizeof(out)); + return true; + }; + + auto writeRpcU32 = [&](uint32_t addr, uint32_t value) -> bool + { + if (!addr) + { + return false; + } + uint8_t *ptr = getMemPtr(rdram, addr); + if (!ptr) + { + return false; + } + std::memcpy(ptr, &value, sizeof(value)); + return true; + }; + + const bool isDtxUrpc = (sid == kDtxRpcSid) && (rpcNum >= 0x400u) && (rpcNum < 0x500u); + uint32_t dtxUrpcCommand = isDtxUrpc ? (rpcNum & 0xFFu) : 0u; + uint32_t dtxUrpcFn = 0; + uint32_t dtxUrpcObj = 0; + uint32_t dtxUrpcSend0 = 0; + bool dtxUrpcDispatchAttempted = false; + bool dtxUrpcFallbackEmulated = false; + bool dtxUrpcFallbackCreate34 = false; + bool hasUrpcHandler = false; + if (isDtxUrpc) + { + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf, dtxUrpcSend0); + } + if (dtxUrpcCommand < 64u) + { + (void)readRpcU32(kDtxUrpcFnTableBase + (dtxUrpcCommand * 4u), dtxUrpcFn); + (void)readRpcU32(kDtxUrpcObjTableBase + (dtxUrpcCommand * 4u), dtxUrpcObj); + } + hasUrpcHandler = (dtxUrpcCommand < 64u) && (dtxUrpcFn != 0u); + } + const bool allowServerDispatch = !isDtxUrpc || hasUrpcHandler; + + if (sd && sd->func && (sid != kDtxRpcSid || isDtxUrpc) && allowServerDispatch) + { + dtxUrpcDispatchAttempted = dtxUrpcDispatchAttempted || isDtxUrpc; + handled = rpcInvokeFunction(rdram, ctx, runtime, sd->func, rpcNum, sd->buf, sendSize, 0, &resultPtr); + if (handled && resultPtr == 0 && sd->buf) + { + resultPtr = sd->buf; + } + if (handled && resultPtr == 0 && recvBuf) + { + resultPtr = recvBuf; + } + } + + if (!handled && isDtxUrpc && sendBuf && sendSize > 0) + { + // Only dispatch through dtx_rpc_func when a URPC handler is registered in the table. + // If the slot is empty, defer to the fallback emulation below. + if (hasUrpcHandler) + { + dtxUrpcDispatchAttempted = true; + handled = rpcInvokeFunction(rdram, ctx, runtime, 0x2fabc0u, rpcNum, sendBuf, sendSize, 0, &resultPtr); + if (handled && resultPtr == 0) + { + resultPtr = sendBuf; + } + } + } + + if (!handled && sid == kDtxRpcSid) + { + if (rpcNum == 2 && recvBuf && recvSize >= sizeof(uint32_t)) + { + uint32_t dtxId = 0; + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf, dtxId); + } + + uint32_t remoteHandle = 0; + { + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_remote_by_id.find(dtxId); + if (it != g_dtx_remote_by_id.end()) + { + remoteHandle = it->second; + } + if (!remoteHandle) + { + remoteHandle = rpcAllocServerAddr(rdram); + if (!remoteHandle) + { + remoteHandle = rpcAllocPacketAddr(rdram); + } + if (!remoteHandle) + { + remoteHandle = kRpcServerPoolBase + ((dtxId & 0xFFu) * kRpcServerStride); + } + g_dtx_remote_by_id[dtxId] = remoteHandle; + } + } + + (void)writeRpcU32(recvBuf, remoteHandle); + if (recvSize > sizeof(uint32_t)) + { + rpcZeroRdram(rdram, recvBuf + sizeof(uint32_t), recvSize - sizeof(uint32_t)); + } + handled = true; + resultPtr = recvBuf; + } + else if (rpcNum == 3) + { + uint32_t remoteHandle = 0; + if (sendBuf && sendSize >= sizeof(uint32_t) && readRpcU32(sendBuf, remoteHandle) && remoteHandle) + { + std::lock_guard lock(g_dtx_rpc_mutex); + for (auto it = g_dtx_remote_by_id.begin(); it != g_dtx_remote_by_id.end(); ++it) + { + if (it->second == remoteHandle) + { + g_dtx_remote_by_id.erase(it); + break; + } + } + } + if (recvBuf && recvSize > 0) + { + rpcZeroRdram(rdram, recvBuf, recvSize); + } + handled = true; + resultPtr = recvBuf; + } + else if (rpcNum >= 0x400 && rpcNum < 0x500) + { + dtxUrpcFallbackEmulated = true; + const uint32_t urpcCommand = rpcNum & 0xFFu; + uint32_t outWords[4] = {1u, 0u, 0u, 0u}; + uint32_t outWordCount = 1u; + + auto readSendWord = [&](uint32_t index, uint32_t &out) -> bool + { + const uint64_t byteOffset = static_cast(index) * sizeof(uint32_t); + if (!sendBuf || sendSize < (byteOffset + sizeof(uint32_t))) + { + return false; + } + return readRpcU32(sendBuf + static_cast(byteOffset), out); + }; + + switch (urpcCommand) + { + case 32u: // SJRMT_RBF_CREATE + case 33u: // SJRMT_MEM_CREATE + case 34u: // SJRMT_UNI_CREATE + { + uint32_t arg0 = 0; + uint32_t arg1 = 0; + uint32_t arg2 = 0; + (void)readSendWord(0u, arg0); + (void)readSendWord(1u, arg1); + (void)readSendWord(2u, arg2); + + uint32_t mode = 0; + uint32_t wkAddr = 0; + uint32_t wkSize = 0; + if (urpcCommand == 34u) + { + mode = arg0; + wkAddr = arg1; + wkSize = arg2; + dtxUrpcFallbackCreate34 = true; + } + else if (urpcCommand == 33u) + { + wkAddr = arg0; + wkSize = arg1; + } + else + { + wkAddr = arg0; + wkSize = (arg1 != 0u) ? arg1 : arg2; + } + + wkSize = dtxNormalizeSjrmtCapacity(wkSize); + + std::lock_guard lock(g_dtx_rpc_mutex); + const uint32_t handle = dtxAllocUrpcHandleLocked(); + DtxSjrmtState state{}; + state.handle = handle; + state.mode = mode; + state.wkAddr = wkAddr; + state.wkSize = wkSize; + state.readPos = 0u; + state.writePos = 0u; + state.roomBytes = wkSize; + state.dataBytes = 0u; + state.uuid0 = 0x53524D54u; // "SRMT" + state.uuid1 = handle; + state.uuid2 = wkAddr; + state.uuid3 = wkSize; + g_dtx_sjrmt_by_handle[handle] = state; + + outWords[0] = handle ? handle : 1u; + outWordCount = 1u; + break; + } + case 35u: // SJRMT_DESTROY + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + g_dtx_sjrmt_by_handle.erase(handle); + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 36u: // SJRMT_GET_UUID + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + outWords[0] = it->second.uuid0; + outWords[1] = it->second.uuid1; + outWords[2] = it->second.uuid2; + outWords[3] = it->second.uuid3; + } + else + { + outWords[0] = 0u; + outWords[1] = 0u; + outWords[2] = 0u; + outWords[3] = 0u; + } + outWordCount = 4u; + break; + } + case 37u: // SJRMT_RESET + { + uint32_t handle = 0; + (void)readSendWord(0u, handle); + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + const uint32_t cap = (it->second.wkSize == 0u) ? 0x4000u : it->second.wkSize; + it->second.readPos = 0u; + it->second.writePos = 0u; + it->second.roomBytes = cap; + it->second.dataBytes = 0u; + } + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 38u: // SJRMT_GET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t nbyte = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(2u, nbyte); + + uint32_t ptr = 0u; + uint32_t len = 0u; + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + + if (streamId == 0u) + { + len = std::min(nbyte, state.roomBytes); + ptr = state.wkAddr + (cap ? (state.writePos % cap) : 0u); + if (cap != 0u) + { + state.writePos = (state.writePos + len) % cap; + } + state.roomBytes -= len; + } + else if (streamId == 1u) + { + len = std::min(nbyte, state.dataBytes); + ptr = state.wkAddr + (cap ? (state.readPos % cap) : 0u); + if (cap != 0u) + { + state.readPos = (state.readPos + len) % cap; + } + state.dataBytes -= len; + } + } + + outWords[0] = ptr; + outWords[1] = len; + outWordCount = 2u; + break; + } + case 39u: // SJRMT_UNGET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t len = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(3u, len); + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (streamId == 0u) + { + const uint32_t delta = (cap == 0u) ? 0u : (len % cap); + if (cap != 0u) + { + state.writePos = (state.writePos + cap - delta) % cap; + } + state.roomBytes = std::min(cap, state.roomBytes + len); + } + else if (streamId == 1u) + { + const uint32_t delta = (cap == 0u) ? 0u : (len % cap); + if (cap != 0u) + { + state.readPos = (state.readPos + cap - delta) % cap; + } + state.dataBytes = std::min(cap, state.dataBytes + len); + } + } + + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 40u: // SJRMT_PUT_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t len = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(3u, len); + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + DtxSjrmtState &state = it->second; + const uint32_t cap = (state.wkSize == 0u) ? 0x4000u : state.wkSize; + if (streamId == 0u) + { + state.roomBytes = std::min(cap, state.roomBytes + len); + } + else if (streamId == 1u) + { + state.dataBytes = std::min(cap, state.dataBytes + len); + } + } + + outWords[0] = 1u; + outWordCount = 1u; + break; + } + case 41u: // SJRMT_GET_NUM_DATA + { + uint32_t handle = 0; + uint32_t streamId = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + outWords[0] = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; + } + else + { + outWords[0] = 0u; + } + outWordCount = 1u; + break; + } + case 42u: // SJRMT_IS_GET_CHUNK + { + uint32_t handle = 0; + uint32_t streamId = 0; + uint32_t nbyte = 0; + (void)readSendWord(0u, handle); + (void)readSendWord(1u, streamId); + (void)readSendWord(2u, nbyte); + + uint32_t available = 0u; + std::lock_guard lock(g_dtx_rpc_mutex); + auto it = g_dtx_sjrmt_by_handle.find(handle); + if (it != g_dtx_sjrmt_by_handle.end()) + { + available = (streamId == 0u) ? it->second.roomBytes : it->second.dataBytes; + } + outWords[0] = (available >= nbyte) ? 1u : 0u; + outWords[1] = available; + outWordCount = 2u; + break; + } + case 43u: // SJRMT_INIT + case 44u: // SJRMT_FINISH + { + outWords[0] = 1u; + outWordCount = 1u; + break; + } + default: + { + uint32_t urpcRet = 1u; + if (sendBuf && sendSize >= sizeof(uint32_t)) + { + (void)readRpcU32(sendBuf, urpcRet); + } + if (urpcCommand == 0u) + { + std::lock_guard lock(g_dtx_rpc_mutex); + urpcRet = dtxAllocUrpcHandleLocked(); + } + if (urpcRet == 0u) + { + urpcRet = 1u; + } + outWords[0] = urpcRet; + outWordCount = 1u; + break; + } + } + + if (recvBuf && recvSize > 0u) + { + const uint32_t recvWordCapacity = static_cast(recvSize / sizeof(uint32_t)); + const uint32_t wordsToWrite = std::min(outWordCount, recvWordCapacity); + for (uint32_t i = 0; i < wordsToWrite; ++i) + { + (void)writeRpcU32(recvBuf + (i * sizeof(uint32_t)), outWords[i]); + } + + // SJRMT_IsGetChunk callers read rbuf[1] even when nout==1. + if (urpcCommand == 42u && outWordCount > 1u) + { + (void)writeRpcU32(recvBuf + sizeof(uint32_t), outWords[1]); + } + + if (recvSize > (wordsToWrite * sizeof(uint32_t))) + { + rpcZeroRdram(rdram, recvBuf + (wordsToWrite * sizeof(uint32_t)), + recvSize - (wordsToWrite * sizeof(uint32_t))); + } + } + + handled = true; + resultPtr = recvBuf; + } + } + + if (recvBuf && recvSize > 0) + { + if (handled && resultPtr) + { + rpcCopyToRdram(rdram, recvBuf, resultPtr, recvSize); + } + else if (!handled && sendBuf && sendSize > 0) + { + size_t copySize = (sendSize < recvSize) ? sendSize : recvSize; + rpcCopyToRdram(rdram, recvBuf, sendBuf, copySize); + } + else if (!handled) + { + rpcZeroRdram(rdram, recvBuf, recvSize); + } + } + + if (isDtxUrpc) + { + static int dtxUrpcLogCount = 0; + if (dtxUrpcLogCount < 64) + { + uint32_t dtxUrpcRecv0 = 0; + if (recvBuf && recvSize >= sizeof(uint32_t)) + { + (void)readRpcU32(recvBuf, dtxUrpcRecv0); + } + std::cout << "[SifCallRpc:DTX] rpcNum=0x" << std::hex << rpcNum + << " cmd=0x" << dtxUrpcCommand + << " fn=0x" << dtxUrpcFn + << " obj=0x" << dtxUrpcObj + << " send0=0x" << dtxUrpcSend0 + << " recv0=0x" << dtxUrpcRecv0 + << " resultPtr=0x" << resultPtr + << " handled=" << std::dec << (handled ? 1 : 0) + << " dispatch=" << (dtxUrpcDispatchAttempted ? 1 : 0) + << " emu=" << (dtxUrpcFallbackEmulated ? 1 : 0) + << " emu34=" << (dtxUrpcFallbackCreate34 ? 1 : 0) + << std::endl; + ++dtxUrpcLogCount; + } + } + + if (endFunc) + { + rpcInvokeFunction(rdram, ctx, runtime, endFunc, endParam, 0, 0, 0, nullptr); + } + + static int logCount = 0; + if (logCount < 10) + { + std::cout << "[SifCallRpc] client=0x" << std::hex << clientPtr + << " sid=0x" << sid + << " rpcNum=0x" << rpcNum + << " mode=0x" << mode + << " sendBuf=0x" << sendBuf + << " recvBuf=0x" << recvBuf + << " recvSize=0x" << recvSize + << " size=" << std::dec << sendSize << std::endl; + ++logCount; + } + + { + std::lock_guard lock(g_rpc_mutex); + g_rpc_clients[clientPtr].busy = false; + } + + setReturnS32(ctx, 0); +} + +void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sdPtr = getRegU32(ctx, 4); + uint32_t sid = getRegU32(ctx, 5); + uint32_t func = getRegU32(ctx, 6); + uint32_t buf = getRegU32(ctx, 7); + // stack args: cfunc, cbuf, qd... + uint32_t sp = getRegU32(ctx, 29); + uint32_t cfunc = 0; + uint32_t cbuf = 0; + uint32_t qd = 0; + readStackU32(rdram, sp, 0x10, cfunc); + readStackU32(rdram, sp, 0x14, cbuf); + readStackU32(rdram, sp, 0x18, qd); + + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + if (!sd) + { + setReturnS32(ctx, -1); + return; + } + + sd->sid = static_cast(sid); + sd->func = func; + sd->buf = buf; + sd->size = 0; + sd->cfunc = cfunc; + sd->cbuf = cbuf; + sd->size2 = 0; + sd->client = 0; + sd->pkt_addr = 0; + sd->rpc_number = 0; + sd->recvbuf = 0; + sd->rsize = 0; + sd->rmode = 0; + sd->rid = 0; + sd->base = qd; + sd->link = 0; + sd->next = 0; + + if (qd) + { + t_SifRpcDataQueue *queue = reinterpret_cast(getMemPtr(rdram, qd)); + if (queue) + { + if (!queue->link) + { + queue->link = sdPtr; + } + else + { + uint32_t curPtr = queue->link; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (!cur->link) + { + cur->link = sdPtr; + break; + } + if (cur->link == sdPtr) + break; + curPtr = cur->link; + } + } + } + } + + { + std::lock_guard lock(g_rpc_mutex); + g_rpc_servers[sid] = {sid, sdPtr}; + for (auto &entry : g_rpc_clients) + { + if (entry.second.sid == sid) + { + t_SifRpcClientData *cd = reinterpret_cast(getMemPtr(rdram, entry.first)); + if (cd) + { + cd->server = sdPtr; + cd->buf = sd->buf; + cd->cbuf = sd->cbuf; + } + } + } + } + + std::cout << "[SifRegisterRpc] sid=0x" << std::hex << sid << " sd=0x" << sdPtr << std::dec << std::endl; + setReturnS32(ctx, 0); +} + +void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t clientPtr = getRegU32(ctx, 4); + std::lock_guard lock(g_rpc_mutex); + auto it = g_rpc_clients.find(clientPtr); + if (it == g_rpc_clients.end()) + { + setReturnS32(ctx, 0); + return; + } + setReturnS32(ctx, it->second.busy ? 1 : 0); +} + +void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t qdPtr = getRegU32(ctx, 4); + int threadId = static_cast(getRegU32(ctx, 5)); + + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + if (!qd) + { + setReturnS32(ctx, -1); + return; + } + + qd->thread_id = threadId; + qd->active = 0; + qd->link = 0; + qd->start = 0; + qd->end = 0; + qd->next = 0; + + { + std::lock_guard lock(g_rpc_mutex); + if (!g_rpc_active_queue) + { + g_rpc_active_queue = qdPtr; + } + else + { + uint32_t curPtr = g_rpc_active_queue; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + if (curPtr == qdPtr) + break; + t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (!cur->next) + { + cur->next = qdPtr; + break; + } + curPtr = cur->next; + } + } + } + + setReturnS32(ctx, 0); +} + +void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t qdPtr = getRegU32(ctx, 4); + if (!qdPtr) + { + setReturnU32(ctx, 0); + return; + } + + std::lock_guard lock(g_rpc_mutex); + if (!g_rpc_active_queue) + { + setReturnU32(ctx, 0); + return; + } + + if (g_rpc_active_queue == qdPtr) + { + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + g_rpc_active_queue = qd ? qd->next : 0; + setReturnU32(ctx, qdPtr); + return; + } + + uint32_t curPtr = g_rpc_active_queue; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcDataQueue *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (cur->next == qdPtr) + { + t_SifRpcDataQueue *rem = reinterpret_cast(getMemPtr(rdram, qdPtr)); + cur->next = rem ? rem->next : 0; + setReturnU32(ctx, qdPtr); + return; + } + curPtr = cur->next; + } + + setReturnU32(ctx, 0); +} + +void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t sdPtr = getRegU32(ctx, 4); + uint32_t qdPtr = getRegU32(ctx, 5); + + t_SifRpcDataQueue *qd = reinterpret_cast(getMemPtr(rdram, qdPtr)); + if (!qd || !sdPtr) + { + setReturnU32(ctx, 0); + return; + } + + if (qd->link == sdPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + qd->link = sd ? sd->link : 0; + if (sd) + sd->link = 0; + setReturnU32(ctx, sdPtr); + return; + } + + uint32_t curPtr = qd->link; + for (int guard = 0; guard < 1024 && curPtr; ++guard) + { + t_SifRpcServerData *cur = reinterpret_cast(getMemPtr(rdram, curPtr)); + if (!cur) + break; + if (cur->link == sdPtr) + { + t_SifRpcServerData *sd = reinterpret_cast(getMemPtr(rdram, sdPtr)); + cur->link = sd ? sd->link : 0; + if (sd) + sd->link = 0; + setReturnU32(ctx, sdPtr); + return; + } + curPtr = cur->link; + } + + setReturnU32(ctx, 0); +} + +void sceSifCallRpc(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + SifCallRpc(rdram, ctx, runtime); +} + +void sceSifSendCmd(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t cid = getRegU32(ctx, 4); + uint32_t packetAddr = getRegU32(ctx, 5); + uint32_t packetSize = getRegU32(ctx, 6); + uint32_t srcExtra = getRegU32(ctx, 7); + + uint32_t sp = getRegU32(ctx, 29); + uint32_t destExtra = 0; + uint32_t sizeExtra = 0; + readStackU32(rdram, sp, 0x10, destExtra); + readStackU32(rdram, sp, 0x14, sizeExtra); + + if (sizeExtra > 0 && srcExtra && destExtra) + { + rpcCopyToRdram(rdram, destExtra, srcExtra, sizeExtra); + } + + static int logCount = 0; + if (logCount < 5) + { + std::cout << "[sceSifSendCmd] cid=0x" << std::hex << cid + << " packet=0x" << packetAddr + << " psize=0x" << packetSize + << " extra=0x" << destExtra << std::dec << std::endl; + ++logCount; + } + + // Return non-zero on success. + setReturnS32(ctx, 1); +} + +void sceRpcGetPacket(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t queuePtr = getRegU32(ctx, 4); + setReturnS32(ctx, static_cast(queuePtr)); +} + diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl new file mode 100644 index 0000000..d9c9ad3 --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_system.inl @@ -0,0 +1,347 @@ +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 + + std::cout << "PS2 GsSetCrt: interlaced=" << interlaced + << ", videoMode=" << videoMode + << ", frameMode=" << frameMode << std::endl; +} + +void GsGetIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint64_t imr = 0; + if (runtime) + { + imr = runtime->memory().gs().imr; + } + + std::cout << "PS2 GsGetIMR: Returning IMR=0x" << std::hex << imr << std::dec << std::endl; + + setReturnU64(ctx, imr); // Return in $v0/$v1 +} + +void GsPutIMR(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint64_t newImr = getRegU32(ctx, 4) | ((uint64_t)getRegU32(ctx, 5) << 32); // $a0 = lower 32 bits, $a1 = upper 32 bits + uint64_t oldImr = 0; + if (runtime) + { + oldImr = runtime->memory().gs().imr; + runtime->memory().gs().imr = newImr; + } + std::cout << "PS2 GsPutIMR: Setting IMR=0x" << std::hex << newImr << std::dec << std::endl; + setReturnU64(ctx, oldImr); +} + +void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int mode = getRegU32(ctx, 4); // $a0 - video mode (various flags) + + std::cout << "PS2 GsSetVideoMode: mode=0x" << std::hex << mode << std::dec << std::endl; + + // 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_config_initialized = true; + } + + 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 + if (!rdram || bufferAddr == 0u) + { + setReturnS32(ctx, -1); + return; + } + + // Match buffer-based module loads to stable synthetic tags so module ID lookup remains deterministic. + const std::string moduleTag = makeSifModuleBufferTag(rdram, bufferAddr); + 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 = (v0 != 0u) ? v0 : 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); +} + +// 0x3C SetupThread: returns stack pointer (stack + stack_size) +// args: $a0 = stack base, $a1 = stack size, $a2 = gp, $a3 = entry point +void SetupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t stackBase = getRegU32(ctx, 4); + uint32_t stackSize = getRegU32(ctx, 5); + uint32_t sp = stackBase + stackSize; + setReturnS32(ctx, sp); +} + +// 0x3D SetupHeap: returns heap base/start pointer +void SetupHeap(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + const uint32_t heapBase = getRegU32(ctx, 4); // $a0 + const uint32_t heapSize = getRegU32(ctx, 5); // $a1 (optional size) + + if (runtime) + { + uint32_t heapLimit = PS2_RAM_SIZE; + if (heapSize != 0u && heapBase < PS2_RAM_SIZE) + { + const uint64_t candidateLimit = static_cast(heapBase) + static_cast(heapSize); + heapLimit = static_cast(std::min(candidateLimit, PS2_RAM_SIZE)); + } + runtime->configureGuestHeap(heapBase, heapLimit); + 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) +{ + if (runtime) + { + setReturnU32(ctx, runtime->guestHeapEnd()); + return; + } + + setReturnU32(ctx, getRegU32(ctx, 4)); +} + +// 0x5A 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); +} + +// 0x5B GetThreadTLS (stub): return 0 +void GetThreadTLS(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + auto info = ensureCurrentThreadInfo(ctx); + if (!info) + { + setReturnU32(ctx, 0); + return; + } + + if (info->tlsBase == 0) + { + info->tlsBase = allocTlsAddr(rdram); + } + + setReturnU32(ctx, info->tlsBase); +} + +// 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; + } + + int tid = g_currentThreadId; + { + std::lock_guard lock(g_exit_handler_mutex); + g_exit_handlers[tid].push_back({func, arg}); + } + + setReturnS32(ctx, 0); +} diff --git a/ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl new file mode 100644 index 0000000..f45356d --- /dev/null +++ b/ps2xRuntime/src/lib/syscalls/ps2_syscalls_thread.inl @@ -0,0 +1,764 @@ +static void applySuspendStatusLocked(ThreadInfo &info) +{ + if (info.waitType != TSW_NONE) + { + info.status = THS_WAITSUSPEND; + } + else + { + info.status = THS_SUSPEND; + } +} + +static void runExitHandlersForThread(int tid, uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + if (!runtime || !ctx) + return; + + std::vector handlers; + { + std::lock_guard lock(g_exit_handler_mutex); + auto it = g_exit_handlers.find(tid); + if (it == g_exit_handlers.end()) + return; + handlers = std::move(it->second); + g_exit_handlers.erase(it); + } + + for (const auto &handler : handlers) + { + if (!handler.func) + continue; + try + { + rpcInvokeFunction(rdram, ctx, runtime, handler.func, handler.arg, 0, 0, 0, nullptr); + } + catch (const ThreadExitException &) + { + // ignore + } + catch (const std::exception &) + { + } + } +} + +void FlushCache(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void ResetEE(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + std::cerr << "Syscall: ResetEE - Halting Execution (Not fully implemented)" << std::endl; + exit(0); // Should we exit or just halt the execution? +} + +void SetMemoryMode(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, 0); +} + +void CreateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + uint32_t paramAddr = getRegU32(ctx, 4); // $a0 points to ThreadParam + const uint32_t *param = reinterpret_cast(getConstMemPtr(rdram, paramAddr)); + + if (!param) + { + std::cerr << "CreateThread error: invalid ThreadParam address 0x" << std::hex << paramAddr << std::dec << std::endl; + setReturnS32(ctx, -1); + return; + } + + auto info = std::make_shared(); + info->attr = param[0]; + info->entry = param[1]; + info->stack = param[2]; + info->stackSize = param[3]; + + auto looksLikeGuestPtr = [](uint32_t v) -> bool + { + if (v == 0) + { + return true; + } + const uint32_t norm = v & 0x1FFFFFFFu; + return norm < PS2_RAM_SIZE && norm >= 0x10000u; + }; + + auto looksLikePriority = [](uint32_t v) -> bool + { + // Typical EE priorities are very small integers (1..127). + return v <= 0x400u; + }; + + const uint32_t gpA = param[4]; + const uint32_t prioA = param[5]; + const uint32_t gpB = param[5]; + const uint32_t prioB = param[4]; + + // Prefer the standard EE layout (gp at +0x10, priority at +0x14), + // but keep a fallback for callsites that used the swapped decode. + if (looksLikeGuestPtr(gpA) && looksLikePriority(prioA)) + { + info->gp = gpA; + info->priority = prioA; + } + else if (looksLikeGuestPtr(gpB) && looksLikePriority(prioB)) + { + info->gp = gpB; + info->priority = prioB; + } + else + { + info->gp = gpA; + info->priority = prioA; + } + + info->option = param[6]; + info->currentPriority = static_cast(info->priority); + + int id = 0; + { + std::lock_guard lock(g_thread_map_mutex); + id = g_nextThreadId++; + g_threads[id] = info; + } + + std::cout << "[CreateThread] id=" << id + << " entry=0x" << std::hex << info->entry + << " stack=0x" << info->stack + << " size=0x" << info->stackSize + << " gp=0x" << info->gp + << " prio=" << std::dec << info->priority << std::endl; + + setReturnS32(ctx, id); +} + +void DeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); // $a0 + auto info = lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + { + std::lock_guard lock(info->m); + if (info->status != THS_DORMANT) + { + setReturnS32(ctx, KE_NOT_WAIT); // for now + return; + } + } + + { + std::lock_guard lock(g_thread_map_mutex); + g_threads.erase(tid); + } + + setReturnS32(ctx, KE_OK); +} + +void StartThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); // $a0 = thread id + uint32_t arg = getRegU32(ctx, 5); // $a1 = user arg + + auto info = lookupThreadInfo(tid); + if (!info) + { + std::cerr << "StartThread error: unknown thread id " << tid << std::endl; + setReturnS32(ctx, -1); + return; + } + + { + std::lock_guard lock(info->m); + if (info->started) + { + setReturnS32(ctx, tid); // Already started + return; + } + + info->started = true; + info->status = THS_RUN; + info->arg = arg; + } + + if (!runtime->hasFunction(info->entry)) + { + std::cerr << "[StartThread] entry 0x" << std::hex << info->entry << std::dec << " is not registered" << std::endl; + setReturnS32(ctx, -1); + return; + } + + const uint32_t callerSp = getRegU32(ctx, 29); + const uint32_t callerGp = getRegU32(ctx, 28); + + { + std::lock_guard lock(info->m); + if (info->stack == 0 && info->stackSize != 0) + { + const uint32_t autoStack = runtime->guestMalloc(info->stackSize, 16u); + if (autoStack != 0) + { + info->stack = autoStack; + std::cout << "[StartThread] id=" << tid + << " auto-stack=0x" << std::hex << autoStack + << " size=0x" << info->stackSize << std::dec << std::endl; + } + } + + if (info->stack != 0 && info->stackSize == 0) + { + // Some games leave size zero in the thread param even though a stack + // buffer is supplied; use a conservative default instead of caller SP. + info->stackSize = 0x800u; + } + } + + g_activeThreads.fetch_add(1, std::memory_order_relaxed); + std::thread([=]() mutable + { + { + std::string name = "PS2Thread_" + std::to_string(tid); + ThreadNaming::SetCurrentThreadName(name); + } + R5900Context threadCtxCopy{}; + R5900Context *threadCtx = &threadCtxCopy; + + uint32_t threadSp = callerSp; + if (info->stack) + { + const uint32_t stackSize = (info->stackSize != 0) ? info->stackSize : 0x800u; + threadSp = (info->stack + stackSize) & ~0xFu; + } + uint32_t threadGp = info->gp; + const uint32_t normalizedGp = threadGp & 0x1FFFFFFFu; + if (threadGp == 0 || normalizedGp < 0x10000u || normalizedGp >= PS2_RAM_SIZE) + { + threadGp = callerGp; + } + + SET_GPR_U32(threadCtx, 29, threadSp); + SET_GPR_U32(threadCtx, 28, threadGp); + SET_GPR_U32(threadCtx, 4, info->arg); + SET_GPR_U32(threadCtx, 31, 0); + threadCtx->pc = info->entry; + + PS2Runtime::RecompiledFunction func = runtime->lookupFunction(info->entry); + g_currentThreadId = tid; + + std::cout << "[StartThread] id=" << tid + << " entry=0x" << std::hex << info->entry + << " sp=0x" << GPR_U32(threadCtx, 29) + << " gp=0x" << GPR_U32(threadCtx, 28) + << " arg=0x" << info->arg << std::dec << std::endl; + + bool exited = false; + try + { + func(rdram, threadCtx, runtime); + } + catch (const ThreadExitException &) + { + exited = true; + } + catch (const std::exception &e) + { + std::cerr << "[StartThread] id=" << tid << " exception: " << e.what() << std::endl; + } + + if (!exited) + { + std::cout << "[StartThread] id=" << tid << " returned (pc=0x" + << std::hex << threadCtx->pc << std::dec << ")" << std::endl; + } + + runExitHandlersForThread(tid, rdram, threadCtx, runtime); + + { + std::lock_guard lock(info->m); + info->started = false; + info->status = THS_DORMANT; + } + + g_activeThreads.fetch_sub(1, std::memory_order_relaxed); }) + .detach(); + + // for now report success to the caller. + setReturnS32(ctx, 0); +} + +void ExitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + runExitHandlersForThread(g_currentThreadId, rdram, ctx, runtime); + auto info = ensureCurrentThreadInfo(ctx); + if (info) + { + std::lock_guard lock(info->m); + info->terminated = true; + info->forceRelease = true; + info->status = THS_DORMANT; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; + } + if (info) + { + info->cv.notify_all(); + } + throw ThreadExitException(); +} + +void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = g_currentThreadId; + runExitHandlersForThread(tid, rdram, ctx, runtime); + auto info = ensureCurrentThreadInfo(ctx); + if (info) + { + std::lock_guard lock(info->m); + info->terminated = true; + info->forceRelease = true; + info->status = THS_DORMANT; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; + } + if (info) + { + info->cv.notify_all(); + } + { + std::lock_guard lock(g_thread_map_mutex); + g_threads.erase(tid); + } + throw ThreadExitException(); +} + +void TerminateThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, -1); + return; + } + + { + std::lock_guard lock(info->m); + info->terminated = true; + info->forceRelease = true; + info->status = THS_DORMANT; + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount = 0; + } + info->cv.notify_all(); + + if (tid == g_currentThreadId) + { + runExitHandlersForThread(tid, rdram, ctx, runtime); + throw ThreadExitException(); + } + setReturnS32(ctx, 0); +} + +void SuspendThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, -1); + return; + } + + { + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, -1); + return; + } + info->suspendCount++; + applySuspendStatusLocked(*info); + } + info->cv.notify_all(); + + if (tid == g_currentThreadId) + { + std::unique_lock lock(info->m); + info->cv.wait(lock, [&]() + { return info->suspendCount == 0 || info->terminated.load(); }); + if (info->terminated.load()) + { + throw ThreadExitException(); + } + info->status = THS_RUN; + } + + setReturnS32(ctx, 0); +} + +void ResumeThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, -1); + return; + } + + { + std::lock_guard lock(info->m); + if (info->suspendCount <= 0) + { + setReturnS32(ctx, -1); + return; + } + info->suspendCount--; + if (info->suspendCount == 0) + { + if (info->waitType != TSW_NONE) + { + info->status = THS_WAIT; + } + else + { + info->status = (tid == g_currentThreadId) ? THS_RUN : THS_READY; + } + } + } + info->cv.notify_all(); + setReturnS32(ctx, 0); +} + +void GetThreadId(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + setReturnS32(ctx, g_currentThreadId); +} + +void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + uint32_t statusAddr = getRegU32(ctx, 5); + + if (tid == 0) // TH_SELF + { + tid = g_currentThreadId; + } + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, -1); + return; + } + + ee_thread_status_t *status = reinterpret_cast(getMemPtr(rdram, statusAddr)); + if (!status) + { + setReturnS32(ctx, -1); + return; + } + + std::lock_guard lock(info->m); + status->status = info->status; + status->func = info->entry; + status->stack = info->stack; + status->stack_size = info->stackSize; + status->gp_reg = info->gp; + status->initial_priority = info->priority; + status->current_priority = info->currentPriority; + status->attr = info->attr; + status->option = info->option; + status->waitType = info->waitType; + status->waitId = info->waitId; + status->wakeupCount = info->wakeupCount; + setReturnS32(ctx, 0); +} + +void SleepThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + auto info = ensureCurrentThreadInfo(ctx); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + throwIfTerminated(info); + + int ret = 0; + std::unique_lock lock(info->m); + + if (info->wakeupCount > 0) + { + info->wakeupCount--; + info->status = THS_RUN; + info->waitType = TSW_NONE; + info->waitId = 0; + ret = 0; + } + else + { + info->status = THS_WAIT; + info->waitType = TSW_SLEEP; + info->waitId = 0; + info->forceRelease = false; + + info->cv.wait(lock, [&]() + { return info->wakeupCount > 0 || info->forceRelease.load() || info->terminated.load(); }); + + if (info->terminated.load()) + { + throw ThreadExitException(); + } + + info->status = THS_RUN; + info->waitType = TSW_NONE; + info->waitId = 0; + + if (info->forceRelease.load()) + { + info->forceRelease = false; + ret = KE_RELEASE_WAIT; + } + else + { + if (info->wakeupCount > 0) + info->wakeupCount--; + ret = 0; + } + } + + lock.unlock(); + waitWhileSuspended(info); + setReturnS32(ctx, ret); +} + +void WakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + { + std::lock_guard lock(info->m); + if (info->status == THS_DORMANT) + { + setReturnS32(ctx, KE_DORMANT); + return; + } + if (info->status == THS_WAIT && info->waitType == TSW_SLEEP) + { + if (info->suspendCount > 0) + { + info->status = THS_SUSPEND; + } + else + { + info->status = THS_READY; + } + info->waitType = TSW_NONE; + info->waitId = 0; + info->wakeupCount++; + info->cv.notify_one(); + } + else + { + info->wakeupCount++; + } + } + setReturnS32(ctx, 0); +} + +void iWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + WakeupThread(rdram, ctx, runtime); +} + +void CancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, -1); + return; + } + + int previous = 0; + { + std::lock_guard lock(info->m); + previous = info->wakeupCount; + info->wakeupCount = 0; + } + setReturnS32(ctx, previous); +} + +void iCancelWakeupThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } + + auto info = lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + int previous = 0; + { + std::lock_guard lock(info->m); + previous = info->wakeupCount; + info->wakeupCount = 0; + } + setReturnS32(ctx, previous); +} + +void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + int newPrio = static_cast(getRegU32(ctx, 5)); + + if (tid == 0) + tid = g_currentThreadId; + + auto info = (tid == g_currentThreadId) ? ensureCurrentThreadInfo(ctx) : lookupThreadInfo(tid); + if (info) + { + int oldPrio = info->currentPriority; + info->currentPriority = newPrio; + setReturnS32(ctx, oldPrio); // Return old priority? + } + else + { + setReturnS32(ctx, -1); + } +} + +void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + static int logCount = 0; + int prio = static_cast(getRegU32(ctx, 4)); + if (logCount < 16) + { + std::cout << "[RotateThreadReadyQueue] prio=" << prio << std::endl; + ++logCount; + } + if (prio >= 128) + { + setReturnS32(ctx, -1); + return; + } + setReturnS32(ctx, 0); +} + +void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + int tid = static_cast(getRegU32(ctx, 4)); + if (tid == 0) + { + setReturnS32(ctx, KE_ILLEGAL_THID); + return; + } + + auto info = lookupThreadInfo(tid); + if (!info) + { + setReturnS32(ctx, KE_UNKNOWN_THID); + return; + } + + bool wasWaiting = false; + int waitType = 0; + int waitId = 0; + + { + std::lock_guard lock(info->m); + if (info->status == THS_WAIT) + { + wasWaiting = true; + waitType = info->waitType; + waitId = info->waitId; + info->forceRelease = true; + info->waitType = TSW_NONE; + info->waitId = 0; + if (info->suspendCount > 0) + { + info->status = THS_SUSPEND; + } + else + { + info->status = THS_READY; + } + } + } + + if (!wasWaiting) + { + setReturnS32(ctx, KE_NOT_WAIT); + return; + } + + info->cv.notify_all(); + + if (waitType == TSW_SEMA) + { + auto sema = lookupSemaInfo(waitId); + if (sema) + { + sema->cv.notify_all(); + } + } + else if (waitType == TSW_EVENT) + { + auto eventFlag = lookupEventFlagInfo(waitId); + if (eventFlag) + { + eventFlag->cv.notify_all(); + } + } + setReturnS32(ctx, 0); +} + +void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx, PS2Runtime *runtime) +{ + ReleaseWaitThread(rdram, ctx, runtime); +} diff --git a/ps2xRuntime/src/runner/main.cpp b/ps2xRuntime/src/main.cpp similarity index 100% rename from ps2xRuntime/src/runner/main.cpp rename to ps2xRuntime/src/main.cpp diff --git a/ps2xTest/src/code_generator_tests.cpp b/ps2xTest/src/code_generator_tests.cpp index 900b251..21f68c4 100644 --- a/ps2xTest/src/code_generator_tests.cpp +++ b/ps2xTest/src/code_generator_tests.cpp @@ -1,6 +1,7 @@ #include "MiniTest.h" #include "ps2recomp/code_generator.h" #include "ps2recomp/instructions.h" +#include "ps2recomp/ps2_recompiler.h" #include "ps2recomp/types.h" #include #include @@ -612,7 +613,15 @@ void register_code_generator_tests() t.IsTrue(generated.find("switch (jumpTarget)") != std::string::npos, "JR $31 should emit switch for internal targets"); t.IsTrue(generated.find("case 0x1308u: goto label_1308;") != std::string::npos, "switch should include return address from internal JAL"); }); - + + tc.Run("resolveStubTarget allows leading underscore alias", [](TestCase &t) { + t.Equals(PS2Recompiler::resolveStubTarget("_rand"), StubTarget::Stub, + "_rand should resolve via rand stub alias"); + t.Equals(PS2Recompiler::resolveStubTarget("_GetThreadId"), StubTarget::Syscall, + "_GetThreadId should resolve via GetThreadId syscall alias"); + t.Equals(PS2Recompiler::resolveStubTarget("_DefinitelyNotARealCall"), StubTarget::Unknown, + "unknown names must still stay unknown"); + }); }); }