Skip to content

Commit 43e5968

Browse files
doublegateclaude
andauthored
feat(accuracysnes): A5.18 — BRK is 8 cycles native, 7 in emulation (#315)
The extra native cycle is the PBR push: a native frame is PBR/PCH/PCL/P and an emulation frame is the same without PBR, so the difference is one stack WRITE (8 clocks), not an internal cycle. Nothing returns, and that is what isolates BRK. The obvious chain is BRK into a handler that RTIs back -- but RTI also differs by one stack access between the modes (A5.19), so the pair would come back at two accesses per iteration and this row would report its sibling's result added to its own. Instead V_BRK_VEC points at the instruction AFTER the BRK, which the runtime's `jmp (V_BRK_VEC)` trampoline reaches directly: there is no handler body, so no handler body can differ between the modes. The frames are abandoned and S is restored after each span. Both BRK vectors already share that one trampoline -- runtime.s documents the sharing as deliberate, since emulation conflates $FFFE between IRQ and BRK -- so the trampoline's cost is the same code in both spans. The repeat count was MEASURED, not estimated, and the first draft got that wrong. Four iterations on an estimate of ~35 dots each wrapped the H counter: native read $FFF7, a wrapped negative, against emulation's 323 -- a native span SHORTER than the emulation one, the arithmetic opposite of the assertion. An iteration is really ~99 dots. Three measure 297 native against 291 emulation, difference exactly 6, both clear of the 341 wrap. Injecting "never push PBR" into op_brk fires the row's assertion, with A6.05 ("native pushes 4 bytes") failing alongside as corroboration. Coverage 354 -> 355 of 443 (301 on-cart + 54 scenes), battery 342 tests at 100% on-cart, three references agree. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 8f19133 commit 43e5968

12 files changed

Lines changed: 703 additions & 381 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
### Added
1313

14+
- **`A5.18` — `BRK` costs 8 cycles native and 7 in emulation.** The extra native cycle is the PBR
15+
push: a native frame is PBR/PCH/PCL/P and an emulation frame is the same without PBR, so the
16+
difference is one stack *write* (8 clocks) rather than an internal cycle.
17+
18+
**Nothing returns, and that is what isolates `BRK`.** The obvious chain is `BRK` into a handler
19+
that `RTI`s back — but `RTI` *also* differs by one stack access between the modes (`A5.19`), so
20+
the pair would come back at two accesses per iteration and this row would be reporting its
21+
sibling's result added to its own. Instead `V_BRK_VEC` points at the instruction *after* the
22+
`BRK`, which the runtime's `jmp (V_BRK_VEC)` trampoline reaches directly: no handler body exists,
23+
so no handler body can differ between the modes. The frames are simply abandoned and `S` is
24+
restored after each span. Both `BRK` vectors already share that one trampoline — `runtime.s`
25+
documents the sharing as deliberate, since emulation conflates `$FFFE` between IRQ and BRK — and
26+
the consequence here is exactly what the row needs.
27+
28+
**The repeat count was measured, not estimated, and the first draft got that wrong.** Four
29+
iterations on an estimate of ~35 dots each wrapped the H counter: native came back `$FFF7`, a
30+
wrapped negative, against emulation's 323 — a native span *shorter* than the emulation one, the
31+
arithmetic opposite of the assertion. An iteration is really ~99 dots. Three of them measure
32+
**297 native against 291 emulation** for a difference of exactly 6, both clear of the 341-dot
33+
wrap. Injecting "never push PBR" into `op_brk` fires the row's assertion, with `A6.05` ("native
34+
pushes 4 bytes") failing alongside it as corroboration. Coverage `354 → 355 of 443`.
35+
1436
- **`E3.13` — a write to `$00F0-$00FF` lands in the RAM shadow as well as in the register.** The
1537
register block wins every SPC read of those addresses, so the assertion cannot be checked by
1638
writing and reading back; it needs a second reader of APU RAM that skips the register decode, and

‎crates/rustysnes-test-harness/tests/accuracysnes.rs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ const A5_08_SLOTS: [(u8, &str); 7] = [
596596
];
597597

598598
/// The measurement slots the `v1.29.0` Group E batch records.
599-
const GROUP_E_BATCH_SLOTS: [(u16, &str); 14] = [
599+
const GROUP_E_BATCH_SLOTS: [(u16, &str); 17] = [
600600
(256, "E8.01 KON sweep key-on mask (bit 7 = voice 0)"),
601601
(
602602
257,
@@ -618,6 +618,9 @@ const GROUP_E_BATCH_SLOTS: [(u16, &str); 14] = [
618618
(265, "E9.09 its low byte at $0000 (E9.12 masks bit 0)"),
619619
(270, "E3.13 control OUTX, a copy in ordinary RAM"),
620620
(271, "E3.13 OUTX decoded from under $00F0-$00FF"),
621+
(272, "A5.18 3x BRK native (expect ~297)"),
622+
(273, "A5.18 3x BRK emulation (expect ~291)"),
623+
(274, "A5.18 native - emulation (expect 6)"),
621624
(266, "E3.06 timer 0 ticks over the interval"),
622625
(
623626
267,

‎docs/STATUS.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ compositor (`docs/adr/0014`, T-CA-10) becomes the sole renderer — its compose/
2323
per-dot against live registers (mid-line CGRAM/OAM/`INIDISP` at dot resolution; BG-fetch-ahead is
2424
Phase 4c, not yet landed) — and the first-party AccuracySNES
2525
cartridge matures into a usable instrument (AccuracyCoin-style paged menu + automatic skyline
26-
results + per-test B-skip + a Select WRAM debug viewer) with its battery at **354 of 443** dossier
27-
assertions (300 on-cart + 54 rendered scenes).
26+
results + per-test B-skip + a Select WRAM debug viewer) with its battery at **355 of 443** dossier
27+
assertions (301 on-cart + 54 rendered scenes).
2828
**`v1.22.0 "Horizon"` fixes the long-standing DSP-1 continuous-mode Mode-7 flat floor** (Pilotwings
2929
flight, SMK track): the shared NEC-DSP host-sync (`run_until_rqm`) stopped one host-write too early,
3030
so reading a host input word raised RQM before the firmware cleared DRC to 16-bit — mis-framing the
@@ -241,7 +241,7 @@ tracked here, always current, reaffirmed every release:
241241
| Core/Curated coprocessors (oracle-gated) | ✅ **3 / 3, honesty gate green** | DSP-1 (4 commercial ROMs), Super FX/GSU (58 Krom ROMs + per-opcode suite), SA-1 (18 commercial carts) — `ORACLE_COPROCESSORS` |
242242
| BestEffort coprocessors, real-title validated | ✅ **10 / 11** | DSP-2, DSP-4, ST010, S-DD1, CX4, OBC1 boot a real commercial title to real gameplay content. **DSP-3 (SD Gundam GX) + ST011 (2-dan Morita Shougi)** are liveness/determinism-validated — detection + `host_accesses > 0` + bit-identical framebuffer (`dsp3_st011_oncart`). **ST018 (Nidan Morita Shogi 2) + S-RTC (Daikaijuu Monogatari II)** are detection + boot + determinism-validated against their real carts (`srtc_st018_oncart`), but their coprocessor's core function is **usage-gated** (the ST018 shogi AI runs only on the computer's move; the S-RTC clock is read at specific moments), so it is not exercised in a headless boot |
243243
| BestEffort coprocessors, no booting dump | ⚠️ **1 / 11** | SPC7110 — **two** local Tengai Makyou Zero dumps both fail to boot to content: the 7 MiB one is a fan-translation ROM hack needing a patch-only memory region no cartridge has (`docs/audit/spc7110-boot-crash-2026-07-08.md`); a 5 MiB dump (sha256 `8620203d…`) freezes at a near-blank screen with zero coprocessor activity, and does not match the documented-good original (`69d06a3f…`). The correct original-cartridge dump remains the ROM-sourcing gap (`docs/rom-test-corpus.md`) |
244-
| AccuracySNES (first-party battery) | ✅ **354 / 443 assertions covered, 100% on-cart pass** | A 341-test self-scoring battery spanning Groups A-G. **354 of 443** dossier assertions covered: **300 on-cart** + **54 via rendered scenes** (`docs/adr/0013`), kept as separate columns on purpose (`docs/accuracysnes-coverage.md`, regenerated with the ROM so it cannot drift). Every scored row is inject-verified for non-vacuity and cross-validated headlessly against **Mesen2, snes9x and ares** — three independent references as of `v1.29.0`, with MesenCE additionally serving as the per-dot compositor's blueprint and exact-frame oracle. Provenance gate green. Ships an AccuracyCoin-style on-cart UI as of `v1.21.0` (paged menu + automatic skyline results + per-test B-skip + a Select WRAM debug viewer). Remaining ~89 assertions are compositor-gated hi-res, second-image G-rows, treacherous CPU/dot-model timing, or provably uncoverable — though "uncoverable" has twice proved too strong (`E5.06` and `B2.02`/`B2.03` all landed after being parked) (`docs/accuracysnes-coverability-audit-2026-07-23.md`) |
244+
| AccuracySNES (first-party battery) | ✅ **355 / 443 assertions covered, 100% on-cart pass** | A 342-test self-scoring battery spanning Groups A-G. **355 of 443** dossier assertions covered: **301 on-cart** + **54 via rendered scenes** (`docs/adr/0013`), kept as separate columns on purpose (`docs/accuracysnes-coverage.md`, regenerated with the ROM so it cannot drift). Every scored row is inject-verified for non-vacuity and cross-validated headlessly against **Mesen2, snes9x and ares** — three independent references as of `v1.29.0`, with MesenCE additionally serving as the per-dot compositor's blueprint and exact-frame oracle. Provenance gate green. Ships an AccuracyCoin-style on-cart UI as of `v1.21.0` (paged menu + automatic skyline results + per-test B-skip + a Select WRAM debug viewer). Remaining ~88 assertions are compositor-gated hi-res, second-image G-rows, treacherous CPU/dot-model timing, or provably uncoverable — though "uncoverable" has twice proved too strong (`E5.06` and `B2.02`/`B2.03` all landed after being parked) (`docs/accuracysnes-coverability-audit-2026-07-23.md`) |
245245
| Determinism contract | ✅ **proven** | bit-identical framebuffer/audio across runs; save-state round-trip proven across all three board tiers (no-coprocessor, Curated, BestEffort) |
246246

247247
**Named residuals, tracked not hidden:** the 65816 `e1.e` divergence (`docs/adr/0002`);

‎docs/accuracysnes-coverage.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Every sub-group of Part V is enumerated, so this is a **complete** statement of
1212
| `A2` | 10 | 10 | 0 | — |
1313
| `A3` | 10 | 10 | 0 | — |
1414
| `A4` | 10 | 10 | 0 | — |
15-
| `A5` | 15 | 13 | 0 | A5.18, A5.20 |
15+
| `A5` | 15 | 14 | 0 | A5.20 |
1616
| `A6` | 15 | 13 | 0 | A6.13, A6.15 |
1717
| `A7` | 5 | 5 | 0 | — |
1818
| `A8` | 6 | 6 | 0 | — |
@@ -52,7 +52,7 @@ Every sub-group of Part V is enumerated, so this is a **complete** statement of
5252
| `F1` | 22 | 13 | 0 | F1.13, F1.15, F1.16, F1.17, F1.18, F1.19, F1.20, F1.21, F1.22 |
5353
| `G1` | 18 | 15 | 0 | G1.06, G1.13, G1.18 |
5454

55-
**300 of 443** enumerated assertion rows covered by an on-cart test, plus **54** covered only by a rendered scene (`docs/adr/0013`) — **354 of 443** in total.
55+
**301 of 443** enumerated assertion rows covered by an on-cart test, plus **54** covered only by a rendered scene (`docs/adr/0013`) — **355 of 443** in total.
5656

5757
The two columns are kept apart on purpose. An on-cart result means the same thing on any emulator and on real hardware; a rendered scene needs a host holding the golden. Adding them into one figure would quietly change what the number claims.
5858

‎docs/accuracysnes-plan.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ AccuracySNES closed ticket **T-04**. The follow-on tickets minted here are **T-0
1313

1414
| | |
1515
|---|---|
16-
| Tests | **341** (scoring + golden vectors + region SKIP per image) — *tests, not assertions; see the note below the table* |
17-
| Assertion coverage | **354 of 443** dossier assertions — **300 on-cart** + **54 rendered scenes**, kept as separate columns (`docs/accuracysnes-coverage.md`) |
16+
| Tests | **342** (scoring + golden vectors + region SKIP per image) — *tests, not assertions; see the note below the table* |
17+
| Assertion coverage | **355 of 443** dossier assertions — **301 on-cart** + **54 rendered scenes**, kept as separate columns (`docs/accuracysnes-coverage.md`) |
1818
| Rendered scenes | **54** declared, all blessed and matching on both scene hosts (`docs/adr/0013`); **54** dossier rows have a scene as their only cover |
1919
| Pass rate | **100.00%** on-cart, floor enforced at 1.00 by `tests/accuracysnes.rs` |
2020
| Cross-validated | **Three references** as of `v1.29.0`. Mesen2 agrees on every test but `F1.03`, which clocks both ports out of one latch and so needs the port-2 input its Lua runner cannot drive; snes9x has 14 recorded divergences and **ares** 3, each with a citation in `scripts/accuracysnes/crossval.sh`. A headless **MesenCE** is separately the per-dot compositor's blueprint + exact-frame oracle. All images. |

‎tests/roms/AccuracySNES/ERROR_CODES.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,14 @@ Provenance: **Documented** (WDC/GTE/VLSI instruction-operation tables; docs/accu
596596
|---|---|---|
597597
| 1 | `$02` | LDX abs did not cost one extra 8-clock access with x=0 |
598598

599+
### A5.18 — BRK 8 native, 7 emul
600+
601+
Provenance: **Documented** (WDC datasheet instruction-operation table; docs/accuracysnes-timing-oracle.md). Kind: scored.
602+
603+
| Code | Byte | Meaning |
604+
|---|---|---|
605+
| 1 | `$02` | BRK did not push one extra byte in native mode (8 cycles vs 7): the native frame is PBR, PCH, PCL, P and the emulation frame is the same without PBR, so a core pushing the same three bytes in both reads zero here |
606+
599607
### A5.19 — RTI 7 native, 6 emul
600608

601609
Provenance: **Documented** (WDC datasheet instruction-operation table; docs/accuracysnes-timing-oracle.md). Kind: scored.

0 commit comments

Comments
 (0)