Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **AccuracySNES: `A5.19` — `RTI` costs 7 cycles native and 6 emulation.** The extra native cycle is
the PBR pull, an 8-clock WRAM read rather than a 6-clock internal cycle, so eight iterations differ
by 16 dots. Group A on-cart coverage goes 12 → 13 of 15 in `A5`.

The RTIs are chained through **one** `rti` instruction: every return frame is built before
`measure_begin`, all returning to the `@spin` label that is the `rti` itself, except the
first-pushed one — pulled last — which returns to `@done`. The measured span therefore holds
nothing but the RTIs, with no loop counter or branch inside it.

Three traps on the way, all recorded in `docs/accuracysnes-plan.md` §`A5.19`. The 341-dot line
wrap returned native **42** against emulation **76** — the native reading smaller, the arithmetic
opposite of the assertion — diagnosed by measuring at two repeat counts and reading the slope.
`rep` cannot clear `m` while `E = 1`, so `measure_result`'s 16-bit arithmetic silently ran 8-bit
and pinned the reading at a constant regardless of repeat count. And the one that forced the
design: `hv_begin`/`hv_end` run at the caller's register width, so the instrument's own overhead
**does not cancel across a mode boundary** — every other `A5` differential compares two spans in
the same mode. The `xce` pairs now sit inside the span, so both arms run the instrument natively
and differ only in the mode the RTIs execute in.

Verified by injection at the site the row names: making native `RTI` advance `S` without paying
the bus cycle collapses the difference to exactly 0 and fails the row. snes9x's divergence count
is unchanged, so it passes the row as well.

- **Netplay: a connection-quality readout, a graded desync banner, and a disconnect that actually
fires.** The two preceding entries built the machinery; nothing in the frontend read it. The
session's transport is now wrapped in `LivenessTransport` and ticked once per frame from
Expand Down
4 changes: 2 additions & 2 deletions docs/accuracysnes-coverage.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Every sub-group of Part V is enumerated, so this is a **complete** statement of
| `A2` | 10 | 10 | 0 | — |
| `A3` | 10 | 10 | 0 | — |
| `A4` | 10 | 10 | 0 | — |
| `A5` | 15 | 12 | 0 | A5.18, A5.19, A5.20 |
| `A5` | 15 | 13 | 0 | A5.18, A5.20 |
| `A6` | 15 | 13 | 0 | A6.13, A6.15 |
| `A7` | 5 | 5 | 0 | — |
| `A8` | 6 | 6 | 0 | — |
Expand Down Expand Up @@ -52,7 +52,7 @@ Every sub-group of Part V is enumerated, so this is a **complete** statement of
| `F1` | 22 | 13 | 0 | F1.13, F1.15, F1.16, F1.17, F1.18, F1.19, F1.20, F1.21, F1.22 |
| `G1` | 18 | 15 | 0 | G1.06, G1.13, G1.18 |

**291 of 443** enumerated assertion rows covered by an on-cart test, plus **53** covered only by a rendered scene (`docs/adr/0013`) — **344 of 443** in total.
**292 of 443** enumerated assertion rows covered by an on-cart test, plus **53** covered only by a rendered scene (`docs/adr/0013`) — **345 of 443** in total.

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.

Expand Down
39 changes: 39 additions & 0 deletions docs/accuracysnes-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,45 @@ So nothing is asserted and nothing is recorded. What is known:
The next attempt should start from a scratch build that dumps `$213F` before and after each step
into slots verified unused, rather than from a folded variant that hides where the discrepancy is.

### `A5.19` — a cross-mode differential needs the instrument on the same side of the mode switch

`RTI` costs 7 cycles native and 6 emulation; the extra one is the PBR pull, an 8-clock WRAM read,
so eight iterations differ by 16 dots. Landed, and it took three revisions, each a trap worth
naming.

**The shape that works.** Chain the RTIs through *one* `rti` instruction: build every return frame
before `measure_begin`, all of them returning to the `@spin` label that is the `rti` itself, except
the first-pushed one — pulled last — which returns to `@done`. The span then holds nothing but the
RTIs, with no loop counter or branch inside it to dilute the signal.

**Trap 1: the 341-dot line wrap**, the same one `A5.08` records. Sixteen RTIs is a ~200-dot span;
it wrapped and reported native **42** against emulation **76** — the native reading *smaller*, the
arithmetic opposite of the assertion. Diagnose it by measuring at two repeat counts and reading the
**slope**: 4 → 8 RTIs moved native by exactly 52 dots = 4 x 13, the predicted per-RTI cost, which
proved the per-iteration model right and only the offset junk. Eight RTIs is a ~104-dot span.

**Trap 2: `rep` cannot clear `m` while `E = 1`.** `measure_result` emits 16-bit arithmetic; run it
in emulation and it silently executes 8-bit, `sta` writes one byte, and the reading sticks at a
constant **regardless of repeat count**. A number that does not move when the work moves is the
signature. `measure_end` is width-neutral (`php`/`plp`), so latch first and widen after.

**Trap 3, the one that blocked the design: the instrument's own overhead does not cancel across a
mode boundary.** `hv_begin`/`hv_end` are subroutines that run at whatever width the caller is in, so
the native and emulation spans carried *different* fixed overheads — measured native 279 against
emulation 332, emulation larger when the assertion says it must be smaller. Every other `A5`
differential compares two spans in the same mode, which is why this had never surfaced.

The fix is to put the `xce` pairs **inside** the span, so both arms run the instrument natively and
contain identical non-RTI instructions, differing only in the mode the RTIs execute in. The native
arm therefore carries a deliberately no-op `clc`/`xce` at each end. With that, both spans carry the
same 189-dot overhead: native 293, emulation 277, difference **16**, and 4 RTIs gives **8** — linear
in the repeat count, which is what distinguishes a per-iteration cost from a fixed artifact.

Verified by injection at the site the row names: making native `RTI` advance `S` without paying the
bus cycle collapses the difference to exactly **0** and fails the row. snes9x's divergence count is
unchanged, so it passes the row too; Mesen2 could not be consulted (the headless runner times out in
this environment, which predates this work).

Comment on lines +458 to +496

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether STATUS.md and the to-dos sprint files reference A5.19.
set -euo pipefail

rg -n 'A5\.19' docs/STATUS.md to-dos/*.md 2>/dev/null || echo "No A5.19 reference found in STATUS.md or to-dos/"

Repository: doublegate/RustySNES

Length of output: 207


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- docs/STATUS.md ---'
rg -n -C 3 'A5|accuracysnes|coverage|12|13|15' docs/STATUS.md || true

printf '%s\n' '--- sprint files ---'
rg -n -i -C 2 'A5|accuracysnes|accuracy|19' to-dos --glob '*.md' || true

printf '%s\n' '--- plan and generated coverage references ---'
rg -n -C 2 'A5\.19|12 *of *15|13 *of *15|A5' docs/accuracysnes-plan.md docs/accuracysnes-coverage.md 2>/dev/null || true

Repository: doublegate/RustySNES

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- A5 entries in generated coverage ---'
rg -n -C 2 'A5|Group A|group A|12|13|15' docs/accuracysnes-coverage.md | head -200 || true

printf '%s\n' '--- A5.19 and nearby plan entries ---'
rg -n -C 8 'A5\.1[6-9]|A5\.20|A5\.19' docs/accuracysnes-plan.md || true

printf '%s\n' '--- all ticket-like A5.19 references in tracked files ---'
git grep -n -E 'A5[.]19|A5[ _-]?19' -- ':!snesdev_wiki' || true

printf '%s\n' '--- candidate sprint files ---'
git ls-files 'to-dos/**' | rg -i 'accuracy|phase-6|sprint|roadmap' | head -200

Repository: doublegate/RustySNES

Length of output: 19236


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- phase-6 sprint status ---'
rg -n -C 4 'A5|T-04|T-06|AccuracySNES|coverage|344|345|292|291' \
  to-dos/phase-6-accuracy-to-100/sprint-1-triage.md \
  to-dos/phase-6-accuracy-to-100/overview.md \
  to-dos/ROADMAP.md | head -240 || true

printf '%s\n' '--- exact dashboard totals ---'
sed -n '227,240p' docs/STATUS.md
sed -n '9,18p' docs/accuracysnes-coverage.md
sed -n '432,452p' to-dos/ROADMAP.md

Repository: doublegate/RustySNES

Length of output: 23320


Synchronize AccuracySNES coverage metadata.

docs/STATUS.md and to-dos/ROADMAP.md report 291 on-cart and 344 total assertions. The generated coverage reports 292 on-cart and 345 total, with A5 at 13 of 15. Record A5.19 as complete and remove it from the blocked A5 list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/accuracysnes-plan.md` around lines 458 - 496, Synchronize the
AccuracySNES coverage metadata by marking A5.19 complete in docs/STATUS.md and
to-dos/ROADMAP.md, updating the totals to 292 on-cart and 345 total assertions,
and changing A5 coverage to 13 of 15. Remove A5.19 from the blocked A5 list
while preserving the other entries.

Source: Coding guidelines

### `A5.20` — MVN's per-byte cost does not measure, and the number is interesting

> **Read the conclusion first.** The `A5.20` **cart test** is withdrawn; no test ships. The
Expand Down
8 changes: 8 additions & 0 deletions tests/roms/AccuracySNES/ERROR_CODES.md
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,14 @@ Provenance: **Documented** (WDC/GTE/VLSI instruction-operation tables; docs/accu
|---|---|---|
| 1 | `$02` | LDX abs did not cost one extra 8-clock access with x=0 |

### A5.19 — RTI 7 native, 6 emul

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

| Code | Byte | Meaning |
|---|---|---|
| 1 | `$02` | RTI did not cost one extra stack pull in native mode (7 vs 6) |

### A6.11 — WAI wakes, no vector

Provenance: **Documented** (WDC datasheet: WAI wakes on the interrupt line; I gates the vector). Kind: scored.
Expand Down
Loading
Loading