Skip to content

Reference Projects PS2Recomp

Italo Dell Areti edited this page May 18, 2026 · 1 revision

Reference Projects: PS2Recomp

Repo ran-j/PS2Recomp
License GPL v3
Console PlayStation 2
Local clone PS1Recomp-workspace/PS2Recomp/

PS2Recomp adapts the N64Recomp approach to the PS2's R5900. Architecturally the closest project to ours — same three-binary split, same in-tree runtime, same TOML config style.

What we borrowed

Three-binary pipeline

ps2xAnalyzer + ps2xRecomp + ps2xRuntime. We mirror this structure verbatim, with PS1-specific names:

PS2Recomp ps1-recomp
ps2xAnalyzer/ ps1Analyzer/
ps2xRecomp/ ps1Recomp/
ps2xRuntime/ ps1Runtime/

Runtime macros

PS2Recomp's ps2_runtime_macros.h is the template for our ps1_runtime_macros.h:

// Arithmetic
#define ADD32(a, b)  ((uint32_t)((a) + (b)))
#define SUB32(a, b)  ((uint32_t)((a) - (b)))
#define AND32(a, b)  ((uint32_t)((a) & (b)))
#define OR32(a, b)   ((uint32_t)((a) | (b)))

// Memory
#define READ32(addr)       (*(uint32_t*)((rdram) + ((addr) & RAM_MASK)))
#define WRITE32(addr, val) (*(uint32_t*)((rdram) + ((addr) & RAM_MASK)) = (val))

// Registers (with $zero protection)
#define GPR_U32(ctx, reg)         ((reg == 0) ? 0U : ctx->r[reg])
#define SET_GPR_U32(ctx, reg, val) do { if (reg != 0) ctx->r[reg] = (val); } while (0)

The PS1 versions drop SSE / 128-bit and shrink RAM_MASK from 32 MB to 2 MB.

TOML config schema

The general shape is the same:

[general]
input = "path/to/game.elf"
output = "output/"

stubs = ["printf", "malloc", "free", "memcpy"]
skip  = ["abort", "exit"]

[patches]
instructions = [
  { address = "0x100004", value = "0x00000000" },
]

[[jump_tables.table]]
address = "0xABCD"
entries = [{ index = 0, target = "0x1234" }]

We use the same [general] / stubs / skip / [patches] / [jump_tables] shape, with PS1-specific additions ([[hle_functions]], [[overlays]]).

Analyzer pipeline

PS2Recomp::ElfAnalyzer::analyze() runs 11 passes (entry-point detection, library function classification, control-flow graph, jump-table detection, performance-critical paths, recursive functions, register usage, function signatures). Our ps1Analyzer runs a smaller set tailored to PS1, but the structure (pipeline of named passes inside a single class) is copied.

What we adapted

PS2 PS1 Reason
128-bit regs (SSE __m128i) 32-bit regs (uint32_t) PS1 has no SIMD
32 MB RAM (PS2_RAM_MASK) 2 MB RAM (0x1FFFFF) Hardware limit
VU (Vector Unit, 128-bit) GTE (fixed-point 3D) Different coprocessor
FPU (COP1, float) none PS1 has no FPU
MMI (128-bit SIMD) none PS1 has no SIMD
COP2 = VU macro COP2 = GTE Different coprocessor
ps2_stubs.cpp (121 KB) PsyQ stubs (smaller) PsyQ is leaner than PS2 system libs

What we did differently

  • Hash-based PsyQ detection vs manual stub lists — the project's main architectural divergence. See PsyQ HLE.
  • No single_file_output flag yet — we always emit one recompiled_out.cpp. PS2Recomp supports both modes; the per-function split is on our roadmap.
  • Test coverage — 557 gtest vs ~10 PS2Recomp test files.

Clone this wiki locally