CPUState¶
MIPS R3000A register file, GTE registers and memory callbacks.
The recompiled game is C that reads and writes these fields where the original instructions read and wrote registers; there is no interpreter and no other copy of the state. One instance exists for the session, owned by the sim thread. An override receives a pointer to it at the entry of the function it overrides and the pointer is valid until the override returns: keep no copy of it, and touch it from no other thread.
Calling convention (MIPS o32, as the game was compiled): arguments in
a0..a3, a fifth and later argument in the caller's frame at
sp + 16, sp + 20, ... at entry, read through read_word. The
return value is in v0, with v1 carrying the high word of a 64-bit
return. ra is the return address, written by the caller before the
call, and sp at entry is the caller's stack pointer: the overridden
function has not yet built its frame.
Preservation: the recompiled function saves and restores exactly the
registers its original code did, so at return s0..s7, sp, fp and
gp hold their entry values and every other register is clobbered.
An override that runs around api->base(cpu) sees this too: read the
arguments before base(), the result after it, and expect a0..a3,
t0..t9, v0, v1 and ra to have changed across it. The
natural-signature wrappers save and restore a0..a3, v0, v1 and
ra around the calls they make and nothing else. An override that
calls api->call itself and then returns into the original must
restore the argument registers it overwrote.
pc is written by the recompiled code only in a build regenerated
with per-instruction PC tracking (OPENPETE_REGEN_PC_TRACKING=1) and
at indirect jumps; in a stock build it is stale and not a way to find
where the game is. cop0 is carried for completeness: the game
executes no coprocessor-0 instruction. The GTE arrays are live: every
recompiled COP2 instruction executes against them.
The memory callbacks translate a guest vaddr in kuseg, kseg0 or kseg1
to the 2 MB RAM, the 1 KB scratchpad or a registered region (the mod
arena included) and access it at the requested width; an unmapped
read returns 0 and an unmapped write is dropped. Addresses are
naturally aligned, as the game's are. api->guest resolves the same
map to a host pointer, so the two are interchangeable for bulk work.
Savestates, rewind and runahead carry the register file, from zero
through gte_ctrl, with guest RAM; the callback pointers are bound
once at boot and are not timeline state.
Declared in psx_runtime.h, shipped in sdk/; include it beside openpete_mod_api.h, which only forward-declares the type.
Fields¶
| Field | Type | Meaning |
|---|---|---|
zero |
uint32_t |
Hard-wired 0. |
at |
uint32_t |
Assembler temporary. |
v0 |
uint32_t |
Return value; the low word of a 64-bit return. |
v1 |
uint32_t |
High word of a 64-bit return; otherwise scratch. |
a0 |
uint32_t |
First argument. |
a1 |
uint32_t |
Second argument. |
a2 |
uint32_t |
Third argument. |
a3 |
uint32_t |
Fourth argument; later ones are on the stack at sp + 16 and up. |
t0 |
uint32_t |
Caller-saved temporaries. |
t1 |
uint32_t |
Caller-saved temporaries. |
t2 |
uint32_t |
Caller-saved temporaries. |
t3 |
uint32_t |
Caller-saved temporaries. |
t4 |
uint32_t |
Caller-saved temporaries. |
t5 |
uint32_t |
Caller-saved temporaries. |
t6 |
uint32_t |
Caller-saved temporaries. |
t7 |
uint32_t |
Caller-saved temporaries. |
s0 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s1 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s2 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s3 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s4 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s5 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s6 |
uint32_t |
Callee-saved; a function returns them as it found them. |
s7 |
uint32_t |
Callee-saved; a function returns them as it found them. |
t8 |
uint32_t |
Caller-saved temporaries. |
t9 |
uint32_t |
Caller-saved temporaries. |
k0 |
uint32_t |
Kernel reserved; unused by the game. |
k1 |
uint32_t |
Kernel reserved; unused by the game. |
gp |
uint32_t |
Global pointer: base of the game's small-data area, constant across the session. |
sp |
uint32_t |
Stack pointer; the caller's frame at override entry. |
fp |
uint32_t |
Frame pointer (s8); callee-saved. |
ra |
uint32_t |
Return address of the current call. |
pc |
uint32_t |
Program counter; maintained only under PC-tracking regeneration. |
hi |
uint32_t |
Multiply/divide result, high word or remainder. |
lo |
uint32_t |
Multiply/divide result, low word or quotient. |
cop0[32] |
uint32_t |
System coprocessor registers; the game never touches them. |
gte_data[32] |
uint32_t |
GTE data registers (VXY0..LZCR), live. |
gte_ctrl[32] |
uint32_t |
GTE control registers (rotation matrix, translation, screen offsets, flags), live. |
uint32_t (*read_word)(uint32_t addr) |
— | Aligned 32-bit load. |
void (*write_word)(uint32_t addr, uint32_t value) |
— | Aligned 32-bit store. |
uint16_t (*read_half)(uint32_t addr) |
— | Aligned 16-bit load. |
void (*write_half)(uint32_t addr, uint16_t value) |
— | Aligned 16-bit store. |
uint8_t (*read_byte)(uint32_t addr) |
— | 8-bit load. |
void (*write_byte)(uint32_t addr, uint8_t value) |
— | 8-bit store. |
uint32_t (*lwl)(uint32_t addr, uint32_t rt_value) |
— | Unaligned load, left part, merged into the rt_value word. |
uint32_t (*lwr)(uint32_t addr, uint32_t rt_value) |
— | Unaligned load, right part, merged into the rt_value word. |
void (*swl)(uint32_t addr, uint32_t rt_value) |
— | Unaligned store, left part. |
void (*swr)(uint32_t addr, uint32_t rt_value) |
— | Unaligned store, right part. |