-
Notifications
You must be signed in to change notification settings - Fork 83
Ps2xRuntime
ps2xRuntime is the runtime library that executes the recompiled C++ output generated by ps2_recomp.
It provides a minimal PlayStation 2 “Execution Environment” for the recompiled game code: memory, CPU context, MMIO routing, function dispatch, and syscall/stub plumbing.
Status: Work in progress. The runtime is currently the most underdeveloped part of the project. Expect to implement/fix syscalls and stubs per-game.
-
Put your recompiled
.cppoutput into:ps2xRuntime/src/runner/
-
Put any generated or required headers into:
ps2xRuntime/include/
-
Memory model
- 32MB main RAM (RDRAM)
- 16KB scratchpad
- basic regions for EE I/O, BIOS window, GS registers, VU memory windows
-
CPU context (
R5900Context)- 128-bit GPRs (stored as
__m128i r[32]) - PC, HI/LO + HI1/LO1, SA
- COP0 registers
- COP1 (FPU) registers
- VU0 macro-mode state (VF/VI/Q/ACC/etc.)
- 128-bit GPRs (stored as
-
Function table
- Maps guest addresses → generated C++ function pointers
- Acts like “dynamic linking” for translated code
-
Syscall / stub entry points
- A central syscall handler (
handleSyscall) - Stub hooks used by
stubs/skipgenerated wrappers
- A central syscall handler (
-
Hybrid fast/slow memory access
- Fast path: direct masked RDRAM reads/writes
- Slow path: routes “special addresses” to runtime
Load*/Store*to handle MMIO / scratchpad / BIOS / etc.
Typical layout used by this project:
-
ps2xRuntime/include/- runtime public headers (
ps2_runtime.h,ps2_runtime_macros.h, call lists, stubs APIs)
- runtime public headers (
-
ps2xRuntime/src/- runtime implementation
-
src/lib/contains syscall implementations and helpers -
src/runner/is the place where you drop generated game code
- Run with minimal config and no aggressive skipping.
- Fix hard blockers first (
function not found, syscall TODO, critical IO stubs). - Use temporary return stubs only to classify call importance.
- Promote temporary fixes to real implementations.
- Move per-game hacks into game overrides keyed by ELF metadata.
- Re-test from cold boot after each batch.
This is a minimal main() that loads the ELF, registers functions, and runs:
#include "ps2_runtime.h"
#include "register_functions.h"
#include <iostream>
#include <string>
int main(int argc, char *argv[])
{
if (argc < 2)
{
std::cout << "Usage: " << argv[0] << " <elf_file>" << std::endl;
return 1;
}
std::string elfPath = argv[1];
PS2Runtime runtime;
if (!runtime.initialize("ps2xRuntime (Raylib host)"))
{
std::cerr << "Failed to initialize PS2 runtime" << std::endl;
return 1;
}
// Generated by the recompiler output revied by you
registerAllFunctions(runtime);
if (!runtime.loadELF(elfPath))
{
std::cerr << "Failed to load ELF file: " << elfPath << std::endl;
return 1;
}
runtime.run();
return 0;
}Symptom: Warning: Function at address 0x... not found
- Ensure generated
register_functions.cppis actually compiled into runner. - Ensure
registerAllFunctions(runtime)is called beforeloadELF. - If address is valid but missing, add address binding or override registration for that address.
Symptom: Warning: Unimplemented PS2 stub called. name=...
- Add/adjust
general.stubsmapping for stripped address. - Or implement that stub in
ps2xRuntime/src/lib/stubs/....
Symptom: [Syscall TODO] ...
- Implement numeric syscall path in
ps2xRuntime/src/lib/ps2_syscalls.cppand relatedsyscalls/*.inl.
Symptom: CPU is doing some work at PC 0x... PC not updating.
- You are likely in a loop/spin/wait path.
- Open the generated function for that PC address (
sub_..._0x...cpp) and inspect loop condition and called helpers. - Set debugger breakpoints in:
PS2Runtime::lookupFunctionps2_stubs::TODO_NAMEDps2_syscalls::TODO
The runtime is intentionally minimal. Many games will require additional work here.
You will likely need to implement missing syscalls and fix existing ones in:
ps2xRuntime/include/ps2_call_list.hps2xRuntime/src/lib/ps2_syscalls.cpp
- Run the game and observe failures (missing syscall ID, TODO stub hit, or unhandled path).
- Identify the syscall/stub being used by the game.
- Add or fix the implementation in
ps2_syscalls.cpp. - Update the mapping/list in
ps2_call_list.h. - Rebuild and repeat.
-
stubs: generated code calls a runtime stub handler (by name). -
skip: generated code usesps2_stubs::TODO_NAMED("...")style placeholders.
Both cases require the runtime to provide a safe behavior:
- return a sane default value
- emulate minimal behavior if needed
- or log+abort when it must not proceed
ps2_runtime_macros.h provides:
- arithmetic helpers (
ADD32,SUB32_OV, etc.) - PS2 MMI/VU-friendly helpers via SSE intrinsics
- fast read/write macros (
FAST_READ32, etc.) - slow-path fallback macros (
READ32,WRITE32, etc.) that route MMIO/special addresses throughPS2Runtime
If you see weird game behavior, pay special attention to:
- address classification in
PS2Runtime::isSpecialAddress - correctness of
Load*/Store*implementations for the region the game touches
This runtime uses SIMD types and intrinsics heavily (__m128i, SSE).
-
MSVC: includes
<intrin.h> -
Clang/GCC on x86/x64: uses
<immintrin.h>/<smmintrin.h> -
Non-x86 platforms (ARM): build with
USE_SSE2NEONand providesse2neon.h
If your target platform does not support SSE natively, enabling USE_SSE2NEON is required.
- Start with a known, simple ELF first (homebrew / debug build).
- Keep
stubsandskiplists small and intentional. - Watch for:
- bad pointer translations (guest → host)
- missing syscall/stub behavior
- games writing to MMIO regions (timers/DMAC/INTC/GS/VIF)
- If you hit instruction translation issues earlier, fix those in the recompiler output first (the runtime assumes the translated code is semantically valid).
- Many syscalls and I/O behaviors are incomplete
- Complex DMA / VIF / GS behaviors may be missing or simplified
- Some games will require per-title fixes (stubs, patches, and MMIO handling)