Skip to content

feat(accuracysnes): add A6.15 — all 256 opcodes defined, only STP hangs - #331

Merged
doublegate merged 3 commits into
mainfrom
feat/a6-15-opcode-definedness
Aug 2, 2026
Merged

feat(accuracysnes): add A6.15 — all 256 opcodes defined, only STP hangs#331
doublegate merged 3 commits into
mainfrom
feat/a6-15-opcode-definedness

Conversation

@doublegate

@doublegate doublegate commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Coverage 360 → 361 of 443.

What the row claims

The 65C816 has no undefined opcodes. Every one of the 256 bytes is a documented instruction with a documented length, and exactly one — STP — stops the processor until RESET. The cart executes each of the 241 straight-line opcodes in a WRAM sandbox and counts three outcomes against the length Table 5-4 of the WDC W65C816S datasheet gives it: returned where it should, returned late, or did not return.

cpu_opcodes.rs is built from the matrix's own columnar rules with a gate that fails the build if they leave a slot empty or fill one twice — which caught XBA ($EB) missing on the first run.

The terminator cannot be a return

TXS and TCS move the stack pointer — with x = 1 a TXS puts it in page zero — so the return address is no longer where an RTS would pop it from. Control comes back through a JMP, and because a JMP is three bytes, the exit addresses are chosen so its own operand bytes are harmless one-byte instructions:

BUF+0:        <opcode + safe operands>   ; L bytes
BUF+L:        JMP $AAAA                  ; $4C $AA $AA  — $AA is TAX
BUF+L+3..15:  $EA fill
BUF+16:       JMP $B8B8                  ; $4C $B8 $B8  — $B8 is CLV

An opcode consuming one byte too many therefore executes a register transfer and walks into the fill, instead of executing half an address. ($7E:EAEA was the first choice and is wrong — it sits inside the space runtime.inc reserves for widening the measurement channel to 1792 slots.)

The watchdog takes two strikes

NMI fires once per vblank, and across 241 sandbox runs it will eventually land inside a healthy one — so one hit cannot mean "stuck". The handler counts hits per opcode and abandons only on the second.

Dangerous-when-correct opcodes are handled, not excluded

opcode hazard handling
MVN/MVP move A + 1 bytes — $FFFF would move 65536 A = 0 in the preamble
XCE flips to emulation mode only if C is set CLC first makes it a no-op
TXS/TCS move SP the exits restore it from WRAM
SED, PLP leave d/anything set the exits re-establish m, x, d, c

Verified by injection, twice

injected bug result
WDM made a three-byte instruction exactly 1 LATE, first-bad $42
TRB dp made to jam (PC rewound) exactly 1 NO-RETURN, first-bad $14, watchdog rescues the battery

Picking that second injection is not free: SED and CLC both hang the cart before A6.15 runs, because the runtime and earlier Group A rows execute them. TRB is executed nowhere else on the cart.

The bug that made this look like a reference defect

The NMI handler runs with whatever data bank the sandbox left — $7E — so lda $4210 read a WRAM byte and the NMI was never acknowledged. Three hosts happened never to land an NMI where that showed. ares did, and the row reported PLA ($68) overshooting on ares alone, stably, with all 240 other opcodes agreeing.

That reads exactly like a reference bug, and it was ours. The chain that settled it:

  1. A diagnostic replacing the terminator with INX fill showed both ares and RustySNES resuming at the correct offset — so PLA's length was never in question.
  2. Sweeping only $68 passed on ares — so the failure needed the full sweep's elapsed time.
  3. Disarming the watchdog made all four hosts agree — so it was the watchdog.

lda f:$004210 is DBR-independent and fixes it. The handler now also preserves A: RTI restores P and PC but not the accumulator, and an interrupt that silently rewrites A is not transparent to what it interrupted.

With the fix the injections are exact where they had been approximate — the jam injection reported two stuck opcodes and 63 clean ones before, and reports one and 240 now.

Verification

  • cargo run -p accuracysnes-gen — 346 tests; bank $00 6,249 bytes free
  • cargo fmt --all --check, cargo clippy --workspace --exclude rustysnes-android --all-targets -- -D warnings, RUSTDOCFLAGS="-D warnings" cargo doc — clean
  • cargo test -p rustysnes-test-harness --features test-roms — 56 passed, battery 311/311
  • crossval.sh3 references agree, 55 scenes match / 0 unblessed / 0 mismatched, ARES_KNOWN_FAILURES unchanged at 3

Two harness bugs the injections found, both now gated: the driver's scratch first sat at $7E:0170, which a bus.rs row also uses (WRAM scratch has no allocator, unlike the measurement channel); and moving it into $7E:02xx would have put it inside the sandbox's own direct page, where the first direct-page operand would have overwritten the driver's state with its own test data.

🤖 Generated with Claude Code

Adds AccuracySNES assertion A6.15 for 65C816 opcode-definedness. It executes 241 straight-line opcodes in a WRAM sandbox and checks the documented instruction length. Coverage increases from 360 to 361 of 443; on-cart coverage increases from 303 to 304.

The claim is false if any executable opcode returns at the wrong length, returns late, or does not return. The test detects these cases with JMP terminators and a two-strike NMI watchdog.

The watchdog now acknowledges NMI through DBR-independent $4210 access and preserves the accumulator. The test isolates hazardous instructions, including MVN, MVP, XCE, TXS, TCS, SED, and PLP.

Injected defects produce the expected failures: a three-byte WDM reports a late return, and a jammed TRB reports a non-returning opcode. No emulator-core behavior changes.

…angs

Coverage 360 -> 361 of 443.

The row executes each of the 241 straight-line 65C816 opcodes in a WRAM sandbox
and counts three outcomes against the length Table 5-4 of the WDC W65C816S
datasheet documents: returned where it should, returned late, or did not
return. The expectation comes from the datasheet via a new cpu_opcodes.rs, built
from the matrix's own columnar rules with a gate that fails the build if they do
not tile it -- which caught XBA ($EB) missing on the first run.

The terminator cannot be a return. TXS and TCS move the stack pointer, and with
x=1 a TXS puts it in page zero, so the return address is no longer where an RTS
would pop it from. Control comes back through a JMP, and because a JMP is three
bytes the exit addresses are chosen so its OWN operand bytes are harmless
one-byte instructions: $AAAA (TAX) clean, $B8B8 (CLV) overshoot. An opcode that
consumes one byte too many therefore executes a register transfer and walks into
a NOP fill rather than executing half an address. ($7E:EAEA was the first choice
and is wrong -- it is inside the space runtime.inc reserves for widening the
measurement channel.)

The watchdog takes TWO strikes. NMI fires once per vblank and across 241 sandbox
runs will eventually land inside a healthy one, so one hit cannot mean stuck.

Four opcodes are dangerous even when correct and the preamble handles each
rather than excluding it: MVN/MVP move A+1 bytes so A=0; XCE flips to emulation
mode only if C is set so CLC first makes it a no-op; TXS/TCS are why the exits
restore SP from WRAM; SED and PLP are why they re-establish m, x, d and c.

Verified by injection twice. WDM made three bytes -> exactly one LATE, first-bad
$42. TRB dp made to jam -> exactly one NO-RETURN, first-bad $14, watchdog
rescuing the battery. Picking that second injection is not free: SED and CLC
both hang the cart BEFORE A6.15 runs, because the runtime and earlier Group A
rows execute them.

Also fixes the bug that made this look like a reference defect. The NMI handler
runs with the sandbox's DBR ($7E), so `lda $4210` read a WRAM byte and RDNMI was
never acknowledged. Three hosts never landed an NMI where it showed; ares did,
and the row reported PLA ($68) overshooting on ares alone. It was ours, not
ares'. Long addressing fixes it, and the handler now preserves A as well --
RTI restores P and PC but not the accumulator.

Battery 311/311. Three references agree; ARES_KNOWN_FAILURES stays 3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 2, 2026 14:53
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Added a complete 65C816 opcode metadata table and the AccuracySNES A6.15 test. The test executes 241 opcodes in WRAM sandboxes, uses safe terminators and operands, handles stuck instructions with an NMI watchdog, and records exact execution results. The change also updates coverage and documents reference-rendering behavior.

Changes

A6.15 opcode validation

Layer / File(s) Summary
Opcode metadata and validation
tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs, tests/roms/AccuracySNES/gen/src/main.rs
Added Flow, Mode, Op, and table(). The table defines all 256 opcodes, classifies control flow, validates unique coverage, and tests datasheet edge cases and four-byte opcode counts.
Opcode sandbox execution
tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs, tests/roms/AccuracySNES/gen/src/tests/mod.rs, tests/roms/AccuracySNES/gen/src/dsl.rs
Added A6.15, safe operand encoding, WRAM sandbox construction, exit stubs, assembler data access, processor-state setup, and aggregate test registration.
Watchdog and result assertions
tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
Added two-strike NMI watchdog handling, clean and overshoot exits, stuck-opcode reporting, exact execution assertions, and host-side layout checks.
Coverage and measurement integration
tests/roms/AccuracySNES/gen/src/dossier.rs, crates/rustysnes-test-harness/tests/accuracysnes.rs, docs/accuracysnes-plan.md, CHANGELOG.md
Registered A6.15, added four measurement slots, updated coverage counts, and documented the watchdog correction and E2.10 sweep update.

Reference-rendering discrepancy documentation

Layer / File(s) Summary
Per-dot rendering discrepancy
CHANGELOG.md, crates/rustysnes-test-harness/tests/undisbeliever_golden.rs
Reclassified inidisp_forgot_to_force_blank as a MesenCE reference disagreement. Documented the differing span-based and per-dot rendering behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant A615 as A6.15 test
  participant Table as Opcode table
  participant Sandbox as WRAM sandbox
  participant Watchdog as NMI watchdog
  participant Report as Result handlers
  A615->>Table: Read opcode and documented length
  A615->>Sandbox: Build safe opcode sandbox
  A615->>Sandbox: Execute opcode
  Sandbox->>Report: Return at clean or overshoot terminator
  Watchdog->>Report: Redirect after second NMI hit
  Report->>A615: Record outcome and continue sweep
Loading

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 10
✅ Passed checks (10 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses a valid Conventional Commit format, an imperative subject, and accurately describes the A6.15 opcode-coverage change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Changelog Entry ✅ Passed The full diff from origin/main to HEAD modifies CHANGELOG.md and adds an A6.15 entry documenting the AccuracySNES cartridge coverage and opcode test.
Docs-As-Spec ✅ Passed The diff changes no chip implementation crate. The only crates change is test-harness reporting: four A6.15 measurement labels and explanatory comments; emulator behavior is unchanged.
Accuracysnes Bookkeeping ✅ Passed A6.15 is registered in the battery and mapped to A6.15; all required generated artifacts are in the diff, no scene changed, and plan and coverage counts both increase by one.
No Panic On Untrusted Input ✅ Passed The only new expect calls operate on locally built opcode slots/table data; the other is inside #[cfg(test)]. No new panic call consumes ROM, save-state, netplay, scripting, or user path input.
Safety Comment On New Unsafe ✅ Passed The PR adds no unsafe blocks, unsafe fn, unsafe attributes, or unsafe impls in its Rust diff; the SAFETY-comment requirement is therefore not triggered.

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR extends the AccuracySNES cartridge’s CPU coverage by adding a new scored row (A6.15) that executes each straight-line 65C816 opcode in a WRAM sandbox to validate that all opcodes are defined and that only STP is a true “hang” instruction. It introduces a generator-side, datasheet-derived opcode matrix (cpu_opcodes.rs) to keep the row’s expectations independent of rustysnes-cpu, and wires the new row through the generator outputs, harness reporting, and docs/coverage accounting.

Changes:

  • Add A6.15 test generation (cpu_defined.rs) and integrate it into the generated test list / dossier mapping.
  • Add generator-side 65C816 opcode table (cpu_opcodes.rs) derived from WDC datasheet rules, with invariants enforced by unit tests.
  • Update generated cartridge artifacts + harness/docs/changelog to include the new row and updated assertion coverage totals.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/roms/AccuracySNES/gen/src/tests/mod.rs Registers the new cpu_defined module and includes A6.15 in the generated test list.
tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs Implements the A6.15 WRAM sandbox sweep + watchdog + measurement recording.
tests/roms/AccuracySNES/gen/src/main.rs Adds cpu_opcodes module to the generator crate.
tests/roms/AccuracySNES/gen/src/dossier.rs Adds dossier mapping for A6.15.
tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs New datasheet-based opcode matrix (mode/length/flow), used as the expectation source for A6.15.
tests/roms/AccuracySNES/asm/tests_group_a.s Generated assembly output updated to include the new test and its tables/stubs.
tests/roms/AccuracySNES/SOURCE_CATALOG.tsv Catalog entry added for A6.15 and offsets shifted accordingly (generated output).
tests/roms/AccuracySNES/ERROR_CODES.md Adds the new row’s failure-code documentation (generated output).
crates/rustysnes-test-harness/tests/accuracysnes.rs Adds slot labels for A6.15 measurement outputs (283–286).
docs/accuracysnes-plan.md Updates reported assertion coverage totals (360 → 361).
docs/accuracysnes-coverage.md Updates on-cart covered assertion count (303 → 304) and total coverage (360 → 361).
CHANGELOG.md Documents the new row and the watchdog fix; needs a small consistency tweak for the prior E2.10 coverage number.

Comment on lines +194 to +197
Mode::Implied => {}
// A direct-page offset into the seeded window page. `$10`/`$12` hold the indirect pointers.
Mode::Direct | Mode::StackRelative => out[1] = 0x10,
// An immediate value of zero, and — for a branch — a zero DISPLACEMENT, which is what makes
Comment thread CHANGELOG.md Outdated
With the fix the injections are exact where they had been approximate: the jam injection reported
two stuck opcodes and 63 clean ones before, and reports one and 240 now.

- **`E2.10` — the full 256-opcode SPC700 cycle sweep, and the coverage number moves to 360 of 443.**
Comment on lines +463 to +464
/// Nine opcodes are four bytes: the eight ALU long forms' two columns less the ones that are
/// jumps, plus `JSL` and `JML`. Counting them is a cheap check that the long columns tiled.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🤖 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/cpu_opcodes.rs`:
- Around line 24-27: Update the module documentation near the 8-bit-accumulator
explanation to identify tests/cpu_defined.rs, specifically its run_sandbox
function, as the sandbox that establishes sep #$30; remove the incorrect
sweep.rs reference while preserving the surrounding historical context.
- Around line 463-473: Update the rustdoc for
the_long_modes_are_where_they_should_be to state that eighteen opcodes are four
bytes, matching the existing inline comment and assert_eq! expectation; leave
the test logic unchanged.

In `@tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs`:
- Around line 640-647: Update the A6.15 row title and the module heading to
state that the sweep executes 241 opcodes, not all 256. Explicitly identify the
15 Flow::Leaves opcodes as out of scope because their zero-length tables cause
them to be skipped, including STP and the eight jumps/calls, three returns, BRK,
COP, and WAI.
- Around line 393-396: Add a unit test in the existing tests module that emits
an exit stub through emit_exit_stubs into a scratch Asm, counts its emitted
.byte operands, and asserts the count equals STUB_LEN. In the same test, assert
the byte at JML_TARGET_AT - 1 is $5C, preserving the JML opcode alignment and
validating both constants against the generated stub.
- Around line 583-591: Update the FIRST_BAD sentinel flow: initialize FIRST_BAD
to $00 instead of $FF, adjust note_bad so it treats zero as “none” while storing
the first failing opcode as opcode + 1, and relabel slot 286 to document the
encoded value and zero sentinel. Preserve the existing first-failure behavior
and verdict checks.
- Around line 627-631: In the generated sequence around the `OVER` and `STUCK`
additions, narrow the accumulator to 8-bit mode before `adc f:${var::STUCK}` so
the instruction reads only the `STUCK` byte. Preserve the existing low-byte
masking and ensure the addition still produces the intended `OVER + STUCK`
result without depending on `FIRST_BAD` or an afterward-discarded high byte.
- Around line 195-196: Update the operand generation around Mode::Direct and
Mode::StackRelative so long-indirect cases use the operand that reads the
contiguous pointer at D+$10–D+$12, with the bank byte immediately following the
16-bit address. Adjust run_sandbox seeding so those three bytes form the window
address in bank $7E, and add an assertion validating this invariant. Handle
StackRelative separately with an S-relative operand and correct the misleading
comment.
- Around line 343-348: Update the stub-generation loop to iterate over names
only, since target is discarded there and patched later in the separate
target-patching logic. Also revise the rustdoc describing the stub sequence to
include the emitted REP #$30 between CLC and restoring the saved state, without
removing instructions or changing STUB_LEN.
🪄 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: e9b6ee9b-c44d-4457-aac6-03c03fb4c086

📥 Commits

Reviewing files that changed from the base of the PR and between c6406de and a823dc5.

⛔ Files ignored due to path filters (6)
  • docs/accuracysnes-coverage.md is excluded by !docs/accuracysnes-coverage.md and included by docs/**
  • tests/roms/AccuracySNES/ERROR_CODES.md is excluded by !tests/roms/AccuracySNES/ERROR_CODES.md and included by tests/**
  • tests/roms/AccuracySNES/SOURCE_CATALOG.tsv is excluded by !**/*.tsv, !tests/roms/AccuracySNES/SOURCE_CATALOG.tsv and included by tests/**
  • tests/roms/AccuracySNES/asm/tests_group_a.s is excluded by !tests/roms/AccuracySNES/asm/tests_group_a.s and included by tests/**
  • tests/roms/AccuracySNES/build/accuracysnes-pal.sfc is excluded by !tests/roms/AccuracySNES/build/** and included by tests/**
  • tests/roms/AccuracySNES/build/accuracysnes.sfc is excluded by !tests/roms/AccuracySNES/build/** and included by tests/**
📒 Files selected for processing (8)
  • CHANGELOG.md
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • docs/accuracysnes-plan.md
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (6)
  • GitHub Check: lint
  • GitHub Check: accuracysnes
  • GitHub Check: test-light
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: build demo + docs
  • GitHub Check: review
🧰 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/accuracysnes-plan.md
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Chip-behavior changes must update both the chip implementation and the corresponding docs/<subsystem>.md documentation.

A chip change must update both the chip implementation and its corresponding docs/<chip>.md documentation in the same change.

Files:

  • docs/accuracysnes-plan.md
  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • CHANGELOG.md
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Do not commit or vendor the generated snesdev_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 relevant to-dos/ sprint file.

**/*: Preserve the one-directional crate graph: chip crates must not depend on one another; rustysnes-core ties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keep docs/STATUS.md as the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNES v2.0 or engine-lineage anchors as project releases.

Files:

  • docs/accuracysnes-plan.md
  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • CHANGELOG.md
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
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/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 generated docs/accuracysnes-coverage.md, and any statement of coverage that
is broader than what the corresponding test actually asserts.

Files:

  • docs/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/accuracysnes-plan.md
  • CHANGELOG.md
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use Rust edition 2024 and the toolchain pinned in rust-toolchain.toml (Rust 1.96).
Run cargo fmt --all --check; Rust code must remain rustfmt-compliant.
Run Clippy with cargo clippy --workspace --all-targets -- -D warnings; warnings must not remain.
New public Rust items must have rustdoc because missing_docs is a workspace lint.
Do not run cargo clippy --all-features; scripting and script-wasm are mutually exclusive. Use explicit per-feature jobs instead.

**/*.rs: Do not introduce .unwrap(), .expect(), or panic!() 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 new unsafe { ... } block or unsafe fn must 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 because unsafe_code is a workspace lint.

**/*.rs: Use Rust edition 2024 with the pinned 1.96 toolchain; satisfy workspace pedantic, nursery, missing_docs, and unsafe_code warnings because CI runs with -D warnings. Document every public item.
Keep unsafe code restricted to the frontend and FFI, and include a // SAFETY: justification for each use.
Keep hot paths allocation-free.
Treat rustysnes_core::Bus as 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:

  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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 its dossier.rs::MAP entry, all required regenerated artifacts, and matching count changes in docs/accuracysnes-plan.md. Artifact presence must be judged from the path-filter exclusion list, not from unreadable contents.

Files:

  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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:

  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
tests/roms/AccuracySNES/gen/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Rebuild AccuracySNES after any change to gen/ or asm/; never hand-edit generated asm/tests_group_a.s or asm/scenes.s.

Files:

  • tests/roms/AccuracySNES/gen/src/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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::MAP must 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/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
crates/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/*.rs: Preserve the master-clock lockstep timing model.
rustysnes-core::Bus owns 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.
Keep unsafe confined to existing allowed areas, namely frontend and FFI code, and document every unsafe block with a // SAFETY: comment.

Files:

  • crates/rustysnes-test-harness/tests/accuracysnes.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 matching docs/<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
crates/**

⚙️ CodeRabbit configuration file

crates/**: Emulator core. Hot paths are allocation-free; unsafe requires a // SAFETY: comment
naming the invariant. Any change to save-stated fields needs a FORMAT_VERSION bump and a
docs/adr/0006 bump-log entry. Behavior changes must update the matching docs/<chip>.md
in the same change.

Files:

  • crates/rustysnes-test-harness/tests/accuracysnes.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.md when 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 (5)
📚 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/tests/mod.rs
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/main.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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/mod.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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/mod.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
🔇 Additional comments (14)
tests/roms/AccuracySNES/gen/src/dossier.rs (1)

105-105: LGTM!

crates/rustysnes-test-harness/tests/accuracysnes.rs (1)

602-602: LGTM!

Also applies to: 632-638

docs/accuracysnes-plan.md (1)

17-17: LGTM!

CHANGELOG.md (1)

14-40: LGTM!

Also applies to: 42-60

tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs (1)

29-98: LGTM!

Also applies to: 100-296, 298-330, 332-388, 390-461

tests/roms/AccuracySNES/gen/src/main.rs (1)

14-14: LGTM!

tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs (7)

226-296: LGTM!


299-330: LGTM!


402-467: LGTM!


481-516: LGTM!

Also applies to: 522-581


76-145: LGTM!

Also applies to: 151-182, 650-749


356-361: 🩺 Stability & Availability

Keep the $00 bank byte. a6_15 and its targets are linked in TESTS within bank $00 for all AccuracySNES configurations. The proposed patch is unnecessary.

			> Likely an incorrect or invalid review comment.

599-611: 🗄️ Data Integrity & Integration

Remove this slot-collision finding. Slots 283–286 are uniquely owned by the A6.15 test, mapped in the harness, and covered by dossier::check_slots; no other writer reaches this range.

			> Likely an incorrect or invalid review comment.
tests/roms/AccuracySNES/gen/src/tests/mod.rs (1)

15-15: LGTM!

Also applies to: 28-28

Comment thread tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs Outdated
Comment thread tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs Outdated
Comment on lines +195 to +196
// A direct-page offset into the seeded window page. `$10`/`$12` hold the indirect pointers.
Mode::Direct | Mode::StackRelative => out[1] = 0x10,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The long-indirect opcodes read a pointer whose bank byte is $00, and the seeded 24-bit pointer at D+$12 is never used.

Mode::Direct covers [dp] ($07 column) and [dp],Y ($17 column) as well as the plain and 16-bit-indirect forms, and this arm gives all of them operand $10. run_sandbox at lines 474-479 seeds $0210/$0211 with $5000, $0212/$0213 with $5000, and $0214 with $7E.

A long-indirect opcode with operand $10 therefore fetches three bytes from $0210, $0211, $0212, which are $00, $50, $00. The effective address is $00:5000, not $7E:5000. The 24-bit pointer the seeding builds at $0212-$0214 is unreachable, because no emitted operand byte is ever $12. This affects the sixteen [dp] and [dp],Y opcodes across the eight ALU bases, including the two stores.

The comment on line 195 and the comment at lines 471-472 both assert that an indirect access cannot leave the window. That rationale does not hold for those sixteen opcodes. no_operand_reaches_the_sandbox_or_the_runtime_variables does not catch it either: it only checks b[1] >= 0x10.

Make one pointer serve both widths by placing the bank byte directly after the 16-bit pointer.

Separately, Mode::StackRelative is folded into this arm and given the same $10. Stack-relative operands are offsets from S, not from D, so the comment's description of them as "a direct-page offset into the seeded window page" is wrong.

Proposed fix to the pointer seeding
-    a.c("Seed the operand window: the indirect pointers at D+$10 (16-bit) and D+$12 (24-bit) both");
-    a.c("point back into the window, so an indirect load cannot reach MMIO or the sandbox.");
+    a.c("Seed the operand window. One pointer at D+$10 serves both widths: the two address bytes");
+    a.c("are read by (dp) and (dp),Y, and [dp] and [dp],Y read the bank byte that follows them.");
     a.l("rep #$30");
     a.l(&format!("lda #${WINDOW:04X}"));
     a.l(&format!("sta f:$7E{:04X}", SANDBOX_DP + 0x10));
-    a.l(&format!("sta f:$7E{:04X}", SANDBOX_DP + 0x12));
     a.l("sep #$20");
     a.l("lda #$7E");
-    a.l(&format!("sta f:$7E{:04X}", SANDBOX_DP + 0x14));
+    a.l(&format!("sta f:$7E{:04X}", SANDBOX_DP + 0x12));

Add a gate that asserts the seeded bytes at D+$10, D+$11 and D+$12 form the window address in bank $7E, so a future change to the seeding cannot silently reintroduce the $00 bank.

🤖 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_defined.rs` around lines 195 - 196,
Update the operand generation around Mode::Direct and Mode::StackRelative so
long-indirect cases use the operand that reads the contiguous pointer at
D+$10–D+$12, with the bank byte immediately following the 16-bit address. Adjust
run_sandbox seeding so those three bytes form the window address in bank $7E,
and add an assertion validating this invariant. Handle StackRelative separately
with an S-relative operand and correct the misleading comment.

Source: Path instructions

Comment thread tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs Outdated
Comment on lines +393 to +396
/// Bytes in one exit stub. Pinned as a constant because the copy loop counts them, and asserted
/// against the emitted table by a unit test — a miscount would copy a truncated stub into WRAM and
/// the first opcode to reach it would run off the end.
const STUB_LEN: u16 = 19;

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 | 🟠 Major | ⚡ Quick win

STUB_LEN's doc claims a unit test that does not exist.

Lines 393-395 state that STUB_LEN is "asserted against the emitted table by a unit test", and name the consequence of a miscount: the copy loop at lines 365-374 would write a truncated stub into WRAM.

The tests module at lines 650-749 contains five tests. None counts the bytes emit_exit_stubs emits. STUB_LEN = 19 is correct today — 2 + 1 + 1 + 2 + 4 + 1 + 3 + 1 + 4 — but nothing holds it correct, and JML_TARGET_AT = 16 is unguarded in the same way.

Add the gate the doc already promises. Emit the stub into a scratch Asm, count the .byte operands, and compare against STUB_LEN. Assert that the byte at offset JML_TARGET_AT - 1 is $5C, which pins the patch offset to the JML opcode.

🤖 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_defined.rs` around lines 393 - 396,
Add a unit test in the existing tests module that emits an exit stub through
emit_exit_stubs into a scratch Asm, counts its emitted .byte operands, and
asserts the count equals STUB_LEN. In the same test, assert the byte at
JML_TARGET_AT - 1 is $5C, preserving the JML opcode alignment and validating
both constants against the generated stub.

Source: Path instructions

Comment thread tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
Comment thread tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
Comment on lines +640 to +647
a.finish(
"A6.15",
'A',
"all 256 opcodes defined",
Provenance::Documented("WDC W65C816S datasheet, Table 5-4 opcode matrix"),
Kind::Scored,
None,
)

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

The row title claims 256 opcodes; the sweep exercises 241.

finish names the row "all 256 opcodes defined", and the module title at line 1 states "every 65C816 opcode is defined, and only STP hangs".

The sweep executes 241 opcodes. The fifteen Flow::Leaves opcodes get a table length of zero and are skipped at line 270. STP is one of them, so the emitted program never runs it and asserts nothing about whether it hangs. The eight jumps and calls, the three returns, BRK, COP and WAI are likewise never executed, so the row makes no claim about their definedness either.

Restate the title and the module heading to match the 241 opcodes the program executes, and say which fifteen are out of scope and why.

🤖 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_defined.rs` around lines 640 - 647,
Update the A6.15 row title and the module heading to state that the sweep
executes 241 opcodes, not all 256. Explicitly identify the 15 Flow::Leaves
opcodes as out of scope because their zero-length tables cause them to be
skipped, including STP and the eight jumps/calls, three returns, BRK, COP, and
WAI.

Source: Path instructions

Nine findings, all real. Two were functional and both are operand-safety
violations the row's own docs claimed did not exist.

- STACK-RELATIVE operands reached MMIO. The cart's stack sits at $1FFF, so
  `ORA $10,S` addressed $00:200F and a PLA read $00:2000 -- both outside WRAM,
  in the unmapped/MMIO region. The preamble now moves SP into page 1 for the
  run; the exits already restored the cart's own from SAVED_SP.
- The LONG indirects read a pointer with a bank byte of $00. `[dp]` and
  `[dp],Y` take all three bytes at the direct-page pointer, and the first draft
  seeded two separate pointers, giving the long one bank $00 -- so it reached
  $00:5000, unmapped, rather than $7E:5000. There is now ONE pointer at D+$10,
  seeded 24-bit; the 16-bit indirects read its first two bytes and take the
  bank from DBR, which the preamble sets to $7E.

- $FF was the "no failure" sentinel AND a real opcode: $FF is SBC long,X, which
  the row executes, so a genuine failure there would have been reported as
  `none`. ($DB is STP, not $FF -- I had that wrong in the first version of the
  test that was supposed to prove the sentinel safe, and the test caught me.)
  The sentinel is now $00 = BRK, which is in the not-executed set and therefore
  can never be a real answer. A unit test pins both halves of that.
- STUB_LEN's doc claimed a unit test that did not exist. It exists now, and it
  counts the bytes the table actually emits.
- cpu_opcodes.rs named sweep.rs as the sandbox that establishes sep #$30; the
  consumer of that table is tests/cpu_defined.rs.
- The four-byte-opcode test's doc said nine where the assertion says eighteen.
- emit_exit_stubs bound `target` and discarded it, and its doc omitted the
  REP #$30 the stub emits.
- The CHANGELOG's E2.10 entry restated a coverage total that A6.15's entry then
  superseded in the same cumulative section.

Re-verified after the SP and pointer changes, which move the image: battery
311/311, three references agree, 55 scenes match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_defined.rs`:
- Around line 347-349: Correct the instruction name in the stub documentation
comment by replacing TXS with TCS, matching the emitted instruction and the
accumulator-based stack restoration behavior.
🪄 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: f09eb62c-ba42-4f21-a937-3dbdb904f8cc

📥 Commits

Reviewing files that changed from the base of the PR and between a823dc5 and 317bdf6.

⛔ Files ignored due to path filters (4)
  • tests/roms/AccuracySNES/ERROR_CODES.md is excluded by !tests/roms/AccuracySNES/ERROR_CODES.md and included by tests/**
  • tests/roms/AccuracySNES/asm/tests_group_a.s is excluded by !tests/roms/AccuracySNES/asm/tests_group_a.s and included by tests/**
  • tests/roms/AccuracySNES/build/accuracysnes-pal.sfc is excluded by !tests/roms/AccuracySNES/build/** and included by tests/**
  • tests/roms/AccuracySNES/build/accuracysnes.sfc is excluded by !tests/roms/AccuracySNES/build/** and included by tests/**
📒 Files selected for processing (5)
  • CHANGELOG.md
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: lint
  • GitHub Check: accuracysnes
  • GitHub Check: test-light
  • GitHub Check: build demo + docs
  • GitHub Check: review
🧰 Additional context used
📓 Path-based instructions (12)
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use Rust edition 2024 and the toolchain pinned in rust-toolchain.toml (Rust 1.96).
Run cargo fmt --all --check; Rust code must remain rustfmt-compliant.
Run Clippy with cargo clippy --workspace --all-targets -- -D warnings; warnings must not remain.
New public Rust items must have rustdoc because missing_docs is a workspace lint.
Do not run cargo clippy --all-features; scripting and script-wasm are mutually exclusive. Use explicit per-feature jobs instead.

**/*.rs: Do not introduce .unwrap(), .expect(), or panic!() 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 new unsafe { ... } block or unsafe fn must 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 because unsafe_code is a workspace lint.

**/*.rs: Use Rust edition 2024 with the pinned 1.96 toolchain; satisfy workspace pedantic, nursery, missing_docs, and unsafe_code warnings because CI runs with -D warnings. Document every public item.
Keep unsafe code restricted to the frontend and FFI, and include a // SAFETY: justification for each use.
Keep hot paths allocation-free.
Treat rustysnes_core::Bus as 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:

  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Chip-behavior changes must update both the chip implementation and the corresponding docs/<subsystem>.md documentation.

A chip change must update both the chip implementation and its corresponding docs/<chip>.md documentation in the same change.

Files:

  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • CHANGELOG.md
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Do not commit or vendor the generated snesdev_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 relevant to-dos/ sprint file.

**/*: Preserve the one-directional crate graph: chip crates must not depend on one another; rustysnes-core ties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keep docs/STATUS.md as the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNES v2.0 or engine-lineage anchors as project releases.

Files:

  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • CHANGELOG.md
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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 its dossier.rs::MAP entry, all required regenerated artifacts, and matching count changes in docs/accuracysnes-plan.md. Artifact presence must be judged from the path-filter exclusion list, not from unreadable contents.

Files:

  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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:

  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
tests/roms/AccuracySNES/gen/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Rebuild AccuracySNES after any change to gen/ or asm/; never hand-edit generated asm/tests_group_a.s or asm/scenes.s.

Files:

  • tests/roms/AccuracySNES/gen/src/dsl.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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::MAP must 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/dsl.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs
crates/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/*.rs: Preserve the master-clock lockstep timing model.
rustysnes-core::Bus owns 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.
Keep unsafe confined to existing allowed areas, namely frontend and FFI code, and document every unsafe block with a // SAFETY: comment.

Files:

  • crates/rustysnes-test-harness/tests/accuracysnes.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 matching docs/<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
crates/**

⚙️ CodeRabbit configuration file

crates/**: Emulator core. Hot paths are allocation-free; unsafe requires a // SAFETY: comment
naming the invariant. Any change to save-stated fields needs a FORMAT_VERSION bump and a
docs/adr/0006 bump-log entry. Behavior changes must update the matching docs/<chip>.md
in the same change.

Files:

  • crates/rustysnes-test-harness/tests/accuracysnes.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.md when 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
**/*.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:

  • CHANGELOG.md
🧠 Learnings (3)
📚 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/dsl.rs
  • tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs
  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.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_defined.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_defined.rs
🔇 Additional comments (6)
crates/rustysnes-test-harness/tests/accuracysnes.rs (1)

602-602: LGTM!

Also applies to: 632-638

CHANGELOG.md (1)

14-40: LGTM!

Also applies to: 42-60, 61-61

tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs (2)

709-727: Pin JML_TARGET_AT against the emitted stub.

This test verifies STUB_LEN, but it does not verify that byte offset JML_TARGET_AT - 1 still contains $5C. A future edit can move JML while preserving the 19-byte length. The target patch would then overwrite unrelated stub bytes at runtime. Assert the JML opcode position for both emitted stubs.


97-100: LGTM!

Also applies to: 249-262, 351-394, 400-402, 478-530, 602-606, 628-631, 673-677, 729-746

tests/roms/AccuracySNES/gen/src/cpu_opcodes.rs (1)

25-27: LGTM!

Also applies to: 463-465

tests/roms/AccuracySNES/gen/src/dsl.rs (1)

196-205: LGTM!

Comment on lines +347 to +349
/// `TXS`: `SEP #$30`, `CLD`, `CLC`, `REP #$30`, put the saved stack pointer back, put the direct
/// page back, then `JML` into the driver. `DBR` is left alone — the stub runs in bank `$7E` so `PHK`/`PLB`
/// there would set the wrong one; the driver does it on arrival instead.

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

Correct the instruction name in the stub documentation.

Line 347 names TXS, but line 364 emits TCS ($1B). TXS would restore the stack pointer from X. This stub restores it from A. Replace TXS with TCS.

Proposed fix
-/// `TXS`: `SEP #$30`, `CLD`, `CLC`, `REP #$30`, put the saved stack pointer back, put the direct
+/// `TCS`: `SEP #$30`, `CLD`, `CLC`, `REP #$30`, put the saved stack pointer back, put the direct
📝 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.

Suggested change
/// `TXS`: `SEP #$30`, `CLD`, `CLC`, `REP #$30`, put the saved stack pointer back, put the direct
/// page back, then `JML` into the driver. `DBR` is left alone — the stub runs in bank `$7E` so `PHK`/`PLB`
/// there would set the wrong one; the driver does it on arrival instead.
/// `TCS`: `SEP #$30`, `CLD`, `CLC`, `REP #$30`, put the saved stack pointer back, put the direct
/// page back, then `JML` into the driver. `DBR` is left alone — the stub runs in bank `$7E` so `PHK`/`PLB`
/// there would set the wrong one; the driver does it on arrival instead.
🤖 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_defined.rs` around lines 347 - 349,
Correct the instruction name in the stub documentation comment by replacing TXS
with TCS, matching the emitted instruction and the accumulator-based stack
restoration behavior.

… not our gap

It was recorded as "the last per-dot framebuffer gap" -- a Phase 4d item, to be
closed by tracking the CGRAM redirect target in the fetch stage per layer, then
re-blessing. Reading the third reference overturns that.

The recorded account of MesenCE was incomplete. It was described as updating
InternalCgramAddress only inside GetRgbColor, "so a transparent pixel leaves the
previous opaque column's value standing". It does not: SnesPpu::RenderBgColor()
runs AFTER every layer render in a span and sets InternalCgramAddress = 0 for
each backdrop column it fills. What survives a span is therefore decided by pass
ordering across the whole span -- an artefact of a span-based renderer, not a
per-dot physical process.

ares settles it. PPU::DAC::paletteColor sets latch.cgramAddress = palette
UNCONDITIONALLY, called during per-dot priority resolution in the DAC, including
paletteColor(0) for the transparent case (dac.cpp:71). That is RustySNES's model
exactly: one assignment per dot, from the composited pixel, backdrop giving zero.

So RustySNES and ares implement the same per-dot model and MesenCE differs for
architectural reasons; the 7fc6 in the golden encodes MesenCE's renderer rather
than hardware. The engine is NOT changed to match a pass-ordering artefact, and
the "when 4d lands, remove the entry and re-bless" instruction is withdrawn.

Two corroborations. C3.12 -- the scored cart row that asserts the redirect target
directly -- passes on BOTH Mesen2 and RustySNES, so the two agree on the
assertion and differ only on this homebrew framebuffer hash. And C3.12's own
provenance is a per-dot statement, "a CGRAM access during active display uses the
colour the PPU IS DRAWING", which the per-dot model implements directly and a
span-ordering model only approximates.

Same shape as the Mode-5 first-pixel claim, with the lesson from that retraction
applied: the third reference was read BEFORE publishing, not after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

Adds AccuracySNES test A6.15 to execute all 241 straight-line 65C816 opcodes in a WRAM sandbox with an NMI watchdog to verify opcode length and return safety, while reclassifying inidisp_forgot_to_force_blank as a reference rendering model divergence based on ares analysis.

Blocking issues

None found.

Suggestions

  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs:3036 (generated assembly in asm/tests_group_a.s:1269-1273): assert_a16_range(0, 0, ...) emits a lower-bound check (cmp #$0000 / bcs :+ / jmp @fail2). Because unsigned accumulator values are always >= 0, bcs always branches and the jmp @fail2 following cmp #$0000 is dead code. Consider special-casing min == 0 in assert_a16_range or adding an assert_a16_zero helper to omit redundant lower-bound comparisons.

Nitpicks

  • tests/roms/AccuracySNES/gen/src/tests/cpu_defined.rs:249-252: Constants declared inside private mod var (OP, LEN, SAVED_SP, etc.) use pub const. Prefer module-private const inside private modules.

Automated first-pass review by agy on a self-hosted runner -- not a human review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@crates/rustysnes-test-harness/tests/undisbeliever_golden.rs`:
- Around line 41-50: Align the PPU documentation with the final
per-dot/span-renderer model rather than stating that transparent pixels retain
the previous opaque address. Update the explanation in
crates/rustysnes-test-harness/tests/undisbeliever_golden.rs lines 41-50 and the
corresponding release-note explanation in CHANGELOG.md lines 162-170; both sites
must describe the same backdrop-column zeroing and pass-ordering behavior.
- Around line 32-35: Update the `PERDOT_KNOWN_GAPS` entry and the related branch
diagnostics around `inidisp_forgot_to_force_blank` to use the
reference-disagreement classification instead of “known per-dot gap (Phase 4d
pending)” or “per-dot gap hash changed.” Keep the intentional mismatch handling
intact while removing all obsolete Phase 4d/pending-gap wording from CI output.
🪄 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: 751025c7-d3fa-4e9c-8edf-d1496cc724fe

📥 Commits

Reviewing files that changed from the base of the PR and between 317bdf6 and 77880bb.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • crates/rustysnes-test-harness/tests/undisbeliever_golden.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: accuracysnes
  • GitHub Check: test-light
  • GitHub Check: lint
  • GitHub Check: review
  • GitHub Check: build demo + docs
🧰 Additional context used
📓 Path-based instructions (9)
crates/**/*.rs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

crates/**/*.rs: Preserve the master-clock lockstep timing model.
rustysnes-core::Bus owns 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.
Keep unsafe confined to existing allowed areas, namely frontend and FFI code, and document every unsafe block with a // SAFETY: comment.

Files:

  • crates/rustysnes-test-harness/tests/undisbeliever_golden.rs
**/*.rs

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.rs: Use Rust edition 2024 and the toolchain pinned in rust-toolchain.toml (Rust 1.96).
Run cargo fmt --all --check; Rust code must remain rustfmt-compliant.
Run Clippy with cargo clippy --workspace --all-targets -- -D warnings; warnings must not remain.
New public Rust items must have rustdoc because missing_docs is a workspace lint.
Do not run cargo clippy --all-features; scripting and script-wasm are mutually exclusive. Use explicit per-feature jobs instead.

**/*.rs: Do not introduce .unwrap(), .expect(), or panic!() 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 new unsafe { ... } block or unsafe fn must 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 because unsafe_code is a workspace lint.

**/*.rs: Use Rust edition 2024 with the pinned 1.96 toolchain; satisfy workspace pedantic, nursery, missing_docs, and unsafe_code warnings because CI runs with -D warnings. Document every public item.
Keep unsafe code restricted to the frontend and FFI, and include a // SAFETY: justification for each use.
Keep hot paths allocation-free.
Treat rustysnes_core::Bus as 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/undisbeliever_golden.rs
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Chip-behavior changes must update both the chip implementation and the corresponding docs/<subsystem>.md documentation.

A chip change must update both the chip implementation and its corresponding docs/<chip>.md documentation in the same change.

Files:

  • crates/rustysnes-test-harness/tests/undisbeliever_golden.rs
  • CHANGELOG.md
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Do not commit or vendor the generated snesdev_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 relevant to-dos/ sprint file.

**/*: Preserve the one-directional crate graph: chip crates must not depend on one another; rustysnes-core ties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keep docs/STATUS.md as the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNES v2.0 or engine-lineage anchors as project releases.

Files:

  • crates/rustysnes-test-harness/tests/undisbeliever_golden.rs
  • CHANGELOG.md
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 matching docs/<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/undisbeliever_golden.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/undisbeliever_golden.rs
crates/**

⚙️ CodeRabbit configuration file

crates/**: Emulator core. Hot paths are allocation-free; unsafe requires a // SAFETY: comment
naming the invariant. Any change to save-stated fields needs a FORMAT_VERSION bump and a
docs/adr/0006 bump-log entry. Behavior changes must update the matching docs/<chip>.md
in the same change.

Files:

  • crates/rustysnes-test-harness/tests/undisbeliever_golden.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.md when 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
**/*.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:

  • CHANGELOG.md
🔇 Additional comments (1)
CHANGELOG.md (1)

14-40: LGTM!

Also applies to: 44-61

Comment on lines +32 to +35
/// # This is a reference disagreement, not a RustySNES gap
///
/// It was recorded as "a Phase 4d (PPU access-during-render) gap — when 4d lands, remove the entry
/// and re-bless". Reading the third reference (2026-08-02) overturned that:

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

Remove the obsolete “Phase 4d pending” classification.

The new text classifies inidisp_forgot_to_force_blank as a reference disagreement, but PERDOT_KNOWN_GAPS and the existing branch at Lines 147-159 still report known per-dot gap (Phase 4d pending) and per-dot gap hash changed. Rename the classification and diagnostics so CI output does not identify an intentional reference mismatch as a pending RustySNES defect.

🤖 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 `@crates/rustysnes-test-harness/tests/undisbeliever_golden.rs` around lines 32
- 35, Update the `PERDOT_KNOWN_GAPS` entry and the related branch diagnostics
around `inidisp_forgot_to_force_blank` to use the reference-disagreement
classification instead of “known per-dot gap (Phase 4d pending)” or “per-dot gap
hash changed.” Keep the intentional mismatch handling intact while removing all
obsolete Phase 4d/pending-gap wording from CI output.

Comment on lines +41 to +50
/// - **MesenCE** writes it per *layer fetch* and then runs `RenderBgColor()` — which zeroes backdrop
/// columns — **after** every layer render in a span. What survives a span is therefore decided by
/// pass ordering across the whole span, which is an artefact of a span-based renderer rather than
/// a per-dot physical process.
///
/// So the `7fc6` encodes MesenCE's renderer architecture. Two corroborations: `C3.12` — the scored
/// AccuracySNES row that asserts the redirect target directly — **passes on both Mesen2 and
/// RustySNES**, so they agree on the assertion and differ only on this homebrew hash; and `C3.12`'s
/// provenance is a per-dot statement ("uses the colour the PPU *is drawing*"), which the per-dot
/// model implements directly.

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

Resolve the conflicting PPU specification.

The changed documentation states that MesenCE zeroes backdrop columns through span pass ordering. docs/ppu.md still states that transparent pixels retain the previous opaque address. Keep the specification consistent before merge.

  • crates/rustysnes-test-harness/tests/undisbeliever_golden.rs#L41-L50: align the harness explanation with the final PPU model.
  • CHANGELOG.md#L162-L170: align the release-note explanation with the same PPU model.
📍 Affects 2 files
  • crates/rustysnes-test-harness/tests/undisbeliever_golden.rs#L41-L50 (this comment)
  • CHANGELOG.md#L162-L170
🤖 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 `@crates/rustysnes-test-harness/tests/undisbeliever_golden.rs` around lines 41
- 50, Align the PPU documentation with the final per-dot/span-renderer model
rather than stating that transparent pixels retain the previous opaque address.
Update the explanation in
crates/rustysnes-test-harness/tests/undisbeliever_golden.rs lines 41-50 and the
corresponding release-note explanation in CHANGELOG.md lines 162-170; both sites
must describe the same backdrop-column zeroing and pass-ordering behavior.

Source: Path instructions

@doublegate
doublegate merged commit 368cc9f into main Aug 2, 2026
16 checks passed
@doublegate
doublegate deleted the feat/a6-15-opcode-definedness branch August 2, 2026 17:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants