Skip to content

feat(accuracysnes): publish scenes on a known field (T-04-H) - #299

Merged
doublegate merged 1 commit into
mainfrom
feat/accuracysnes-scene-field-gate
Aug 1, 2026
Merged

feat(accuracysnes): publish scenes on a known field (T-04-H)#299
doublegate merged 1 commit into
mainfrom
feat/accuracysnes-scene-field-gate

Conversation

@doublegate

@doublegate doublegate commented Aug 1, 2026

Copy link
Copy Markdown
Owner

The second of the four v1.29.0 machinery items: publish a scene only on a known field — a change to run_scenes, not to a scene.

The problem it fixes

The scene protocol had no frame-parity control. An interlace scene therefore asks a question whose answer alternates every frame, and C7.12 (a 16x32 sprite under OBJ interlace renders as 16x16) produced three different hashes on three emulators — the only three-way split any scene has produced. That was never three cores disagreeing about interlace: each host simply landed on whichever field its own frame counter happened to be on.

run_scenes now publishes the scene ID only on frames whose $213F bit 7 is set, so every sighting the host counts is the same field. SCENE_FRAMES grows 8 → 12, because at half the publication rate the old value put the host's fourth sighting on the window's last frame — one of the two ends the capture protocol exists to avoid.

The 53 blessed scenes are unaffected on all three hosts. A still picture hashes the same on either field, so gating only moves which wall-clock frame the fourth sighting is.

It did what it was built to do

c7-obj-interlace-halves-height is c7-objsel-size-6 with one SETINI bit changed and nothing else, so the pair differs by exactly OBJ interlace.

hash
snes9x 0x266241e43ab85064
Mesen2 0x266241e43ab85064
RustySNES 0x16f7ab8c7f97b7f8

Three-way split → two-way. The two references, which share no code and no host, now produce the identical picture.

The residual, characterised rather than argued about

Pixel dumps localise it completely. Both renders agree on everything C7.12 asserts — the 16x32 sprite occupies 16 display rows, its 32x64 neighbour 32 — and differ only in which field's source rows are drawn: the references take the even ones, RustySNES the odd. One source row, nothing else.

Left unblessed, and not called a RustySNES bug. "RustySNES alone" is this project's signature for a real defect, and RustySNES is not alone: its row + field and its $213F bit 7 are both ares' (sfc/ppu/object.cpp:122, sfc/ppu/io.cpp:178), and both cores toggle the field at the same V wrap. So this is the bsnes/ares lineage against snes9x and Mesen2 — 2 vs 1 under this project's own provenance rule, not 2 vs 2 — and no primary source says which field maps to which rows. Recorded as a variant set, the same treatment the Mode-5 hi-res cluster already gets. The tiebreaker is ares actually running the cart, the same blocked item A2.10 waits on.

C9.03/C9.06 are deliberately not written on top of this. Screen interlace carries the identical parity dependency, so they would land unblessed for the identical reason; two more unblessable scenes is not progress.

Verification

  • cargo fmt --check, cargo clippy --workspace --exclude rustysnes-android --all-targets -- -D warnings — clean.
  • cargo test -p rustysnes-test-harness --test accuracysnes --features test-roms56 passed, 0 failed.
  • cargo test -p rustysnes-test-harness --test accuracysnes_scenes --features test-roms — 53 blessed match, 1 unblessed.
  • REF_PROJ=$PWD/ref-proj bash scripts/accuracysnes/crossval.shsnes9x: OK (14 known), Mesen2: OK (2 known), 53 scene(s) match, 1 unblessed, 0 mismatched on both, cross-validation: 2 reference(s) agree with the cart.

🤖 Generated with Claude Code

Summary

run_scenes now publishes scene IDs only when $213F bit 7 is set. This fixes inconsistent field parity for interlace scenes. The defect occurs when an interlace scene spans fields with different parity.

The change adds dossier assertion C7.12 (c7-obj-interlace-halves-height). SCENE_FRAMES increases from 8 to 12. The blessed-scene coverage denominator remains 53 because C7.12 remains an unblessed variant.

The claim is false if scene IDs publish on frames with bit 7 clear, if snes9x and Mesen2 produce different C7.12 hashes, or if the 53 existing blessed scenes change. RustySNES still differs in selected source rows. C9.03 and C9.06 remain intentionally unblessed.

The scene protocol had no frame-parity control, so an interlace scene asked a
question whose answer alternates every frame. C7.12 produced three different
hashes on three emulators for exactly that reason -- not three cores
disagreeing about interlace, but each host landing on whichever field its own
frame counter happened to be on.

run_scenes now publishes the scene ID only on frames whose $213F bit 7 is set.
SCENE_FRAMES grows 8 -> 12 because at half the publication rate the old value
put the host's fourth sighting on the window's last frame, one of the two ends
the capture protocol exists to avoid. The 53 blessed scenes are unaffected on
all three hosts: a still picture hashes the same on either field.

With the gate, snes9x and Mesen2 produce the IDENTICAL hash for the new
c7-obj-interlace-halves-height scene. The three-way split is a two-way one.

The residual is left unblessed and is fully characterised rather than argued
about: pixel dumps show both renders agree on everything C7.12 asserts -- the
16x32 sprite occupies 16 display rows -- and differ only in which field's
source rows are drawn. RustySNES is not alone on its side; its `row + field`
and its $213F bit 7 are both ares', so this is the bsnes/ares lineage against
the other two, which is 2 vs 1 under this project's provenance rule and not
2 vs 2. No primary source says which field maps to which rows. Recorded as a
variant set, per the rule the Mode-5 hi-res cluster is already under.

C9.03/C9.06 are deliberately not written on top of this: screen interlace has
the identical parity dependency and they would land unblessed for the
identical reason.

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

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9d8d36c8-56c2-4f69-bd69-53a4ea0a40ae

📥 Commits

Reviewing files that changed from the base of the PR and between 2e5c2ec and 51467cb.

⛔ Files ignored due to path filters (5)
  • docs/accuracysnes-coverage.md is excluded by !docs/accuracysnes-coverage.md and included by docs/**
  • tests/roms/AccuracySNES/asm/scenes.s is excluded by !tests/roms/AccuracySNES/asm/scenes.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/**
  • tests/roms/AccuracySNES/build/scenes.tsv is excluded by !**/*.tsv, !tests/roms/AccuracySNES/build/** and included by tests/**
📒 Files selected for processing (5)
  • CHANGELOG.md
  • docs/accuracysnes-plan.md
  • tests/roms/AccuracySNES/asm/runtime.inc
  • tests/roms/AccuracySNES/asm/runtime.s
  • tests/roms/AccuracySNES/gen/src/scenes.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout. (6)
  • GitHub Check: test-light
  • GitHub Check: accuracysnes
  • GitHub Check: lint
  • GitHub Check: copilot-pull-request-reviewer
  • GitHub Check: review
  • GitHub Check: build demo + docs
🧰 Additional context used
📓 Path-based instructions (17)
**/*

📄 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/asm/runtime.inc
  • tests/roms/AccuracySNES/asm/runtime.s
  • tests/roms/AccuracySNES/gen/src/scenes.rs
  • CHANGELOG.md
  • docs/accuracysnes-plan.md
tests/roms/AccuracySNES/asm/runtime.inc

⚙️ CodeRabbit configuration file

tests/roms/AccuracySNES/asm/runtime.inc: Shared symbol definitions. Every address here is claimed by something; a new variable that
overlaps an existing block, or a block whose stated width is smaller than what the code
stores into it, is the failure mode. Check the arithmetic of VAR_BASE + $nn offsets
against their declared sizes and against the neighbouring definitions.

Files:

  • tests/roms/AccuracySNES/asm/runtime.inc
tests/roms/AccuracySNES/asm/**/*.s

📄 CodeRabbit inference engine (AGENTS.md)

tests/roms/AccuracySNES/asm/**/*.s: Do not hand-edit asm/tests_group_a.s or asm/scenes.s; regenerate them with cargo run -p accuracysnes-gen.
Use assertion helpers for every verdict, including non-equality conditions; never hand-write a verdict byte such as sta V_TEST_RESULT.
Do not let a guard subsume the assertion it protects; mask guards so the assertion remains independently capable of failing, and validate named bug injections.
Inject faults at the site named by the test, not at another site that merely changes the observed result.
When a result depends on timing phase, record it and score nothing; otherwise widen comparisons by one sampling interval when appropriate. Use the NTSC/PAL drift gate as a tripwire.
Wait for results to become valid before measuring them; preserve required waits such as the vblank/automatic joypad-read delay.
Use the measurement channel without slot collisions; dossier::check_slots must pass, and widen the channel when it is full.
Keep Groups A and B in bank $00; do not relocate them, because their tests depend on program-bank placement and access speed.
Do not place a segment across a bank boundary; use _far wrappers for relocated groups and .loword(...) for address immediates when a group leaves bank $00.
Assembly helpers must be width-neutral (php/plp); emit correct .a8/.a16 directives after every sep/rep.
Every APU test program must call release_to_ipl, which re-maps the IPL ROM before returning control.
Bound every APU handshake wait; if the APU does not answer, report SKIP rather than hanging the battery.

Files:

  • tests/roms/AccuracySNES/asm/runtime.s
tests/roms/AccuracySNES/**/*.s

📄 CodeRabbit inference engine (AGENTS.md)

Respect the Group F input contract: runners must hold both controllers for the entire run using PAD_CONTRACT = $9050 and PAD2_CONTRACT = $60A0; run F1.07 before arming auto-read.

Files:

  • tests/roms/AccuracySNES/asm/runtime.s
tests/roms/AccuracySNES/asm/runtime.s

⚙️ CodeRabbit configuration file

tests/roms/AccuracySNES/asm/runtime.s: Hand-written 65816. The battery runs under forced blank throughout; anything that lifts it
must put it back. Helpers are width-neutral (php/plp) by convention — flag one that is
not, because ca65's .a8/.a16 state is file-global and a mismatch emits silently wrong
instruction lengths.

Files:

  • tests/roms/AccuracySNES/asm/runtime.s
**/*.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/scenes.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/scenes.rs
  • CHANGELOG.md
  • docs/accuracysnes-plan.md
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/scenes.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/scenes.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/scenes.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/scenes.rs
tests/roms/AccuracySNES/gen/src/scenes.rs

⚙️ CodeRabbit configuration file

tests/roms/AccuracySNES/gen/src/scenes.rs: Rendered scenes are hashed framebuffers. A scene proves nothing unless the picture it draws
differs from every other scene's — a stable hash that three emulators agree on is not
evidence, because a scene that renders the wrong thing is equally stable and equally agreed.
Flag any scene whose setup may not be able to express the difference it claims: a periodic
canvas that hides a scroll, a square sprite where a tall one was meant, a register the mode
ignores.

Files:

  • tests/roms/AccuracySNES/gen/src/scenes.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
  • docs/accuracysnes-plan.md
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
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
🧠 Learnings (4)
📚 Learning: 2026-07-22T12:38:06.947Z
Learnt from: doublegate
Repo: doublegate/RustySNES PR: 206
File: tests/roms/AccuracySNES/asm/runtime.inc:0-0
Timestamp: 2026-07-22T12:38:06.947Z
Learning: In `tests/roms/**/asm/runtime.inc`, when adding “low-RAM” runtime variables, verify that the new variable’s VAR_BASE-relative range/map does not overlap any existing variable declarations (including VAR_BASE + offsets ranges), regardless of declaration order. Do not rely on the measurement-slot collision gate as it may not cover the full variable map (e.g., `V_CURSOR_PREV` owns `VAR_BASE + $2A` through `$2B` even though its declaration is out of numeric order). Reviewers should check overlaps against all declarations, not just adjacent/ordered ones or gate-covered slots.

Applied to files:

  • tests/roms/AccuracySNES/asm/runtime.inc
📚 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/scenes.rs
📚 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
🔇 Additional comments (5)
tests/roms/AccuracySNES/asm/runtime.inc (1)

142-142: LGTM!

Also applies to: 226-231

tests/roms/AccuracySNES/asm/runtime.s (1)

2089-2136: LGTM!

docs/accuracysnes-plan.md (1)

392-425: LGTM!

Also applies to: 966-971

CHANGELOG.md (1)

14-40: LGTM!

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

1895-1947: 📐 Maintainability & Code Quality

No additional generated-scene bookkeeping is required. dossier.rs::MAP covers on-cart tests; scenes use Scene.dossier and validate_scenes. The generated catalogs contain 54 matching scenes, and the documentation correctly keeps the blessed-scene count at 53 because C7.12 remains unblessed.

			> Likely an incorrect or invalid review comment.

Walkthrough

Changes

AccuracySNES scene capture

Layer / File(s) Summary
Field-gated scene publication
tests/roms/AccuracySNES/asm/runtime.inc, tests/roms/AccuracySNES/asm/runtime.s
The runtime adds V_SCENE_ID, increases SCENE_FRAMES from 8 to 12, and publishes the scene ID only when $213F bit 7 is set.
OBJ interlace scene
tests/roms/AccuracySNES/gen/src/scenes.rs
C7.12 adds an OBJ interlace scene with size pair 6, two sprites, and SETINI bit 1.
AccuracySNES status documentation
docs/accuracysnes-plan.md, CHANGELOG.md
The documentation records the matching snes9x/Mesen2 hash, the RustySNES variant, and deferred interlace cases.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant run_scenes
  participant PPU2_213F as PPU2 status $213F
  participant V_SCENE_ID
  run_scenes->>PPU2_213F: read field status each frame
  alt bit 7 is set
    run_scenes->>V_SCENE_ID: publish 1-based scene ID
  else bit 7 is clear
    run_scenes->>V_SCENE_ID: publish zero
  end
Loading

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 9 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Accuracysnes Bookkeeping ⚠️ Warning The added scene is in the diff, but asm/tests_group_a.s, SOURCE_CATALOG.tsv, and ERROR_CODES.md are in neither the diff nor the filtered-file list. Regenerate and include all required AccuracySNES artifacts, or ensure each unchanged artifact appears in the path-filter report for this pull request.
✅ Passed checks (9 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 Commits format and accurately describes the field-gated scene publication change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 PR diff from merge-base 2e5c2ec to HEAD modifies CHANGELOG.md with a 27-line AccuracySNES entry describing field-gated scene publication.
Docs-As-Spec ✅ Passed The PR changes no files under crates/rustysnes-/. It changes AccuracySNES test-ROM assets and accuracy documentation only, so the crate-to-chip-doc requirement is not triggered.
No Panic On Untrusted Input ✅ Passed The commit adds no .unwrap(), .expect(), or panic!() lines. New Rust code only adds local scene data, and runtime changes are assembly register operations.
Safety Comment On New Unsafe ✅ Passed The commit adds no unsafe { ... } blocks or unsafe fn; the changed Rust file has no unsafe syntax, and the exact added-line search found no unsafe additions.

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

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR field-gates AccuracySNES scene publication on $213F bit 7, expands SCENE_FRAMES from 8 to 12, and adds test scene C7.12 (c7-obj-interlace-halves-height).

Blocking issues

None found.

Suggestions

  • tests/roms/AccuracySNES/asm/scenes.s:2256 (and tests/roms/AccuracySNES/gen/src/scenes.rs:1911): Redundant sep #$20 immediately following jsr scene_oam_reset, as accumulator mode is already 8-bit from line 2252.

Nitpicks

  • tests/roms/AccuracySNES/asm/runtime.inc:142: V_SCENE_ID is allocated as a 16-bit variable (u16), but only the 8-bit low byte is written to R_SCENE.

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

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

Updates the AccuracySNES rendered-scene protocol to publish scene IDs only on a known PPU field (via $213F bit 7), eliminating host-dependent frame-parity ambiguity for interlaced scenes and enabling consistent cross-emulator hashing. Adds the new interlace-dependent scene C7.12 to validate OBJ interlace behavior under the now field-pinned protocol.

Changes:

  • Field-gate the run_scenes publication window and widen SCENE_FRAMES (8 → 12) to preserve an interior capture sighting.
  • Add c7-obj-interlace-halves-height (C7.12) and regenerate the scene catalog/assembly outputs.
  • Update AccuracySNES planning/coverage docs and the changelog to reflect the new protocol and the newly-characterized (but unblessed) interlace scene.

Reviewed changes

Copilot reviewed 8 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/roms/AccuracySNES/gen/src/scenes.rs Adds the new C7.12 scene definition to the generator source.
tests/roms/AccuracySNES/build/scenes.tsv Regenerates the host manifest to include the new scene (index 54).
tests/roms/AccuracySNES/asm/scenes.s Regenerates assembled scene implementation and updates _scene_count/entries.
tests/roms/AccuracySNES/asm/runtime.s Implements field-gated scene-ID publishing in run_scenes.
tests/roms/AccuracySNES/asm/runtime.inc Adds V_SCENE_ID scratch and updates SCENE_FRAMES to 12 with rationale.
docs/accuracysnes-plan.md Records the new field-gating machinery and documents the remaining interlace variant split.
docs/accuracysnes-coverage.md Updates the generated coverage listing to include C7.12 as a scene.
CHANGELOG.md Documents the protocol change and the new interlace scene’s observed behavior across references.

@doublegate
doublegate merged commit e7fcca2 into main Aug 1, 2026
17 checks passed
@doublegate
doublegate deleted the feat/accuracysnes-scene-field-gate branch August 1, 2026 05:49
doublegate added a commit that referenced this pull request Aug 1, 2026
…m (T-04-H) (#302)

C11.03 -- each M7x * ORG product is masked to a multiple of 64 before the
terms accumulate -- is now covered by a Mode 7 scene with M7A and M7B both
$0101, deliberately not round numbers. With M7B = $0101 the discarded part is
`line MOD 64`, a different amount on every line, so a core that accumulates
the full products samples a different texel on roughly a quarter of the
columns of most lines. Every other Mode 7 scene uses round matrix values,
which hide this completely -- none of them was evidence for the row.

Blessed at 0xc032679c9076440a, which all three references produce, stable
across repeated runs. Non-vacuity confirmed by removing the mask from
fetch_mode7_column: this scene's hash moves. Two existing scenes move under
that injection too, so the mask was already witnessed in aggregate -- what
this adds is a scene whose stated purpose is the mask.

RETRACTION. PR #299 claimed the field gate turned C7.12's three-way emulator
split into a two-way one, citing snes9x and Mesen2 producing the identical
hash. That was ONE RUN and it does not hold: the identical ROM run twice under
Mesen2 gives 0x16f7ab8c7f97b7f8 then 0x266241e43ab85064, the two field
parities alternating run to run. RustySNES and snes9x are each stable; Mesen2
is not.

The gate is necessary but not sufficient and the missing half is host-side:
the cart publishes only on its chosen field, but which RENDERED frame a host
associates with the R_SCENE value it read is not pinned, and Mesen2's headless
runner does not make that association deterministically. The interlace scene
is withdrawn rather than left unblessed, because a scene reporting a different
hash each run is noise in the gate output. The gate itself stays -- cart-side
half, moves no golden, costs only battery runtime.

Coverage is unchanged at 350 of 443: C11.03 replaces C7.12 one-for-one,
because the withdrawn scene was being counted as C7.12's scene-cover while
still unblessed. What changed is that the row now counted has a golden all
three references agree on.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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