Commit Graph

26 Commits

Author SHA1 Message Date
Matthew Stanley 5b7e69b441 Remove the sljit Tier-2 in-process JIT backend
The tier mis-compiled and has been hard-gated off since 2026-06-25; overlay
gaps fall to gcc/tcc shards and the dirty-RAM interpreter, unchanged.

Re-do on master of the removal validated on the stale branch (6da8c02),
reconciled with everything master grew since the fork:

- Delete overlay_sljit.{c,h}, overlay_compile_worker.{c,h}, lib/sljit,
  runtime/tests/sljit_*.c, and the runtime.cmake sources/include-dirs.
- Strip the sljit provider/backend from code_provider.{c,h},
  overlay_backend.{c,h} ("sljit" in overlay_backend/PSX_OVERLAY_BACKEND now
  degrades to auto so stale configs still boot), main.cpp, debug_server.c
  (sljit_status/sljit_async/sljit_try TCP commands), overlay_loader.{c,h}
  (live policy, JIT-on-miss, persisted-shard cache, async worker glue,
  psx_sljit_call/cop2/memx host helpers — reachable only via the removed
  cpu->sljit_helpers table), and cpu_state.h (SLJIT_HLP_* enum + the
  sljit_helpers[] member; CPUState layout change reshards via the cg tag).
- Master-only entanglement resolved before deletion: 28165d6's Einhander WS
  player-X bound emit existed in three paths (gcc code_generator, sljit,
  dirty_ram_interp). The gcc shard link contract now forwards
  psx_ws_player_x_bound (ABI v13, previous commit), so deleting the sljit
  emit path loses no widescreen behavior; ffc6688's game-dispatch block
  inside the dead psx_sljit_call helper goes with it.
- Reword comments that described sljit as a live backend (gpu.{c,h},
  ws_*_detect.h, cosim_state.c, dirty_ram_interp.c, autocompile.h,
  boot_state.h, config_loader.h, compile_overlays.py, and the CPS marker
  comments the recompiler emits).

recompiler psxrecomp-game builds clean; game runtimes rebuilt + boot-verified
separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 22:22:38 -07:00
Matthew Stanley 0e5c38d558 feat: add generic CD-DA playback and CUE pregaps
Adapted from https://github.com/mstan/psxrecomp/commit/0e9df70ca2da6ec67c8e7a1f3e5aaa025fddc369

Co-authored-by: Kareem Olim <kareemolim@gmail.com>
2026-07-14 20:03:02 -07:00
Matthew Stanley 7ae052534a spu: add hardware Gaussian interpolation
Port the reusable Gaussian interpolation slice from PR #16 while keeping its reverb engine on a separate branch.

Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 18:56:51 -07:00
Matthew Stanley 19e526d57d gpu: reject oversized hardware primitives
Apply the PS1 1023x511 size rule to every polygon triangle, line, and polyline continuation before widescreen transforms while preserving textured-command state latching.

Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 18:35:07 -07:00
Matthew Stanley 928085d5fb gpu: use exclusive PS1 software-raster edges
Adapt PR #14's biased 32.32 triangle edge DDA and half-open bottom/right coverage across the software raster paths, with a focused pure edge-math regression test.

Source: https://github.com/mstan/psxrecomp/pull/14

Source-commit: https://github.com/mstan/psxrecomp/commit/91a654f61dff18806f3eea43f72a30cb34430695

Co-authored-by: Kareem Olim <kareemolim@gmail.com>
2026-07-14 18:34:32 -07:00
Matthew Stanley c2b9d467e5 runtime: discover decodable unmarked overlay code
Admit post-EXE RAM to the dirty interpreter only after a real control transfer and an existing MIPS decodability check. Keep the already-merged overlay initialization guard out of this focused change.

Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-14 18:12:08 -07:00
Matthew Stanley e247155cd6 overlay: use platform-native shared-library suffixes
Adapted from NyperYuhgard's PR #13 POSIX overlay work.

Co-authored-by: NyperYuhgard <93950153+NyperYuhgard@users.noreply.github.com>
2026-07-14 15:58:34 -07:00
Matt Stanley f88828327e Merge pull request #18 from mstan/fix/posix-overlay-export-scan-douglas
Load cached overlay exports on POSIX
2026-07-14 00:03:48 -07:00
Matt Stanley a03350fb68 Merge pull request #23 from mstan/fix/overlay-init-guard-shaneomac
Guard overlay entry points before initialization
2026-07-14 00:03:41 -07:00
Matt Stanley 23edb88b68 Merge pull request #20 from mstan/fix/cdrom-encoded-interrupts-kem0x
Fix encoded CD-ROM interrupt mask matching
2026-07-14 00:03:38 -07:00
Matthew Stanley c0fa9b1e89 overlay: load cached exports on POSIX
Port Douglas Vanderveer's manifest-driven macOS/Linux overlay discovery onto the current cache index and exact-dispatch loader. Validate both filename key fields, preserve ABI cleanup and rescan idempotence, and cover the POSIX primitives with a real shared-library fixture.

Co-authored-by: douglasjv <vanderveerdouglas@gmail.com>
2026-07-13 21:52:25 -07:00
Matthew Stanley 5b8098a9d9 runtime: guard overlay loader before initialization
Adapt only the fail-closed overlay lifecycle checks from mstan/psxrecomp#16 commit e6809ccf7d. Add a focused structural regression test and record the excluded dirty-interpreter and game/runtime work.

Co-authored-by: Martin Penkava <mpenkava1337@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 21:48:39 -07:00
Matthew Stanley 76ddcc008a test(spu): pin END-only voice mute semantics
Document the overlap with kem0x PR #14 and explicitly record the game-specific and unrelated changes excluded from this branch.

Co-authored-by: Kareem Olim <kareemolim@gmail.com>
2026-07-13 21:40:11 -07:00
Matthew Stanley 52eb8e8a24 Fix encoded CD-ROM interrupt mask matching
Adapt the CD interrupt mask/status comparison from kem0x PR #14 and centralize it in a tested helper. The HC05 exposes numeric INT1..INT5 reasons but gates the line with a direct mask/status AND, so the common 0x07 mask must also pass INT4 and INT5.

Source: https://github.com/mstan/psxrecomp/pull/14

Source-commit: https://github.com/mstan/psxrecomp/commit/dd9e5c65073dd41fbac2d965d2f945a866ec59e2

Co-authored-by: Kareem Olim <kareemolim@gmail.com>
2026-07-13 21:38:58 -07:00
Matthew Stanley 9b54441a8a repo cleanup: drop 16 stale handoff/overnight/resolved-bug docs; untrack scratch scripts + test binaries
Deleted dated session-scratch (HANDOFF*, OVERNIGHT*, SUMMARY*) and resolved-bug
writeups (memcard audit, Tomba2 attract/FMV/Whoopee, codex analysis, load4 wedge
superseded). KEPT docs still cited by canonical docs/source (RECURSION_BUG,
WEDGE_load4, CASE_A_AOT_GAP) and all current subsystem-design docs.
Untracked (gitignored, kept on disk): tools/_options_session/ (80 scratch scripts
w/ hardcoded paths) + runtime/tests/*.exe (build artifacts; .c sources stay).
Genericized sibling-project absolute paths in bios_hle.h + crash_trace.c; fixed
README link CURRENT_STATE->FAITHFUL_TIMING_PLAN.
2026-07-06 11:34:35 -07:00
Matthew Stanley 01b4d91e8a WIP: forward-port Ape Escape framework (dev/ape-escape) onto master
Comprehensive 3-way merge of the dev/ape-escape branch into master for
the isolated wt/ape-escape worktree. Brought in every complementary ape
change automatically (dma.c, memory.c interrupt byte/half lanes,
function_analysis discovery, main_bios vector seeds, iso_reader
iso_sector_count, config_loader, crash-trace fields, headers). Resolved
the 13 conflicts by rule:

  - CPS dispatch / recompiler (code_generator, full_function_emitter,
    dirty_ram_interp, traps, main_psx) -> KEEP MASTER (evolved system).
  - CD model (cdrom.c) -> take ape (the model that booted Ape).
  - interrupts.c/.h -> reverted to MASTER (ape's interrupt-delivery is
    dispatch-coupled; the merged Frankenstein stormed).
  - sio/debug_server/freeze_heartbeat/main.cpp -> keep master (evolved).
  - crash_trace.c -> master (ape freeze-dump fns omitted to avoid the
    debug-ring cascade).

STATUS: does NOT boot yet. ape-cdrom + master-interrupts derails to
PC=0 at frame ~2092 (insta-close); ape-cdrom + ape-interrupts storms
(297M exception re-entries) at frame 2077. Root finding: ape's CD
anti-storm is coupled to ape's interrupt-delivery + call-depth + EPC
model, which master replaced with CPS continuation-passing, so the CD
subsystem does not forward-port as pure hardware simulation. Preserved
as the reference state for a design consult on bridging the two models.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 15:55:23 -07:00
Matthew Stanley b001e03ce4 sljit: block-local GPR register allocation for overlay shards
Replace the memory-backed GPR model (every instruction loaded operands
from cpu->gpr[] and stored results back) with a block-local cache of MIPS
GPRs in host SAVED registers (S2..). Saved registers are callee-saved, so
they survive the read_/write_ icalls a plain load/store emits; the cache is
only flushed/reset at basic-block boundaries and around helper/call icalls
that touch cpu->gpr[] in memory. Prologue cost scales with register
pressure (saveds sized per-fragment from a distinct-GPR popcount).

Invariants: flush dirty->memory before every control transfer and before a
helper/call that reads gpr; reset (drop mappings) at branch-target labels
(after a preceding flush) and after any helper/call that writes gpr; gpr[0]
never cached.

Validation:
- Standalone emitter harness 66/66 (runtime/tests/sljit_emit_test.c).
- Offline differential (runtime/tests/sljit_offline_diff.c, --dump): the
  new emitter is BYTE-IDENTICAL to the pre-regalloc (HEAD) emitter across
  313 overlay functions, run from identical state + call stub. This is the
  primary correctness proof — stronger than a single live diff.
- sljit_sp_probe.c: $sp-balance probes (prologue/epilogue, branch-delay,
  register-sp, calls, loops).

A regalloc bug can only surface as a same-state differential divergence
(shards run live only after passing it), never a silently-wrong shard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:52:30 -07:00
Matthew Stanley e5aea83496 sljit emitter: broad coverage — jr-tables, GTE/COP2, unaligned mem, link branches
One batch widening to cover most remaining overlay instruction classes;
validated end-to-end via the live differential rather than per-op.

- jr rX (computed / jump tables): CTRL_TAILJUMP — capture the target before
  the delay slot, then cpu->pc = target and return; the dispatch loop
  re-dispatches (matches the interpreter's jr). jr $ra stays the clean-
  return terminator. (A terminator-less tail-dispatch fragment still
  declines — safe.)
- GTE/COP2: MFC2/CFC2/MTC2/CTC2/cmd (op 0x12), LWC2 (0x32), SWC2 (0x3A)
  via psx_sljit_cop2(), a runtime helper mirroring the interpreter exactly.
- Unaligned mem: LWL/LWR/SWL/SWR via psx_sljit_memx(), likewise.
- BLTZAL/BGEZAL: link $ra = pc+8 before the delay slot, then branch.

Helper-emitted classes get parity by construction (the helper IS the
interpreter's code). Harness 66/66 (regression); runtime builds clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:52:29 -07:00
Matthew Stanley ad1e5a223f sljit emitter: jal/jalr with the call contract (+ KSEG entry fix)
The big coverage unlock — the emitter now accepts functions that call
others, not just leaves.

- New runtime helper psx_sljit_call(cpu,target,return_pc,check) in
  overlay_loader.c, mirroring the dirty-RAM interpreter's call path
  EXACTLY (psx_dispatch_game_compiled / overlay_loader_call_native /
  psx_dispatch_call + g_psx_call_bail + psx_call_contract). Returns 1 if
  the shard must return immediately (transfer/bail unwind), 0 to continue.
- Emitter: jal/jalr -> CTRL_CALL. Link reg + (jalr) dynamic target are
  written BEFORE the delay slot; the helper is called AFTER it via S2
  (helper address) with the result driving a conditional return. jal uses
  an absolute target, jalr a dynamic one captured in S1.
- KSEG integration fix: the loader now passes the VIRTUAL entry to the
  emitter (was passing phys with image_base_vram=0), so return_pc and
  jal/J targets carry the KSEG bits the guest uses. The standalone harness
  passed a virtual entry, which is why it stayed green; the live
  differential caught the phys/virtual mismatch (saved $ra off by the
  0x80000000 KSEG bit).

Verified: sljit_emit_test.c extended (jal, jalr, transfer/bail) -- 66/66
pass. Full psx-runtime build clean. Live differential re-validation of
the KSEG fix is the next step.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:52:29 -07:00
Matthew Stanley 355171662b sljit emitter: DIV/DIVU (guarded idiv) + J (absolute jump)
- DIV/DIVU emit branch-guarded host DIVMOD_S32/U32: the divide-by-zero
  and INT_MIN/-1 cases (which trap host idiv) are intercepted with local
  sljit branches BEFORE the divide, reproducing the interpreter's defined
  results exactly (lo=(a<0)?1:-1,hi=a for /0; lo=INT_MIN,hi=0 for the
  signed overflow; lo=0xFFFFFFFF,hi=a for unsigned /0).
- J (absolute): fragment-relative target = (target26<<2 & 0x1FFFFFFF) -
  entry_phys; reuses the branch machinery's unconditional arm. classify_
  control now takes entry_phys; CTRL_JUMP added.

Verified: sljit_emit_test.c extended (signed/unsigned div incl. /0 both
signs + INT_MIN/-1 overflow; forward-J skip) — 52/52 pass. Full
psx-runtime build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:52:29 -07:00
Matthew Stanley 9123784a6a sljit emitter: internal conditional branches + mult/hi-lo
Widen the leaf emitter from single-block to multi-block leaf functions,
and add the mult/hi-lo arithmetic family.

- Two-pass compile: pass 1 linearly scans to the jr $ra terminator,
  collecting PC-relative branch targets + delay-slot offsets and
  validating targets (in-range, 4-aligned, not landing on a delay slot);
  pass 2 emits an sljit label per target, captures each branch predicate
  into a saved reg (S1) BEFORE its delay slot, emits the delay slot, then
  the conditional jump, and binds jumps to labels at the end.
- Branches: BEQ/BNE/BLEZ/BGTZ/BLTZ/BGEZ.
- Arithmetic: MULT/MULTU (64-bit sign/zero-extend -> MUL -> split into
  hi/lo) + MFHI/MFLO/MTHI/MTLO.
- Still deferred to the interpreter (abort): J/JAL/JALR, BLTZAL/BGEZAL,
  DIV/DIVU, LWL family, GTE/COP, jr-tables.

Verified: sljit_emit_test.c extended with loops (backward branch),
forward conditionals (max), and signed/unsigned mult — 40/40 pass. Full
psx-runtime build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:52:29 -07:00
Matthew Stanley 4e932d05fe sljit §7 step 4 (first slice) + step 5: leaf-function emitter, diff-gated
The MIPS->sljit emitter, paralleling dirty_ram_interp.c's exec_one but
emitting host code. First slice = single-block LEAF functions ending in
`jr $ra`; everything else aborts the whole fragment to the interpreter
(precision over recall).

- overlay_sljit.c: memory-backed GPRs, 32-bit ops (R3000A wraparound),
  loads/stores via icall through the cpu read/write callbacks (same
  MMIO/page-watch/dirty-tracking path as the interpreter). Opcodes:
  SLL/SRL/SRA/SLLV/SRLV/SRAV, ADD(U)/SUB(U), AND/OR/XOR/NOR, SLT(U),
  ADDI(U)/SLTI(U)/ANDI/ORI/XORI/LUI, LB/LH/LW/LBU/LHU/SB/SH/SW, SYNC.
- compile_fragment now returns CompiledFragment{fn,code_lo,code_len};
  overlay_loader registers an sljit candidate (crc hashed from the live
  bytes it JIT'd from, same live-byte validation as gcc candidates).
- Step 5 wiring is VALIDATION-GATED: JIT-on-miss fires only when
  backend==sljit AND the same-state differential is armed (never in a
  shadow run). Normal play stays byte-identical; live sljit execution
  stays off until the differential gate passes.
- Debug: sljit_try <addr> probe; sljit_status reports shards_registered.

Verified: runtime/tests/sljit_emit_test.c (links overlay_sljit.c +
sljitLir.c only) JITs hand-built leaf functions and checks output —
26/26 pass incl. gpr0 invariant, sign/zero-ext, signed/unsigned compare,
and the decline cases. Full psx-runtime build clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 12:52:29 -07:00
Matthew Stanley 70c1eb8a51 Merge master (Bug B pacing fix + overlay cache campaign) into dev/ape-escape
Brings the ape-escape framework (real-EPC interrupt delivery,
g_psx_call_depth safe-point gating, continuation-entry codegen, DualShock
controller config, exception/entryint/irq-check rings, interp
dispatch_until + exit logging + static-text fallback) up to master
035a9fa (frame-pacing TOCTOU fix, vsync self-heal, watchdog classifier,
overlay JIT cache + autocapture/autocompile, Bug A interp call contract,
prod build hygiene).

Key reconciliations (ambiguity resolved toward master per plan):
- interrupts.c: real-EPC delivery (psx_check_interrupts_at) is the
  primary entry; the legacy psx_check_interrupts wrapper keeps the
  0x80000048 sentinel fallback (ape tip restored it), so old-flavor
  generated code keeps working interrupt delivery. Master's bottom
  compat shim removed (would have recursed). Both observability paths
  kept (event-ring gate outcomes + ape irq-check ring; new
  GATE_CALL_DEPTH/GATE_BAD_RESUME kinds).
- Claimed-IRQ cooldown: master's per-source CLAIMED_PROGRESS_QUANTUM=8
  (SIO exempt) over ape's unconditional 0; ape's 2026-06-05 starvation
  note kept as a MERGE NOTE — first knob to A/B if title-LOAD regresses.
  (User-validated: Tomba title-LOAD slot1/slot2 renders and plays on
  this merge — the historical ape-fw blackout is gone.)
- dirty_ram_interp.c: ape's dispatch_until_impl structure (stop_addr,
  static-text fallback, exit-log ring) with master's Bug A call contract
  (psx_dispatch_game_compiled + overlay_loader_call_native at interp
  jal/jalr), overlay loader/fingerprint entry checks, freeze-gated flow
  ring, chain-target attribution, and interp-active bracketing on the
  public wrappers. Ape tip's final local-flow rule kept: dirty game-text
  NEVER chains locally (the tip reversed its own earlier widening).
- freeze_heartbeat.c: classifiers composed — per-frame storm threshold
  (master) + progress-counter gating and 6s window (ape) + slow_frames
  kind (master), thresholds rescaled to the window.
- code_generator.cpp: ape's continuation-entry switch + master's
  refactored scan_jr_tables.
- debug_server.c: ape originals win over master's ported copies
  (bioscall ring, card trace with the richer address set); master-only
  tools kept (dirty_*_dump_file, card_trace filter handler).
- dma.c/cdrom.c: master's overlay-capture path + ape's DMA/command
  history diagnostics, both wired.

Builds: recompiler + Tomba runtime (legacy gen x merged runtime).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 22:05:13 -07:00
Matthew Stanley ffc15219fd fix: Bug B hard freeze — TOCTOU underflow in frame pacing slept ~24.7 days
Root cause (freeze dump 1781046069, addr2line-verified): the wall-clock
pacing loop in sdl_vblank_present read the performance counter twice per
iteration — once in the while-condition, once to compute remaining time.
If the frame deadline was crossed between the two reads (a preemption
window), the unsigned subtraction underflowed, *1000 overflowed, and
SDL_Delay received ~2^64/10^4 ms mod 2^32 ≈ 24.7 days at the standard
10 MHz QPC. The app parks in one SDL_Delay with every counter frozen,
wedged between 'i_stat |= VBLANK' (interrupts.c:286) and exception
delivery — which is why dumps showed i_stat=1 pinned. VBLANK was being
ACK'd every frame right up to the wedge in every dump examined; the
'VBLANK-ACK reentry storm' framing was a symptom misread.

- frame_pacing.c/h: shared race-free pacer (single counter read per
  decision, compare-before-subtract, sleep clamped to one period).
  Both call sites switched: main.cpp sdl_vblank_present and
  beetle_main.cpp. Unit test pins the underflow case.
- Vsync self-heal: the 'recovered storm' dump (1781045865) was 8/8
  main-thread samples inside wglSwapBuffers — NV driver blocking ~1.5s
  per present. After 3 presents >250ms, drop driver vsync (we pace to
  59.94 Hz ourselves). Counters surfaced in the freeze dump.
- Watchdog classifier: 'reentry_storm' threshold (5K/sec) was below the
  healthy baseline (~2K reentry blocks/frame × 30-60 fps), mislabeling
  normal play and host stalls — that label misdirected two prior
  investigations. Now normalized per frame (>20K/frame), plus a new
  slow_frames kind (<5 fps) with multi-sample stack capture.

Verified: test_frame_pacing ALL PASS; live Tomba paced at 59.962 fps
over 20s (single-process measurement).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-09 20:09:25 -07:00
Matthew Stanley 834f440161 WIP: ape-fw title-LOAD debugging snapshot (uncommitted session work)
Captures in-progress Ape Escape framework + Tomba title-LOAD investigation:
- recompiler/src/function_analysis.cpp: hardened is_bios_dispatch_thunk pass
  (never gut a li/jr candidate already inside a discovered function).
- runtime: cdrom/dma/dirty_ram_interp/interrupts/sio/traps/debug_server +
  freeze_heartbeat + main.cpp debugging deltas; SIO card-protocol test updates.

Snapshot commit to consolidate loose WIP onto dev/ape-escape; not a finished
change. See handoff: title-LOAD black = card op-loop never delivering SwCARD
spec 0x8000 completion event.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 23:04:23 -07:00
Matthew Stanley 5b3c7dd08d phase4: freeze_check diagnostic + accumulated overnight infra
Today's session work
- runtime/src/debug_server.c: freeze_check TCP cmd snapshots all liveness
  counters (PC, exception entries, exception_reentry_blocks, dirty_ram
  blocks/insns, fn_entry total + top-K histogram, SIO IRQ pending +
  countdown, sio_stat/i_stat/i_mask, mc_max_state) for tight-loop
  diagnosis when the runtime hangs
- runtime/src/debug_server.c: wtrace slot 12 covering [0x72F0..0x72F4]
  (data-byte counter); measured peak=1 confirming setup_data_phase resets
  the counter mid-cycle
- runtime/src/interrupts.c: exception_entries_total + exception_reentry_blocks
  counters + psx_get_freeze_diag accessor
- runtime/src/sio.c: sio_get_freeze_diag accessor for IRQ pacing internals
- memory entries: phase4_card_gate_setup_writes_2026_04_28 (refutes the
  prior \"missing [0x7568+slot] clearer\" framing — setup functions write
  LSB=0 directly), phase4_72f0_one_byte_per_vblank_2026_04_28 (peak=1
  measurement)

Overnight session work bundled in
- runtime/src/dirty_ram_interp.{c,h}: small MIPS interpreter for
  install-at-runtime kernel RAM stubs (Rule 18; e.g., SIO data-byte
  handler the BIOS writes to RAM 0xCF0)
- recompiler/src/full_function_emitter.{cpp,h}: 3057 jal/jalr continuation
  return labels added to dispatch table (continuation fix); branch
  fall-through fix for conditional-branch delay slots
- recompiler/src/strict_translator.cpp: matching translator updates
- generated/: regenerated SCPH1001 dispatch + full C output reflecting
  the recompiler changes
- runtime/include/debug_server.h, src/memory.c: card_txn ring + SIO IRQ
  ring + EvCB walk ring + per-PC dirty_ram counters + always-on wtrace
  coverage for [0x755A], [0x7514], [0x7528], [0x7520], [0x74A4],
  [0x75C0..C8], [0x74B8..C0]
- recompiler/tests/, runtime/tests/: SIO/card protocol unit tests
- tools/: 60+ Python diagnostic scripts (chain_trace analysis, SIO trace
  diff, EvCB walker, wtrace dumpers, etc.)
- docs/: psx_bios_disasm reference + dynamic_handler_install rationale
- OVERNIGHT_2026_04_28.md, SUMMARY_20260412_2.md: session logs

Phase 4 status (still blocked, root cause better understood)
- BIOS card chain runs through R-handlers correctly (chain_trace shows
  R1..R11 cycling 26x); shell IS retriggering _card_read
- Install handler at ROM 0xBFC15F1C increments [0x72F0] correctly per
  SIO IRQ; gate at <0x80 / <0x7F is correct
- Bottleneck: SIO IRQ countdown is access-paced, fires ~1× per VBlank
  vs ~128× needed; setup_data_phase (called from outer coord on VBlank)
  zeroes [0x72F0] before counter reaches 128
- Both attempted fixes today caused freezes:
  * VBlank deferral on [0x75C0]==1 — pending VBlank already in i_stat
    delivered anyway; tighter dispatch gate also locked up
  * Cycle-paced sio_tick at rates 50, 200 (and gated on [0x75C0]==1) —
    runtime freezes once data phase activates, root cause unidentified
- delay=5 narrows the threshold (peak=2 vs peak=1, no freeze) but
  doesn't get to 0x80
- Real fix likely requires deeper instrumentation of the freeze (call
  stack / dispatch state at the moment of TCP starvation)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 21:29:19 -07:00