Skip to content

test(accuracysnes): three Group E rows — E8.01, E9.02, E5.06 (T-04-E) - #298

Merged
doublegate merged 19 commits into
mainfrom
feat/accuracysnes-group-e
Aug 1, 2026
Merged

test(accuracysnes): three Group E rows — E8.01, E9.02, E5.06 (T-04-E)#298
doublegate merged 19 commits into
mainfrom
feat/accuracysnes-group-e

Conversation

@doublegate

Copy link
Copy Markdown
Owner

Closes the v1.29.0 Group E batch. Coverage 347 → 350 of 443 (297 on-cart + 53 scenes), battery 338 tests at 100%, and MESEN2_KNOWN_FAILURES comes down from 3 to 2.

snes9x: OK (14 known divergence(s))
Mesen2: OK (2 known divergence(s))
snes9x: 53 scene(s) match, 0 unblessed, 0 mismatched
Mesen2: 53 scene(s) match, 0 unblessed, 0 mismatched
cross-validation: 2 reference(s) agree with the cart

E8.01 — the two earlier drafts were measuring their own timing

Both timed a delay: write KON, count until ENVX leaves zero. That delay is (P − φ) mod P — a sawtooth in the write's phase against the DSP's examination grid — and two readings of a sawtooth cannot recover its period. φ is fixed by where the IPL handshake left the SPC700, which the S-CPU paces, so it differs between the NTSC and PAL images of the same build. The poll-count draft duly reported a difference of 2 on NTSC and 0 on PAL. Neither draft was ever scored.

The replacement measures writes the DSP never sees. KON is a register, not a queue: a second write before the DSP next looks cancels the first, and that voice is never keyed. Eight writes 24 SPC cycles apart span 168 cycles, and a half-open interval of length L contains ⌊L/P⌋ or ⌊L/P⌋ + 1 grid points for every phase:

KON examined period examinations in 168 cycles voices started
every second sample (hardware) 64 cycles 2 or 3 3 or 4
every sample 32 cycles 5 or 6 6 or 7

RustySNES measures 3 (mask $25 — voices 2, 5, 7). Mesen2, which failed the phase-fragile draft, passes this one.

E9.02 — the step, not the state

E9.01 already pins the frozen seed at $4000, so this scores the transition: one LFSR step turns $81 (full-scale negative) into $3F/$1F (positive, halved). A core emitting the register's 15-bit value directly reads positive throughout — the DC-heavy noise the errata's "highpass" remark exists to exclude.

E5.06 — the first assertion was vacuous, and the injection proved it

"At least one of 64 OUTX readings is negative" looked airtight for a purely positive drive. Injecting the named bug made the row pass harder: clamping the store gave 64 negatives out of 64, against the correct decoder's 32. A clamping decoder pins the buffer at +7FFEh, and the gaussian interpolator's three-term partial sum (the truncation E5.13 is about) overflows a signed 16-bit intermediate from that constant — the interpolator, not the decoder, was supplying the sign.

The row now asserts bipolarity: a saturating decoder settles, and a settled decoder's readings carry one sign. Both saturating variants — clamp at 15 bits, saturate at 16 after the shift — fail it.

Stated, not buried

E9.02 is the only row that moves the noise LFSR, and nothing can put it back: FLG bit 7 is the DSP's soft reset and does not re-seed the shift register (checked against ares, which re-seeds only in DSP::power; a comment in voice_program claimed otherwise and is corrected). So it is registered last, and both it and E9.01 stand down as SKIP on a menu restart — the same gate F1.07 uses for its unwritten $4218. The harness's restart-idempotence check moves from first_run_passed - 1 to - 3.

Verification

  • cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps, cargo test --workspace — all clean.
  • cargo test -p rustysnes-test-harness --test accuracysnes --features test-roms56 passed, 0 failed.
  • REF_PROJ=$PWD/ref-proj bash scripts/accuracysnes/crossval.sh — as quoted above.
  • Every row injected at the site it names, confirming which code fires: E8.01 → per-sample KON examination → FAIL code 1; E9.02 → unipolar noise output → code 1, sign-preserving step → code 3, four-place shift → code 4; E5.06 → 15-bit clamp and 16-bit saturate → code 2. No row moved under another row's injection.

🤖 Generated with Claude Code

doublegate and others added 16 commits July 31, 2026 22:48
PAL, interlace on, field set, V=311 is 1368 master clocks and 341 dots.

Its shape is the opposite of the short line's, which is why these are
two changes and not one: B2.02 substitutes dot lengths, while this
appends a whole extra 4-clock dot and leaves the two 6-clock dots
alone (339*4 + 2*6 = 1368). So it moves the H wrap, via a new
Ppu::dots_this_line, rather than the Bus's clock table -- and the H
counter reaches dot 340 on that line and on no other.

The H-IRQ comparator's upper bound follows the same per-line count
instead of the constant, so an HTIME landing on dot 340 is a genuine
match on the long line and stays suppressed everywhere else. It could
previously never match anywhere.

PAL interlaced frames alternate 425,568 / 425,572. The test enables
interlace through SETINI $2133 bit 0 rather than poking the field, and
asserts the SET of frame lengths rather than their order -- which phase
carries the extra 4 clocks depends on the field's power-on value, and
pinning that would pin an initial condition this row does not care
about. Progressive PAL is asserted separately to be unaffected. Both
fail under their own injection.

872 workspace tests pass; no golden moved, the long line being in
vblank.

Separate and still unmodelled: the interlaced frame's extra scanline
(263/313 vs 262/312). B2.03 is reachable without it since V=311 is the
last PAL line either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both scanline variants are modelled now, so the section that recorded a
doubt about reachability is replaced with what was actually built and
what remains.

The part worth writing down: B2.07 does NOT follow from the model
change. The frame-length difference is 4 master clocks -- one dot --
and the hv_begin/hv_end instrument costs a measured 175 dots, so
resolving it needs a differential across many frames or a different
instrument. That is the same wall A5.18 is parked on, and assuming
B2.07 is now easy would waste a session.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Records the concrete design rather than leaving the next session to
restart from the dossier's one-line assertion.

E8.01 follows e8_02's shape, which already solves the hard parts:
e8_02_sounding_voice builds the voice, e8_02_time_to_envx counts
timer-2 ticks to a non-zero ENVX, and T2DIV = 2 gives exactly one tick
per output sample.

The assertion is a DIFFERENTIAL: write KON twice, the second offset by
one sample (32 SPC cycles), and compare the delays. Polled every second
sample means they differ by 1; polled every sample means they are
equal. So the row asserts "the two differ", which assert_a16_range
expresses -- one of the cases the never-hand-write-a-verdict-byte rule
exists for.

Carries forward both traps e8_02 already paid for: the voice must be
fully silent before the second key-on (reuse its arming guard, do not
re-derive it), and T2DIV must stay 2 because 1 puts the reading next to
TnOUT's wrap and the NTSC/PAL drift gate catches it on PAL alone.

Verify by injecting at the named site -- force the KON poll to every
sample in the DSP and confirm this row's code fires.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Deliberately not a PR. The row fails on all three implementations, and
committing it to a mergeable branch would break the battery.

Authored, registered, and clean through every generator gate -- the
24-column menu-label cap, the dossier map, and the slot-collision gate
each caught something real and each was fixed (slots moved to 256-258
off the printed free list).

Then the cross-check rejected it on RustySNES (FAIL code 2), snes9x
(14 -> 15 failing) and Mesen2 (1 -> 2). Three implementations failing
identically is the broken-test signature, and it is correct here.

Diagnosis: code 2 is phase A's band guard and the band is wrong. I set
4..13 by reusing E8.02's numbers, but E8.02's 4..11 bounds a DELTA
between a sounding-voice baseline and a key-on, while E8.01 phase A
reads an ABSOLUTE delay -- key-on latency plus the poll-loop cost. A
band for a difference cannot bound a raw reading.

The fix is not to widen it. Measure slots 256/257 out of a real run and
set the band from what the instrument reports. And because phase A
never passed, the difference assertion has never been meaningfully
evaluated -- so it is still unverified whether the 16-NOP offset moves
the reading at all, and whether 32 SPC cycles really is one output
sample here rather than an assumption from round numbers.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two real defects, neither of which was the band I was asked to fix.

Both phases must start from silence. The first draft keyed phase A off
e8_02_sounding_voice, which leaves the voice at full scale, so its poll
exited on the first pass (1 tick) while phase B, keyed from silence,
measured 8. The 7-tick gap was that asymmetry, not the poll phase -- no
band would have made that comparison mean anything. Extracted
e8_01_silence and used it before both phases, one guard slot covering
both.

The offset must be a HALF sample, not a whole one. A full-sample offset
measured a difference of ZERO: at T2DIV = 2 one timer tick, one output
sample and the shift are all 32 SPC cycles, so shifting by exactly that
moves timer, sample clock and KON in lockstep and cancels. Eight NOPs
move KON's phase within the sample and measure 8 vs 7 -- the +/-1 a
16 kHz poll predicts.

Bands now come from measured values, not from E8.02's delta band.

Still not a PR: Mesen2 went 1 -> 3 failing. One is the pre-existing
A2.10; the others are unidentified and may be the documented trap where
adding a test shifts the DSP poll phase for a downstream row. Identify
which before landing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
E8.01 is catalogue index 263 and reads $01 -- plain PASS -- under
Mesen2. The rows that fail are Group F input rows (idx279 = F1.03, byte
$04 = FAIL code 2): the port-2 limitation the oracle fix documented,
materialising. mesen_crossval.lua now sends one setInput call, so
PAD2_CONTRACT-dependent rows cannot pass under MesenCE. Nothing to do
with the DSP poll phase.

Also records the status encoding, because misreading it cost a round:
odd = PASS (odd != $01 being "PASS variant n"), even = "FAIL code b/2",
$00 = NOTRUN, $FF = SKIP. A dump of non-$01 bytes looks alarming and is
mostly passes -- 28 of 29 such entries were PASS variants or a SKIP.
Count even bytes only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A direct WRAM probe counts 1 even-byte failure (F1.03, the port-2 row)
stably at 1400/1600/2000 frames, while crossval.sh reports "Mesen2: 3
failing test(s)" on the same build.

E8.01 reads $01 -- PASS -- in all three probe runs, so it is not among
them. But the counts do not match and I have not explained why.
Candidates, none checked: the runner's MAX_FRAMES of 4000 puts the cart
in a different phase; the runner also scores the PAL image and may fold
those into the same line; or it iterates n = rd16(COUNT) over a range
the probe's 0..335 loop does not.

Recorded rather than rounded off, because a row that passes while the
tally it lives in is unexplained is how a later real regression gets
absorbed into "the usual 3".

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

crossval.sh runs ref-proj/Mesen2/.../Mesen.dll under dotnet; every probe
in this investigation used ref-proj/MesenCE/.../Mesen, a separate native
build. Both directories exist, and the "Mesen2" label in the crossval
output means the .dll. So "1 failure" and "3 failures" were never in
conflict -- they measured different implementations. A probe meant to
predict the gate must use the dotnet binary.

With that settled, E8.01 is: pass on RustySNES, pass on snes9x (back to
its baseline 14), pass on MesenCE (stable across 1400-4000 frames), and
FAIL code 4 on Mesen2 -- the difference assertion. The half-sample
offset moves the reading on three implementations and not on Mesen2.

That is the favourable three-agree-one-differs shape, and both of this
cart's Mesen2-only disagreements now sit in that one runner. But it is
not proof: a harness bug upstream of an implementation produces the same
signature and did once here. Next step is ares or bsnes as a fourth
opinion.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Attempted bsnes as the tiebreak for E8.01's Mesen2-only disagreement.
The core is built and libretro_crossval.c takes any core as argv[1],
but the host exits 255 with "core exposes no usable SYSTEM_RAM (0
bytes, need 131072)": bsnes's libretro core does not publish WRAM
through retro_get_memory_data, so the battery's verdicts cannot be read
out of it. ares is source-only, not built.

Recorded so the next attempt does not repeat this route. A fourth
opinion needs ares built, bsnes driven through its own debugger, or a
bsnes libretro build exposing RETRO_MEMORY_SYSTEM_RAM.

E8.01 stays on this WIP branch: passing on three implementations with
the fourth's disagreement characterised, but the discriminator that
would settle which side is right has not been run.

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

Went to mirror SNES9X_KNOWN_FAILURES with a Mesen2 constant and listed
the set instead of the count. It moves:

  before E8.01: 1 failure, reported as A2.10 (idx 11)
  after  E8.01: 3 -- idx263 E8.01 code 4, idx279 F1.03 code 2,
                     idx286 code 2

A2.10 is absent from the second list. It failed, then passed, with
nothing touching it. A MESEN2_KNOWN_FAILURES = 3 constant would
therefore encode noise and mask a future real regression inside "the
usual 3".

This is the cart's documented timing-phase trap showing up in the
RUNNER rather than in a row, and it matters beyond E8.01: Mesen2 cannot
be a reliable gate until its verdicts are reproducible run-to-run.

Also recorded: MesenCE is Mesen Community Edition, the maintained fork
of the same emulator, and it passes E8.01 -- so the disagreement is
between two builds of one lineage, not two independent
implementations. That weakens the "three implementations agree" reading
and is a further reason not to bless the row yet.

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

I reported Mesen2's failing set as moving (A2.10 failing then passing)
and concluded the runner could not be trusted as a gate. That was
wrong, and the fault was the instrument: my probes read R_STATUS at a
fixed frame count (1600, 4000) while the battery finishes at ~479, so
they were sampling bytes after the cart had spent 1000+ frames in its
results menu with Start held. mesen_crossval.lua reads at DONE == $A5,
which is why it was consistent throughout.

Read at DONE, Mesen2 is exactly reproducible: two runs, identical set,
both completing at frame 479 --

  idx263 E8.01 code 4
  idx279 F1.03 code 2
  idx286 F1.10 code 2

F1.03 and F1.10 are Group F input rows, i.e. the documented
PAD2_CONTRACT port-2 limitation of the single-setInput fix. So E8.01 is
the ONLY unexplained Mesen2 failure.

Any future probe of this cart must read at DONE, not at a frame budget.

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

Adds MESEN2_KNOWN_FAILURES=3 and MESEN2_PAL_KNOWN_FAILURES=1 on the same
mechanism as the snes9x constant, set only after reading the failing SET
at R_DONE == $A5 and reproducing it exactly across runs. crossval.sh now
reports "2 reference(s) agree with the cart" instead of DISAGREEMENT.

Two of the three NTSC rows are the documented port-2 limitation (F1.03,
F1.10): one setInput call, so PAD2_CONTRACT rows cannot pass under this
runner. Covered by the in-repo harness and snes9x.

The third is E8.01, and enumerating rather than counting is what
exposed the real problem: it fails on the NTSC image and PASSES on the
PAL image, same Mesen2 build. The S-DSP's 32 kHz sample rate is
region-independent, so a genuine KON-poll-rate assertion cannot care
which image it runs on. A verdict that flips with the region encodes a
timing PHASE, not the poll rate.

So E8.01 is not merged, and the reason is not the missing fourth
opinion -- the row is not robust. Its agreement on the other three
implementations may be alignment luck rather than the mechanism. Fixing
it means sweeping several sub-sample offsets and asserting at least one
moves the delay, instead of betting on a single chosen offset.

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

Worked out the actual fix rather than leaving "sweep more offsets",
which does not escape the problem.

The timer is the cause: at T2DIV = 2 one tick IS one output sample, so
a 32-cycle shift moves the tick boundary and the KON together and
cancels exactly (measured diff 0), while a 16-cycle shift dodges that
but only crosses a poll boundary for some starting phases. Sweeping
offsets inherits both halves -- whole-tick offsets always cancel, the
rest stay phase-dependent.

Replace the instrument: count poll-loop iterations until ENVX reads
non-zero instead of timer ticks. Resolution drops from a whole sample
to a few SPC cycles, the counter no longer quantises to the sample
clock, and a sub-sample offset shows up directly. This is what rescued
F1.09, which was vacuous at line granularity and landed once it counted
polls.

Also notes that removing the timer frees PORT3 from its guard-sibling
duty, so a four-point phase sweep can report without a second upload.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Implements the prescribed fix: e8_01_poll_count replaces the timer with
a counted poll loop, removing the sample-clock quantisation that made a
whole-sample offset cancel exactly.

The instrument is genuinely better -- counts land at 12-14 passes where
the timer gave 5-11 ticks, so a sub-sample shift moves the reading by
~2 units instead of 0-or-1.

But the region asymmetry SURVIVES. On Mesen2, read at DONE: NTSC gives
A=14 B=12 (diff 2), PAL gives A=13 B=13 (diff 0). Same emulator, same
row. So the timer was a confound, not the confound.

Phase A itself differs by region (14 vs 13), which points at the real
problem: something region-dependent moves the cart's own starting phase
between the two images, so the row needs its measurement anchored to a
known DSP phase before any offset is applied -- not merely a finer
counter.

Bands are left stale on purpose (both images now fail code 2, the band
guard, rather than code 4). Re-banding to make both pass would hide the
asymmetry, which is the actual open question.

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

# Conflicts:
#	docs/accuracysnes-plan.md
Coverage 347 -> 350 of 443 (297 on-cart + 53 scenes); battery 338 tests at
100%. Both references agree: snes9x 14 known divergences, Mesen2 2 (down from
3), 53/53 scenes on each.

E8.01 (KON examined at 16 kHz). The two earlier drafts timed a delay from the
KON write to the voice starting. That delay is a sawtooth in the write's phase
against the DSP's examination grid, and two readings of a sawtooth cannot
recover its period — so the poll-count draft reported a difference of 2 on the
NTSC image and 0 on the PAL image of the same build, because the phase is set
by where the IPL handshake left the SPC700 and the S-CPU paces that. Neither
draft was scored.

The replacement measures writes the DSP never sees: KON is a register, not a
queue, so a second write before the DSP next looks cancels the first. Eight
writes 24 SPC cycles apart span 168 cycles, and a half-open interval of that
length contains floor(168/P) or floor(168/P)+1 grid points for EVERY phase —
3-4 voices started at 16 kHz, 6-7 at 32 kHz. Mesen2, which failed the
phase-fragile draft, passes this one.

E9.02 (noise output is bipolar). E9.01 pins the frozen seed; this scores the
transition, because one LFSR step must turn a full-scale negative reading ($81)
into a positive one at about half scale. A core emitting the register's 15-bit
value directly reads positive throughout — the DC-heavy noise the errata's
highpass remark excludes.

E5.06 (BRR wraps at 15 bits). The first assertion — "at least one of 64 OUTX
readings is negative" — was vacuous, and the injection is what proved it:
clamping the store made the row pass HARDER, 64 negatives against the correct
decoder's 32. A clamping decoder pins the buffer at +7FFEh and the gaussian
interpolator's three-term partial sum overflows from that constant, so the
interpolator was supplying the sign. The row now asserts bipolarity, and both
saturating variants fail it.

E9.01 and E9.02 stand down as SKIP on a menu restart: E9.02 steps the LFSR and
FLG bit 7 does not re-seed it (checked against ares, which re-seeds only in
DSP::power — a comment claiming otherwise is corrected). The harness's
restart-idempotence check moves from first_run_passed - 1 to - 3.

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

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@doublegate, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: db4483fa-a5a5-4476-9012-72596502c1ad

📥 Commits

Reviewing files that changed from the base of the PR and between b2057b2 and 32b8639.

⛔ 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 (9)
  • CHANGELOG.md
  • crates/rustysnes-test-harness/tests/accuracysnes.rs
  • docs/STATUS.md
  • docs/accuracysnes-plan.md
  • docs/apu.md
  • scripts/accuracysnes/crossval.sh
  • tests/roms/AccuracySNES/gen/src/dossier.rs
  • tests/roms/AccuracySNES/gen/src/tests/apu.rs
  • to-dos/ROADMAP.md

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

doublegate and others added 2 commits August 1, 2026 00:38
Both are real and neither changes a byte of the ROM (checksum $0BE1 either
way): the noise-rate literal becomes hex, and `all()` carries an explicit
too_many_lines allow rather than being split, because the registration order
is load-bearing in three places and a function boundary would hide that.

My local clippy run reported clean before the push — it had reused a stale
fingerprint for accuracysnes-gen from the `cargo run` that preceded it. Touch
the changed sources before trusting a clippy pass on a crate you just ran.

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

docs/apu.md gains the two S-DSP properties AccuracySNES now scores on-cart:
KON is examined every second sample so a close-following write cancels the
one before it, and FLG bit 7 does NOT re-seed the noise LFSR (ares re-seeds
only in DSP::power). The second is why E9.01/E9.02 are power-on-only.

docs/STATUS.md: 344/443 -> 350/443, 332 -> 338 tests. Its "provably
uncoverable" phrasing is softened, because E5.06 and B2.02/B2.03 were all in
that bucket and all three have now landed.

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

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

Adds three new AccuracySNES Group E (SPC700/S-DSP) rows to close out the v1.29.0 batch, along with regenerated catalogs/docs and small harness + cross-validation updates to reflect the new measurements and known divergences.

Changes:

  • Add new Group E rows E5.06 (BRR 15-bit wrap), E8.01 (KON poll rate), and E9.02 (bipolar noise output), including a cold-boot gate and ordering constraints for the noise LFSR.
  • Update the test harness to report the new batch’s measurement slots and adjust restart-idempotence expectations (-1-3).
  • Update cross-validation script expectations for Mesen2 known divergences, and regenerate/update catalogs + docs + changelog entries.

Reviewed changes

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

Show a summary per file
File Description
tests/roms/AccuracySNES/gen/src/tests/apu.rs Implements the three new Group E tests and supporting helper refactors/gates.
tests/roms/AccuracySNES/gen/src/dossier.rs Adds dossier mapping entries for the new rows.
tests/roms/AccuracySNES/SOURCE_CATALOG.tsv Regenerated catalog entries to include the new tests.
tests/roms/AccuracySNES/ERROR_CODES.md Adds documented failure-code tables for E5.06, E8.01, and E9.02.
tests/roms/AccuracySNES/asm/tests_group_a.s Regenerated assembly output including the new test bodies and APU program blobs.
crates/rustysnes-test-harness/tests/accuracysnes.rs Reports new measurement slots and updates restart-idempotence assertion to account for new SKIPs.
scripts/accuracysnes/crossval.sh Adds Mesen2 known-divergence constants and enforces expected counts for NTSC/PAL runs.
docs/accuracysnes-plan.md Updates plan/status narrative to mark these rows as landed and records the design rationale.
docs/accuracysnes-coverage.md Updates coverage counts to reflect the three newly covered assertions.
CHANGELOG.md Records the new rows, coverage delta, and the restart-idempotence behavior change.

Counts re-derived from docs/accuracysnes-coverage.md, which regenerates with
the ROM, rather than from the hand-maintained estimate the row carried.

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

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR adds three on-cart AccuracySNES APU test rows (E8.01, E9.02, and E5.06) covering key-on examination frequency, noise output bipolarity, and 15-bit BRR wrapping, along with test harness reporting, updated catalog metadata, error code specs, cross-validation baseline updates, and documentation.

Blocking issues

None found.

Suggestions

  • crates/rustysnes-test-harness/tests/accuracysnes.rs:108-112: The integration test assertion (1..=8).contains(&voices) for E8.01 (slot 257) checks that the slot was written, but hardware expects 3 or 4 started voices (and the on-cart test asserts 2..=5). Tightening this harness check to (2..=5).contains(&voices) would detect unexpected host timing or harness regressions earlier.
  • scripts/accuracysnes/crossval.sh:198,266: MESEN2_PAL_KNOWN_FAILURES=1 differs from MESEN2_KNOWN_FAILURES=2 due to PAL/NTSC input port handling asymmetry (F1.10 vs F1.03). Annotating line 198 with the exact index that passes on PAL (F1.10) will save triage time during future Mesen2 cross-validation updates.

Nitpicks

  • gen/src/tests/apu.rs:2998: In e8_01(), p.adc_a_dp(0x13) relies on SPC700 ADC A, dp computing (A << 1) | C because MOV A, dp leaves the Carry flag untouched; a brief inline comment explaining this idiom would help future test authors.

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

@doublegate
doublegate merged commit 2e5c2ec into main Aug 1, 2026
16 checks passed
@doublegate
doublegate deleted the feat/accuracysnes-group-e branch August 1, 2026 05:03
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