Files
Shokunin 1a90c04c27 Hook coprocessor reads into GPRs in the PGXP shadow
MFC0 and CFC2 overwrite a GPR, but the recompiled code did not hook them
(and neither interpreter hooked MFC0/CFC0), so the register kept the
shadow of whatever vertex it held before. Validation by value cannot
prove provenance when the new word equals the old one. Emit PGXP_ALU
after MFC0 and PGXP_COP2 after CFC2, matching the interpreters' CFC2
hook, and hook MFC0/CFC0 in both interpreters. OpenBIOS regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 19:15:44 -07:00

95 lines
4.6 KiB
C

#ifndef PGXP_HOOKS_H
#define PGXP_HOOKS_H
/* PGXP dataflow-shadowing hook surface (docs/ENHANCEMENTS.md G1.2/G1.3).
*
* The runtime keeps a host-only "shadow" of sub-pixel GTE projection results
* and follows them BY PROVENANCE as the game moves them through registers and
* RAM into a GP0 packet (pgxp.cpp). Generated code participates by calling
* these hooks after the guest operation completes.
*
* Gating model (the G1.3 decision): the emitter writes PGXP_*() macro
* invocations unconditionally; this header expands them to real calls only
* when the translation unit is compiled with -DPSX_PGXP=1 (the pgxp build
* variant). In the base variant the macros expand to ((void)0), so base
* objects compile to exactly the pre-feature code — the feature costs nothing
* unless a title links the pgxp target.
*
* RULES for emit sites:
* - Macro arguments MUST be side-effect free (they vanish in base builds).
* - Hooks run AFTER the guest architectural effect (value = what actually
* loaded/stored/resulted), so the shadow can validate against reality.
* - `cpu` must be in scope at every emit site (it always is in generated C).
*
* The interpreters (dirty_ram_interp.c, psx_interpreter.c) call the
* psx_pgxp_* functions directly — they are runtime code compiled once, and
* every hook early-outs on a single global when the feature is off.
*
* Overlay DLLs cannot link runtime symbols directly; they forward through the
* PGXPHooks table below (OverlayCallbacks.pgxp, overlay_api.h). A NULL table
* no-ops every hook — precision shadowing is a visual enhancement, never
* load-bearing.
*
* LICENSING: this is a clean-room implementation of the publicly documented
* PGXP technique (psx-spx, public design write-ups). Do NOT port code from
* the vendored duckstation/ (CC BY-NC-ND) or beetle-psx/ (GPL) trees; they
* are black-box behavioral oracles only. See docs/ENHANCEMENTS.md G1.
*/
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
struct CPUState;
/* Funnel entry points (implemented in runtime/src/pgxp.cpp). The raw
* instruction word carries the op/rs/rt/rd/imm decode so the emitted call is
* uniform per class; `value` is the architectural result of the operation.
*
* load: rt <- [addr]; value = loaded word/half/byte (post-extension)
* store: [addr] <- rt; value = the stored word/half/byte
* alu: rd/rt <- op(s1, s2); result = the written value (also LUI, HILO
* moves, MOVE idioms and MFC0/CFC0 — the body decodes the class
* from instr)
* muldiv: HI/LO <- op(s1, s2)
* cop2: MFC2/CFC2/MTC2/CTC2 (addr = 0) and LWC2/SWC2 (addr = guest addr);
* value = the transferred word
*/
void psx_pgxp_load (struct CPUState *cpu, uint32_t instr, uint32_t addr, uint32_t value);
void psx_pgxp_store (struct CPUState *cpu, uint32_t instr, uint32_t addr, uint32_t value);
void psx_pgxp_alu (struct CPUState *cpu, uint32_t instr, uint32_t result, uint32_t s1, uint32_t s2);
void psx_pgxp_muldiv(struct CPUState *cpu, uint32_t instr, uint32_t hi, uint32_t lo, uint32_t s1, uint32_t s2);
void psx_pgxp_cop2 (struct CPUState *cpu, uint32_t instr, uint32_t value, uint32_t addr);
/* Forwarder table for overlay DLLs (OverlayCallbacks.pgxp). Appended-last
* member semantics apply: a NULL pointer (older host) means "no shadowing". */
typedef struct PGXPHooks {
void (*load) (struct CPUState *cpu, uint32_t instr, uint32_t addr, uint32_t value);
void (*store) (struct CPUState *cpu, uint32_t instr, uint32_t addr, uint32_t value);
void (*alu) (struct CPUState *cpu, uint32_t instr, uint32_t result, uint32_t s1, uint32_t s2);
void (*muldiv)(struct CPUState *cpu, uint32_t instr, uint32_t hi, uint32_t lo, uint32_t s1, uint32_t s2);
void (*cop2) (struct CPUState *cpu, uint32_t instr, uint32_t value, uint32_t addr);
} PGXPHooks;
#if defined(PSX_PGXP) && PSX_PGXP
#define PGXP_LOAD(instr, addr, val) psx_pgxp_load(cpu, (instr), (addr), (val))
#define PGXP_STORE(instr, addr, val) psx_pgxp_store(cpu, (instr), (addr), (val))
#define PGXP_ALU(instr, res, s1, s2) psx_pgxp_alu(cpu, (instr), (res), (s1), (s2))
#define PGXP_MULDIV(instr, hi, lo, s1, s2) psx_pgxp_muldiv(cpu, (instr), (hi), (lo), (s1), (s2))
#define PGXP_COP2(instr, val, addr) psx_pgxp_cop2(cpu, (instr), (val), (addr))
#else
#define PGXP_LOAD(instr, addr, val) ((void)0)
#define PGXP_STORE(instr, addr, val) ((void)0)
#define PGXP_ALU(instr, res, s1, s2) ((void)0)
#define PGXP_MULDIV(instr, hi, lo, s1, s2) ((void)0)
#define PGXP_COP2(instr, val, addr) ((void)0)
#endif
#ifdef __cplusplus
}
#endif
#endif /* PGXP_HOOKS_H */