From 4b59d8d2c8a38f1f23a268e127cf5c9302a07229 Mon Sep 17 00:00:00 2001 From: Ran-j Date: Thu, 17 Apr 2025 01:57:13 -0300 Subject: [PATCH] feat: added runtime base stubs definitions --- ps2xRuntime/CMakeLists.txt | 17 + ps2xRuntime/include/ps2_runtime.h | 35 +- ps2xRuntime/include/ps2_stubs.h | 103 +--- ps2xRuntime/include/ps2_syscalls.h | 115 ++++ ps2xRuntime/src/ps2_memory.cpp | 30 +- ps2xRuntime/src/ps2_runtime.cpp | 58 +- ps2xRuntime/src/ps2_stubs.cpp | 954 ++++++++++++----------------- ps2xRuntime/src/ps2_syscalls.cpp | 499 +++++++++++++++ 8 files changed, 1133 insertions(+), 678 deletions(-) create mode 100644 ps2xRuntime/include/ps2_syscalls.h create mode 100644 ps2xRuntime/src/ps2_syscalls.cpp diff --git a/ps2xRuntime/CMakeLists.txt b/ps2xRuntime/CMakeLists.txt index 1757913..135f996 100644 --- a/ps2xRuntime/CMakeLists.txt +++ b/ps2xRuntime/CMakeLists.txt @@ -5,15 +5,32 @@ project(PS2Runtime VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +include(FetchContent) +set(FETCHCONTENT_QUIET FALSE) +set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) +set(BUILD_GAMES OFF CACHE BOOL "" FORCE) + +FetchContent_Declare( + raylib + GIT_REPOSITORY "https://github.com/raysan5/raylib.git" + GIT_TAG "5.5" # we will migrate to 4.2.0 later, trust me it will be better + GIT_PROGRESS TRUE +) +FetchContent_MakeAvailable(raylib) + add_library(ps2_runtime STATIC src/ps2_memory.cpp src/ps2_runtime.cpp + src/ps2_stubs.cpp + src/ps2_syscalls.cpp ) target_include_directories(ps2_runtime PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ) +target_link_libraries(ps2_runtime PRIVATE raylib) + install(TARGETS ps2_runtime LIBRARY DESTINATION lib ARCHIVE DESTINATION lib diff --git a/ps2xRuntime/include/ps2_runtime.h b/ps2xRuntime/include/ps2_runtime.h index 340bbed..3c2e300 100644 --- a/ps2xRuntime/include/ps2_runtime.h +++ b/ps2xRuntime/include/ps2_runtime.h @@ -8,6 +8,7 @@ #include #include // For SSE/AVX instructions +constexpr uint32_t PS2_RAM_MASK = 0xF0000000; constexpr uint32_t PS2_RAM_BASE = 0x00000000; constexpr uint32_t PS2_RAM_SIZE = 32 * 1024 * 1024; // 32MB constexpr uint32_t PS2_SCRATCHPAD_BASE = 0x70000000; @@ -131,8 +132,40 @@ struct R5900Context // Reset COP1 state fcr31 = 0; } + + ~R5900Context() = default; }; +inline uint32_t getRegU32(const R5900Context *ctx, int reg) +{ + // Check if reg is valid (0-31) + if (reg < 0 || reg > 31) + return 0; + return ctx->r[reg].m128i_u32[0]; +} + +inline void setReturnU32(R5900Context *ctx, uint32_t value) +{ + ctx->r[2] = _mm_set_epi32(0, 0, 0, value); // $v0 +} + +inline void setReturnS32(R5900Context *ctx, int32_t value) +{ + ctx->r[2] = _mm_set_epi32(0, 0, 0, value); // $v0 Sign extension handled by cast? TODO Check MIPS ABI. +} + +inline uint8_t *getMemPtr(uint8_t *rdram, uint32_t addr) +{ + constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1; + return rdram + (addr & PS2_RAM_MASK); +} + +inline const uint8_t *getConstMemPtr(uint8_t *rdram, uint32_t addr) +{ + constexpr uint32_t PS2_RAM_MASK = PS2_RAM_SIZE - 1; + return rdram + (addr & PS2_RAM_MASK); +} + // PS2 GS (Graphics Synthesizer) registers struct GSRegisters { @@ -299,10 +332,8 @@ private: PS2Memory m_memory; R5900Context m_cpuContext; - // Function table for recompiled code std::unordered_map m_functionTable; - // Currently loaded modules struct LoadedModule { std::string name; diff --git a/ps2xRuntime/include/ps2_stubs.h b/ps2xRuntime/include/ps2_stubs.h index 633a367..f8d0edf 100644 --- a/ps2xRuntime/include/ps2_stubs.h +++ b/ps2xRuntime/include/ps2_stubs.h @@ -2,11 +2,16 @@ #define PS2_STUBS_H #include "ps2_runtime.h" +#include -// Standard library implementation stubs namespace ps2_stubs { + // Memory operations + void malloc(uint8_t *rdram, R5900Context *ctx); + void free(uint8_t *rdram, R5900Context *ctx); + void calloc(uint8_t *rdram, R5900Context *ctx); + void realloc(uint8_t *rdram, R5900Context *ctx); void memcpy(uint8_t *rdram, R5900Context *ctx); void memset(uint8_t *rdram, R5900Context *ctx); void memmove(uint8_t *rdram, R5900Context *ctx); @@ -18,87 +23,41 @@ namespace ps2_stubs void strlen(uint8_t *rdram, R5900Context *ctx); void strcmp(uint8_t *rdram, R5900Context *ctx); void strncmp(uint8_t *rdram, R5900Context *ctx); + void strcat(uint8_t *rdram, R5900Context *ctx); + void strncat(uint8_t *rdram, R5900Context *ctx); + void strchr(uint8_t *rdram, R5900Context *ctx); + void strrchr(uint8_t *rdram, R5900Context *ctx); + void strstr(uint8_t *rdram, R5900Context *ctx); // I/O operations void printf(uint8_t *rdram, R5900Context *ctx); void sprintf(uint8_t *rdram, R5900Context *ctx); + void snprintf(uint8_t *rdram, R5900Context *ctx); void puts(uint8_t *rdram, R5900Context *ctx); - - // Memory allocation - void malloc(uint8_t *rdram, R5900Context *ctx); - void free(uint8_t *rdram, R5900Context *ctx); - void calloc(uint8_t *rdram, R5900Context *ctx); - void realloc(uint8_t *rdram, R5900Context *ctx); + void fopen(uint8_t *rdram, R5900Context *ctx); + void fclose(uint8_t *rdram, R5900Context *ctx); + void fread(uint8_t *rdram, R5900Context *ctx); + void fwrite(uint8_t *rdram, R5900Context *ctx); + void fprintf(uint8_t *rdram, R5900Context *ctx); + void fseek(uint8_t *rdram, R5900Context *ctx); + void ftell(uint8_t *rdram, R5900Context *ctx); + void fflush(uint8_t *rdram, R5900Context *ctx); // Math functions void sqrt(uint8_t *rdram, R5900Context *ctx); void sin(uint8_t *rdram, R5900Context *ctx); void cos(uint8_t *rdram, R5900Context *ctx); + void tan(uint8_t *rdram, R5900Context *ctx); void atan2(uint8_t *rdram, R5900Context *ctx); + void pow(uint8_t *rdram, R5900Context *ctx); + void exp(uint8_t *rdram, R5900Context *ctx); + void log(uint8_t *rdram, R5900Context *ctx); + void log10(uint8_t *rdram, R5900Context *ctx); + void ceil(uint8_t *rdram, R5900Context *ctx); + void floor(uint8_t *rdram, R5900Context *ctx); + void fabs(uint8_t *rdram, R5900Context *ctx); + + void TODO(uint8_t *rdram, R5900Context *ctx); } -// PS2 system call implementation stubs -namespace ps2_syscalls -{ - void FlushCache(uint8_t *rdram, R5900Context *ctx); - void ExitThread(uint8_t *rdram, R5900Context *ctx); - void SleepThread(uint8_t *rdram, R5900Context *ctx); - void CreateThread(uint8_t *rdram, R5900Context *ctx); - void StartThread(uint8_t *rdram, R5900Context *ctx); - void SifInitRpc(uint8_t *rdram, R5900Context *ctx); - void SifBindRpc(uint8_t *rdram, R5900Context *ctx); - void SifCallRpc(uint8_t *rdram, R5900Context *ctx); - void SetGsCrt(uint8_t *rdram, R5900Context *ctx); -} - -// MMI operation implementations -namespace ps2_mmi -{ - void PADDW(R5900Context *ctx, int rd, int rs, int rt); - void PSUBW(R5900Context *ctx, int rd, int rs, int rt); - void PCGTW(R5900Context *ctx, int rd, int rs, int rt); - void PMAXW(R5900Context *ctx, int rd, int rs, int rt); - void PADDH(R5900Context *ctx, int rd, int rs, int rt); - void PSUBH(R5900Context *ctx, int rd, int rs, int rt); - void PCGTH(R5900Context *ctx, int rd, int rs, int rt); - void PMAXH(R5900Context *ctx, int rd, int rs, int rt); - void PADDB(R5900Context *ctx, int rd, int rs, int rt); - void PSUBB(R5900Context *ctx, int rd, int rs, int rt); - void PCGTB(R5900Context *ctx, int rd, int rs, int rt); - - void PEXTLW(R5900Context *ctx, int rd, int rs, int rt); - void PEXTUW(R5900Context *ctx, int rd, int rs, int rt); - void PEXTLH(R5900Context *ctx, int rd, int rs, int rt); - void PEXTUH(R5900Context *ctx, int rd, int rs, int rt); - void PEXTLB(R5900Context *ctx, int rd, int rs, int rt); - void PEXTUB(R5900Context *ctx, int rd, int rs, int rt); - - void PMADDW(R5900Context *ctx, int rd, int rs, int rt); - void PMSUBW(R5900Context *ctx, int rd, int rs, int rt); - void PMADDH(R5900Context *ctx, int rd, int rs, int rt); - void PMSUBH(R5900Context *ctx, int rd, int rs, int rt); - - void PAND(R5900Context *ctx, int rd, int rs, int rt); - void POR(R5900Context *ctx, int rd, int rs, int rt); - void PXOR(R5900Context *ctx, int rd, int rs, int rt); - void PNOR(R5900Context *ctx, int rd, int rs, int rt); -} - -// Hardware register handlers -namespace ps2_hardware -{ - // DMA channel handlers - void handleDmacReg(uint32_t address, uint32_t value); - - // GS register handlers - void handleGsReg(uint32_t address, uint32_t value); - - // VIF register handlers - void handleVif0Reg(uint32_t address, uint32_t value); - void handleVif1Reg(uint32_t address, uint32_t value); - - void handleTimerReg(uint32_t address, uint32_t value); - void handleSPU2Reg(uint32_t address, uint32_t value); -} - -#endif // PS2_RUNTIME_H \ No newline at end of file +#endif // PS2_STUBS_H diff --git a/ps2xRuntime/include/ps2_syscalls.h b/ps2xRuntime/include/ps2_syscalls.h new file mode 100644 index 0000000..f4ac51f --- /dev/null +++ b/ps2xRuntime/include/ps2_syscalls.h @@ -0,0 +1,115 @@ +#ifndef PS2_SYSCALLS_H +#define PS2_SYSCALLS_H + +#include "ps2_runtime.h" + +#define PS2_FIO_O_RDONLY 0x0001 +#define PS2_FIO_O_WRONLY 0x0002 +#define PS2_FIO_O_RDWR 0x0003 +#define PS2_FIO_O_NBLOCK 0x0010 +#define PS2_FIO_O_APPEND 0x0100 +#define PS2_FIO_O_CREAT 0x0200 +#define PS2_FIO_O_TRUNC 0x0400 +#define PS2_FIO_O_EXCL 0x0800 +#define PS2_FIO_O_NOWAIT 0x8000 + +#define PS2_SEEK_SET 0 +#define PS2_SEEK_CUR 1 +#define PS2_SEEK_END 2 + +namespace ps2_syscalls +{ + void FlushCache(uint8_t *rdram, R5900Context *ctx); + void ResetEE(uint8_t *rdram, R5900Context *ctx); + void SetMemoryMode(uint8_t *rdram, R5900Context *ctx); + + void CreateThread(uint8_t *rdram, R5900Context *ctx); + void DeleteThread(uint8_t *rdram, R5900Context *ctx); + void StartThread(uint8_t *rdram, R5900Context *ctx); + void ExitThread(uint8_t *rdram, R5900Context *ctx); + void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx); + void TerminateThread(uint8_t *rdram, R5900Context *ctx); + void SuspendThread(uint8_t *rdram, R5900Context *ctx); + void ResumeThread(uint8_t *rdram, R5900Context *ctx); + void GetThreadId(uint8_t *rdram, R5900Context *ctx); + void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx); + void SleepThread(uint8_t *rdram, R5900Context *ctx); + void WakeupThread(uint8_t *rdram, R5900Context *ctx); + void iWakeupThread(uint8_t *rdram, R5900Context *ctx); + void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx); + void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx); + void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx); + void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx); + + void CreateSema(uint8_t *rdram, R5900Context *ctx); + void DeleteSema(uint8_t *rdram, R5900Context *ctx); + void SignalSema(uint8_t *rdram, R5900Context *ctx); + void iSignalSema(uint8_t *rdram, R5900Context *ctx); + void WaitSema(uint8_t *rdram, R5900Context *ctx); + void PollSema(uint8_t *rdram, R5900Context *ctx); + void iPollSema(uint8_t *rdram, R5900Context *ctx); + void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx); + void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx); + + void CreateEventFlag(uint8_t *rdram, R5900Context *ctx); + void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx); + void SetEventFlag(uint8_t *rdram, R5900Context *ctx); + void iSetEventFlag(uint8_t *rdram, R5900Context *ctx); + void ClearEventFlag(uint8_t *rdram, R5900Context *ctx); + void iClearEventFlag(uint8_t *rdram, R5900Context *ctx); + void WaitEventFlag(uint8_t *rdram, R5900Context *ctx); + void PollEventFlag(uint8_t *rdram, R5900Context *ctx); + void iPollEventFlag(uint8_t *rdram, R5900Context *ctx); + void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx); + void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx); + + void SetAlarm(uint8_t *rdram, R5900Context *ctx); + void iSetAlarm(uint8_t *rdram, R5900Context *ctx); + void CancelAlarm(uint8_t *rdram, R5900Context *ctx); + void iCancelAlarm(uint8_t *rdram, R5900Context *ctx); + + void EnableIntc(uint8_t *rdram, R5900Context *ctx); + void DisableIntc(uint8_t *rdram, R5900Context *ctx); + void EnableDmac(uint8_t *rdram, R5900Context *ctx); + void DisableDmac(uint8_t *rdram, R5900Context *ctx); + + void SifStopModule(uint8_t *rdram, R5900Context *ctx); + void SifLoadModule(uint8_t *rdram, R5900Context *ctx); + void SifInitRpc(uint8_t *rdram, R5900Context *ctx); + void SifBindRpc(uint8_t *rdram, R5900Context *ctx); + void SifCallRpc(uint8_t *rdram, R5900Context *ctx); + void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx); + void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx); + void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx); + void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx); + void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx); + + void fioOpen(uint8_t *rdram, R5900Context *ctx); + void fioClose(uint8_t *rdram, R5900Context *ctx); + void fioRead(uint8_t *rdram, R5900Context *ctx); + void fioWrite(uint8_t *rdram, R5900Context *ctx); + void fioLseek(uint8_t *rdram, R5900Context *ctx); + void fioMkdir(uint8_t *rdram, R5900Context *ctx); + void fioChdir(uint8_t *rdram, R5900Context *ctx); + void fioRmdir(uint8_t *rdram, R5900Context *ctx); + void fioGetstat(uint8_t *rdram, R5900Context *ctx); + void fioRemove(uint8_t *rdram, R5900Context *ctx); + + void GsSetCrt(uint8_t *rdram, R5900Context *ctx); + void GsGetIMR(uint8_t *rdram, R5900Context *ctx); + void GsPutIMR(uint8_t *rdram, R5900Context *ctx); + void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx); + + void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx); + void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx); + void GetRomName(uint8_t *rdram, R5900Context *ctx); + void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx); + void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx); + + void TODO(uint8_t *rdram, R5900Context *ctx); + + // Helper functions + void RenderFrame(uint8_t *rdram); +} + +#endif // PS2_SYSCALLS_H \ No newline at end of file diff --git a/ps2xRuntime/src/ps2_memory.cpp b/ps2xRuntime/src/ps2_memory.cpp index ca988f1..df7a224 100644 --- a/ps2xRuntime/src/ps2_memory.cpp +++ b/ps2xRuntime/src/ps2_memory.cpp @@ -47,12 +47,36 @@ bool PS2Memory::initialize(size_t ramSize) } std::memset(m_scratchpad, 0, PS2_SCRATCHPAD_SIZE); - // Initialize IO registers with default values - m_ioRegisters.clear(); - // Initialize TLB entries m_tlbEntries.clear(); + // Allocate IOP RAM + iop_ram = new uint8_t[2 * 1024 * 1024]; // 2MB + if (!iop_ram) + { + delete[] m_rdram; + delete[] m_scratchpad; + m_rdram = nullptr; + m_scratchpad = nullptr; + return false; + } + + // Initialize IOP RAM with zeros + std::memset(iop_ram, 0, 2 * 1024 * 1024); + + // Initialize I/O registers + m_ioRegisters.clear(); + + // Initialize GS registers + memset(&gs_regs, 0, sizeof(gs_regs)); + + // Initialize VIF registers + memset(&vif0_regs, 0, sizeof(vif0_regs)); + memset(&vif1_regs, 0, sizeof(vif1_regs)); + + // Initialize DMA registers + memset(dma_regs, 0, sizeof(dma_regs)); + return true; } catch (const std::exception &e) diff --git a/ps2xRuntime/src/ps2_runtime.cpp b/ps2xRuntime/src/ps2_runtime.cpp index 2f9361f..39242e4 100644 --- a/ps2xRuntime/src/ps2_runtime.cpp +++ b/ps2xRuntime/src/ps2_runtime.cpp @@ -3,12 +3,12 @@ #include #include #include - + #define ELF_MAGIC 0x464C457F // "\x7FELF" in little endian #define ET_EXEC 2 // Executable file - + #define EM_MIPS 8 // MIPS architecture - + struct ElfHeader { uint32_t magic; @@ -44,27 +44,27 @@ struct ProgramHeader uint32_t flags; uint32_t align; }; - + #define PT_LOAD 1 // Loadable segment PS2Runtime::PS2Runtime() -{ +{ std::memset(&m_cpuContext, 0, sizeof(m_cpuContext)); - + // R0 is always zero in MIPS m_cpuContext.r[0] = _mm_set1_epi32(0); // Stack pointer (SP) and global pointer (GP) will be set by the loaded ELF - + m_functionTable.clear(); - + m_loadedModules.clear(); } PS2Runtime::~PS2Runtime() -{ +{ m_loadedModules.clear(); - + m_functionTable.clear(); } @@ -75,7 +75,7 @@ bool PS2Runtime::initialize() std::cerr << "Failed to initialize PS2 memory" << std::endl; return false; } - + registerBuiltinStubs(); return true; @@ -83,15 +83,7 @@ bool PS2Runtime::initialize() void PS2Runtime::registerBuiltinStubs() { - // Register common PS2 library functions as stubs - - // Standard C library stubs - registerFunction(0xFFFFFFFF, [](uint8_t *rdram, R5900Context *ctx) - { std::cout << "Stub: printf called" << std::endl; }); - - // PS2-specific system call stubs - registerFunction(0xFFFFFFFE, [](uint8_t *rdram, R5900Context *ctx) - { std::cout << "Stub: FlushCache called with mode: " << ctx->r[4].m128i_u32[0] << std::endl; }); + // TODO } bool PS2Runtime::loadELF(const std::string &elfPath) @@ -103,28 +95,23 @@ bool PS2Runtime::loadELF(const std::string &elfPath) return false; } - // Read ELF header ElfHeader header; file.read(reinterpret_cast(&header), sizeof(header)); - // Check ELF magic number if (header.magic != ELF_MAGIC) { std::cerr << "Invalid ELF magic number" << std::endl; return false; } - // Check if it's a MIPS executable if (header.machine != EM_MIPS || header.type != ET_EXEC) { std::cerr << "Not a MIPS executable ELF file" << std::endl; return false; } - // Store entry point m_cpuContext.pc = header.entry; - // Read program headers and load segments for (uint16_t i = 0; i < header.phnum; i++) { ProgramHeader ph; @@ -132,7 +119,7 @@ bool PS2Runtime::loadELF(const std::string &elfPath) file.read(reinterpret_cast(&ph), sizeof(ph)); if (ph.type == PT_LOAD && ph.filesz > 0) - { + { std::cout << "Loading segment: 0x" << std::hex << ph.vaddr << " - 0x" << (ph.vaddr + ph.memsz) << " (size: 0x" << ph.memsz << ")" << std::dec << std::endl; @@ -149,7 +136,6 @@ bool PS2Runtime::loadELF(const std::string &elfPath) uint8_t *dest = m_memory.getRDRAM() + physAddr; std::memcpy(dest, buffer.data(), ph.filesz); - // Zero-initialize the rest (bss-like sections) if (ph.memsz > ph.filesz) { std::memset(dest + ph.filesz, 0, ph.memsz - ph.filesz); @@ -157,7 +143,6 @@ bool PS2Runtime::loadELF(const std::string &elfPath) } } - // Create a loaded module entry LoadedModule module; module.name = elfPath.substr(elfPath.find_last_of("/\\") + 1); module.baseAddress = 0x00100000; // Typical base address for PS2 executables @@ -182,22 +167,21 @@ PS2Runtime::RecompiledFunction PS2Runtime::lookupFunction(uint32_t address) { return it->second; } - + std::cerr << "Warning: Function at address 0x" << std::hex << address << std::dec << " not found" << std::endl; - - static RecompiledFunction defaultFunction = [](uint8_t* rdram, R5900Context* ctx) - { - std::cerr << "Error: Called unimplemented function at address 0x" << std::hex << ctx->pc << std::dec << std::endl; - }; + + static RecompiledFunction defaultFunction = [](uint8_t *rdram, R5900Context *ctx) + { + std::cerr << "Error: Called unimplemented function at address 0x" << std::hex << ctx->pc << std::dec << std::endl; + }; return defaultFunction; } void PS2Runtime::run() -{ +{ RecompiledFunction entryPoint = lookupFunction(m_cpuContext.pc); - // Set up initial CPU state m_cpuContext.r[4] = _mm_set1_epi32(0); // A0 = 0 (argc) m_cpuContext.r[5] = _mm_set1_epi32(0); // A1 = 0 (argv) m_cpuContext.r[29] = _mm_set1_epi32(0x02000000); // SP = top of RAM @@ -215,4 +199,4 @@ void PS2Runtime::run() { std::cerr << "Error during program execution: " << e.what() << std::endl; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/ps2xRuntime/src/ps2_stubs.cpp b/ps2xRuntime/src/ps2_stubs.cpp index 5ddac19..26e6947 100644 --- a/ps2xRuntime/src/ps2_stubs.cpp +++ b/ps2xRuntime/src/ps2_stubs.cpp @@ -1,616 +1,442 @@ - #include "ps2_stubs.h" +#include "ps2_runtime.h" +#include +#include +#include +#include +#include +#include +#include +#include - -// Stub implementations for standard library functions namespace ps2_stubs { - // Memory operations - void memcpy(uint8_t *rdram, R5900Context *ctx) + + void ps2_stubs::malloc(uint8_t *rdram, R5900Context *ctx) { - uint32_t dst = ctx->r[4].m128i_u32[0]; // a0 = destination - uint32_t src = ctx->r[5].m128i_u32[0]; // a1 = source - uint32_t size = ctx->r[6].m128i_u32[0]; // a2 = size - - uint32_t physDst = dst & 0x1FFFFFFF; - uint32_t physSrc = src & 0x1FFFFFFF; - - std::memcpy(rdram + physDst, rdram + physSrc, size); - - ctx->r[2] = _mm_set1_epi32(dst); + // TODO } - void memset(uint8_t *rdram, R5900Context *ctx) + void ps2_stubs::free(uint8_t *rdram, R5900Context *ctx) { - uint32_t dst = ctx->r[4].m128i_u32[0]; // a0 = destination - uint32_t value = ctx->r[5].m128i_u32[0] & 0xFF; // a1 = fill value (byte) - uint32_t size = ctx->r[6].m128i_u32[0]; // a2 = size - - uint32_t physDst = dst & 0x1FFFFFFF; - - std::memset(rdram + physDst, value, size); - - ctx->r[2] = _mm_set1_epi32(dst); + // TODO } - void memmove(uint8_t *rdram, R5900Context *ctx) + void ps2_stubs::calloc(uint8_t *rdram, R5900Context *ctx) { - uint32_t dst = ctx->r[4].m128i_u32[0]; // a0 = destination - uint32_t src = ctx->r[5].m128i_u32[0]; // a1 = source - uint32_t size = ctx->r[6].m128i_u32[0]; // a2 = size - - uint32_t physDst = dst & 0x1FFFFFFF; - uint32_t physSrc = src & 0x1FFFFFFF; - - std::memmove(rdram + physDst, rdram + physSrc, size); - - ctx->r[2] = _mm_set1_epi32(dst); + // TODO } - void memcmp(uint8_t *rdram, R5900Context *ctx) + void ps2_stubs::realloc(uint8_t *rdram, R5900Context *ctx) { - uint32_t ptr1 = ctx->r[4].m128i_u32[0]; // a0 = first buffer - uint32_t ptr2 = ctx->r[5].m128i_u32[0]; // a1 = second buffer - uint32_t size = ctx->r[6].m128i_u32[0]; // a2 = size - - uint32_t phys1 = ptr1 & 0x1FFFFFFF; - uint32_t phys2 = ptr2 & 0x1FFFFFFF; - - int result = std::memcmp(rdram + phys1, rdram + phys2, size); - - ctx->r[2] = _mm_set1_epi32(result); + // TODO } - // String operations - void strcpy(uint8_t *rdram, R5900Context *ctx) + void ps2_stubs::memcpy(uint8_t *rdram, R5900Context *ctx) { - uint32_t dst = ctx->r[4].m128i_u32[0]; // a0 = destination - uint32_t src = ctx->r[5].m128i_u32[0]; // a1 = source + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + size_t size = getRegU32(ctx, 6); // $a2 - uint32_t physDst = dst & 0x1FFFFFFF; - uint32_t physSrc = src & 0x1FFFFFFF; + uint8_t *hostDest = getMemPtr(rdram, destAddr); + const uint8_t *hostSrc = getConstMemPtr(rdram, srcAddr); - char *destStr = reinterpret_cast(rdram + physDst); - const char *srcStr = reinterpret_cast(rdram + physSrc); - - std::strcpy(destStr, srcStr); - - ctx->r[2] = _mm_set1_epi32(dst); - } - - void strncpy(uint8_t *rdram, R5900Context *ctx) - { - uint32_t dst = ctx->r[4].m128i_u32[0]; // a0 = destination - uint32_t src = ctx->r[5].m128i_u32[0]; // a1 = source - uint32_t n = ctx->r[6].m128i_u32[0]; // a2 = max length - - uint32_t physDst = dst & 0x1FFFFFFF; - uint32_t physSrc = src & 0x1FFFFFFF; - - char *destStr = reinterpret_cast(rdram + physDst); - const char *srcStr = reinterpret_cast(rdram + physSrc); - - std::strncpy(destStr, srcStr, n); - - ctx->r[2] = _mm_set1_epi32(dst); - } - - void strlen(uint8_t *rdram, R5900Context *ctx) - { - uint32_t str = ctx->r[4].m128i_u32[0]; // a0 = string - - uint32_t physStr = str & 0x1FFFFFFF; - const char *strPtr = reinterpret_cast(rdram + physStr); - - size_t length = std::strlen(strPtr); - - ctx->r[2] = _mm_set1_epi32(static_cast(length)); - } - - void strcmp(uint8_t *rdram, R5900Context *ctx) - { - uint32_t str1 = ctx->r[4].m128i_u32[0]; // a0 = first string - uint32_t str2 = ctx->r[5].m128i_u32[0]; // a1 = second string - - uint32_t phys1 = str1 & 0x1FFFFFFF; - uint32_t phys2 = str2 & 0x1FFFFFFF; - - const char *str1Ptr = reinterpret_cast(rdram + phys1); - const char *str2Ptr = reinterpret_cast(rdram + phys2); - - int result = std::strcmp(str1Ptr, str2Ptr); - - ctx->r[2] = _mm_set1_epi32(result); - } - - void strncmp(uint8_t *rdram, R5900Context *ctx) - { - uint32_t str1 = ctx->r[4].m128i_u32[0]; // a0 = first string - uint32_t str2 = ctx->r[5].m128i_u32[0]; // a1 = second string - uint32_t n = ctx->r[6].m128i_u32[0]; // a2 = max length - - uint32_t phys1 = str1 & 0x1FFFFFFF; - uint32_t phys2 = str2 & 0x1FFFFFFF; - - const char *str1Ptr = reinterpret_cast(rdram + phys1); - const char *str2Ptr = reinterpret_cast(rdram + phys2); - - int result = std::strncmp(str1Ptr, str2Ptr, n); - - ctx->r[2] = _mm_set1_epi32(result); - } - - // I/O operations - void printf(uint8_t *rdram, R5900Context *ctx) - { - uint32_t fmtAddr = ctx->r[4].m128i_u32[0]; // a0 = format string - - uint32_t physAddr = fmtAddr & 0x1FFFFFFF; - const char *fmt = reinterpret_cast(rdram + physAddr); - - // Simple implementation - just print the format string TODO we would parse the format and handle arguments - std::cout << "printf: " << fmt << std::endl; - - ctx->r[2] = _mm_set1_epi32(1); - } - - void sprintf(uint8_t *rdram, R5900Context *ctx) - { - uint32_t destAddr = ctx->r[4].m128i_u32[0]; // a0 = destination buffer - uint32_t fmtAddr = ctx->r[5].m128i_u32[0]; // a1 = format string - - uint32_t physDest = destAddr & 0x1FFFFFFF; - uint32_t physFmt = fmtAddr & 0x1FFFFFFF; - - char *destStr = reinterpret_cast(rdram + physDest); - const char *fmt = reinterpret_cast(rdram + physFmt); - - std::strcpy(destStr, fmt); - - size_t len = std::strlen(fmt); - ctx->r[2] = _mm_set1_epi32(static_cast(len)); - } - - void puts(uint8_t *rdram, R5900Context *ctx) - { - uint32_t strAddr = ctx->r[4].m128i_u32[0]; // a0 = string - - uint32_t physAddr = strAddr & 0x1FFFFFFF; - const char *str = reinterpret_cast(rdram + physAddr); - - std::cout << str << std::endl; - - // Return success (1) in v0 - ctx->r[2] = _mm_set1_epi32(1); - } - - // Memory allocation TODO need a proper PS2 heap implementation - void malloc(uint8_t *rdram, R5900Context *ctx) - { - uint32_t size = ctx->r[4].m128i_u32[0]; // a0 = size - - // Simple implementation - allocate from top of memory - - static uint32_t heapTop = 0x01000000; // Example heap start - - uint32_t allocAddr = heapTop; - heapTop += ((size + 15) & ~15); // Align to 16 bytes - - ctx->r[2] = _mm_set1_epi32(allocAddr); - } - - void free(uint8_t *rdram, R5900Context *ctx) - { - uint32_t ptr = ctx->r[4].m128i_u32[0]; // a0 = pointer - - // Simple implementation - do nothing - } - - void calloc(uint8_t *rdram, R5900Context *ctx) - { - uint32_t nmemb = ctx->r[4].m128i_u32[0]; // a0 = number of elements - uint32_t size = ctx->r[5].m128i_u32[0]; // a1 = size of each element - - uint32_t totalSize = nmemb * size; - - // Call malloc - ctx->r[4] = _mm_set1_epi32(totalSize); - malloc(rdram, ctx); - - // Zero the memory - uint32_t allocAddr = ctx->r[2].m128i_u32[0]; - uint32_t physAddr = allocAddr & 0x1FFFFFFF; - std::memset(rdram + physAddr, 0, totalSize); - - // Return value already in v0 from malloc - } - - void realloc(uint8_t *rdram, R5900Context *ctx) - { - uint32_t ptr = ctx->r[4].m128i_u32[0]; // a0 = pointer - uint32_t size = ctx->r[5].m128i_u32[0]; // a1 = new size - - // Simple implementation - allocate new block and copy - - if (ptr == 0) + if (hostDest && hostSrc) { - ctx->r[4] = _mm_set1_epi32(size); - malloc(rdram, ctx); - return; + ::memcpy(hostDest, hostSrc, size); + } + 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; } - // Allocate new block - uint32_t oldPtr = ptr; - ctx->r[4] = _mm_set1_epi32(size); - malloc(rdram, ctx); - uint32_t newPtr = ctx->r[2].m128i_u32[0]; - - // Copy data from old to new - uint32_t physOld = oldPtr & 0x1FFFFFFF; - uint32_t physNew = newPtr & 0x1FFFFFFF; - std::memcpy(rdram + physNew, rdram + physOld, size); - - // Return value already in v0 + ctx->r[2] = ctx->r[4]; // Return dest pointer ($v0 = $a0) } - // Math functions - void sqrt(uint8_t *rdram, R5900Context *ctx) + void ps2_stubs::memset(uint8_t *rdram, R5900Context *ctx) { - // Assuming the argument is in the first FPU register (f12) - float arg = ctx->f[12]; - float result = std::sqrt(arg); + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + int value = (int)(getRegU32(ctx, 5) & 0xFF); // $a1 (char value) + uint32_t size = getRegU32(ctx, 6); // $a2 - // Return result in f0 - ctx->f[0] = result; - } + uint8_t *hostDest = getMemPtr(rdram, destAddr); - void sin(uint8_t *rdram, R5900Context *ctx) - { - // Assuming the argument is in the first FPU register (f12) - float arg = ctx->f[12]; - float result = std::sin(arg); - - // Return result in f0 - ctx->f[0] = result; - } - - void cos(uint8_t *rdram, R5900Context *ctx) - { - // Assuming the argument is in the first FPU register (f12) - float arg = ctx->f[12]; - float result = std::cos(arg); - - // Return result in f0 - ctx->f[0] = result; - } - - void atan2(uint8_t *rdram, R5900Context *ctx) - { - // Assuming the arguments are in f12 and f13 - float y = ctx->f[12]; - float x = ctx->f[13]; - float result = std::atan2(y, x); - - // Return result in f0 - ctx->f[0] = result; - } -} - -// PS2 system call implementation stubs -namespace ps2_syscalls -{ - void FlushCache(uint8_t *rdram, R5900Context *ctx) - { - uint32_t operation = ctx->r[4].m128i_u32[0]; // a0 = operation - - std::cout << "FlushCache called with operation: " << operation << std::endl; - - // Operation values: - // 0 = Instruction cache - // 1 = Data cache - // 2 = Both - } - - void ExitThread(uint8_t *rdram, R5900Context *ctx) - { - uint32_t exitCode = ctx->r[4].m128i_u32[0]; // a0 = exit code - - std::cout << "ExitThread called with exit code: " << exitCode << std::endl; - - // This would terminate the current thread - } - - void SleepThread(uint8_t *rdram, R5900Context *ctx) - { - std::cout << "SleepThread called" << std::endl; - - // This would suspend the current thread - } - - void CreateThread(uint8_t *rdram, R5900Context *ctx) - { - uint32_t threadParam = ctx->r[4].m128i_u32[0]; // a0 = thread parameter - - // We need to extract all thread creation parameters - std::cout << "CreateThread called with parameter: " << std::hex << threadParam << std::dec << std::endl; - - // Return a dummy thread ID - ctx->r[2] = _mm_set1_epi32(1); - } - - void StartThread(uint8_t *rdram, R5900Context *ctx) - { - uint32_t threadId = ctx->r[4].m128i_u32[0]; // a0 = thread ID - uint32_t priority = ctx->r[5].m128i_u32[0]; // a1 = priority - - std::cout << "StartThread called with ID: " << threadId << ", priority: " << priority << std::endl; - - // Return success (0) - ctx->r[2] = _mm_set1_epi32(0); - } - - void SifInitRpc(uint8_t *rdram, R5900Context *ctx) - { - uint32_t mode = ctx->r[4].m128i_u32[0]; // a0 = mode - - std::cout << "SifInitRpc called with mode: " << mode << std::endl; - - // Return success (0) - ctx->r[2] = _mm_set1_epi32(0); - } - - void SifBindRpc(uint8_t *rdram, R5900Context *ctx) - { - uint32_t clientPtr = ctx->r[4].m128i_u32[0]; // a0 = client pointer - uint32_t rpcId = ctx->r[5].m128i_u32[0]; // a1 = RPC ID - uint32_t mode = ctx->r[6].m128i_u32[0]; // a2 = mode - - std::cout << "SifBindRpc called with client: " << std::hex << clientPtr - << ", RPC ID: " << rpcId << ", mode: " << mode << std::dec << std::endl; - - // Return success (0) - ctx->r[2] = _mm_set1_epi32(0); - } - - void SifCallRpc(uint8_t *rdram, R5900Context *ctx) - { - uint32_t clientPtr = ctx->r[4].m128i_u32[0]; // a0 = client pointer - uint32_t command = ctx->r[5].m128i_u32[0]; // a1 = command - uint32_t mode = ctx->r[6].m128i_u32[0]; // a2 = mode - - std::cout << "SifCallRpc called with client: " << std::hex << clientPtr - << ", command: " << command << ", mode: " << mode << std::dec << std::endl; - - // Return success (0) - ctx->r[2] = _mm_set1_epi32(0); - } - - void SetGsCrt(uint8_t *rdram, R5900Context *ctx) - { - uint32_t interlaced = ctx->r[4].m128i_u32[0]; // a0 = interlaced - uint32_t mode = ctx->r[5].m128i_u32[0]; // a1 = mode (NTSC/PAL) - uint32_t field = ctx->r[6].m128i_u32[0]; // a2 = field - - std::cout << "SetGsCrt called with interlaced: " << interlaced - << ", mode: " << mode << ", field: " << field << std::endl; - - // No return value - } -} - -// MMI operation implementations -namespace ps2_mmi -{ - void PADDW(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_add_epi32(ctx->r[rs], ctx->r[rt]); - } - - void PSUBW(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_sub_epi32(ctx->r[rs], ctx->r[rt]); - } - - void PCGTW(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_cmpgt_epi32(ctx->r[rs], ctx->r[rt]); - } - - void PMAXW(R5900Context *ctx, int rd, int rs, int rt) - { - // we could use _mm_max_epi32 but is SSE4.1 - __m128i mask = _mm_cmpgt_epi32(ctx->r[rs], ctx->r[rt]); - ctx->r[rd] = _mm_or_si128( - _mm_and_si128(mask, ctx->r[rs]), - _mm_andnot_si128(mask, ctx->r[rt])); - } - - void PADDH(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_add_epi16(ctx->r[rs], ctx->r[rt]); - } - - void PSUBH(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_sub_epi16(ctx->r[rs], ctx->r[rt]); - } - - void PCGTH(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_cmpgt_epi16(ctx->r[rs], ctx->r[rt]); - } - - void PMAXH(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_max_epi16(ctx->r[rs], ctx->r[rt]); - } - - void PADDB(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_add_epi8(ctx->r[rs], ctx->r[rt]); - } - - void PSUBB(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_sub_epi8(ctx->r[rs], ctx->r[rt]); - } - - void PCGTB(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_cmpgt_epi8(ctx->r[rs], ctx->r[rt]); - } - - void PEXTLW(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_unpacklo_epi32(ctx->r[rt], ctx->r[rs]); - } - - void PEXTUW(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_unpackhi_epi32(ctx->r[rt], ctx->r[rs]); - } - - void PEXTLH(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_unpacklo_epi16(ctx->r[rt], ctx->r[rs]); - } - - void PEXTUH(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_unpackhi_epi16(ctx->r[rt], ctx->r[rs]); - } - - void PEXTLB(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_unpacklo_epi8(ctx->r[rt], ctx->r[rs]); - } - - void PEXTUB(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_unpackhi_epi8(ctx->r[rt], ctx->r[rs]); - } - - void PMADDW(R5900Context *ctx, int rd, int rs, int rt) - { - // We need to compute (rs * rt) + (HI:LO) and store in both rd and HI:LO - // This is a simplification as the actual MMI operation is more complex - - // Just do vector multiplication - __m128i product = _mm_mullo_epi32(ctx->r[rs], ctx->r[rt]); - - // Add the current HI:LO values - // This is a simplified version - __m128i hiLo = _mm_set_epi32(0, ctx->hi, 0, ctx->lo); - __m128i result = _mm_add_epi32(product, hiLo); - - // Set result and update HI:LO - ctx->r[rd] = result; - ctx->lo = _mm_extract_epi32(result, 0); - ctx->hi = _mm_extract_epi32(result, 1); - } - - void PMSUBW(R5900Context *ctx, int rd, int rs, int rt) - { - // Similar to PMADDW but subtracts - __m128i product = _mm_mullo_epi32(ctx->r[rs], ctx->r[rt]); - - __m128i hiLo = _mm_set_epi32(0, ctx->hi, 0, ctx->lo); - __m128i result = _mm_sub_epi32(hiLo, product); - - ctx->r[rd] = result; - ctx->lo = _mm_extract_epi32(result, 0); - ctx->hi = _mm_extract_epi32(result, 1); - } - - void PAND(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_and_si128(ctx->r[rs], ctx->r[rt]); - } - - void POR(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_or_si128(ctx->r[rs], ctx->r[rt]); - } - - void PXOR(R5900Context *ctx, int rd, int rs, int rt) - { - ctx->r[rd] = _mm_xor_si128(ctx->r[rs], ctx->r[rt]); - } - - void PNOR(R5900Context *ctx, int rd, int rs, int rt) - { - __m128i orResult = _mm_or_si128(ctx->r[rs], ctx->r[rt]); - ctx->r[rd] = _mm_xor_si128(orResult, _mm_set1_epi32(0xFFFFFFFF)); - } -} - -// Hardware register handlers -namespace ps2_hardware -{ - void handleDmacReg(uint32_t address, uint32_t value) - { - uint32_t channel = (address >> 8) & 0xF; - uint32_t reg = address & 0xFF; - - std::cout << "DMA channel " << channel << " register " << std::hex << reg - << " written with value " << value << std::dec << std::endl; - - // Check if this is the start bit in the channel control register - if (reg == 0 && (value & 0x100)) + if (hostDest) { - std::cout << "Starting DMA transfer on channel " << channel << std::endl; - - // Initiate DMA transfer here + ::memset(hostDest, value, size); } + else + { + std::cerr << "memset error: Invalid address provided." << std::endl; + } + + ctx->r[2] = ctx->r[4]; // Return dest pointer ($v0 = $a0) } - // GS register handlers - void handleGsReg(uint32_t address, uint32_t value) + void ps2_stubs::memmove(uint8_t *rdram, R5900Context *ctx) { - std::cout << "GS register " << std::hex << address << " written with value " - << value << std::dec << std::endl; - - // Update GS state and possibly trigger rendering + // TODO } - // VIF register handlers - void handleVif0Reg(uint32_t address, uint32_t value) + void ps2_stubs::memcmp(uint8_t *rdram, R5900Context *ctx) { - uint32_t reg = address & 0xFF; + uint32_t ptr1Addr = getRegU32(ctx, 4); // $a0 + uint32_t ptr2Addr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 - std::cout << "VIF0 register " << std::hex << reg << " written with value " - << value << std::dec << std::endl; + const uint8_t *hostPtr1 = getConstMemPtr(rdram, ptr1Addr); + const uint8_t *hostPtr2 = getConstMemPtr(rdram, ptr2Addr); + int result = 0; // Default if pointers are bad - // Handle VIF0 operations + if (hostPtr1 && hostPtr2) + { + result = ::memcmp(hostPtr1, hostPtr2, size); + } + else + { + std::cerr << "memcmp error: Invalid address provided." << std::endl; + result = 1; + } + setReturnS32(ctx, result); } - void handleVif1Reg(uint32_t address, uint32_t value) + void ps2_stubs::strcpy(uint8_t *rdram, R5900Context *ctx) { - uint32_t reg = address & 0xFF; + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 - std::cout << "VIF1 register " << std::hex << reg << " written with value " - << value << std::dec << std::endl; + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - // Handle VIF1 operations + if (hostDest && hostSrc) + { + ::strcpy(hostDest, hostSrc); + } + else + { + std::cerr << "strcpy error: Invalid address provided." << std::endl; + } + + ctx->r[2] = ctx->r[4]; // Return dest pointer ($v0 = $a0) } - void handleTimerReg(uint32_t address, uint32_t value) + void ps2_stubs::strncpy(uint8_t *rdram, R5900Context *ctx) { - uint32_t timer = (address >> 4) & 0x3; - uint32_t reg = address & 0xF; + uint32_t destAddr = getRegU32(ctx, 4); // $a0 + uint32_t srcAddr = getRegU32(ctx, 5); // $a1 + uint32_t size = getRegU32(ctx, 6); // $a2 - std::cout << "Timer " << timer << " register " << std::hex << reg << " written with value " - << value << std::dec << std::endl; + char *hostDest = reinterpret_cast(getMemPtr(rdram, destAddr)); + const char *hostSrc = reinterpret_cast(getConstMemPtr(rdram, srcAddr)); - // Update timer state + if (hostDest && hostSrc) + { + ::strncpy(hostDest, hostSrc, size); + // Null termination if possible + if (size > 0) + hostDest[size - 1] = '\0'; + } + else + { + std::cerr << "strncpy error: Invalid address provided." << std::endl; + } + ctx->r[2] = ctx->r[4]; // Return dest pointer ($v0 = $a0) } - void handleSPU2Reg(uint32_t address, uint32_t value) + void ps2_stubs::strlen(uint8_t *rdram, R5900Context *ctx) { - std::cout << "SPU2 register " << std::hex << address << " written with value " - << value << std::dec << std::endl; + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + size_t len = 0; - // Update audio state + if (hostStr) + { + len = ::strlen(hostStr); + } + else + { + std::cerr << "strlen error: Invalid address provided." << std::endl; + } + setReturnU32(ctx, (uint32_t)len); } -} -#endif // PS2_STUBS_H \ No newline at end of file + + void ps2_stubs::strcmp(uint8_t *rdram, R5900Context *ctx) + { + 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; // Default if pointers bad + + if (hostStr1 && hostStr2) + { + result = ::strcmp(hostStr1, hostStr2); + } + else + { + std::cerr << "strcmp error: Invalid address provided." << std::endl; + result = 1; // Indicate difference on error + } + setReturnS32(ctx, result); + } + + void ps2_stubs::strncmp(uint8_t *rdram, R5900Context *ctx) + { + 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; // Default if pointers bad + + if (hostStr1 && hostStr2) + { + result = ::strncmp(hostStr1, hostStr2, size); + } + else + { + std::cerr << "strncmp error: Invalid address provided." << std::endl; + result = 1; + } + setReturnS32(ctx, result); + } + + void ps2_stubs::strcat(uint8_t *rdram, R5900Context *ctx) + { + 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." << std::endl; + } + + ctx->r[2] = ctx->r[4]; // Return dest pointer ($v0 = $a0) + } + + void ps2_stubs::strncat(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::strchr(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::strrchr(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::strstr(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::printf(uint8_t *rdram, R5900Context *ctx) + { + uint32_t format_addr = getRegU32(ctx, 4); // $a0 + + // We can't easily handle variable arguments in this simple implementation, + // Check if the address is within RDRAM + + if ((format_addr & PS2_RAM_MASK) == 0) + { + const char *format = (const char *)(rdram + (format_addr & 0x1FFFFFF)); + ::printf("PS2 printf: %s\n", format); + } + else + { + std::cerr << "strcat error: Invalid address provided." << std::endl; + } + + setReturnS32(ctx, (int32_t)1); // TODO fix this later + } + + void ps2_stubs::sprintf(uint8_t *rdram, R5900Context *ctx) + { + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + uint32_t format_addr = getRegU32(ctx, 5); // $a1 + + // We can't easily handle variable arguments in this simple implementation, + // so we'll just copy the format string to the destination without substituting any arguments + + if ((str_addr & PS2_RAM_MASK) == 0 && (format_addr & PS2_RAM_MASK) == 0) + { + char *str = (char *)(rdram + (str_addr & 0x1FFFFFF)); + const char *format = (const char *)(rdram + (format_addr & 0x1FFFFFF)); + ::strcpy(str, format); + } + else + { + std::cerr << "strcat error: Invalid address provided." << std::endl; + } + + setReturnS32(ctx, 0); + } + + void ps2_stubs::snprintf(uint8_t *rdram, R5900Context *ctx) + { + uint32_t str_addr = getRegU32(ctx, 4); // $a0 + size_t size = getRegU32(ctx, 5); // $a1 + uint32_t format_addr = getRegU32(ctx, 6); // $a2 + + // We can't easily handle variable arguments in this simple implementation, + // so we'll just copy the format string to the destination without substituting any arguments + + // Check if the addresses are within RDRAM + if ((str_addr & PS2_RAM_MASK) == 0 && (format_addr & PS2_RAM_MASK) == 0) + { + // Addresses are within RDRAM, use direct access + char *str = (char *)(rdram + (str_addr & 0x1FFFFFF)); + const char *format = (const char *)(rdram + (format_addr & 0x1FFFFFF)); + ::strncpy(str, format, size); + str[size - 1] = '\0'; // Ensure null termination + } + else + { + std::cerr << "strcat error: Invalid address provided." << std::endl; + } + + setReturnS32(ctx, 0); + } + + void ps2_stubs::puts(uint8_t *rdram, R5900Context *ctx) + { + uint32_t strAddr = getRegU32(ctx, 4); // $a0 + const char *hostStr = reinterpret_cast(getConstMemPtr(rdram, strAddr)); + + int result = -1; + if (hostStr) + { + result = std::puts(hostStr); + } + else + { + std::cerr << "puts error: Invalid address provided." << std::endl; + } + + setReturnS32(ctx, result >= 0 ? 0 : -1); // Return 0 on success, -1 on error like PS2 libs might + } + + void ps2_stubs::fopen(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fclose(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fwrite(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fprintf(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fseek(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::ftell(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fflush(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::sqrt(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::sin(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::cos(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::tan(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::atan2(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::pow(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::exp(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::log(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::log10(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::ceil(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::floor(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::fabs(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ps2_stubs::TODO(uint8_t *rdram, R5900Context *ctx) + { + uint32_t stub_num = getRegU32(ctx, 2); // $v0 often holds stub num before call + std::cerr << "Warning: Unimplemented stub called. PC=0x" << std::hex << ctx->pc + << " stub # (approx): 0x" << stub_num << std::dec << std::endl; + setReturnS32(ctx, -1); // Return error + } +} \ No newline at end of file diff --git a/ps2xRuntime/src/ps2_syscalls.cpp b/ps2xRuntime/src/ps2_syscalls.cpp new file mode 100644 index 0000000..39a80ff --- /dev/null +++ b/ps2xRuntime/src/ps2_syscalls.cpp @@ -0,0 +1,499 @@ + +#include "ps2_syscalls.h" +#include "ps2_runtime.h" +#include "ps2_stubs.h" +#include +#include +#include +#include +#include +#include +#include +#include + +std::unordered_map g_fileDescriptors; +int g_nextFd = 3; // Start after stdin, stdout, stderr + +int allocatePs2Fd(FILE* file) +{ + if (!file) + return -1; + int fd = g_nextFd++; + g_fileDescriptors[fd] = file; + return fd; +} + +FILE* getHostFile(int ps2Fd) +{ + auto it = g_fileDescriptors.find(ps2Fd); + if (it != g_fileDescriptors.end()) + { + return it->second; + } + return nullptr; +} + +void releasePs2Fd(int ps2Fd) +{ + 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) +{ + std::string pathStr(ps2Path); + if (pathStr.rfind("host0:", 0) == 0) + { + // Map host0: to ./host_fs/ relative to executable + std::filesystem::path hostBasePath = std::filesystem::current_path() / "host_fs"; + std::filesystem::create_directories(hostBasePath); // Ensure it exists + return (hostBasePath / pathStr.substr(6)).string(); + } + else if (pathStr.rfind("cdrom0:", 0) == 0) + { + // Map cdrom0: to ./cd_fs/ relative to executable (for example) + std::filesystem::path cdBasePath = std::filesystem::current_path() / "cd_fs"; + std::filesystem::create_directories(cdBasePath); // Ensure it exists + return (cdBasePath / pathStr.substr(7)).string(); + } + // Add other mappings (mc0:, hdd0:, etc.) as needed + std::cerr << "Warning: Unsupported PS2 path prefix: " << pathStr << std::endl; + return ""; +} + +#include "ps2_syscalls.h" + +namespace ps2_syscalls +{ + void FlushCache(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ResetEE(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SetMemoryMode(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void CreateThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void DeleteThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void StartThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ExitThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ExitDeleteThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void TerminateThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SuspendThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ResumeThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GetThreadId(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ReferThreadStatus(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SleepThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void WakeupThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iWakeupThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ChangeThreadPriority(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void RotateThreadReadyQueue(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ReleaseWaitThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iReleaseWaitThread(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void CreateSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void DeleteSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SignalSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iSignalSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void WaitSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void PollSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iPollSema(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ReferSemaStatus(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iReferSemaStatus(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void CreateEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void DeleteEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SetEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iSetEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ClearEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iClearEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void WaitEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void PollEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iPollEventFlag(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void ReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iReferEventFlagStatus(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SetAlarm(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iSetAlarm(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void CancelAlarm(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void iCancelAlarm(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void EnableIntc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void DisableIntc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void EnableDmac(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void DisableDmac(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifStopModule(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifLoadModule(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifInitRpc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifBindRpc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifCallRpc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifRegisterRpc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifCheckStatRpc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifSetRpcQueue(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifRemoveRpcQueue(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifRemoveRpc(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioOpen(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioClose(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioRead(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioWrite(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioLseek(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioMkdir(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioChdir(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioRmdir(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioGetstat(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void fioRemove(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GsSetCrt(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GsGetIMR(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GsPutIMR(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GsSetVideoMode(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GetOsdConfigParam(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SetOsdConfigParam(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void GetRomName(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void SifLoadElfPart(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void sceSifLoadModule(uint8_t *rdram, R5900Context *ctx) + { + // TODO + } + + void TODO(uint8_t *rdram, R5900Context *ctx) + { + uint32_t syscall_num = getRegU32(ctx, 2); // $v0 often holds syscall num before call + std::cerr << "Warning: Unimplemented syscall called. PC=0x" << std::hex << ctx->pc + << " Syscall # (approx): 0x" << syscall_num << std::dec << std::endl; + setReturnS32(ctx, -1); // Return error + } + + // Helper functions + void RenderFrame(uint8_t *rdram) + { + // TODO + } +} \ No newline at end of file