Files
psxrecomp/docs/internal/WEDGE_load4_shell_rootcause.md
T
Matthew Stanley 77d036bfb6 docs: production-friendly contributor docs + declutter root
Make the repo approachable for outside contributors and an IDE integrator:

- New contributor docs:
  - docs/EXECUTION_MODEL.md — semi-technical tour of the static / native-overlay
    / interpreter execution model and how coverage gaps get plugged.
  - docs/ARCHITECTURE.md — component-level view (recompiler, runtime, LLE+HLE
    BIOS, overlay backend static->gcc->tcc, renderers, oracle model).
  - docs/BUILDING.md — accurate dependency table, per-platform prereqs, build
    steps, and the submodule + local-junction framework-linking setup.
  - CONTRIBUTING.md — distilled core rules, verification expectations, PR flow.
- Declutter root: move ~14 internal dev-logs into docs/internal/ (history
  preserved via rename) with an index README; repoint CLAUDE.md references.
- README: correct build recipe, LLE+HLE + overlay-backend summary, links to the
  new docs.
- code_provider.c: update the stale backend comment to the real static->gcc->tcc
  resolution (dead SLJIT.md reference removed).
- config_schema.md: drop the obsolete "once Phase A2/A3 ships --config" note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:25:16 -07:00

88 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# RESOLVED: load=4 boot wedge — faithful guest-cycle pad ACK timing
Branch wt/tomba2-load-accuracy. When `PSX_RAM_READ_WAIT_CYCLES=4` (the
Beetle-oracle-accurate single-load value, vs the old DuckStation 6), Tomba 2's
boot deterministically wedged in the BIOS shell. **Root-caused and FIXED
2026-06-27.** This is the faithful-core cascade exactly as CLAUDE.md predicts:
an accurate CPU cost exposed an inaccurate *device* timing.
## Symptom (was reproducible, deterministic)
- pc=0 / halt; `total_checks` frozen (~134,756,056), `current_func` in the BIOS
shell (0x1FC2CE28 / 0x1FC2C3E0 — the exact landing varies by a few-cycle
observer-effect, axis-7), `i_stat` 0x80, `i_mask` 0x0D.
- Wedge mechanism (live-verified registers + disasm): a BIOS-shell GUI/object
builder indexes a 1672-byte-stride table at 0x80110EC8 by a handle `s1`
(`s1*1672 + base`) with **only a lower-bound (`bgez`) guard, no upper bound**.
At the wedge `s1 = 0x68 = 104` → `v0 = 0x80110EC8 + 104*1672 = 0x8013B608`, a
wild pointer; the struct copy corrupts RAM → downstream pc=0. (RAM at the shell
funcs was zeroed by the time of the halt — post-mortem RAM is unreliable; the
always-on rings are the ground truth.)
## CONFIRMED root cause (step A — validated, not hypothesised)
The proximate runaway immediately before the wedge is **continuous controller
(pad) polling**, NOT memcard enumeration as previously hypothesised. Live
evidence (`sio_irq_dump` on the wedged runtime): the last 150+ SIO IRQs are
**100% `source=pad`, `delay=4`, `active_device=PAD`, `mc_state=0`** (card idle).
The unfaithfulness (source-confirmed, cross-ref Beetle frontio/sio + axis5 doc
D1/Fix-6): the **pad fast-path** in `sio_write` (`sio.c:1386`) bypassed the
guest-cycle-paced shifter and armed `sio_irq_countdown = SIO_IRQ_DELAY_PAD (=4)`,
which `sio_tick(0)` decrements **once per SIO register access** (sio_tick is only
ever called with cycles=0 — see memory.c:389/498/589, interrupts.c:268). So pad
ACK→IRQ7 timing was **access-count-paced, not guest-cycle-paced**. The card path
(cycle-paced shifter via `sio_advance` ← `psx_advance_cycles`) was already
faithful; only the pad path was not.
Causal chain: with the faithful/faster CPU (load=4) the pad ACK IRQ fires at a
different guest-cycle *phase* relative to the cycle-paced timers/VBLANK than it
did at load=6, diverging the BIOS pad-detection state machine and ultimately
producing the out-of-range handle 104. (The handoff's "memcard save-file
enumeration count" attribution was wrong — it is the SIO **controller** path, a
sub-case of the same axis-5 device-timing weakness.)
## FIX (step B — faithful, validated)
`runtime/src/sio.c`, pad fast-path ACK arm: replaced the access-paced
`sio_irq_countdown = SIO_IRQ_DELAY_PAD` with the **guest-cycle-paced ack
scheduler** the card path uses:
```c
sio_pending_ack = 1;
sio_ack_remaining = SIO_BAUD_CYCLES_DEFAULT + SIO_ACK_CYCLES_DEFAULT; /* 1088+170 */
sio_pending_ack_irq_en = 1;
g_sio_timing_active = 1;
```
`sio_advance()` (driven by `psx_advance_cycles()` per instruction) now delivers
the pad IRQ after a fixed *guest-cycle* delay (one byte-shift + ack pulse =
1258 cyc, identical to the card path), independent of how many SIO register
reads the CPU issues while it waits. Runtime-only change — no regen.
## Validation
- load=4 boots **past the wedge to the intro FMV** (screenshot verified, pixels
on screen — Rule 5), reaching frame 11,000+ stably (wedge was frame 2189,
total_checks frozen). The same FMV the load=6 branch reached.
- Ruler #1 (BIOS EvCB-search [c5c→ca4], native 4500 vs Beetle 4382): native 54
vs Beetle 56, steady −2 = the **known unmodeled load ReadFudge** gap on the
load=4 branch (a listed remaining Stage-2 component), NOT a regression — the
SIO change cannot alter CPU instruction cost, and native being *lower* rules
out a spurious mid-window IRQ.
## Remaining completeness follow-up (recommended, not faithfulness-blocking)
The pad fast-path is now *faithful* but is still a separate code path from the
card shifter. axis5 Fix-6 / the unfinished "1.0e-e2" (comment at sio.c:1868)
specs fully **removing the pad fast-path** so pad bytes flow through the unified
cycle-paced shifter (modeling the byte-shift-in-progress STAT transitions too).
That is a path-unification refactor; it needs pad *input* validation at the
title/menu (not just boot-to-FMV) before adopting. Both pad and card ACK timing
are already guest-cycle-paced, so there is no remaining fake/access-paced site in
the shipping (SIO_MODEL_CYCLE_PACED) config.
Cross-title note: the pad path is shared — regen+smoke Tomba1/MMX6/Ape before any
cross-title merge of this branch (Rule -1: breaking other titles is acceptable;
they regenerate).