diff --git a/ACCURACY_BURNDOWN.md b/ACCURACY_BURNDOWN.md index f5dc975e..fb8ae9ad 100644 --- a/ACCURACY_BURNDOWN.md +++ b/ACCURACY_BURNDOWN.md @@ -169,6 +169,25 @@ bug**, not timing. Beetle spu.cpp; psx-spx "SPU"; validate by audio-sample diff. - [ ] **CDROM**: command set, response timing, sector read (data/XA/CD-DA), seek, shell/lid — Beetle cdrom.cpp; psx-spx "CDROM"; validate by sector/response diff. + - **KNOWN OK-TO-DIVERGE (we are MORE faithful than the oracle) — from PR #9:** + Suppressed in `tools/devtrace_diff.py` (`KNOWN_DIVERGENCES["cdrom"]`; use + `--strict` to see raw). Beetle stays the independent oracle (unpatched); the + tool LABELS these deltas, it does not silence real ones. + - **CD controller version (Test `19h`,`20h`)**: we return `94 09 19 C0` (real + SCPH-1001 sub-CPU, 1994-09-19, per psx-spx "CDROM Test Commands"). Beetle + hardcodes `97 01 10 C2` (`cdc.cpp:2253`, a later PSone board) regardless of + BIOS. Version byte `< 95h` keeps shell CD-init flag `[0xA000DFFC]` clear; + `>= 95h` sets it and forces a spurious boot ReadTOC. So native (94h) issues + NO ReadTOC where Beetle (97h) does → expect a CDROM event delta at CD-init, + possibly cascading briefly. Verified vs psx-spx, NOT the oracle. MMX6+Tomba + soak PASSED (2026-07-06). Trivially revertable (4 bytes, `cdrom.c` Test 0x20). + - **Status reg `0x1F801800` bit 2 (ADPBUSY)**: idles at 0 (was wrongly pinned + set under an "ADPCM empty" label). psx-spx: bit2 = XA playback → 0 when idle. + - **GetID license region**: derived from disc serial (`SCEA`/`SCEE`/`SCEI`) + instead of hardcoded `SCEI`; NTSC-U (MMX6/Tomba) now correctly report `SCEA`. + - Method reminder (top-of-doc LESSON): these were HYPOTHESES until OUTPUT + validation — the MMX6/Tomba playtest soak WAS that validation. Revert any + that regress a shipped title. - [ ] **DMA**: all 7 channels, block/linked-list/chain modes, timing, DICR/DPCR — Beetle dma.cpp; psx-spx "DMA". - [ ] **MDEC**: macroblock decode, IDCT, color conversion, RLE — Beetle mdec.cpp; diff --git a/tools/devtrace_diff.py b/tools/devtrace_diff.py index 124e296a..c1908e3c 100644 --- a/tools/devtrace_diff.py +++ b/tools/devtrace_diff.py @@ -24,6 +24,19 @@ import argparse, json, socket, sys SRC = {0:"vblank",1:"gpu",2:"cdrom",3:"dma",4:"timer0",5:"timer1",6:"timer2", 7:"sio0",8:"sio1",9:"spu",10:"pio"} +# KNOWN INTENTIONAL divergences from the Beetle oracle — where OUR runtime is +# MORE faithful to real hardware than Beetle, so a native/beetle delta on this +# source is EXPECTED, not a bug. Labeled (not red-flagged) unless --strict. +# See ACCURACY_BURNDOWN.md (Axis 5 — CDROM). Add entries here (never silence a +# real divergence): source-name -> one-line reason. +KNOWN_DIVERGENCES = { + "cdrom": "SCPH-1001 CD controller version 94/09/19/C0 (psx-spx) vs Beetle's " + "hardcoded 97/01/10/C2 — version <0x95 keeps the shell CD-init flag " + "[0xA000DFFC] clear, so Beetle issues a spurious boot ReadTOC we do " + "not: expect a CDROM event count/timing delta around CD-init. " + "Also GetID region (SCEA/E/I) + status bit2 ADPBUSY idle=0.", +} + def query(port, cmd, **kw): obj = {"cmd": cmd}; obj.update(kw) s = socket.create_connection(("127.0.0.1", port), timeout=15) @@ -61,6 +74,8 @@ def main(): ap.add_argument("--cyc-hi", type=int, default=None) ap.add_argument("--drift", type=int, default=2_000_000) ap.add_argument("--show", type=int, default=0) + ap.add_argument("--strict", action="store_true", + help="do NOT suppress KNOWN_DIVERGENCES (show them as raw drift)") args = ap.parse_args() nat = pull(args.native, args.cyc_lo, args.cyc_hi) @@ -73,6 +88,7 @@ def main(): sources = sorted(set(ns) | set(bs)) print(f"\n{'source':<8} {'native#':>8} {'beetle#':>8} {'first divergent ordinal (cycle delta)':<40}") print("-" * 78) + suppressed = [] for s in sources: n, b = ns.get(s, []), bs.get(s, []) name = SRC.get(s, f"src{s}") @@ -86,10 +102,21 @@ def main(): break if not flag and len(n) != len(b): flag = f"count differs ({len(n)} vs {len(b)}) — no big per-ordinal drift in first {m}" + diverged = bool(flag) if not flag: flag = "tracks (no drift > threshold)" + known = KNOWN_DIVERGENCES.get(name) + if diverged and known and not args.strict: + flag = f"KNOWN✓ intentional (more faithful) — {flag}" + suppressed.append(name) print(f"{name:<8} {len(n):>8} {len(b):>8} {flag}") + if suppressed and not args.strict: + print("\nKNOWN intentional divergences suppressed (re-run with --strict to" + " see raw): " + ", ".join(suppressed)) + for nm in suppressed: + print(f" {nm}: {KNOWN_DIVERGENCES[nm]}") + if args.show: for s in sources: name = SRC.get(s, f"src{s}")