From 51467cbd1a0c17e1b28fb5dad80b861d69af94cf Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Sat, 1 Aug 2026 01:23:01 -0400 Subject: [PATCH] feat(accuracysnes): publish scenes on a known field (T-04-H) 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) --- CHANGELOG.md | 27 ++++++++ docs/accuracysnes-coverage.md | 1 + docs/accuracysnes-plan.md | 44 +++++++++++-- tests/roms/AccuracySNES/asm/runtime.inc | 8 ++- tests/roms/AccuracySNES/asm/runtime.s | 50 +++++++++++++-- tests/roms/AccuracySNES/asm/scenes.s | 60 +++++++++++++++++- .../AccuracySNES/build/accuracysnes-pal.sfc | Bin 262144 -> 262144 bytes .../roms/AccuracySNES/build/accuracysnes.sfc | Bin 262144 -> 262144 bytes tests/roms/AccuracySNES/build/scenes.tsv | 1 + tests/roms/AccuracySNES/gen/src/scenes.rs | 53 ++++++++++++++++ 10 files changed, 231 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f5e8bd0..477a84e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **AccuracySNES: the scene protocol publishes on a known field, and the interlace three-way split + is down to a two-way one.** `run_scenes` now sets the scene ID only on frames whose `$213F` bit 7 + is set, so every sighting the host counts is the same field. `SCENE_FRAMES` grew 8 → 12: at half + the publication rate the old value put the host's fourth sighting on the window's *last* frame, + which is 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. + + **On the interlace scene it did exactly what it was built to do.** `c7-obj-interlace-halves-height` + (`C7.12`, a 16x32 sprite under OBJ interlace, identical to `c7-objsel-size-6` but for one `SETINI` + bit) previously produced **three** different hashes on three emulators — the only three-way split + any scene has produced, and not a disagreement about interlace at all: each host landed on + whichever field its own frame counter happened to be on. With the gate, snes9x and Mesen2 produce + the **identical** hash `0x266241e43ab85064`. + + **The residual is one source row, and it is not blessed.** Pixel dumps show both renders agree on + everything the row asserts — the 16x32 sprite occupies 16 display rows — and differ only in which + field's source rows are drawn: the references take the even ones, RustySNES the odd. That is not + "RustySNES alone", this project's signature for a real defect: RustySNES's `row + field` and its + `$213F` bit 7 are both ares' (`sfc/ppu/object.cpp:122`, `sfc/ppu/io.cpp:178`), and both toggle the + field at the same V wrap. So it is the bsnes/ares lineage against the other two — **2 vs 1** under + this project's own provenance rule — and no primary source says which field maps to which rows. + Recorded as a variant set; the tiebreaker is ares running the cart, the same blocked item `A2.10` + waits on. `C9.03`/`C9.06` are deliberately not written on top of it: screen interlace carries the + identical parity dependency, so they would land unblessed for the identical reason. + - **AccuracySNES: three Group E rows land — `E8.01`, `E9.02`, `E5.06`.** Coverage **347 → 350 of 443** (297 on-cart + 53 scenes), battery **338 tests at 100%**, and both references agree: `snes9x: OK (14 known divergence(s))`, `Mesen2: OK (2 known divergence(s))`, 53/53 scenes on each. diff --git a/docs/accuracysnes-coverage.md b/docs/accuracysnes-coverage.md index b457d042..115f2601 100644 --- a/docs/accuracysnes-coverage.md +++ b/docs/accuracysnes-coverage.md @@ -130,6 +130,7 @@ Declared in `gen/src/scenes.rs`. Each is reported by the host framebuffer oracle - **`C7.03`** — c7-hflip-sliver-order - **`C8.12`** — c8-force-black-outside-window - **`C5.14`** — c5-4bpp-bitplane-order +- **`C7.12`** — c7-obj-interlace-halves-height ## Tests with no enumerated assertion diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 3d95c9f9..c7b20615 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -389,6 +389,40 @@ Any `C9`/`C7.12` interlace assertion needs the cart to publish a scene only on a a change to `run_scenes`, not to a scene. Worth doing: it unblocks the interlace half of `C9` (`C9.03`, `C9.06`) as well as `C7.12`. +**BUILT 2026-08-01, and it did what it was supposed to — but the scene is still unblessable.** +`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` grew 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 +protocol exists to avoid). The 53 blessed scenes are untouched by it on all three hosts: a still +picture hashes the same on either field. + +On the interlace scene it worked exactly as intended. The **three**-way split became a **two**-way +one, and snes9x and Mesen2 now produce the *identical* hash: + +| | `c7-obj-interlace-halves-height` | +|---|---| +| snes9x | `0x266241e43ab85064` | +| Mesen2 | `0x266241e43ab85064` | +| RustySNES | `0x16f7ab8c7f97b7f8` | + +**And the residual is fully characterised**, which is the part worth keeping. Dumping the pixels +shows both renders agree on everything the row is *about* — the 16x32 sprite occupies 16 display +rows, its 32x64 neighbour 32 — and differ only in **which field's source rows** are drawn: +snes9x/Mesen2 draw the even ones, RustySNES the odd ones. One source row, nothing else. + +**Not blessed, and not called a RustySNES bug either.** "RustySNES alone" is this project's +signature for a real defect, but RustySNES is not alone here: its `row + field` and its +`ppu2.mdr.bit(7) = field()` 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** by 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 per the same rule the Mode-5 hi-res +cluster is under: *do not pick a winner*. The tiebreaker is ares actually running the cart, which is +the same blocked item `A2.10` is waiting on. + +`C9.03`/`C9.06` are deliberately NOT written on top of this. Screen interlace has the identical +parity dependency, so they would land unblessed for the identical reason — two more unblessable +scenes is not progress. Write them when the tiebreaker exists. + ### `E8.03` — "clears `ENDX` even when suppressed" does not mean suppressed by `KOFF` The dossier row reads: *"KON restarts even if playing, zeroing the envelope, and clears ENDX even @@ -929,10 +963,12 @@ documentation-anchored only, because the Mesen2 headless runner times out in thi `C7.05`/`C7.06` landed on the far-IRQ shim, so it is worth saying what that shim does **not** unblock. -- **`C7.12`** (16x32 under OBJ interlace renders as 16x16) is a **scene**, and needs the cart to - publish a scene only on a *known field* — it was written once and produced three different hashes - on three emulators because it asked a question whose answer alternates every frame. That is - `v1.29.0` machinery (`run_scenes`, not a scene), shared with `C9.03`/`C9.06`. +- **`C7.12`** (16x32 under OBJ interlace renders as 16x16) is a **scene**. The `v1.29.0` machinery + it needed — `run_scenes` publishing only on a known field — **is built** (2026-08-01), and it + turned the three-way split into a two-way one: snes9x and Mesen2 now agree exactly, and RustySNES + differs by one field of OBJ-interlace source rows and nothing else. Still unblessed, because that + residual is the ares lineage against the other two and no source settles it. Full account in + §"Interlace scenes need frame-parity control". - **`C7.07`** (Time Over false positive: first sprite 16x16+ at `X = 0-255` with others at negative X) is reachable *as a test* with `C7.06`'s setup, but it would fail — because RustySNES does not diff --git a/tests/roms/AccuracySNES/asm/runtime.inc b/tests/roms/AccuracySNES/asm/runtime.inc index 87ec4cda..04e77547 100644 --- a/tests/roms/AccuracySNES/asm/runtime.inc +++ b/tests/roms/AccuracySNES/asm/runtime.inc @@ -139,6 +139,7 @@ V_HW_H = VAR_BASE + $4A ; H from the most recent wide latch (u16) V_HW_V = VAR_BASE + $4C ; V from that same latch (u16) V_HW_V0 = VAR_BASE + $4E ; V at hv_begin_wide (u16) V_HW_DV = VAR_BASE + $50 ; scratch: lines crossed, counted down (u16) +V_SCENE_ID = VAR_BASE + $52 ; the scene ID `run_scenes` publishes on its field (u16) ; The longest span hv_end_wide will report. Beyond this the varying line length has accumulated past ; usefulness, and far beyond it V wraps the field and the delta goes negative — so both are refused ; with $FFFF rather than returned as a plausible number. 64 lines is ~21,800 dots, two orders of @@ -222,7 +223,12 @@ MEAS_SLOTS = 512 ; occasionally landed on a transition and the gate went red on a different scene each run. An ; intermittently-red gate is worse than a slow one, because it gets ignored. SCENE_SETTLE = 6 -SCENE_FRAMES = 8 +; 12, not 8, because the published window is FIELD-GATED (see `run_scenes`): the ID appears on +; alternate frames only, so 8 wall-clock frames give exactly four sightings and the host's fourth — +; the one it hashes — lands on the window's last frame. That is one of the two ends the host +; protocol exists to avoid; snes9x caught a scene mid-transition there once already. 12 frames give +; six sightings, so the fourth is comfortably interior. +SCENE_FRAMES = 12 ; --- results block --------------------------------------------------------------------------- RESULTS = $7EF000 diff --git a/tests/roms/AccuracySNES/asm/runtime.s b/tests/roms/AccuracySNES/asm/runtime.s index b93a6bdd..ced90776 100644 --- a/tests/roms/AccuracySNES/asm/runtime.s +++ b/tests/roms/AccuracySNES/asm/runtime.s @@ -2086,18 +2086,54 @@ test_restore := test_restore_impl jsr hold_frames plx - sep #$20 - .a8 - txa - inc a - sta f:R_SCENE ; scene IDs are 1-based; 0 means "none yet" - + ; --- The published window is FIELD-GATED ------------------------------------------------- + ; + ; The ID appears only on frames whose PPU2 field flag ($213F bit 7) is SET, so every sighting + ; the host counts is the same field. Without that, an interlace scene asks a question whose + ; answer alternates every frame: `C7.12` (a 16x32 sprite under OBJ interlace renders as 16x16) + ; was written as a scene and produced THREE different hashes on three emulators, the only + ; three-way split any scene has produced. It was not three cores disagreeing about interlace — + ; each host simply landed on whichever field its own frame counter happened to be on. + ; + ; Costs one bit of nothing for the 53 non-interlaced scenes: a still picture hashes the same on + ; either field, so gating only moves WHICH wall-clock frame the host's fourth sighting is. That + ; is why SCENE_FRAMES had to grow — at half the publication rate, the old 8 gave exactly four + ; sightings and the fourth landed on the window's last frame, the one end the host protocol + ; deliberately avoids. + ; + ; What this does NOT fix, and cannot: if two hosts sample WRAM at different points relative to + ; the field toggle, they gate on opposite fields and an interlace scene still disagrees. That is + ; a measurable question, not an assumption — cross-validate any interlace scene before blessing + ; a golden for it (ADR 0013 rule 4). rep #$30 .a16 .i16 + txa + inc a + and #$00FF + sta f:V_SCENE_ID ; scene IDs are 1-based; 0 means "none yet" phx ldx #SCENE_FRAMES - jsr hold_frames +@hold_field: + phx + jsr wait_vblank + plx + sep #$20 + .a8 + lda $213F ; PPU2 status; bit 7 is the field flag + and #$80 + beq @not_our_field + lda f:V_SCENE_ID + bra @publish +@not_our_field: + lda #$00 +@publish: + sta f:R_SCENE + rep #$30 + .a16 + .i16 + dex + bne @hold_field plx sep #$20 diff --git a/tests/roms/AccuracySNES/asm/scenes.s b/tests/roms/AccuracySNES/asm/scenes.s index f17bee1f..3d92369e 100644 --- a/tests/roms/AccuracySNES/asm/scenes.s +++ b/tests/roms/AccuracySNES/asm/scenes.s @@ -2247,11 +2247,68 @@ SCENES_IMPL = 1 rts .endproc +; c7-obj-interlace-halves-height — C7.12 +; `c7-objsel-size-6` with OBJ interlace on (SETINI bit 1) and NOTHING else changed. Under interlace a sprite's displayed line maps to twice its internal row, so the 16x32 small member and the 32x64 large one each occupy half the vertical space they otherwise would — a core that ignores SETINI bit 1 renders this scene identically to its twin, which is exactly the comparison the pair exists to make. THIS SCENE IS FIELD-DEPENDENT: interlace selects even or odd sprite rows by field parity, which is why `run_scenes` gates the published window on the field flag. It was written once WITHOUT that gate and produced three different hashes on three emulators, the only three-way split any scene has produced. +.proc scene_c7_obj_interlace_halves_height + .a16 + .i16 + sep #$20 + .a8 + stz $2105 ; BGMODE 0 + jsr scene_oam_reset + sep #$20 + .a8 + lda #$C0 + sta $2101 ; OBJSEL pair 6, name base word $0000 — same as the twin + rep #$30 + .a16 + .i16 + ldx #$0000 + stx $2102 + sep #$20 + .a8 + lda #40 + sta $2104 ; sprite 0 X + lda #60 + sta $2104 ; sprite 0 Y + lda #$10 + sta $2104 ; tile $10 — printable at 4bpp + lda #$30 + sta $2104 ; attr: palette 0, priority 3 + lda #120 + sta $2104 ; sprite 1 X + lda #60 + sta $2104 ; sprite 1 Y + lda #$10 + sta $2104 + lda #$30 + sta $2104 + rep #$30 + .a16 + .i16 + ldx #$0100 + stx $2102 ; the high table + sep #$20 + .a8 + lda #$08 + sta $2104 ; sprite 0 small (16x32), sprite 1 large (32x64) + lda #$02 + sta $2133 ; SETINI bit 1: OBJ interlace — the ONE difference from the twin + lda #$10 + sta $212C + lda #$0F + sta $2100 + rep #$30 + .a16 + .i16 + rts +.endproc + .segment "CATALOG" .export _scene_count .export _scene_entries _scene_count: - .word 53 + .word 54 _scene_entries: .addr scene_c5_mode1_bg_priority .addr scene_c8_fixed_colour_add @@ -2306,3 +2363,4 @@ _scene_entries: .addr scene_c7_hflip_sliver_order .addr scene_c8_force_black_outside_window .addr scene_c5_4bpp_bitplane_order + .addr scene_c7_obj_interlace_halves_height diff --git a/tests/roms/AccuracySNES/build/accuracysnes-pal.sfc b/tests/roms/AccuracySNES/build/accuracysnes-pal.sfc index d971aa5205643ec6a74936ded61ffafaa834425b..8da1f043ffe30293909e97892972fda4f59c9233 100644 GIT binary patch delta 10211 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 SbZDf)+9zOxZo8A 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 diff --git a/tests/roms/AccuracySNES/build/accuracysnes.sfc b/tests/roms/AccuracySNES/build/accuracysnes.sfc index d4c27f52ee2045145f12e19fc36554fdb3257411..0a8463d867e3bcee1e5d1283efae08bc5ade06d6 100644 GIT binary patch delta 10211 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 diff --git a/tests/roms/AccuracySNES/build/scenes.tsv b/tests/roms/AccuracySNES/build/scenes.tsv index 05959f7c..1a163acf 100644 --- a/tests/roms/AccuracySNES/build/scenes.tsv +++ b/tests/roms/AccuracySNES/build/scenes.tsv @@ -52,3 +52,4 @@ 51 c7-hflip-sliver-order C7.03 52 c8-force-black-outside-window C8.12 53 c5-4bpp-bitplane-order C5.14 +54 c7-obj-interlace-halves-height C7.12 diff --git a/tests/roms/AccuracySNES/gen/src/scenes.rs b/tests/roms/AccuracySNES/gen/src/scenes.rs index 7d77ad95..2857ea1d 100644 --- a/tests/roms/AccuracySNES/gen/src/scenes.rs +++ b/tests/roms/AccuracySNES/gen/src/scenes.rs @@ -1892,6 +1892,59 @@ pub const SCENES: &[Scene] = &[ "sta $2100 ; brightness 15, forced blank off", ], }, + Scene { + id: "c7-obj-interlace-halves-height", + dossier: "C7.12", + what: "`c7-objsel-size-6` with OBJ interlace on (SETINI bit 1) and NOTHING else changed. \ + Under interlace a sprite's displayed line maps to twice its internal row, so the \ + 16x32 small member and the 32x64 large one each occupy half the vertical space \ + they otherwise would — a core that ignores SETINI bit 1 renders this scene \ + identically to its twin, which is exactly the comparison the pair exists to make. \ + THIS SCENE IS FIELD-DEPENDENT: interlace selects even or odd sprite rows by field \ + parity, which is why `run_scenes` gates the published window on the field flag. It \ + was written once WITHOUT that gate and produced three different hashes on three \ + emulators, the only three-way split any scene has produced.", + setup: &[ + "sep #$20", + "stz $2105 ; BGMODE 0", + "jsr scene_oam_reset", + "sep #$20", + "lda #$C0", + "sta $2101 ; OBJSEL pair 6, name base word $0000 — same as the twin", + "rep #$30", + "ldx #$0000", + "stx $2102", + "sep #$20", + "lda #40", + "sta $2104 ; sprite 0 X", + "lda #60", + "sta $2104 ; sprite 0 Y", + "lda #$10", + "sta $2104 ; tile $10 — printable at 4bpp", + "lda #$30", + "sta $2104 ; attr: palette 0, priority 3", + "lda #120", + "sta $2104 ; sprite 1 X", + "lda #60", + "sta $2104 ; sprite 1 Y", + "lda #$10", + "sta $2104", + "lda #$30", + "sta $2104", + "rep #$30", + "ldx #$0100", + "stx $2102 ; the high table", + "sep #$20", + "lda #$08", + "sta $2104 ; sprite 0 small (16x32), sprite 1 large (32x64)", + "lda #$02", + "sta $2133 ; SETINI bit 1: OBJ interlace — the ONE difference from the twin", + "lda #$10", + "sta $212C", + "lda #$0F", + "sta $2100", + ], + }, ]; /// The comment block `scene_low_tiles` carries, split out only to keep `low_tiles_helper` inside