diff --git a/CHANGELOG.md b/CHANGELOG.md index a9300e3d..528d0930 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: the Mesen2 oracle diagnosed, and a stale frame budget aligned.** Three `v1.28.0` + items ended at "no oracle can arbitrate", so the headless runner was investigated rather than + accepted as environmental. Established: the hang is genuinely pre-existing (the `v1.25.0`-era image + times out identically); it is **not** the frame budget (the battery needs ~431 frames and 4000 + still times out); the Lua never sees `DONE = 0xA5` and the results magic never reads as `ACSN` + under either `snesWorkRam`+offset (`$A1`) or `snesMemory`+full address (`$52`), so the failure is + in the **Lua-to-emulator memory bridge or the battery not executing**, not a timeout — exit code + 254 has been misreporting the cause. `emu.log` does not surface in `--testrunner` mode, so + diagnosis has to go out through `emu.stop(value)`. + + Not fixed. `docs/accuracysnes-plan.md` records the next step and says plainly not to spend further + effort on rows needing an arbiter until it resolves. + + Also settled there: the instrument's fixed cost, **measured at 175 dots** (`measure_begin` + immediately followed by `measure_end`), with 8 `NOP`s costing exactly 28 dots marginal — so its + marginal accuracy is exact and only the intercept is large. `A5.19`'s 189-dot intercept *is* the + instrument, and the `v1.28.0` assessment was right. The corrected span budget is `341 - 175 = 166` + dots, which moves a `BRK` round trip from 3 iterations to 4; `A5.18` stays parked on that + arithmetic rather than on a disputed figure. + + Separately real: `mesen_crossval.lua`'s `MAX_FRAMES` was **900** against the harness's 1500, + `mesen_scenes.lua`'s 4000 and `libretro_crossval.c`'s 2000. The harness comment naming the budgets + that must move together omitted this file, which is how it was missed. Aligned to 4000 and the + comment corrected — a latent inconsistency, not the cause of the hang. + - **AccuracySNES: `C7.06` — Time Over reads set by `V = OBJ.YLOC + 1, H = 0`.** `C7` on-cart coverage 7 → 8 of 16. diff --git a/crates/rustysnes-test-harness/tests/accuracysnes.rs b/crates/rustysnes-test-harness/tests/accuracysnes.rs index c1e36ad6..c97bd440 100644 --- a/crates/rustysnes-test-harness/tests/accuracysnes.rs +++ b/crates/rustysnes-test-harness/tests/accuracysnes.rs @@ -56,9 +56,10 @@ const FORMAT_VERSION: u16 = 1; /// Raised from 600 when the cartridge image grew to 256 KiB. Most of the battery's frames are one /// test: `G1.11` walks the entire cartridge byte by byte to check the header checksum, so doubling /// the image doubled it — about 320 of the current 431 frames. The margin at 600 was 169 frames and -/// is now comfortable again. **When this needs raising, check `mesen_scenes.lua`'s `MAX_FRAMES` and -/// `libretro_crossval.c`'s `max_frames` in the same change**: all three bound the same run, and the -/// Mesen2 one silently reports "no scenes" rather than "timed out" from the gate's point of view. +/// is now comfortable again. **When this needs raising, check `MAX_FRAMES` in `mesen_scenes.lua` +/// and `mesen_crossval.lua`, and `libretro_crossval.c`'s `max_frames`, in the same change**: all +/// four bound the same run, and the Mesen2 ones silently report "no scenes" rather than "timed +/// out" from the gate's point of view. const MAX_FRAMES: u32 = 1500; /// Minimum share of scoring tests that must pass. diff --git a/docs/accuracysnes-plan.md b/docs/accuracysnes-plan.md index 9015c1ff..8805ac7e 100644 --- a/docs/accuracysnes-plan.md +++ b/docs/accuracysnes-plan.md @@ -455,6 +455,57 @@ So nothing is asserted and nothing is recorded. What is known: The next attempt should start from a scratch build that dumps `$213F` before and after each step into slots verified unused, rather than from a folded variant that hides where the discrepancy is. +### The instrument's fixed cost is ~175 dots — measured (`v1.29.0`) + +**Measured, after an earlier revision of this section wrongly called the figure into doubt.** The +empty span — `measure_begin` immediately followed by `measure_end`, nothing between — is **175 +dots**. A control in the same probe put 8 `NOP`s at exactly **28 dots** marginal (8 x 14 clocks / 4), +so the instrument's *marginal* accuracy is exact; only its fixed cost is large. + +That settles it: `A5.19`'s 189-dot intercept **is** the instrument (175, plus ~14 for the two no-op +`clc`/`xce` pairs inside its span). The `v1.28.0` assessment was right the first time. + +The doubt came from reading `hv_begin`/`hv_end` and estimating "only ~30 dots should be inside the +delta". That estimate was simply bad arithmetic: the pair runs ~70-90 CPU *cycles* between the two +latches (`sta f:`, `php`/`plp`, `pha`/`pla`, two `jsr`/`rts` pairs, and `hv_read_raw`'s eleven +instructions), and a cycle is 6-8 master clocks — **1.5-2 dots each**, not the ~0.4 the estimate +implied. Counting instructions instead of cycles is what produced the discrepancy. + +**The corrected span budget is `341 - 175 = 166` dots**, slightly more generous than the 152 the +`A5.18` note assumed. That moves a `BRK` round trip (39 dots) from 3 iterations to **4**, giving 8 +dots of signal against `TOL` 2 — better than the 6 that parked it, still a difference-of-differences +accumulating quantisation from four measurements, and still well short of `A5.19`'s 16. `A5.18` +stays parked, on corrected arithmetic rather than a wrong figure; the honest verdict is *borderline*, +not *impossible*. + +### The Mesen2 oracle — diagnosed, not yet fixed (`v1.29.0`) + +Three `v1.28.0` items ended at "no oracle can arbitrate": `C7.07`'s errata, the scene goldens, and +the *dot* half of `C7.05`. So the Mesen2 headless runner was investigated rather than accepted as +"environmental". What is now established: + +- **The hang is genuinely pre-existing.** The `v1.25.0`-era cartridge image, restored from git and + run unmodified, times out identically. Nothing in the `v1.28.0` rows caused it. +- **It is not the frame budget.** The battery needs ~431 frames on RustySNES (`MAX_FRAMES` 1500) and + snes9x is given 2000. Mesen2 still times out at **4000**. +- **`mesen_crossval.lua`'s `MAX_FRAMES = 900` is nonetheless stale** — the harness comment naming the + three budgets that must move together lists `mesen_scenes.lua` (4000) and `libretro_crossval.c`, + but not `mesen_crossval.lua`, which is why it was missed. Worth fixing regardless; it is not the + cause. +- **The Lua never observes `DONE = 0xA5`, and the results magic never reads as `ACSN`** — under + either `emu.memType.snesWorkRam` with a `$F000` offset (reads `$A1`) or `emu.memType.snesMemory` + with the full `$7EF000` address (reads `$52`). Neither is the expected `$41`. So the failure is in + the **Lua-to-emulator memory bridge, or the battery not executing**, and *not* a timeout — the + exit code 254 has been misreporting the cause all along. +- **`emu.log` does not surface in `--testrunner` mode.** Diagnosis has to be smuggled out through the + process exit code (`emu.stop(value)`), which is how the two byte readings above were obtained. + +**Next step:** determine which of the two remaining explanations holds, by exiting with a byte the +runtime writes *early* and whose value is known, then walking the address until it reads back. If the +bridge is the problem the fix is a one-line `memType`/offset change; if the battery genuinely does +not execute, that is a Mesen2 configuration question and a much larger one. Do not spend further +effort on rows that need an arbiter until this resolves. + ### v1.28.0 — what each remaining row actually needs, measured rather than assumed `A5.19` landed (below). Working the rest turned up that several rows filed as "needs no new diff --git a/scripts/accuracysnes/mesen_crossval.lua b/scripts/accuracysnes/mesen_crossval.lua index 8a97146e..36c0488a 100644 --- a/scripts/accuracysnes/mesen_crossval.lua +++ b/scripts/accuracysnes/mesen_crossval.lua @@ -21,7 +21,12 @@ local COUNT = BASE + 0x06 local DONE = BASE + 0x08 local STATUS = BASE + 0x20 -local MAX_FRAMES = 900 +-- Bounds the same run as the in-repo harness's MAX_FRAMES (1500), mesen_scenes.lua's (4000) and +-- libretro_crossval.c's max_frames (2000). This one was left at 900 when the others grew -- the +-- harness comment naming the budgets that must move together does not list this file, which is how +-- it was missed. It is NOT why this runner times out (4000 fails identically); see +-- docs/accuracysnes-plan.md on the Mesen2 oracle. +local MAX_FRAMES = 4000 local frames = 0 local function rd(a)