* fix: semaphore syscalls return sid on success instead of KE_OK The PS2 EE BIOS returns the semaphore ID on success for all semaphore syscalls, not zero. PollSema, WaitSema, SignalSema, and DeleteSema were all returning KE_OK (0) on success, which breaks games that compare the return value against the semaphore ID. DQ8's init code polls a mutex semaphore at 0x12A670 and checks the result against the semaphore ID using bne. With KE_OK returned, the comparison always fails and the game spins forever at PC 0x164978 and never starts. Other games that follow the same EE BIOS convention would hit the same problem. CreateSema already returned the ID correctly; this brings the other four operations in line with the same convention. The fix is five changes in Sync.cpp: the four return sites are updated to return sid, and the count-decrement guard in WaitSema is changed from ret == 0 to ret >= 0. The guard change is necessary because ret is now seeded to sid (a positive value) on the success path, and the old equality check would have silently stopped decrementing the semaphore count. Error paths (KE_WAIT_DELETE, KE_RELEASE_WAIT) are negative and still correctly bypass the decrement. Tests are updated to expect sid instead of KE_OK on success paths, and new test cases cover the blocked-wait-then-signal path (the actual DQ8 scenario), force-release via ReleaseWaitThread, and the count-decrement guard directly. * fix: update stale KE_OK assertions in expansion and SIF RPC tests Two test files were not updated alongside the semaphore return-value change. PollSema and SignalSema now return sid on success; update the four assertions that expected KE_OK on those success paths. * fix: tighten WaitSema count-decrement guard to ret == sid ret is seeded to sid on success and only ever overwritten with negative error codes, so ret == sid precisely expresses "this wait acquired this semaphore" — more explicit than the looser ret >= 0. Suggested by ran-j in PR review.
Runtime Library
The runtime library provides the execution environment for recompiled code, including:
- Memory management (32MB main RAM, scratchpad, etc.)
- Register context (128-bit GPRs, VU0 registers, etc.)
- Function table for dynamic linking
- Basic PS2 system call stubs
How to use:
Take your decompiled code and place the cpp files on ps2xRuntime/src/runner and header files on ps2xRuntime/include and compile/be happy.
Vita Build Notes
The Vita runtime uses Quenom/raylib-5.5-vita for vita build. I recommend build runtime only.
Expected environment:
VITASDKpoints to your VitaSDK root.Quenom/raylib-5.5-vitahas already been built and installed into$VITASDK/arm-vita-eabi.- SDL2 with the PVR backend required by that raylib fork is also installed into the same VitaSDK prefix.
PS2X_DEFAULT_BOOT_ELFis mandatory, you need to define where your game is like "ux0:data/RANJ00001/game/SLUS_201.84".
The CMake for ps2xRuntime consumes those preinstalled headers and libraries from VitaSDK. It does not fetch or install the Vita raylib fork for you.
Adding Custom Function Implementations
You can add custom implementations for PS2 system calls or game functions by:
- Creating function implementations that match the signature:
void function_name(uint8_t* rdram, R5900Context* ctx, PS2Runtime *runtime);
- Registering them with the runtime:
runtime.registerFunction(address, function_name);
Advanced Features
Memory Translation The runtime handles PS2's memory addressing, including:
- KSEG0/KSEG1 direct mapping
- TLB lookups for user memory
- Special memory areas (scratchpad, I/O registers)
Vector Unit Support
PS2-specific 128-bit MMI instructions and VU0 macro mode instructions are supported via SSE/AVX intrinsics.
Instruction Patching
You can patch specific instructions in the recompiled code to fix game issues or implement custom behavior.
Limitations
- Graphics and sound output require external implementations
- Some PS2-specific hardware features may not be fully supported
- Performance may vary based on the complexity of the game