Files
psxrecomp/docs/beetle_guest_cycles_hook.patch
Shokunin 055c33aaa1 docs: complete the Beetle oracle patch set; add a macOS recipe
beetle-linux.md told builders to re-land several Beetle hooks by hand
because they were never exported to docs/: the rtrace and IRQ callbacks,
the guest-cycle clock, PS_CDC::PSXRecomp_GetDecodeVolume and the CDC/DMA
IRQ peeks that beetle_libretro.cpp links against, plus the SPU event
calls and the per-instruction hook the real cyc_watch now needs. Ship
them, one patch per hook, all against the 5759277b base:

- beetle_rtrace_irq_hook.patch
- beetle_guest_cycles_hook.patch
- beetle_cdc_dma_peek_hook.patch
- beetle_spu_event_hook.patch

beetle_cdcmd_trace_hook.patch was generated from a tree that already had
the wtrace block, so applying it after the wtrace patch redefined the
wtrace globals and typedefs and needed a manual fix. It is now a diff on
top of the wtrace and SIO patches; the resulting tree is unchanged.

beetle-macos.md is the macOS recipe, verified from a fresh official
clone on Apple Silicon: `git apply` of all seven patches, the osx static
build, the psx-beetle configure on current master, OpenBIOS under the
retail file names (Beetle otherwise boots its own, different OpenBIOS),
headless runs through SDL's dummy driver, and ruler #2 as the check
before trusting a build. beetle-linux.md now uses the full patch set,
notes the current configure requirements and the headless option, and
gives psx-beetle's actual default port (4382).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 11:06:14 -07:00

85 lines
3.3 KiB
Diff

diff --git a/libretro.cpp b/libretro.cpp
index 0924113d..5258d66e 100644
--- a/libretro.cpp
+++ b/libretro.cpp
@@ -514,6 +514,13 @@ psxrecomp_fntrace_cb_t g_psxrecomp_fntrace_cb = NULL;
psxrecomp_cdcmd_cb_t g_psxrecomp_cdcmd_cb = NULL;
psxrecomp_rtrace_cb_t g_psxrecomp_rtrace_cb = NULL;
psxrecomp_irq_cb_t g_psxrecomp_irq_cb = NULL;
+psxrecomp_pc_cb_t g_psxrecomp_pc_cb = NULL;
+uint64_t psxrecomp_guest_cycle_base = 0;
+
+extern "C" unsigned long long beetle_core_get_guest_cycles(void)
+{
+ return (unsigned long long)psxrecomp_guest_cycle_base;
+}
MultiAccessSizeMem<512 * 1024, uint32, false> *BIOSROM = NULL;
MultiAccessSizeMem<65536, uint32, false> *PIOMem = NULL;
@@ -1277,6 +1284,9 @@ static void PSX_Power(void)
PSX_PRNG.c = 6543217;
PSX_PRNG.lcgo = 0xDEADBEEFCAFEBABEULL;
+ /* PSXRecomp oracle: the guest clock restarts at power-on. */
+ psxrecomp_guest_cycle_base = 0;
+
cd_warned_slow = false;
memset(MainRAM->data32, 0, 2048 * 1024);
@@ -4825,6 +4835,10 @@ void retro_run(void)
assert(timestamp);
+ /* PSXRecomp oracle: fold this frame's CPU timestamp into the absolute
+ * guest clock before the per-frame timestamp rebase below. */
+ psxrecomp_guest_cycle_base += (uint32_t)timestamp;
+
ForceEventUpdates(timestamp);
espec->SoundBufSize = IntermediateBufferPos;
diff --git a/mednafen/psx/cpu.cpp b/mednafen/psx/cpu.cpp
index cbfd20dc..d1789712 100644
--- a/mednafen/psx/cpu.cpp
+++ b/mednafen/psx/cpu.cpp
@@ -709,6 +709,11 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in)
}
#endif
+ /* PSXRecomp oracle hook: per-instruction PC + absolute guest cycle,
+ * sampled before this instruction's fetch is charged. */
+ if(MDFN_UNLIKELY(g_psxrecomp_pc_cb))
+ g_psxrecomp_pc_cb(PC, psxrecomp_guest_cycle_base + (uint32_t)timestamp);
+
//
// Instruction fetch
//
diff --git a/mednafen/psx/psx.h b/mednafen/psx/psx.h
index 05122c8c..0e61795d 100644
--- a/mednafen/psx/psx.h
+++ b/mednafen/psx/psx.h
@@ -198,4 +198,24 @@ extern psxrecomp_rtrace_cb_t g_psxrecomp_rtrace_cb;
typedef void (*psxrecomp_irq_cb_t)(int which);
extern psxrecomp_irq_cb_t g_psxrecomp_irq_cb;
+/* PSXRecomp oracle: absolute guest CPU clock. psxrecomp_guest_cycle_base is
+ * the sum of the CPU timestamps of every completed retro_run() frame since
+ * the last power-on (timestamps restart at 0 each frame), so inside a frame
+ * the absolute guest cycle of an instruction is base + the CPU's running
+ * timestamp. beetle_core_get_guest_cycles() returns the base, i.e. the exact
+ * clock between frames, where the debug server samples it. */
+extern uint64_t psxrecomp_guest_cycle_base;
+extern "C" unsigned long long beetle_core_get_guest_cycles(void);
+
+/* PSXRecomp oracle: per-instruction PC hook (drives the cyc_watch
+ * comparator). Fired by PS_CPU::RunReal before EVERY instruction fetch,
+ * i.e. before any cycle of that instruction (fetch, execute, stalls) is
+ * charged.
+ * pc = guest PC about to be fetched
+ * guest_cycles = absolute guest cycles charged for all prior instructions
+ * NULL (the default) costs one predicted-not-taken branch per instruction;
+ * the listener should install it only while a watch is armed. */
+typedef void (*psxrecomp_pc_cb_t)(uint32_t pc, uint64_t guest_cycles);
+extern psxrecomp_pc_cb_t g_psxrecomp_pc_cb;
+
#endif