Files
psxrecomp/docs/beetle_cdcmd_trace_hook.patch
T
Claude 536ad0f886 debug: PSX_FNTRACE_ALL boot trace; root mechanism of Kula bug 3 found
Adds PSX_FNTRACE_ALL=1 (records every dispatch from power-on) and documents
the full mechanistic chain of the wedge (docs/kula-world-findings.md round 5):

The frame-890 ReadTOC that breaks the game's CD reads is gated by kernel flag
[0xA000DFFC]: the CD-open function 0xBFC0D570 does 'beqz [0xDFFC], skip;
jal ReadTOC'. On ours [0xDFFC]=1 -> ReadTOC -> wedge; on the oracle it stays
0 -> reads continue. The flag is set to 1 by func_1FC1DF50 (a CD control
routine) called from shell code at ra=0x80036024. The shells run the same
verbatim bytes but diverge early (ours 0x35000-0x39000 issuing Test/ReadTOC,
oracle 0x30D00-0x33200 issuing Init/GetTN), so a branch input differs. Traced
via self-stop trap + fntrace-all + gdb watchpoint on [0xDFFC] + capstone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BdjxoRB1f9VQsNzZayCgGS
2026-07-02 20:48:04 +00:00

88 lines
4.4 KiB
Diff

diff --git a/libretro.cpp b/libretro.cpp
index d2e82bca..e9c867c8 100644
--- a/libretro.cpp
+++ b/libretro.cpp
@@ -508,6 +508,11 @@ PS_SPU *PSX_SPU = NULL;
PS_CDC *PSX_CDC = NULL;
FrontIO *PSX_FIO = NULL;
+/* PSXRecomp oracle hooks: NULL when no listener attached. */
+psxrecomp_wtrace_cb_t g_psxrecomp_wtrace_cb = NULL;
+psxrecomp_fntrace_cb_t g_psxrecomp_fntrace_cb = NULL;
+psxrecomp_cdcmd_cb_t g_psxrecomp_cdcmd_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/cdc.cpp b/mednafen/psx/cdc.cpp
index 1daaa5b7..a6b31b66 100644
--- a/mednafen/psx/cdc.cpp
+++ b/mednafen/psx/cdc.cpp
@@ -1318,6 +1318,16 @@ int32_t PS_CDC::Update(const int32_t timestamp)
else
{
const CDC_CTEntry *command = &Commands[PendingCommand];
+ /* PSXRecomp oracle hook: CD command about to execute.
+ * PC of the guest at dispatch time — the command was
+ * WRITTEN earlier (latency), so this is the poll/idle
+ * PC, but it still localizes kernel vs shell vs game. */
+ if(MDFN_UNLIKELY(g_psxrecomp_cdcmd_cb))
+ g_psxrecomp_cdcmd_cb(PendingCommand, ArgsReceiveIn,
+ ArgsReceiveIn > 0 ? ArgsReceiveBuf[0] : 0,
+ ArgsReceiveIn > 1 ? ArgsReceiveBuf[1] : 0,
+ ArgsReceiveIn > 2 ? ArgsReceiveBuf[2] : 0,
+ PSX_CPU ? PSX_CPU->GetRegister(PS_CPU::GSREG_PC, NULL, 0) : 0);
next_time = (this->*(command->func))(ArgsReceiveIn, ArgsReceiveBuf);
PendingCommandPhase = 2;
}
diff --git a/mednafen/psx/psx.h b/mednafen/psx/psx.h
index 704c0a19..fc53362d 100644
--- a/mednafen/psx/psx.h
+++ b/mednafen/psx/psx.h
@@ -137,4 +137,45 @@ 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;
+
+/* PSXRecomp oracle: function-call (JAL/JALR/J/JR) trace callback.
+ * Fired by PS_CPU on every J/JAL/JR/JALR retire, BEFORE the link write.
+ * Caller filters by target_pc to keep volume bounded.
+ * caller_pc = PC of the J/JAL/JR/JALR instruction
+ * target_pc = computed branch target
+ * parent_ra = GPR[31] at time of call (parent's return address)
+ * a0, a1 = argument registers at time of call
+ * kind = 1=J, 2=JAL, 3=JR, 4=JALR */
+typedef void (*psxrecomp_fntrace_cb_t)(uint32_t caller_pc, uint32_t target_pc,
+ uint32_t parent_ra, uint32_t a0,
+ uint32_t a1, uint8_t kind);
+extern psxrecomp_fntrace_cb_t g_psxrecomp_fntrace_cb;
+
+/* PSXRecomp oracle: CD command trace callback.
+ * Fired by PS_CDC just before a command handler executes.
+ * cmd = command byte (0x01 GetStat, 0x02 Setloc, 0x06 ReadN, 0x09 Pause,
+ * 0x0A Init, 0x0E Setmode, 0x15 SeekL, 0x1A GetID, 0x1B ReadS, ...)
+ * nargs = number of argument bytes
+ * arg0..arg2 = first three argument bytes (0 if absent) */
+typedef void (*psxrecomp_cdcmd_cb_t)(uint8_t cmd, uint8_t nargs,
+ uint8_t arg0, uint8_t arg1, uint8_t arg2,
+ uint32_t pc);
+extern psxrecomp_cdcmd_cb_t g_psxrecomp_cdcmd_cb;
+
#endif