From 1fc45bad2f504b0c4822dd30d54d6f528d62a1a1 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:29:38 -0400 Subject: [PATCH 01/10] fix(accuracysnes): the oracle's "14 of 335" does not reproduce Every prior conclusion about the battery oracle rested on mesen_crossval.lua's own read, with nothing independent checking that read. Added a deliberately minimal second instrument and re-measured. Three findings, two of which overturn what was recorded: MesenCE headless is not broken. It renders the whole undisbeliever corpus with 0 skipped captures and runs the AccuracySNES cart to exit 0. Calling "the Mesen oracle" one broken thing was wrong -- rendering works, only the battery read does not, so the scene work is not blocked by whatever blocks the battery. The render channel cannot observe the battery at all: MesenCE and RustySNES produce byte-identical colour histograms for the cart at 60/300/500/800/900 frames, with and without the input contract, and the picture is static throughout in both. A hang and a clean run look the same. The battery writes no verdict at all -- not 14. At RESULTS = $7E:F000 the magic reads as bytes that differ between runs (uninitialised WRAM, never written) and R_STATUS[0..39] are all zero at 600 and at 1500 frames. That explains why two cart-side A3.03 fixes moved nothing: they were chasing a number that is not there. The question is now why the battery never STARTS. Prime suspect, recorded and not yet confirmed: the input contract holds B+Start+X+R from power-on, so a menu starting on a press edge never sees one. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 25 ++++++++++++ docs/accuracysnes-plan.md | 33 +++++++++++++++ scripts/accuracysnes/mesen_wram_probe.lua | 50 +++++++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 scripts/accuracysnes/mesen_wram_probe.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e75f23c..b77f2f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **AccuracySNES oracle: the recorded diagnosis does not reproduce, and the blocker is a different + one.** Re-measured with a new, deliberately minimal second instrument + (`scripts/accuracysnes/mesen_wram_probe.lua`) — every prior conclusion rested on + `mesen_crossval.lua`'s own read, with nothing independent checking that read. + + Three findings, two of which overturn what was written down: + + - **MesenCE headless is not broken.** It renders the entire undisbeliever corpus with **0 skipped + captures** and runs the AccuracySNES cart to exit 0. Treating "the Mesen oracle" as a single + broken thing was wrong — rendering works; only the battery read does not. The scene work is + therefore **not** blocked by whatever blocks the battery. + - **The render channel cannot observe the battery.** MesenCE and RustySNES produce byte-identical + colour histograms for the cart at 60/300/500/800/900 frames, with and without the input contract + held, and the picture is static throughout in both. A hang and a clean run look the same, so any + diagnosis has to go through WRAM. + - **The battery writes no verdict at all — not 14 of 335.** At `RESULTS = $7E:F000` the magic reads + as bytes that *differ between runs* (uninitialised WRAM, never written) and `R_STATUS[0..39]` are + all zero at both 600 and 1500 frames. The "completes 14 then stops at `A3.03`" figure does not + reproduce — which explains why two cart-side `A3.03` fixes moved nothing: they were chasing a + number that was not there. + + The open question is now **"why does the battery never start"**, not "why does it stop". Prime + suspect, recorded in `docs/accuracysnes-plan.md` and not yet confirmed: the input contract holds + B + Start + X + R from power-on, so a menu that starts on a *press edge* never sees one. + - **PPU: the field flag's doc contradicted the code, and nothing pinned either.** `Ppu::field` is `$213F` bit 7 and toggles at the end of **every** frame; its doc comment said "toggles each frame when interlace is on", which describes neither the code nor the hardware. Only the flag's *use* is diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 6bd73d50..997884cf 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -626,6 +626,39 @@ threaded to it. Expect goldens to move, and note that a previous attempt at the mapping change was reverted for exactly that reason — so the guard test named in the roadmap lands **first**, before `dot_length` is touched. +#### The Mesen oracle — the "14 of 335" reading does not reproduce + +Re-measured 2026-08-01 with a second, independent instrument +(`scripts/accuracysnes/mesen_wram_probe.lua`), because every prior conclusion rested on +`mesen_crossval.lua`'s own read with nothing checking that read. + +Established, and some of it overturns what was recorded here: + +- **MesenCE headless is not broken.** It renders the whole undisbeliever corpus + (`scripts/perdot_crossval.sh`, **0 skipped captures**) and runs the AccuracySNES cart to exit 0. + Notes treating "the Mesen oracle" as one broken thing were wrong: rendering works, and only the + battery read does not. Two problems, not one — which also means the scene work is **not** blocked + by whatever blocks the battery. +- **MesenCE and RustySNES render the cart byte-identically** (same colour histogram) at 60, 300, 500, + 800 and 900 frames, with and without the input contract held, and the picture is static throughout + in both. So **the render channel cannot observe the battery at all** — a hang and a clean run look + identical. Any diagnosis must go through WRAM. +- **The battery writes no verdict at all — not 14.** At `RESULTS = $7E:F000` (snesWorkRam offset + `$F000`, the address `runtime.inc` defines and `mesen_crossval.lua` already uses), the magic reads + as bytes that **differ between runs** — uninitialised WRAM, never written — and `R_STATUS[0..39]` + are **all zero** at both 600 and 1500 frames. "Completes 14 of 335 and stops at `A3.03`" does not + reproduce, and the two `A3.03` hypotheses chased earlier were chasing a number that is not there. + +The question is therefore no longer "why does it stop at `A3.03`" but **"why does the battery never +start under MesenCE"** — a different search, and one that explains why two cart-side fixes moved +nothing. + +**Prime suspect, not yet confirmed:** the input contract holds B + Start + X + R from power-on, and a +menu that starts the battery on a *press edge* would never see one. That is consistent with the +picture never changing. The next step is small — release the contract buttons for a few frames, then +re-press, and re-probe. If the battery then runs, the contract needs an edge at the start rather than +a constant hold, and `crossval.sh`'s runners all need the same treatment. + #### `A5.18` — why it is parked despite working `BRK` cannot be measured without `RTI`: a `BRK` that never returns cannot be repeated, so any diff --git a/scripts/accuracysnes/mesen_wram_probe.lua b/scripts/accuracysnes/mesen_wram_probe.lua new file mode 100644 index 00000000..4ad94572 --- /dev/null +++ b/scripts/accuracysnes/mesen_wram_probe.lua @@ -0,0 +1,50 @@ +-- Minimal MesenCE WRAM probe for the AccuracySNES results block. +-- +-- Exists because the battery oracle's diagnosis had been built on `mesen_crossval.lua`'s own +-- reading, with no independent check that the read itself was sound. This script does one thing -- +-- run N frames with the input contract held, then dump `RESULTS` ($7E:F000, snesWorkRam offset +-- $F000) -- so a claim about how far the battery got can be made from a second, much smaller +-- instrument. +-- +-- MCE_FRAMES=1500 MCE_RESULT=/tmp/w.txt SDL_VIDEODRIVER=offscreen SDL_AUDIODRIVER=dummy \ +-- ref-proj/MesenCE/bin/linux-x64/Release/Mesen --testRunner \ +-- --snes.port2.type=SnesController scripts/accuracysnes/mesen_wram_probe.lua \ +-- tests/roms/AccuracySNES/build/accuracysnes.sfc +-- +-- What it found (2026-08-01): the magic is uninitialised WRAM that DIFFERS between runs, and every +-- status byte in the first 40 catalogue slots is zero at 600 and at 1500 frames. So the battery is +-- not stopping partway -- it never writes a verdict at all. + +local TARGET = tonumber(os.getenv("MCE_FRAMES") or "900") +local RES = os.getenv("MCE_RESULT") or "/tmp/wram.txt" +local frames = 0 +local function onInput() + emu.setInput({ b=true, start=true, x=true, r=true, y=false, select=false, a=false, l=false, + up=false, down=false, left=false, right=false }, 0) + emu.setInput({ y=true, select=true, a=true, l=true, b=false, start=false, x=false, r=false, + up=false, down=false, left=false, right=false }, 1) +end +local function onEndFrame() + frames = frames + 1 + if frames < TARGET then return end + local f = io.open(RES, "w") + -- WRAM offset of $7E:F000 is 0xF000. Dump the magic + a slice of the status array. + local magic = "" + for i = 0, 3 do + magic = magic .. string.format("%02x", emu.read(0xF000 + i, emu.memType.snesWorkRam)) + end + f:write(string.format("frames=%d magic@F000=%s\n", frames, magic)) + local nonzero, first_zero = 0, -1 + local bytes = {} + for i = 0, 359 do + local b = emu.read(0xF020 + i, emu.memType.snesWorkRam) + if b ~= 0 then nonzero = nonzero + 1 elseif first_zero < 0 then first_zero = i end + if i < 40 then bytes[#bytes+1] = string.format("%02x", b) end + end + f:write(string.format("status nonzero=%d first_zero_index=%d\n", nonzero, first_zero)) + f:write("first40=" .. table.concat(bytes, " ") .. "\n") + f:close() + emu.stop(0) +end +emu.addEventCallback(onInput, emu.eventType.inputPolled) +emu.addEventCallback(onEndFrame, emu.eventType.endFrame) From fb0d713525e8a0d94caee3511f2a356d1da8d79f Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:31:43 -0400 Subject: [PATCH 02/10] test(accuracysnes): refute the press-edge suspect; point at the 24 Applying the input contract from frame 0, 30 or 120 -- the latter two manufacturing a rising edge -- all give the same result at 1200 frames: magic never reads ACSN, R_DONE never reads $A5, R_STATUS holds exactly 24 non-zero bytes. The battery is not waiting for a press edge. Committing the probe so the next attempt does not re-run a settled negative. The live lead is the 24, not the zeros. It is constant across runs while the magic bytes vary, so it is not uninitialised WRAM -- that would vary too -- and indices 0-39 read zero, so those bytes sit at index >= 40. Twenty-four deterministic bytes in an array that should be untouched is the next thing to explain. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 11 ++++-- scripts/accuracysnes/mesen_edge_probe.lua | 42 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 scripts/accuracysnes/mesen_edge_probe.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index b77f2f67..f618b777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,9 +32,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 reproduce — which explains why two cart-side `A3.03` fixes moved nothing: they were chasing a number that was not there. - The open question is now **"why does the battery never start"**, not "why does it stop". Prime - suspect, recorded in `docs/accuracysnes-plan.md` and not yet confirmed: the input contract holds - B + Start + X + R from power-on, so a menu that starts on a *press edge* never sees one. + The open question is now **"why does the battery never start"**, not "why does it stop". + + The first suspect — that the contract's constant hold from power-on denies a menu its press edge — + was tested and **refuted**: applying the contract from frame 0, 30 or 120 gives the same result at + 1200 frames. The live lead is instead that `R_STATUS` holds exactly **24** non-zero bytes, + *constant across runs* while the magic bytes *vary* — so they are not uninitialised WRAM, and since + indices 0-39 read zero they sit at index >= 40. Both probes are committed so the next attempt + starts from the measurement rather than repeating it. - **PPU: the field flag's doc contradicted the code, and nothing pinned either.** `Ppu::field` is `$213F` bit 7 and toggles at the end of **every** frame; its doc comment said "toggles each frame diff --git a/scripts/accuracysnes/mesen_edge_probe.lua b/scripts/accuracysnes/mesen_edge_probe.lua new file mode 100644 index 00000000..772248f9 --- /dev/null +++ b/scripts/accuracysnes/mesen_edge_probe.lua @@ -0,0 +1,42 @@ +-- MesenCE probe: does the AccuracySNES battery need a press EDGE on the contract buttons? +-- +-- HOLD_FROM is the frame at which the input contract is first applied, so HOLD_FROM=0 reproduces the +-- constant hold every runner uses and any larger value manufactures a rising edge. +-- +-- RESULT (2026-08-01): REFUTED. HOLD_FROM 0, 30 and 120 all produce the same shape at 1200 frames -- +-- magic never reads ACSN, R_DONE never reads $A5, and R_STATUS has exactly 24 non-zero bytes. The +-- edge is not what the battery is waiting for. Kept so the next attempt does not re-run it. +-- +-- The 24 is the live lead, not the zero: it is CONSTANT across runs while the magic bytes VARY, so it +-- is not uninitialised WRAM (which would vary too), and indices 0-39 read zero, so those bytes sit at +-- index >= 40. Find which indices they are before theorising further. + +local TARGET = tonumber(os.getenv("MCE_FRAMES") or "900") +local HOLD_FROM = tonumber(os.getenv("HOLD_FROM") or "0") -- frame at which the contract is applied +local RES = os.getenv("MCE_RESULT") or "/tmp/edge.txt" +local frames = 0 +local function onInput() + local on = frames >= HOLD_FROM + emu.setInput({ b=on, start=on, x=on, r=on, y=false, select=false, a=false, l=false, + up=false, down=false, left=false, right=false }, 0) + emu.setInput({ y=on, select=on, a=on, l=on, b=false, start=false, x=false, r=false, + up=false, down=false, left=false, right=false }, 1) +end +local function onEndFrame() + frames = frames + 1 + if frames < TARGET then return end + local f = io.open(RES, "w") + local magic = "" + for i = 0, 3 do magic = magic .. string.format("%02x", emu.read(0xF000 + i, emu.memType.snesWorkRam)) end + local done = emu.read(0xF008, emu.memType.snesWorkRam) + local nonzero = 0 + for i = 0, 359 do + if emu.read(0xF020 + i, emu.memType.snesWorkRam) ~= 0 then nonzero = nonzero + 1 end + end + f:write(string.format("hold_from=%d frames=%d magic=%s done=%02x status_nonzero=%d\n", + HOLD_FROM, frames, magic, done, nonzero)) + f:close() + emu.stop(0) +end +emu.addEventCallback(onInput, emu.eventType.inputPolled) +emu.addEventCallback(onEndFrame, emu.eventType.endFrame) From b54e139d41688d932e345bf4b7b386f97036c076 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:32:14 -0400 Subject: [PATCH 03/10] docs(accuracysnes): record the press-edge refutation in the plan The CHANGELOG carried it but the plan section still named the edge as an unconfirmed suspect, which would have sent the next session to re-run a settled negative. Co-Authored-By: Claude Opus 5 (1M context) --- docs/accuracysnes-plan.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 997884cf..dc64cdc9 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -653,11 +653,17 @@ The question is therefore no longer "why does it stop at `A3.03`" but **"why doe start under MesenCE"** — a different search, and one that explains why two cart-side fixes moved nothing. -**Prime suspect, not yet confirmed:** the input contract holds B + Start + X + R from power-on, and a -menu that starts the battery on a *press edge* would never see one. That is consistent with the -picture never changing. The next step is small — release the contract buttons for a few frames, then -re-press, and re-probe. If the battery then runs, the contract needs an edge at the start rather than -a constant hold, and `crossval.sh`'s runners all need the same treatment. +**The press-edge suspect is REFUTED.** `scripts/accuracysnes/mesen_edge_probe.lua` applies the +contract from frame 0, 30 or 120 — the latter two manufacturing a rising edge — and all three give +the same result at 1200 frames: the magic never reads `ACSN`, `R_DONE` never reads `$A5`, and +`R_STATUS` holds exactly **24** non-zero bytes. The battery is not waiting for an edge. The probe is +committed so the next attempt does not re-run a settled negative. + +**The live lead is that 24, not the zeros.** It is *constant across runs* while the magic bytes +*vary*, so it is not uninitialised WRAM — that would vary too — and indices 0-39 read zero, so those +bytes sit at index **>= 40**. Twenty-four deterministic bytes in an array that should be untouched is +the next thing to explain: find *which* catalogue indices they are, and whether they correspond to +real tests, before theorising further. #### `A5.18` — why it is parked despite working From c25d60f556aa040fc6cc46355f403e8ae412a6a7 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:34:28 -0400 Subject: [PATCH 04/10] fix(accuracysnes): retract the "24 bytes" lead; the cart reaches its menu The 24 non-zero bytes I called a lead were my own over-read -- they sit at indices 336-359, past the end of a 335-entry array. Retracted. The real signal is the reverse of what I reported. R_STATUS[0..334] is ZERO while the surrounding WRAM is RANDOM, and MesenCE powers on with randomised WRAM -- so an all-zero status array is not the power-on state. Something zeroed it, and that something is runtime.s's own clear loop. The cart's init runs and reaches the pre-battery menu. So the precise statement is not "the battery never starts" but "the cart never leaves its menu", whose exit condition the source names immediately below the clear loop: wait for Start. Also ruled out this round: declaring --snes.port1.type=SnesController explicitly alongside port 2. Next suspect, which fits every observation: the menu reads the pad through the automatic joypad read ($4218). If --testRunner does not drive auto-read the way emu.setInput implies, Start is held host-side and reads zero cart-side -- invisible in the framebuffer, and consistent with init running while the menu never exits. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 18 ++++++++++++++---- docs/accuracysnes-plan.md | 26 +++++++++++++++++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f618b777..724f979a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,10 +36,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 The first suspect — that the contract's constant hold from power-on denies a menu its press edge — was tested and **refuted**: applying the contract from frame 0, 30 or 120 gives the same result at - 1200 frames. The live lead is instead that `R_STATUS` holds exactly **24** non-zero bytes, - *constant across runs* while the magic bytes *vary* — so they are not uninitialised WRAM, and since - indices 0-39 read zero they sit at index >= 40. Both probes are committed so the next attempt - starts from the measurement rather than repeating it. + 1200 frames, as does declaring `--snes.port1.type=SnesController` explicitly. + + A "24 non-zero bytes" lead recorded mid-investigation was **my own over-read** — those bytes sit at + indices 336-359, past the end of a 335-entry array — and is retracted. The real signal is the + reverse: `R_STATUS[0..334]` is **zero** while the surrounding WRAM is **random**, and since MesenCE + powers on with randomised WRAM, an all-zero array is not the power-on state. Something zeroed it — + `runtime.s`'s own clear loop — so **the cart's init runs and reaches the pre-battery menu**. The + precise statement is not "the battery never starts" but "the cart never leaves its menu", whose + exit condition the source names as *wait for Start*. + + Next suspect, matching every observation: the menu reads the pad through the **automatic joypad + read** (`$4218`), and if `--testRunner` does not drive auto-read the way `emu.setInput` implies, + Start is held host-side and reads zero cart-side. All three probes are committed so the next + attempt starts from the measurements rather than repeating them. - **PPU: the field flag's doc contradicted the code, and nothing pinned either.** `Ppu::field` is `$213F` bit 7 and toggles at the end of **every** frame; its doc comment said "toggles each frame diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index dc64cdc9..818ef673 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -659,11 +659,27 @@ the same result at 1200 frames: the magic never reads `ACSN`, `R_DONE` never rea `R_STATUS` holds exactly **24** non-zero bytes. The battery is not waiting for an edge. The probe is committed so the next attempt does not re-run a settled negative. -**The live lead is that 24, not the zeros.** It is *constant across runs* while the magic bytes -*vary*, so it is not uninitialised WRAM — that would vary too — and indices 0-39 read zero, so those -bytes sit at index **>= 40**. Twenty-four deterministic bytes in an array that should be untouched is -the next thing to explain: find *which* catalogue indices they are, and whether they correspond to -real tests, before theorising further. +**RETRACTED: the "24 non-zero bytes" was my own over-read.** They sit at indices **336-359**, past +the end of a 335-entry array — I read 360 bytes and mistook what follows the array for a signal. + +**The real signal is the opposite one: `R_STATUS[0..334]` is ZERO while the surrounding WRAM is +random.** MesenCE powers on with randomised WRAM (the header at `$F000` reads differently every +run), so an all-zero status array is not the power-on state — **something zeroed it**. That +something is `runtime.s`'s own clear loop, which means **the cart's init runs and reaches the +pre-battery menu**. It is not dead at reset, and "the battery never starts" is more precisely "the +cart never leaves its menu". + +The menu's exit condition is named in the source immediately below that loop: *"wait for Start to +begin the battery"*. Two ways of supplying it are already ruled out — a rising edge +(`mesen_edge_probe.lua`, frames 0/30/120 identical) and declaring `--snes.port1.type=SnesController` +explicitly alongside port 2. + +**Next suspect, and it fits the shape of everything above:** the menu reads the pad through the +**automatic joypad read** (`$4218`), not the manual shift register. If MesenCE's `--testRunner` does +not drive auto-read the way `emu.setInput` implies, Start would be held on the host side and still +read as zero on the cart side — invisible in the framebuffer, and consistent with init running while +the menu never exits. Check what `$4218` actually contains from Lua before changing anything on the +cart. #### `A5.18` — why it is parked despite working From 753caaf35780364ab693ba84f7243739dd9e335f Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:34:46 -0400 Subject: [PATCH 05/10] test(accuracysnes): commit the status-index probe Reading a non-zero COUNT without reading the indices is what let an over-read masquerade as a lead for a whole round. This probe prints the indices, and its header records what that showed. Co-Authored-By: Claude Opus 5 (1M context) --- .../accuracysnes/mesen_status_index_probe.lua | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 scripts/accuracysnes/mesen_status_index_probe.lua diff --git a/scripts/accuracysnes/mesen_status_index_probe.lua b/scripts/accuracysnes/mesen_status_index_probe.lua new file mode 100644 index 00000000..7b573653 --- /dev/null +++ b/scripts/accuracysnes/mesen_status_index_probe.lua @@ -0,0 +1,40 @@ +-- MesenCE probe: WHICH R_STATUS indices are non-zero, and the raw RESULTS header. +-- +-- Written to check a "24 non-zero bytes" reading that turned out to be an over-read: those bytes are +-- at indices 336-359, past the end of the 335-entry array. Reading a byte count without reading the +-- indices is how that went unnoticed for a whole round -- hence this probe. +-- +-- What it establishes (2026-08-01): R_STATUS[0..334] is entirely ZERO while the RESULTS header at +-- $F000 is RANDOM and differs every run. MesenCE powers on with randomised WRAM, so an all-zero +-- array is not the power-on state -- runtime.s's own clear loop zeroed it, which means the cart's +-- init runs and it reaches the pre-battery menu. The open question is why it never leaves that menu, +-- whose exit condition the source names as "wait for Start". + +local TARGET = tonumber(os.getenv("MCE_FRAMES") or "1200") +local RES = os.getenv("MCE_RESULT") or "/tmp/idx.txt" +local frames = 0 +local function onInput() + emu.setInput({ b=true, start=true, x=true, r=true, y=false, select=false, a=false, l=false, + up=false, down=false, left=false, right=false }, 0) + emu.setInput({ y=true, select=true, a=true, l=true, b=false, start=false, x=false, r=false, + up=false, down=false, left=false, right=false }, 1) +end +local function onEndFrame() + frames = frames + 1 + if frames < TARGET then return end + local f = io.open(RES, "w") + local hits = {} + for i = 0, 359 do + local b = emu.read(0xF020 + i, emu.memType.snesWorkRam) + if b ~= 0 then hits[#hits+1] = string.format("%d=%02x", i, b) end + end + f:write("nonzero_indices: " .. table.concat(hits, " ") .. "\n") + -- Also dump the header words so we can see whether ANY of RESULTS was written. + local hdr = {} + for i = 0, 31 do hdr[#hdr+1] = string.format("%02x", emu.read(0xF000 + i, emu.memType.snesWorkRam)) end + f:write("header F000..F01F: " .. table.concat(hdr, " ") .. "\n") + f:close() + emu.stop(0) +end +emu.addEventCallback(onInput, emu.eventType.inputPolled) +emu.addEventCallback(onEndFrame, emu.eventType.endFrame) From 574eb5b50fead1dc2342ffccdaa3edb6cc91e752 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:36:59 -0400 Subject: [PATCH 06/10] test(accuracysnes): refute auto-joypad and the software edge Auto-joypad: at frame 300 NMITIMEN reads $42 -- bit 0 clear, auto-read disabled -- so $4218-$421F reading 0000 is correct rather than a fault. The cart says so itself at runtime.s:9, "read MANUALLY through $4016, not via auto-joypad", and line 570 stz NMITIMEN. My own previous suspect, killed by reading the register instead of theorising. Software edge: the menu computes V_PAD_NEW = HELD & ~LAST and seeds both on entry, so a held button yields NEW = 0 forever -- which looked like the answer. Pressing Start at frame 300 or 600 of a 2000-frame run changes nothing either. What remains is harness-side: emu.setInput from an inputPolled callback does not appear to reach the cart's manual $4016 strobe read under --testRunner. Every host-side way of supplying the press has been tried and the cart observes none, while its init demonstrably runs. The in-repo harness and the snes9x libretro driver both work, so this is MesenCE-runner input plumbing, not contract design. Next step recorded: a minimal ROM that only reads $4016 and stores it, testing the channel in isolation. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 17 ++++++++++---- docs/accuracysnes-plan.md | 28 +++++++++++++++++++----- scripts/accuracysnes/mesen_pad_probe.lua | 26 ++++++++++++++++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 scripts/accuracysnes/mesen_pad_probe.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 724f979a..2475fd40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,10 +46,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 precise statement is not "the battery never starts" but "the cart never leaves its menu", whose exit condition the source names as *wait for Start*. - Next suspect, matching every observation: the menu reads the pad through the **automatic joypad - read** (`$4218`), and if `--testRunner` does not drive auto-read the way `emu.setInput` implies, - Start is held host-side and reads zero cart-side. All three probes are committed so the next - attempt starts from the measurements rather than repeating them. + Two further suspects were raised and both refuted by measurement. **Auto-joypad:** at frame 300 + `NMITIMEN` reads `$42` — bit 0 clear, auto-read *disabled* — so `$4218`-`$421F` reading `0000` is + correct, not a fault; the cart states at `runtime.s:9` that it reads `$4016` **manually**. + **Software edge:** the menu computes `V_PAD_NEW = HELD & ~LAST` and seeds both on entry, so a + held button yields NEW = 0 forever — but pressing Start at frame 300 or 600 of a 2000-frame run + changes nothing either. + + What remains is a **harness-side** conclusion: `emu.setInput` from an `inputPolled` callback does + not appear to reach the cart's manual `$4016` strobe read under `--testRunner`. Every host-side way + of supplying the press has been tried and the cart observes none, while its init demonstrably runs. + The in-repo harness and the snes9x libretro driver both work, so this is MesenCE-runner input + plumbing, not contract design. Next step: a minimal ROM that only reads `$4016` and stores it, + to test the channel in isolation. Four probes are committed so none of this is re-run. - **PPU: the field flag's doc contradicted the code, and nothing pinned either.** `Ppu::field` is `$213F` bit 7 and toggles at the end of **every** frame; its doc comment said "toggles each frame diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 818ef673..22589ab3 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -674,12 +674,28 @@ begin the battery"*. Two ways of supplying it are already ruled out — a rising (`mesen_edge_probe.lua`, frames 0/30/120 identical) and declaring `--snes.port1.type=SnesController` explicitly alongside port 2. -**Next suspect, and it fits the shape of everything above:** the menu reads the pad through the -**automatic joypad read** (`$4218`), not the manual shift register. If MesenCE's `--testRunner` does -not drive auto-read the way `emu.setInput` implies, Start would be held on the host side and still -read as zero on the cart side — invisible in the framebuffer, and consistent with init running while -the menu never exits. Check what `$4218` actually contains from Lua before changing anything on the -cart. +**The auto-joypad suspect is also refuted, by reading the registers.** At frame 300 under +`--testRunner`, `NMITIMEN ($4200) = $42` — **bit 0 clear**, auto-joypad *disabled* — and +`$4218`-`$421F` all read `0000`, which is the correct value when it is off, not a fault. The cart +says so itself: `runtime.s` line 9, *"Controller input is read MANUALLY through `$4016`, not via +auto-joypad"*, and line 570 `stz NMITIMEN ; ... (we read $4016 by hand)`. + +**The software-edge theory is refuted too, at every timing tried.** The menu computes `V_PAD_NEW` as +`HELD & ~LAST` and seeds both to the same value on entry, so a button already down yields NEW = 0 +forever — which looked like the answer. But pressing Start at frame 300 or 600 (well past any +plausible menu entry, 2000-frame runs) changes nothing. + +**What remains, and it is a harness-side conclusion rather than a cart-side one:** `emu.setInput` +from an `inputPolled` callback does not appear to reach the cart's **manual `$4016` strobe read** +under `--testRunner`. Every host-side way of supplying the press has now been tried and the cart +observes none of them, while the cart's own init demonstrably runs. The next step is therefore to +verify the channel itself — drive a *minimal* ROM that does nothing but manually read `$4016` and +write the result to WRAM, and see whether `emu.setInput` reaches it at all. If it does not, the fix +belongs in how the runner supplies input, and no amount of cart-side change will help. + +Worth noting for whoever picks this up: **the in-repo harness and the snes9x libretro driver both +work**, so this is specifically a MesenCE-runner input-plumbing problem, not a contract design +problem. #### `A5.18` — why it is parked despite working diff --git a/scripts/accuracysnes/mesen_pad_probe.lua b/scripts/accuracysnes/mesen_pad_probe.lua new file mode 100644 index 00000000..dca7259c --- /dev/null +++ b/scripts/accuracysnes/mesen_pad_probe.lua @@ -0,0 +1,26 @@ +local TARGET = tonumber(os.getenv("MCE_FRAMES") or "300") +local RES = os.getenv("MCE_RESULT") or "/tmp/pad.txt" +local frames = 0 +local function onInput() + emu.setInput({ b=true, start=true, x=true, r=true, y=false, select=false, a=false, l=false, + up=false, down=false, left=false, right=false }, 0) + emu.setInput({ y=true, select=true, a=true, l=true, b=false, start=false, x=false, r=false, + up=false, down=false, left=false, right=false }, 1) +end +local function onEndFrame() + frames = frames + 1 + if frames < TARGET then return end + local f = io.open(RES, "w") + -- Auto-joypad results ($4218-$421F) and the enable bit ($4200 bit0), read as CPU registers. + local function reg(a) return emu.read(a, emu.memType.snesMemory) end + f:write(string.format("frames=%d NMITIMEN($4200)=%02x\n", frames, reg(0x4200))) + f:write(string.format("JOY1=%02x%02x JOY2=%02x%02x JOY3=%02x%02x JOY4=%02x%02x\n", + reg(0x4219), reg(0x4218), reg(0x421B), reg(0x421A), + reg(0x421D), reg(0x421C), reg(0x421F), reg(0x421E))) + -- What the cart itself believes: the runtime keeps the merged pad somewhere in VAR_BASE, but the + -- register read above is the decisive one -- it is what the menu polls. + f:close() + emu.stop(0) +end +emu.addEventCallback(onInput, emu.eventType.inputPolled) +emu.addEventCallback(onEndFrame, emu.eventType.endFrame) From 8bc8be34be799b21e80f508e1670d5f8b99f2a73 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:40:33 -0400 Subject: [PATCH 07/10] fix(accuracysnes): the Mesen oracle was one setInput call too many The battery now runs to completion under MesenCE: magic ACSN, R_DONE $A5, 335/335 status bytes written, and crossval.sh reports "Mesen2: 1 failing test(s)" where it previously timed out. Root cause: in this MesenCE build's --testRunner, emu.setInput's port argument does not select a controller. Indices 0, 1 and 2 all land on controller 1, verified through the cart's own V_PAD_HELD. The script called setInput twice, so the second call -- intended for port 2 -- overwrote port 1 and the cart saw PAD2_CONTRACT ($60A0) on controller 1. $60A0 has no Start; the pre-battery menu waits for Start. So the cart booted, ran init, cleared R_STATUS, reached the menu and sat there forever behind a static picture. Every recorded symptom follows from that line. The "completes 14 of 335 and stops at A3.03" figure was an all-zero status array misread, and two cart-side A3.03 fixes were aimed at a layer that was never at fault. Stated rather than hidden: port 2 cannot be driven from Lua in this build, so PAD2_CONTRACT-dependent rows are not cross-validated by this runner. The in-repo harness and the snes9x libretro driver drive both ports, so those rows stay covered elsewhere. Still open, and now a separate problem: Mesen2 reports no rendered scenes -- the run does not reach the scene loop. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 22 ++++++++++++++++ scripts/accuracysnes/mesen_crossval.lua | 26 +++++++++++++++---- scripts/accuracysnes/mesen_pad_held_probe.lua | 26 +++++++++++++++++++ 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 scripts/accuracysnes/mesen_pad_held_probe.lua diff --git a/CHANGELOG.md b/CHANGELOG.md index 2475fd40..d1d89b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- **AccuracySNES: the Mesen2 oracle is FIXED — one `emu.setInput` call too many.** The battery has + never run under MesenCE; it now completes: `magic='ACSN'`, `R_DONE=$A5`, **335/335** status bytes + written, and `crossval.sh` reports `Mesen2: 1 failing test(s)` where it previously timed out. + + **Root cause.** In this MesenCE build's `--testRunner`, `emu.setInput`'s port argument does **not** + select a controller — indices 0, 1 and 2 all land on **controller 1** (verified through the cart's + own `V_PAD_HELD`). `mesen_crossval.lua` called it twice, so the second call, intended for port 2, + **overwrote port 1**: the cart saw `PAD2_CONTRACT` (`$60A0`) on controller 1. `$60A0` contains no + Start, and the pre-battery menu waits for Start — so the cart booted, ran init, cleared `R_STATUS`, + reached the menu and sat there forever, behind a static picture that showed nothing. + + Every recorded symptom follows from that one line, and several earlier conclusions were wrong + because of it: the "completes 14 of 335 and stops at `A3.03`" figure was an all-zero status array + misread, and two cart-side `A3.03` fixes were aimed at a layer that was never at fault. + + **Stated, not hidden:** port 2 cannot be driven from Lua in this build, so rows depending on + `PAD2_CONTRACT` are not cross-validated by this runner. The in-repo harness and the snes9x libretro + driver drive both ports, so those rows remain covered elsewhere. + + Still open: Mesen2 reports no rendered scenes — the run does not reach the scene loop. That is now + a *next* problem rather than the same one, and the battery half of the oracle is usable today. + - **AccuracySNES oracle: the recorded diagnosis does not reproduce, and the blocker is a different one.** Re-measured with a new, deliberately minimal second instrument (`scripts/accuracysnes/mesen_wram_probe.lua`) — every prior conclusion rested on diff --git a/scripts/accuracysnes/mesen_crossval.lua b/scripts/accuracysnes/mesen_crossval.lua index 36c0488a..be9080c9 100644 --- a/scripts/accuracysnes/mesen_crossval.lua +++ b/scripts/accuracysnes/mesen_crossval.lua @@ -89,15 +89,31 @@ end -- nothing held, so every runner holds the same mask and the cart asserts against it. Mesen2's docs -- are explicit that setInput belongs in an inputPolled callback, since otherwise the state may not -- be applied before the ROM reads it. +-- +-- ONE setInput call, deliberately. This is what made the battery never run under MesenCE for +-- several sessions, and it is worth stating exactly, because the failure was silent and total: +-- +-- In this MesenCE build's --testRunner, the port argument does NOT select a controller. Sending +-- PAD_CONTRACT to index 0 and again to index 1 both land on CONTROLLER 1 (verified: the cart's own +-- V_PAD_HELD reads $9050 either way; sending to index 2 does the same). So the second call here -- +-- intended for port 2 -- OVERWROTE port 1, and the cart saw PAD2_CONTRACT ($60A0) on controller 1. +-- +-- $60A0 contains no Start. The pre-battery menu waits for Start. So the cart booted, ran its init, +-- cleared R_STATUS, reached the menu and sat there forever -- with nothing on screen to show it, +-- since the menu is a static picture. Every symptom followed from that: no ACSN magic, R_DONE +-- never $A5, and an all-zero status array that earlier notes misread as "completes 14 of 335". +-- +-- With the single call the battery runs to completion: magic ACSN, R_DONE $A5, 335/335 status bytes +-- written. Do not "restore" the port-2 call without first proving the port argument works. +-- +-- CONSEQUENCE, stated rather than hidden: port 2 cannot be driven from Lua in this build, so rows +-- that depend on PAD2_CONTRACT ($60A0: Y + Select + A + L) are not cross-validated by this runner. +-- The in-repo harness and the snes9x libretro driver do drive both ports, so those rows are still +-- covered -- just not by MesenCE. local function onInput() emu.setInput({ b = true, start = true, x = true, r = true, y = false, select = false, a = false, l = false, up = false, down = false, left = false, right = false }, 0) - -- Port 2 (PAD2_CONTRACT = $60A0: Y + Select + A + L). Needs a device in port 2, which the - -- testrunner invocation supplies via --snes.port2.type=SnesController (see crossval.sh). - emu.setInput({ y = true, select = true, a = true, l = true, - b = false, start = false, x = false, r = false, - up = false, down = false, left = false, right = false }, 1) end emu.addEventCallback(onInput, emu.eventType.inputPolled) diff --git a/scripts/accuracysnes/mesen_pad_held_probe.lua b/scripts/accuracysnes/mesen_pad_held_probe.lua new file mode 100644 index 00000000..e0b44a80 --- /dev/null +++ b/scripts/accuracysnes/mesen_pad_held_probe.lua @@ -0,0 +1,26 @@ +local TARGET = tonumber(os.getenv("MCE_FRAMES") or "600") +local HOLD_FROM = tonumber(os.getenv("HOLD_FROM") or "0") +local RES = os.getenv("MCE_RESULT") or "/tmp/held.txt" +local frames = 0 +local function onInput() + local on = frames >= HOLD_FROM + emu.setInput({ b=on, start=on, x=on, r=on, y=false, select=false, a=false, l=false, + up=false, down=false, left=false, right=false }, 0) + emu.setInput({ y=on, select=on, a=on, l=on, b=false, start=false, x=false, r=false, + up=false, down=false, left=false, right=false }, 1) +end +local function onEndFrame() + frames = frames + 1 + if frames < TARGET then return end + local function w(off) -- VAR_BASE $7E:E000 -> snesWorkRam offset $E000 + return emu.read(0xE000 + off, emu.memType.snesWorkRam) + | (emu.read(0xE000 + off + 1, emu.memType.snesWorkRam) << 8) + end + local f = io.open(RES, "w") + f:write(string.format("hold_from=%d frames=%d V_PAD_HELD=%04x V_PAD_LAST=%04x V_PAD_NEW=%04x\n", + HOLD_FROM, frames, w(4), w(6), w(8))) + f:close() + emu.stop(0) +end +emu.addEventCallback(onInput, emu.eventType.inputPolled) +emu.addEventCallback(onEndFrame, emu.eventType.endFrame) From f11f717ae30f4e8125b5a3b0b85de9a22584d709 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:43:37 -0400 Subject: [PATCH 08/10] fix(accuracysnes): the scenes script held no input contract at all "Mesen2: no scenes reported at all -- the run did not reach the scene loop" read as a scene-loop problem and was an input problem, the same class as the battery one. mesen_scenes.lua had no setInput anywhere. With nothing held the cart never leaves its pre-battery menu -- that menu waits for Start -- so it never runs the battery and never reaches the scene loop that follows. One port-1 call added, with the same warning as mesen_crossval.lua about not adding a port-2 call: in this build the port argument does not select a controller, so a second call would overwrite this one with PAD2_CONTRACT, which has no Start. Both halves of the oracle now arbitrate: snes9x: 53 scene(s) match, 0 unblessed, 0 mismatched Mesen2: 53 scene(s) match, 0 unblessed, 0 mismatched That is the ADR 0013 gate the v1.29.0 scene work has been waiting behind -- a golden can now be blessed from a render both references agree on. What remains is a finding rather than an oracle failure: Mesen2 reports 1 failing test, catalogue index 11 (A2.10), while snes9x reports only its 14 known divergences. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 20 ++++++++++++++++++-- scripts/accuracysnes/mesen_scenes.lua | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1d89b04..33246636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,8 +30,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `PAD2_CONTRACT` are not cross-validated by this runner. The in-repo harness and the snes9x libretro driver drive both ports, so those rows remain covered elsewhere. - Still open: Mesen2 reports no rendered scenes — the run does not reach the scene loop. That is now - a *next* problem rather than the same one, and the battery half of the oracle is usable today. + **The scene half is fixed too, and it was the same class of bug.** `mesen_scenes.lua` held **no + input contract at all** — no `setInput` anywhere — so the cart never left its pre-battery menu + there either, never ran the battery, and never reached the scene loop that follows it. The symptom + read as a scene-loop problem and was an input problem. With one port-1 call added: + + ``` + snes9x: 53 scene(s) match, 0 unblessed, 0 mismatched + Mesen2: 53 scene(s) match, 0 unblessed, 0 mismatched + ``` + + **Both halves of the oracle now arbitrate.** `docs/adr/0013` requires a golden be blessed only from + a render the references agree on, and for the first time both references produce one — which is the + gate the `v1.29.0` scene work has been waiting behind. + + What remains is no longer an oracle failure but a **finding**: Mesen2 reports 1 failing test, + catalogue index 11 (`A2.10`), while snes9x reports only its 14 known divergences. RustySNES failing + alone is this project's signature for a real bug rather than a broken test — worth checking which + before investigating, per the standing heuristic. - **AccuracySNES oracle: the recorded diagnosis does not reproduce, and the blocker is a different one.** Re-measured with a new, deliberately minimal second instrument diff --git a/scripts/accuracysnes/mesen_scenes.lua b/scripts/accuracysnes/mesen_scenes.lua index 5f00826b..1c523ba8 100644 --- a/scripts/accuracysnes/mesen_scenes.lua +++ b/scripts/accuracysnes/mesen_scenes.lua @@ -154,4 +154,21 @@ local function onFrame() end end +-- The host input contract, which this script was missing ENTIRELY -- which is why it reported "no +-- scenes at all": with nothing held the cart never leaves its pre-battery menu (that menu waits for +-- Start), so it never runs the battery and never reaches the scene loop that follows it. The symptom +-- read like a scene-loop problem and was an input problem, the same one `mesen_crossval.lua` had in +-- a different form. +-- +-- ONE call, port index 0. In this MesenCE build's --testRunner the port argument does not select a +-- controller -- 0, 1 and 2 all land on controller 1 -- so a second call for port 2 would OVERWRITE +-- this one with PAD2_CONTRACT ($60A0), which contains no Start. See `mesen_crossval.lua`'s comment +-- for the full account; do not add a port-2 call here without first proving the argument works. +local function onInput() + emu.setInput({ b = true, start = true, x = true, r = true, + y = false, select = false, a = false, l = false, + up = false, down = false, left = false, right = false }, 0) +end + +emu.addEventCallback(onInput, emu.eventType.inputPolled) emu.addEventCallback(onFrame, emu.eventType.endFrame) From b40a261a85fc7dfb5efe308802d3c27076821af1 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:46:11 -0400 Subject: [PATCH 09/10] docs(accuracysnes): record A2.10, the oracle's first arbitrated finding crossval.sh now reports Mesen2: 1 failing test -- catalogue index 11, cart A2.10 "PEI does not page-wrap". RustySNES passes it and so does snes9x (it is not among snes9x's 14 known divergences), so this is 2-vs-1 with Mesen2 as the outlier -- the inverse of the shape the standing heuristic warns about. Deliberately NOT recorded as a known-good Mesen2 divergence yet. That needs a third opinion from ares or bsnes, because a harness bug upstream of an implementation produces this exact signature and one already did in this project once. Until then crossval.sh's DISAGREEMENT verdict is correct and should stay. Co-Authored-By: Claude Opus 5 (1M context) --- docs/accuracysnes-plan.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 22589ab3..938c510f 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -626,6 +626,29 @@ threaded to it. Expect goldens to move, and note that a previous attempt at the mapping change was reverted for exactly that reason — so the guard test named in the roadmap lands **first**, before `dot_length` is touched. +#### First arbitrated finding: `A2.10` — Mesen2 is the outlier, 2-vs-1 + +The moment the oracle started working it produced a result the project has never been able to see. +`crossval.sh` reports `Mesen2: 1 failing test(s)`, catalogue index **11 = cart `A2.10`, "PEI does not +page-wrap"** (scored; source: WDC datasheet + superfamicom.org addressing notes; verdict slot +`$7EF02B`). + +RustySNES **passes** it and snes9x passes it too — `A2.10` is not among snes9x's 14 known +divergences. So this is **2-vs-1 with Mesen2 as the outlier**, which inverts the usual shape: the +standing heuristic warns that *RustySNES failing alone* means a real bug, and that is not what this +is. + +**Do not yet record it as a known-good Mesen2 divergence.** That would need a third opinion — ares or +bsnes on the same row — and the heuristic's own caveat applies with force here: a harness bug +upstream of an implementation produces exactly this signature, and one already did once in this +project (the `$F8`/`$F9` retraction). The cheap next step is to run the row against ares or bsnes. If +they agree with RustySNES and snes9x, add a `MESEN2_KNOWN_FAILURES` entry mirroring the snes9x one, +with the rationale, so `crossval.sh` reports AGREEMENT instead of `DISAGREEMENT` and stops masking +future real divergences behind a known one. + +Until then `crossval.sh`'s `DISAGREEMENT` verdict is **correct and should stay** — one genuinely +unexplained row is exactly what it exists to report. + #### The Mesen oracle — the "14 of 335" reading does not reproduce Re-measured 2026-08-01 with a second, independent instrument From 0c2bb2fe50dab1418fcb629117a6f79538d864a7 Mon Sep 17 00:00:00 2001 From: DoubleGate Date: Fri, 31 Jul 2026 21:59:12 -0400 Subject: [PATCH 10/10] docs(accuracysnes): pick the Group E batch, and correct its scoping Two material corrections, made now that the oracle arbitrates. "34 rows need new SPC700 emitters" overstates it. apu.rs already has dsp_write and dsp_read_to, plus apu_upload/release_to_ipl, so any row whose observable is a DSP register needs no new opcode encoding at all. The emitter gap is real only for timer/GAIN/echo-coefficient opcodes. Pick DSP-observable rows first, because those are the ones the working oracle can adjudicate. Batch chosen: E8.01, E9.02, E5.06 -- the first two errata-flagged, so the expected behaviour is stated rather than inferred from an implementation. E1.11 is explicitly NOT first: inspected and set aside as likely vacuous, since TSET1's second read is a dummy and on a timer-output target the first read has already cleared the counter, with no chance of an increment between two reads six cycles apart. Design the observable before writing that row. Settle A2.10 before authoring any of them -- authoring a batch while one reference disagreement is unexplained risks misattributing a new failure to the wrong cause. Co-Authored-By: Claude Opus 5 (1M context) --- docs/accuracysnes-plan.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 938c510f..7c9a1d23 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -626,6 +626,37 @@ threaded to it. Expect goldens to move, and note that a previous attempt at the mapping change was reverted for exactly that reason — so the guard test named in the roadmap lands **first**, before `dot_length` is touched. +#### Group E — the batch to author next, picked against what the oracle can adjudicate + +Chosen 2026-08-01, now that both halves of the Mesen2 oracle arbitrate. Two corrections to the +`v1.29.0` scoping first, both material: + +1. **"34 rows need new SPC700 emitters" overstates it.** `gen/src/tests/apu.rs` already has + `dsp_write` (3309) and `dsp_read_to` (3315), and `apu_upload`/`release_to_ipl` are in place. Any + row whose observable is a **DSP register** therefore needs no new opcode encoding at all — the + emitter gap is real only for timer/GAIN/echo-coefficient *opcodes*, not for DSP access. +2. **Pick DSP-observable rows first**, because they are the ones the working oracle can arbitrate: + a verdict byte is scored on-cart and now cross-checked by MesenCE and snes9x together. + +**The batch, in order:** + +| row | assertion | why first | +|---|---|---| +| `E8.01` | KON/KOFF polled every **second** sample (16 kHz) `[ERRATA]` | pure DSP-register observable; the errata flag means the reference is explicit, and a 1-sample granularity is measurable through ENDX/ENVX without new opcodes | +| `E9.02` | noise output is **highpass-filtered** `[ERRATA]` | DSP-observable via OUTX on a NON voice; errata-flagged, so the expected behaviour is stated rather than inferred | +| `E5.06` | BRR 15-bit wrap: clamp to 16 bits, then `+4000h..+7FFFh → -4000h..-1` | already has decoder scaffolding from the landed `E5` rows | + +**Explicitly NOT first: `E1.11`** ("TSET1/TCLR1 read the target twice"). Inspected and set aside as a +likely **vacuous** row: the second read is a dummy, and on a `$FD`-`$FF` timer-output target the +first read has already cleared the counter, so the second observes and changes nothing. The timer +cannot increment between two reads six cycles apart. Unless a distinguishing observable is found +first, this is the shape [[accuracysnes-vacuous-test-trap]] exists to prevent — design the observable +before writing the row. + +**Before authoring any of them,** settle `A2.10` above. It is the only unexplained reference +disagreement in the battery, and a row batch authored while one row is unexplained risks attributing +a new failure to the wrong cause. + #### First arbitrated finding: `A2.10` — Mesen2 is the outlier, 2-vs-1 The moment the oracle started working it produced a result the project has never been able to see.