Files
psxrecomp/generated/cpu_state.h
T
Matthew Stanley 7c40d33db3 Phase 1 complete: recompiler proven correct on SCPH1001.BIN
Phase 1a (boot slice): 30 instructions at 0xBFC00000, compiles clean.
Phase 1b (instruction coverage): 54 opcodes implemented in strict_translator,
  44,629 walker-reachable instructions covered, 0 missing.
Phase 1c (function discovery): 76 functions from 2 seeds, 2,988 instructions,
  199 call edges, 27 indirect jumps. All validated through StrictTranslator.
Phase 1d (indirect control flow): 27 sites classified — 21 A0/B0/C0 dispatch
  stubs, 4 computed calls, 2 computed tail calls. Ghidra cross-referenced
  with 0 disagreements.
Phase 1e (relocation): ROM-to-RAM copy at FUN_bfc00420 proven with both
  Ghidra (static) and DuckStation (runtime). 69 address aliases, 68/69
  function entries match byte-for-byte in RAM. Normalization rule documented.

Includes PRINCIPLES.md and DEBUG.md adapted from recomp-template.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 21:26:04 -07:00

106 lines
5.0 KiB
C

/* ============================================================================
* cpu_state.h
* ----------------------------------------------------------------------------
* COMPILE-ONLY ARTIFACT FOR PHASE 1a PROOF.
*
* This header exists so that `generated/boot_slice.c` can be type-checked by
* a C compiler with `-c` (object emission only). It is NOT a runtime, it is
* NOT a stub set, and it is NOT the start of the v4 runtime.
*
* Linking any translation unit that includes this header into a runnable
* binary is a build error BY DESIGN: the function-pointer fields in CPUState
* are unset, and `psx_syscall` has no implementation anywhere in the tree.
* If your linker fails on `psx_syscall` or on undefined memory accessors,
* that is the intended behavior. DO NOT add stubs. DO NOT add a default
* implementation. The runtime is Phase 2 work and will land in `runtime/`
* with real hardware-simulation backends.
*
* The Phase 1a recompiler self-validates `boot_slice.c` with `gcc -c` only.
* If you find yourself wanting to write `cpu_state.c` to make linking work,
* stop. Re-read CLAUDE.md sections 0 and 7. Stubs are how every prior
* version of this project failed.
* ========================================================================== */
#ifndef PSXRECOMP_V4_CPU_STATE_H
#define PSXRECOMP_V4_CPU_STATE_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* PS1 R3000A architectural state.
*
* Layout matches recompiler/include/code_generator.h `CPUState` so that
* Phase 2's runtime can drop in without renaming. The cop0 array is added
* here because Phase 1a's strict translator emits MFC0/MTC0/RFE in cop0[N]
* form (matching salvaged codegen convention); Phase 2 may revisit per
* PLAN.md HP1.
*/
typedef struct CPUState {
uint32_t gpr[32]; /* $0..$31; gpr[0] is hardwired zero, never written */
uint32_t pc; /* program counter */
uint32_t hi, lo; /* mult/div result registers */
uint32_t cop0[32]; /* COP0 system control registers (SR, Cause, EPC, ...) */
/* Memory access function pointers. Phase 1a leaves these NULL.
* The recompiled boot_slice.c calls them via cpu->write_word(...);
* dereferencing a NULL function pointer at runtime crashes loudly,
* which is the desired failure mode if anyone tries to RUN this
* artifact (vs. only compile it). */
uint32_t (*read_word)(uint32_t addr);
void (*write_word)(uint32_t addr, uint32_t value);
uint16_t (*read_half)(uint32_t addr);
void (*write_half)(uint32_t addr, uint16_t value);
uint8_t (*read_byte)(uint32_t addr);
void (*write_byte)(uint32_t addr, uint8_t value);
} CPUState;
/* Syscall trampoline. Declared, never defined.
* Any TU that calls this and then links will fail with an unresolved symbol.
* That is intentional. The Phase 2 runtime will provide a real definition
* that routes through the recompiled BIOS exception vector. */
extern void psx_syscall(CPUState* cpu, uint32_t code);
/* Arithmetic-overflow trap trampoline. Declared, never defined.
* Real R3000A raises an Overflow exception (Cause.ExcCode = 0xC) when a
* signed ADD/ADDI/SUB result does not fit in 32 bits. The recompiled C
* for those instructions checks for overflow and calls this function on
* trap; the Phase 2 runtime will define it to deliver the exception
* through the recompiled exception vector. Until then, calling this
* symbol fails to link, which is the intended behavior. DO NOT add a
* default definition. */
extern void psx_arith_overflow(CPUState* cpu);
/* Unaligned-access fail-loud. Declared, never defined.
* Phase 1b loads (LW/LH/LHU) and stores (SW/SH) check the effective
* address alignment before performing the access. On misalignment the
* recompiled C calls this function with the offending address and the
* PC of the load/store instruction, then returns from the slice. Real
* R3000A would raise AdEL (load) or AdES (store) exceptions; we are
* NOT modeling exception delivery in Phase 1b. The intent here is to
* fail loud so that any unaligned access is surfaced rather than
* silently producing the wrong bytes. The Phase 2 runtime will define
* this function (probably as `abort()` with a diagnostic, or as a
* proper exception delivery once the exception path exists). DO NOT
* add a default definition. */
extern void psx_unaligned_access(CPUState* cpu, uint32_t addr, uint32_t pc);
/* BREAK trap fail-loud. Declared, never defined.
* Phase 1b B(10) translates the BREAK instruction (SPECIAL funct 0x0D)
* to a call to this function. `code` is the 20-bit immediate in
* bits [25:6] of the BREAK instruction, `pc` is the address of the
* BREAK itself. After the call the recompiled C `return`s from the
* slice — the strict translator does NOT model R3000A's BREAK
* exception (Cause.ExcCode = 0x9) or the resumption back through
* RFE. That is Phase 2 work. Until the Phase 2 runtime provides this
* symbol, linking fails by design. DO NOT add a default definition. */
extern void psx_break(CPUState* cpu, uint32_t code, uint32_t pc);
#ifdef __cplusplus
}
#endif
#endif /* PSXRECOMP_V4_CPU_STATE_H */