feat(accuracysnes): A5.18 — BRK is 8 cycles native, 7 in emulation - #315
Conversation
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>
WalkthroughAdded AccuracySNES A5.18 coverage for native and emulation-mode ChangesAccuracySNES BRK timing
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 9 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (9 passed)
Comment |
Antigravity review (Gemini via Ultra)This PR adds test row Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
There was a problem hiding this comment.
Pull request overview
Adds a new AccuracySNES CPU timing assertion (A5.18) to measure the cycle difference of BRK between native and emulation mode, and updates the generated ROM artifacts and documentation/counters accordingly.
Changes:
- Introduce
A5.18in the AccuracySNES generator (gen/src/tests/cpu.rs) and wire it into the test list + dossier map. - Regenerate/update AccuracySNES artifacts (assembled Group A, source catalog, error codes) and bump documented coverage/test counts.
- Extend the harness measurement-slot reporting to include A5.18’s raw timing slots.
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/roms/AccuracySNES/gen/src/tests/cpu.rs | Implements the new A5.18 generator-side test logic and assertions. |
| tests/roms/AccuracySNES/gen/src/dossier.rs | Adds A5.18 to the dossier coverage map. |
| tests/roms/AccuracySNES/asm/tests_group_a.s | Regenerated assembled Group A test code including test_a5_18 and updated test tables/count. |
| tests/roms/AccuracySNES/SOURCE_CATALOG.tsv | Catalog updated to include the new A5.18 row and shifted indices. |
| tests/roms/AccuracySNES/ERROR_CODES.md | Documents the new A5.18 failure code meaning. |
| crates/rustysnes-test-harness/tests/accuracysnes.rs | Adds measurement-slot reporting strings for A5.18’s raw timing slots. |
| docs/STATUS.md | Updates the advertised AccuracySNES assertion coverage/test counts. |
| docs/accuracysnes-plan.md | Updates the plan summary counts for tests and assertion coverage. |
| docs/accuracysnes-coverage.md | Updates the coverage summary to reflect A5.18 now covered on-cart. |
| CHANGELOG.md | Records the addition and rationale for A5.18. |
Suppressed comments (2)
tests/roms/AccuracySNES/gen/src/tests/cpu.rs:2860
- This comment says the second span abandons "four" more frames, but the loop runs three BRKs (Y = #$03). Keeping the prose accurate helps when adjusting repeat counts later.
a.c("Put S back before the second span abandons four more frames onto it.");
crates/rustysnes-test-harness/tests/accuracysnes.rs:628
- The slot list is no longer in ascending slot order: the (266/267) entries come after (270–274). Keeping these ordered makes the printed diagnostic output easier to scan and compare across runs.
(266, "E3.06 timer 0 ticks over the interval"),
(
267,
"E3.06 timer 2 ticks over the SAME interval (TnOUT wraps at 16)",
),
| /// abandoned on the stack; four of them is 16 bytes native and 12 emulation, and `S` is put back | ||
| /// after each span rather than during it. |
| sta f:$7EE420 | ||
| ; Put S back before the second span abandons four more frames onto it. | ||
| rep #$30 |
| /// The measurement slots the `v1.29.0` Group E batch records. | ||
| const GROUP_E_BATCH_SLOTS: [(u16, &str); 14] = [ | ||
| const GROUP_E_BATCH_SLOTS: [(u16, &str); 17] = [ |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with 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.
Inline comments:
In `@tests/roms/AccuracySNES/gen/src/tests/cpu.rs`:
- Around line 2792-2797: Update the documentation comment above the relevant CPU
test to describe three abandoned frames, matching the loop’s ldy #$03 and three
BRK executions: 12 bytes in native mode and 9 bytes in emulation mode. Leave the
test implementation and the later iteration-count discussion unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 9693832d-c23a-484e-82ed-deebd8930f29
⛔ Files ignored due to path filters (6)
docs/accuracysnes-coverage.mdis excluded by!docs/accuracysnes-coverage.mdand included bydocs/**tests/roms/AccuracySNES/ERROR_CODES.mdis excluded by!tests/roms/AccuracySNES/ERROR_CODES.mdand included bytests/**tests/roms/AccuracySNES/SOURCE_CATALOG.tsvis excluded by!**/*.tsv,!tests/roms/AccuracySNES/SOURCE_CATALOG.tsvand included bytests/**tests/roms/AccuracySNES/asm/tests_group_a.sis excluded by!tests/roms/AccuracySNES/asm/tests_group_a.sand included bytests/**tests/roms/AccuracySNES/build/accuracysnes-pal.sfcis excluded by!tests/roms/AccuracySNES/build/**and included bytests/**tests/roms/AccuracySNES/build/accuracysnes.sfcis excluded by!tests/roms/AccuracySNES/build/**and included bytests/**
📒 Files selected for processing (6)
CHANGELOG.mdcrates/rustysnes-test-harness/tests/accuracysnes.rsdocs/STATUS.mddocs/accuracysnes-plan.mdtests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
- GitHub Check: accuracysnes
- GitHub Check: test-light
- GitHub Check: lint
- GitHub Check: copilot-pull-request-reviewer
- GitHub Check: build demo + docs
🧰 Additional context used
📓 Path-based instructions (15)
docs/**/*.md
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Before changing a subsystem, consult
docs/architecture.md,docs/STATUS.md,CONTRIBUTING.md, the relevant subsystem documentation, and applicable ADRs.New subsystems must add documentation under
docs/.
Files:
docs/STATUS.mddocs/accuracysnes-plan.md
**/*.{rs,md}
📄 CodeRabbit inference engine (CONTRIBUTING.md)
Chip-behavior changes must update both the chip implementation and the corresponding
docs/<subsystem>.mddocumentation.A chip change must update both the chip implementation and its corresponding
docs/<chip>.mddocumentation in the same change.
Files:
docs/STATUS.mdcrates/rustysnes-test-harness/tests/accuracysnes.rsdocs/accuracysnes-plan.mdtests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rsCHANGELOG.md
**/*
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*: Do not commit or vendor the generatedsnesdev_wiki/mirror; it is gitignored and intended only as a local reference.
Keep commits focused and use Conventional Commits:<type>(<scope>): <subject>, with an imperative subject of at most 72 characters.
Do not use emojis in code, comments, or commit messages.
Before opening a PR, ensure formatting, Clippy, workspace tests, the core embedded build, rustdoc with warnings denied, documentation coverage, and changelog requirements pass.
Ticket completion must be reflected in the relevantto-dos/sprint file.
**/*: Preserve the one-directional crate graph: chip crates must not depend on one another;rustysnes-coreties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keepdocs/STATUS.mdas the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNESv2.0orengine-lineageanchors as project releases.
Files:
docs/STATUS.mdcrates/rustysnes-test-harness/tests/accuracysnes.rsdocs/accuracysnes-plan.mdtests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rsCHANGELOG.md
docs/**/*
📄 CodeRabbit inference engine (docs/testing-strategy.md)
Chip crates should exceed 90% unit-test coverage, and each chip should be fuzzable in isolation.
Files:
docs/STATUS.mddocs/accuracysnes-plan.md
docs/**
⚙️ CodeRabbit configuration file
docs/**: Docs are the spec, not a history log. Flag claims that contradict the code, counts that
contradict the generateddocs/accuracysnes-coverage.md, and any statement of coverage that
is broader than what the corresponding test actually asserts.
Files:
docs/STATUS.mddocs/accuracysnes-plan.md
**/*.md
⚙️ CodeRabbit configuration file
**/*.md: Docs are the spec, not a changelog. Flag prose that has drifted from the code it describes
rather than style nits. The markdownlint gate is pinned to v0.39.0 via pre-commit —
do not report rules that version does not have (MD060 in particular).
Files:
docs/STATUS.mddocs/accuracysnes-plan.mdCHANGELOG.md
crates/**/*.rs
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
crates/**/*.rs: Preserve the master-clock lockstep timing model.
rustysnes-core::Busowns mutable machine state, and the CPU borrows&mut Bus.
Preserve determinism: seed, ROM, and input must produce bit-identical output.
Treat test ROMs as the behavioral specification; when documentation disagrees with passing ROM behavior, update the documentation.
Keepunsafeconfined to existing allowed areas, namely frontend and FFI code, and document everyunsafeblock with a// SAFETY:comment.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.rs
📄 CodeRabbit inference engine (CONTRIBUTING.md)
**/*.rs: Use Rust edition 2024 and the toolchain pinned inrust-toolchain.toml(Rust 1.96).
Runcargo fmt --all --check; Rust code must remain rustfmt-compliant.
Run Clippy withcargo clippy --workspace --all-targets -- -D warnings; warnings must not remain.
New public Rust items must have rustdoc becausemissing_docsis a workspace lint.
Do not runcargo clippy --all-features;scriptingandscript-wasmare mutually exclusive. Use explicit per-feature jobs instead.
**/*.rs: Do not introduce.unwrap(),.expect(), orpanic!()on untrusted external input—such as ROM/save-state bytes, netplay messages, Lua or scripting input, or user-supplied paths—outside#[cfg(test)]code. Use typed errors at those boundaries; locally constructed values or values immediately protected by a checked invariant are allowed.
Every newunsafe { ... }block orunsafe fnmust have an adjacent// SAFETY:comment naming the relied-on invariant and its guarantor. Unsafe code outside the frontend and FFI shims should additionally be questioned becauseunsafe_codeis a workspace lint.
**/*.rs: Use Rust edition 2024 with the pinned 1.96 toolchain; satisfy workspacepedantic,nursery,missing_docs, andunsafe_codewarnings because CI runs with-D warnings. Document every public item.
Keepunsafecode restricted to the frontend and FFI, and include a// SAFETY:justification for each use.
Keep hot paths allocation-free.
Treatrustysnes_core::Busas the owner of mutable emulator state; the CPU borrows&mut Bus.
Use the master clock at 21477270 Hz as the timing master; advance the scheduler in lockstep and run other chips on their divisors.
Maintain determinism: seed, ROM, and input must produce bit-identical audio/video; frontend rate control must not alter emulation results.
When implementing hardware behavior, pin and run the failing test ROM first; treat test ROMs as the specification.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rstests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
crates/rustysnes-*/**/*
📄 CodeRabbit inference engine (Custom checks)
For the full pull request diff against its base branch, any observable behavior change under
crates/rustysnes-<chip>/must be accompanied by an edit to the matchingdocs/<chip>.md; a crate change passes without documentation only when it does not alter observable behavior, with the non-behavioral change stated explicitly.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
**/*.{rs,toml}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{rs,toml}: Additive features must be default-off so shipped/native,no_std, and wasm builds remain byte-identical.
Never use or configure--all-features; validate opt-in feature combinations individually as required by the project recipe.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rstests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
crates/**
⚙️ CodeRabbit configuration file
crates/**: Emulator core. Hot paths are allocation-free;unsaferequires a// SAFETY:comment
naming the invariant. Any change to save-stated fields needs aFORMAT_VERSIONbump and a
docs/adr/0006bump-log entry. Behavior changes must update the matchingdocs/<chip>.md
in the same change.
Files:
crates/rustysnes-test-harness/tests/accuracysnes.rs
tests/roms/AccuracySNES/gen/src/**/*
📄 CodeRabbit inference engine (Custom checks)
For the full pull request diff against its base branch, when a test or scene is added or removed under
tests/roms/AccuracySNES/gen/src/, verify itsdossier.rs::MAPentry, all required regenerated artifacts, and matching count changes indocs/accuracysnes-plan.md. Artifact presence must be judged from the path-filter exclusion list, not from unreadable contents.
Files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
tests/roms/AccuracySNES/gen/**/*.rs
📄 CodeRabbit inference engine (AGENTS.md)
Rebuild AccuracySNES after any change to
gen/orasm/; never hand-edit generatedasm/tests_group_a.sorasm/scenes.s.
Files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
tests/roms/AccuracySNES/gen/src/**
⚙️ CodeRabbit configuration file
tests/roms/AccuracySNES/gen/src/**: This generates a hardware-accuracy test cartridge. Judge each test by whether it can
distinguish the behavior it names from the alternatives, not by whether it passes.Flag, specifically:
- Vacuity. An assertion whose expected value is also what a broken or absent
implementation produces (zero, "unchanged", "not $FF") needs a paired control assertion
that would fail on that implementation. Say which alternative goes uncaught.- Overstated doc comments. The prose above a test is a claim about what it validates.
If it names behaviors the emitted program does not exercise, or asserts a rationale that
is not true of the code, that is a defect even though the test passes.- Shared state. OAM, CGRAM, VRAM and the S-DSP registers are not reset between tests. A
test that does not establish its own starting conditions may be measuring the previous
one; look for an earlier test that leaves the relevant state dirty.- Timing-marginal reads. Reading a register a few cycles after disturbing it, or
asserting on a value that is still moving, produces a verdict that flips when unrelated
code shifts. Prefer a settle, a disarm, or a provably stationary value.- Scanline geometry. Line 0 is a blanking line; the V counter's low byte aliases on a
312-line PAL frame; the visible height is 224 or 239 depending on overscan. Constants
derived from any of these deserve a second look.- Duplicate coverage.
dossier.rs::MAPmust not claim an assertion another test already
implements. There is a build gate for this, but flag it in review too.
Files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
CHANGELOG.md
📄 CodeRabbit inference engine (CONTRIBUTING.md)
User-visible changes must be recorded under the
[Unreleased]section.For the full pull request diff against its base branch, modify
CHANGELOG.mdwhen user-visible behavior changes, including emulator output, frontend features, CLI flags, public APIs, or AccuracySNES cartridge contents. Do not require it for purely internal changes, tests, comments, or CI configuration.
Files:
CHANGELOG.md
🧠 Learnings (6)
📚 Learning: 2026-07-21T01:34:22.909Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 189
File: docs/accuracysnes-plan.md:0-0
Timestamp: 2026-07-21T01:34:22.909Z
Learning: When reviewing the AccuracySNES documentation in docs/accuracysnes-*.md (notably docs/accuracysnes-plan.md vs the generated docs/accuracysnes-coverage.md), treat the reported metrics as intentionally non-equivalent: the battery test count and dossier assertion coverage are not interchangeable. Do not infer one count/coverage from the other during review (e.g., one test may contain multiple assertions, and multiple tests may contribute to a single assertion/row such as E6.02).
Applied to files:
docs/accuracysnes-plan.md
📚 Learning: 2026-07-21T05:22:58.848Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 197
File: docs/accuracysnes-plan.md:598-600
Timestamp: 2026-07-21T05:22:58.848Z
Learning: In the AccuracySNES documentation under `docs/`, when an assertion exists in the research dossier but cannot be measured/verified by the current cartridge timing test, distinguish the dossier assertion from test measurability: keep the original hardware assertion (and any contribution to the coverage denominator) intact, withdraw/stop using the specific test coverage only if the sources cannot decompose the required CPU-cycle timing into bus vs internal components, and mark the row as not measurable using the `[NOT CART-MEASURABLE ...]` annotation with links to the corresponding plan section (e.g., `docs/accuracysnes-plan.md` §A5.20) and the related roadmap/ticket (e.g., `to-dos/ROADMAP.md` ticket `T-06-A`). Ensure the documentation/coverage reporting treats the row as uncovered rather than removing or redefining the assertion.
Applied to files:
docs/accuracysnes-plan.md
📚 Learning: 2026-07-21T02:10:49.581Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 190
File: tests/roms/AccuracySNES/gen/src/tests/ppu.rs:0-0
Timestamp: 2026-07-21T02:10:49.581Z
Learning: For any AccuracySNES tests that perform a runtime measurement investigation using writes to $2137 and $4201, do not reuse measurement/slot indices that may already be owned by another test. Before using a slot, verify it is unused (e.g., via an on-cart probe/readback that confirms the slot contains no prior test result). Then independently record $213F immediately before and immediately after each $2137/$4201 operation, so the test can attribute changes to its own operation and avoid cross-test interference.
Applied to files:
tests/roms/AccuracySNES/gen/src/dossier.rstests/roms/AccuracySNES/gen/src/tests/cpu.rs
📚 Learning: 2026-07-21T05:32:09.180Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 197
File: tests/roms/AccuracySNES/gen/src/tests/cpu.rs:0-0
Timestamp: 2026-07-21T05:32:09.180Z
Learning: For SNES LoROM CPU accuracy tests covering JMP (a,X) and JSR (a,X) pointer-bank wrapping, do not place the tested effective pointer addresses below $8000 in LoROM banks $00-$3F or $80-$BF. In RustySNES, those banks’ $0000-$1FFF map to the same 8 KiB WRAM (so different bank forms alias identically), while $2000-$7FFF is mirrored I/O/open-bus; this can make pointer-bank assertions vacuous. Instead, use ROM-resident pointers at linker-controlled, bank-distinct addresses and include a paired control/target case that distinguishes the carried-bank behavior (e.g., for the triggering form ($FFFE,X) with X=$1002, avoid a situation that produces wrapped $00:1000 aliasing with an incorrectly carried $01:1000).
Applied to files:
tests/roms/AccuracySNES/gen/src/tests/cpu.rs
📚 Learning: 2026-07-21T06:21:34.629Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 198
File: docs/accuracysnes-plan.md:0-0
Timestamp: 2026-07-21T06:21:34.629Z
Learning: When reviewing slot allocation for SNES/ROM measurement channels in generator-driven Rust code, account for slots assigned via generation-time computed writers (e.g., slots derived from formulas like `slot_base = 8 + index * 2`) rather than only literal `record(...)` calls. Trace the computed writer’s full emitted slot range and verify there are no collisions with other opcodes/channels that may use different slot ranges after earlier conflict resolution.
Applied to files:
tests/roms/AccuracySNES/gen/src/tests/cpu.rs
📚 Learning: 2026-07-22T06:12:00.703Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 201
File: tests/roms/AccuracySNES/gen/src/tests/apu.rs:1508-1538
Timestamp: 2026-07-22T06:12:00.703Z
Learning: When working on APU/voice timing in these AccuracySNES test sources, treat NTSC vs PAL differences as a first-class constraint. In particular, do not change `Voice::settle` (or any settling/code-size/timing logic it affects) unless you cross-validate against both regions (NTSC and PAL), because timing/polling phase shifts can cause regressions that may appear as PAL-only test failures. For ROM-specific expectations like `E7.13`, keep the intentionally chosen ENVX range (e.g., `0x68..=0x7C`) unless you re-validate that the absorbed post-KON timing variation still matches across both regions.
Applied to files:
tests/roms/AccuracySNES/gen/src/tests/cpu.rs
🪛 LanguageTool
docs/STATUS.md
[style] ~244-~244: Try using a descriptive adverb here.
Context: ...cs/adr/0013), kept as separate columns on purpose (docs/accuracysnes-coverage.md`, regen...
(ON_PURPOSE_DELIBERATELY)
🔇 Additional comments (6)
tests/roms/AccuracySNES/gen/src/tests/cpu.rs (1)
98-98: LGTM!tests/roms/AccuracySNES/gen/src/dossier.rs (1)
86-86: LGTM!crates/rustysnes-test-harness/tests/accuracysnes.rs (1)
599-623: LGTM!docs/STATUS.md (1)
26-27: LGTM!Also applies to: 244-244
docs/accuracysnes-plan.md (1)
16-17: LGTM!CHANGELOG.md (1)
14-35: LGTM!
| /// So nothing returns. `V_BRK_VEC` is pointed at the instruction *after* the `BRK`, which the | ||
| /// runtime's `jmp (V_BRK_VEC)` trampoline reaches directly — there is no handler body at all, and | ||
| /// therefore no handler body whose cost could differ between the modes. Each frame is simply | ||
| /// abandoned on the stack; four of them is 16 bytes native and 12 emulation, and `S` is put back | ||
| /// after each span rather than during it. | ||
| /// |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Stale iteration count in the doc comment.
Line 2795 states "four of them is 16 bytes native and 12 emulation." The loop uses ldy #$03 and executes brk 3 times, not 4. Three native frames are 12 bytes (3 × 4), and three emulation frames are 9 bytes (3 × 3), not 16 and 12.
This number is a leftover from the original 4-iteration draft. The doc later corrects the iteration count in "the first draft used four iterations... wrapped... Three iterations measure 297 native against 291 emulation," but this earlier paragraph still names the wrong count.
As per path instructions, "The prose above a test is a claim about what it validates. If it names behaviors the emitted program does not exercise... that is a defect even though the test passes."
📝 Proposed fix
-/// abandoned on the stack; four of them is 16 bytes native and 12 emulation, and `S` is put back
+/// abandoned on the stack; three of them is 12 bytes native and 9 emulation, and `S` is put back
/// after each span rather than during it.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// So nothing returns. `V_BRK_VEC` is pointed at the instruction *after* the `BRK`, which the | |
| /// runtime's `jmp (V_BRK_VEC)` trampoline reaches directly — there is no handler body at all, and | |
| /// therefore no handler body whose cost could differ between the modes. Each frame is simply | |
| /// abandoned on the stack; four of them is 16 bytes native and 12 emulation, and `S` is put back | |
| /// after each span rather than during it. | |
| /// | |
| /// So nothing returns. `V_BRK_VEC` is pointed at the instruction *after* the `BRK`, which the | |
| /// runtime's `jmp (V_BRK_VEC)` trampoline reaches directly — there is no handler body at all, and | |
| /// therefore no handler body whose cost could differ between the modes. Each frame is simply | |
| /// abandoned on the stack; three of them is 12 bytes native and 9 emulation, and `S` is put back | |
| /// after each span rather than during it. | |
| /// |
🤖 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 `@tests/roms/AccuracySNES/gen/src/tests/cpu.rs` around lines 2792 - 2797,
Update the documentation comment above the relevant CPU test to describe three
abandoned frames, matching the loop’s ldy #$03 and three BRK executions: 12
bytes in native mode and 9 bytes in emulation mode. Leave the test
implementation and the later iteration-count discussion unchanged.
Source: Path instructions
The extra native cycle is the PBR push: a native
BRKframe is PBR/PCH/PCL/P and an emulation frame is the same without PBR, so the difference is one stack write (8 clocks), not a 6-clock internal cycle.Nothing returns, and that is what isolates
BRKThe obvious chain is
BRKinto a handler thatRTIs back. That measuresBRK + RTI— andRTIalso 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 be reporting its sibling's result added to its own. Deriving one from the other is weaker than measuring it.So nothing returns.
V_BRK_VECpoints at the instruction after theBRK, which the runtime'sjmp (V_BRK_VEC)trampoline reaches directly — there is no handler body at all, so no handler body whose cost could differ between the modes. Each frame is abandoned on the stack andSis restored after each span rather than during it.Both
BRKvectors already share that one trampoline:$FFE6(native) and$FFFE(emulation) both land onbrk_trampoline, whichruntime.sdocuments as deliberate — in emulation$FFFEis shared between IRQ and BRK, so a separate pointer would invent a distinction the machine does not have. The consequence here is exactly what the row needs.The repeat count was measured, not estimated
The first draft used four iterations on an estimate of ~35 dots each. It was wrong by a factor of three, and the H counter wraps at 341:
$FFF7An iteration is really ~99 dots. The lesson is the one
A5.19already recorded and this row had to relearn: a wrapped span does not fail, it returns a plausible small number — record the two raw readings and look at them before choosing the count.Yis the loop counter, notX: emulation forces the index registers to 8 bits, and anything that reads back differently between the two spans is a difference this row would report asBRK's.Inject-verified
if falseon the PBR push inop_brkfires the row's assertion, withA6.05("native pushes 4 bytes") failing alongside it as corroboration.exec.rsrestored byte-clean from a pre-edit snapshot.Verification
cargo fmt --check, workspace clippy at-D warnings, and the 56-test AccuracySNES harness suite all pass.snes9x: OK (15 known),Mesen2: OK (1 known),ares: OK (3 known), 54/54 scenes on both scene hosts,3 reference(s) agree with the cart.Coverage 354 → 355 of 443 (301 on-cart + 54 scenes); battery 342 tests, 100% on-cart.
🤖 Generated with Claude Code
Adds AccuracySNES assertion A5.18.
The test claims that
BRKtakes 8 cycles in native mode and 7 cycles in emulation mode because native mode performs one additional PBR stack write. The claim is false if that write is absent or if the measured timing difference is not 6 dots under the test setup.The test uses three non-returning
BRKexecutions and a shared trampoline. AccuracySNES coverage increases from 354/443 to 355/443 assertions. On-cart coverage increases from 300 to 301.