From fc85d8b34db407b4ed902e7946fc0f66143840f9 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Sat, 27 Jun 2026 12:59:37 -0700 Subject: [PATCH] =?UTF-8?q?tooling:=20interp-path=20cycle=20ruler=20?= =?UTF-8?q?=E2=80=94=20emit=20cyc=5Fobserve=20from=20the=20dirty=20interp?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dirty-RAM interpreter charged cycles but emitted no cyc_observe, so interp-executed PCs were not anchorable by cyc_watch — interp-side cycle work (the muldiv + GTE completion-stalls just added) could only be validated by construction, not measured. Add a gated debug_server_cyc_observe(pc) at the top of the interp's per-instruction loop (parity with the compiled emitter's block-leader observe), so any interpreted PC becomes a cyc_watch anchor. Gated #ifndef PSX_NO_DEBUG_TOOLS (zero prod overhead); early-returns when cyc_watch is disarmed. Validated: Tomba2 boots to FMV no-regression (safe over 150M+ interp insns); a live overlay interp loop (0x8010724C) records 10 stable cyc_watch hits at 38 cycles/iter, parity with compiled-PC anchoring. This is the interp-path ruler enabler; a fully isolated single-component interp loop (cyctest dirty-RAM loop) remains TODO — the cyctest harness does not route indirect jumps to the dirty interp (dirty_ram_insns=0 there), so that fixture needs interp-dispatch wiring. Co-Authored-By: Claude Opus 4.8 --- runtime/src/dirty_ram_interp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/runtime/src/dirty_ram_interp.c b/runtime/src/dirty_ram_interp.c index 372728ed..0f9e91b8 100644 --- a/runtime/src/dirty_ram_interp.c +++ b/runtime/src/dirty_ram_interp.c @@ -1866,8 +1866,17 @@ static int dirty_ram_dispatch_inner(CPUState* cpu, uint32_t addr, uint32_t stop_ enum { MAX_INSNS_PER_DISPATCH = 1000000 }; uint32_t pc = addr; int insns_executed = 0; +#ifndef PSX_NO_DEBUG_TOOLS + extern void debug_server_cyc_observe(uint32_t block_leader_phys); +#endif for (int i = 0; i < MAX_INSNS_PER_DISPATCH; i++) { uint32_t next_pc = 0; +#ifndef PSX_NO_DEBUG_TOOLS + /* Interp-path cycle ruler: make every interpreted PC anchorable by + * cyc_watch (parity with the compiled emitter's block-leader observe). + * Early-returns when cyc_watch is disarmed → ~free in normal runs. */ + debug_server_cyc_observe(pc & 0x1FFFFFFFu); +#endif uint32_t insn = fetch_word(pc & 0x1FFFFFFFu); uint32_t before_s0 = cpu->gpr[16]; uint32_t before_ra = cpu->gpr[31];