Files
psxrecomp/docs/beetle_wtrace_hook.patch
T
Matthew Stanley f8841ead30 case A: Beetle wtrace + recomp verification → memcard blocked on AOT gap
Adds a RAM-write trace hook to mednafen-PSX (SB/SH/SW handlers fire
g_psxrecomp_wtrace_cb after each write), a ring buffer + TCP commands
in psx-beetleoracle, and verification scripts that drive CROSS in
both Beetle and recomp and diff the gate-byte writers.

Conclusion (docs/CASE_A_AOT_GAP.md): the [0x7568+slot] LSB-clear
writers are at RAM PCs 0x5EF4 / 0x5FA8 (and 0x4F54 sets BUSY).
Recomp never dispatches any of those PCs, never executes the
0x4F00..0x4FFF or 0x5800..0x5FFF RAM windows, and never writes the
gate. Selective coverage pattern (interp reaches 0xCF0/0x52xx/0x6xxx
but not 0x4Fxx/0x5xxx) is install/dispatch path gap, not interpreter
bug. PLAN.md:941 already names this work as a hard gate.

Beetle-side modifications live inside the vendored beetle-psx/ git
clone; docs/beetle_wtrace_hook.patch captures the diff so they
survive a re-clone.

Memcard symptom debugging stops here. Next: trace-generated AOT
RAM-code manifest + recompiler ingestion (Phases B–F).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 13:10:05 -07:00

94 lines
3.2 KiB
Diff

diff --git a/libretro.cpp b/libretro.cpp
index 476311b..801915c 100644
--- a/libretro.cpp
+++ b/libretro.cpp
@@ -510,6 +510,9 @@ PS_SPU *PSX_SPU = NULL;
PS_CDC *PSX_CDC = NULL;
FrontIO *PSX_FIO = NULL;
+/* PSXRecomp oracle hook: NULL when no listener attached. */
+psxrecomp_wtrace_cb_t g_psxrecomp_wtrace_cb = NULL;
+
MultiAccessSizeMem<512 * 1024, uint32, false> *BIOSROM = NULL;
MultiAccessSizeMem<65536, uint32, false> *PIOMem = NULL;
MultiAccessSizeMem<2048 * 1024, uint32, false> *MainRAM = NULL;
diff --git a/mednafen/psx/cpu.cpp b/mednafen/psx/cpu.cpp
index f11e243..a8e01e2 100644
--- a/mednafen/psx/cpu.cpp
+++ b/mednafen/psx/cpu.cpp
@@ -2370,13 +2370,17 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
WriteMemory<uint8>(timestamp, address, GPR[rt]);
+ /* PSXRecomp oracle hook: capture (addr, val, PC, ra, size) for wtrace. */
+ if (MDFN_UNLIKELY(g_psxrecomp_wtrace_cb))
+ g_psxrecomp_wtrace_cb(address, GPR[rt] & 0xFFu, PC, GPR[31], 1);
+
if (PGXP_GetModes() & PGXP_MODE_MEMORY)
PGXP_CPU_SB(instr, GPR[rt], address);
DO_LDS();
END_OPF;
- //
+ //
// SH - Store Halfword
//
BEGIN_OPF(SH);
@@ -2397,13 +2401,17 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
else
WriteMemory<uint16>(timestamp, address, GPR[rt]);
+ /* PSXRecomp oracle hook: SH (only if no alignment exception). */
+ if (MDFN_UNLIKELY(g_psxrecomp_wtrace_cb) && !(address & 0x1))
+ g_psxrecomp_wtrace_cb(address, GPR[rt] & 0xFFFFu, PC, GPR[31], 2);
+
if (PGXP_GetModes() & PGXP_MODE_MEMORY)
PGXP_CPU_SH(instr, GPR[rt], address);
DO_LDS();
END_OPF;
- //
+ //
// SW - Store Word
//
BEGIN_OPF(SW);
@@ -2424,6 +2432,10 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
else
WriteMemory<uint32>(timestamp, address, GPR[rt]);
+ /* PSXRecomp oracle hook: SW (only if no alignment exception). */
+ if (MDFN_UNLIKELY(g_psxrecomp_wtrace_cb) && !(address & 0x3))
+ g_psxrecomp_wtrace_cb(address, GPR[rt], PC, GPR[31], 4);
+
if (PGXP_GetModes() & PGXP_MODE_MEMORY)
PGXP_CPU_SW(instr, GPR[rt], address);
diff --git a/mednafen/psx/psx.h b/mednafen/psx/psx.h
index 704c0a1..93446e8 100644
--- a/mednafen/psx/psx.h
+++ b/mednafen/psx/psx.h
@@ -137,4 +137,21 @@ extern unsigned psx_gpu_overclock_shift;
extern uint8_t analog_combo[2];
extern uint8_t analog_combo_hold;
+/* PSXRecomp oracle: SIO byte trace callback.
+ * Fired by FrontIO when a complete byte exchange finishes. */
+class FrontIO;
+extern FrontIO *PSX_FIO;
+typedef void (*psxrecomp_sio_trace_cb_t)(uint8_t tx, uint8_t rx, uint16_t ctrl);
+
+/* PSXRecomp oracle: RAM write trace callback.
+ * Fired by PS_CPU::SB/SH/SW after the write completes.
+ * addr = virtual address (caller masks for physical filter)
+ * value = stored value (sized by `size`)
+ * pc = PC of the executing store instruction
+ * ra = $ra (GPR[31]) at time of store, for one frame of caller context
+ * size = 1, 2, or 4 (byte / halfword / word) */
+typedef void (*psxrecomp_wtrace_cb_t)(uint32_t addr, uint32_t value,
+ uint32_t pc, uint32_t ra, uint8_t size);
+extern psxrecomp_wtrace_cb_t g_psxrecomp_wtrace_cb;
+
#endif