mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
26d03e643a
Root cause of the dwarf-village->overworld native-overlay blue screen (deterministic, every run, at native call #70): The dirty-RAM interpreter's jal/jalr handlers preserved the call's return-address obligation for statically-compiled callees (psx_dispatch_game_compiled -> *next_pc_out = return_pc) and for non-local targets (dispatch_nonlocal_call passes return_pc as stop_addr), but a LOCAL DIRTY target fell through to a bare `cpu->pc = target; return 1`. When that target was a NATIVE overlay candidate, the dispatch loop ran it as a C function; its pc==0 return made psx_dispatch_impl unwind to the C caller, silently dropping the suspended interpreted caller's continuation. Concrete case: main-EXE field dispatcher F=0x800338A8 (undiscovered entry — discovery walked it from 0x338B0; reached via data-table jalr, so it runs under the interpreter) does `jal 0x8011B0D4` per entity. Under native exec F's epilogue never ran: guest sp leaked 0x18 per entity until the world corrupted. Interp-only mode is immune (the callee's own jr ra is interpreted). Evidence chain (full-register fingerprint diff ON vs OFF localized first divergence to sp at exec #70; frozen dirty-block ring shows F re-entered at sp-0x18 with no epilogue between) is in docs §9 and the session memory. Fix: overlay_loader_call_native() — run a validated native candidate as a unit and resume at return_pc — called from both jal and jalr handlers, mirroring the psx_dispatch_game_compiled contract exactly. Side effect: native overlay coverage rose from 86 calls per village->overworld run to ~112,000 (interpreted calls now route to native candidates). Verified: dwarf->overworld renders and plays under native overlays. Same-scene A/B: host CPU 7.31 (native) vs 7.83 (interp) CPU-s per 15s wall. Timing hypotheses refuted by experiment before fixing: IRQ-check cadence (suppress armed, identical failure) and device-event granularity (conservative stepping, identical failure). Also in this commit (the capture tooling that found it): - fingerprint ring now stores full entry/exit register files + guest cycle (FP_CAP 2^20 crc-only -> 2^16 full-reg); overlay_fp_dump takes a path. - insn_freeze <addr> <nth>: one-shot latch freezing insn/block/flow/fntrace rings immediately BEFORE the Nth dispatch of a watched candidate, so the pre-divergence window survives until dumped (ring-first, no arm-then-hope). - dirty_insn_gate <lo> <hi>: runtime-settable extra PC range for the per-insn interp log; native entry/exit markers (transferred=200/201) in that ring. - dirty_insn_dump_file: file dump for the insn ring — the 16MB inline TCP dump wedged the single-threaded debug server. - freeze gating in the flow-log path gates ONLY the ring write, never flow. - debug_client plumbing for the new commands + fntrace_arm/fntrace_dump. - docs/overlay-recompilation-design.md §9: the dispatch call-contract invariant, the Bug A case study, verification numbers, and known residue (data-jump-table discovery-miss class; one unreproduced black-screen one-off; §8.3 tail-position caveat). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>