docs(accuracysnes): AccuracySNES found an ares bug — $F1's timer-2 reset is inverted - #308
Conversation
…set is inverted
E3.06 and E8.02 were the two ares divergences not attributable to a known
snes9x failure, and both read 0 from every timer-2 slot they record. One
mechanism explains both, and it is visible in ares/sfc/smp/io.cpp's $F1
handler:
if(timer0.enable.raise(data.bit(0))) { timer0.stage2 = 0; ... }
if(timer1.enable.raise(data.bit(1))) { timer1.stage2 = 0; ... }
if(!timer2.enable.raise(data.bit(2))) { timer2.stage2 = 0; ... }
// ^ negated, and only here
raise() is true on a 0->1 transition, so timers 0 and 1 reset their counters
ON a raise -- the documented behaviour. Timer 2 resets on anything EXCEPT a
raise, so every later $F1 write clears it, including the write that stops the
timer. Both rows enable timer 2, run an interval, write $F1 again to stop it,
then read $FF; in ares that stopping write has already zeroed T2OUT.
ares is internally inconsistent here, which is the strongest evidence
available that this is a stray `!` rather than intent: its own timers 0 and 1
do the un-negated version, and RustySNES, snes9x and Mesen2 all treat the
three identically (rustysnes-apu/src/lib.rs's 0x01 arm resets stage2/stage3
only when `raised`).
This is the first bug in a reference emulator AccuracySNES has found, and it
took the third opinion to see: with only snes9x and Mesen2 both rows passed
everywhere and there was nothing to investigate.
E3.06's own weakness stands separately -- its 8..15 band still ends one tick
short of TnOUT's 4-bit wrap, which is why the previous change made it record
its counts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📜 Recent review details⏰ Context from checks skipped due to timeout. (4)
🧰 Additional context used📓 Path-based instructions (5)**/*.{rs,md}📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
CHANGELOG.md📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*📄 CodeRabbit inference engine (CONTRIBUTING.md)
Files:
**/*.md⚙️ CodeRabbit configuration file
Files:
scripts/accuracysnes/**⚙️ CodeRabbit configuration file
Files:
🪛 LanguageToolscripts/accuracysnes/ares_host/README.md[grammar] ~73-~73: Ensure spelling is correct (QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1) 🔇 Additional comments (2)
WalkthroughThe changes document an ares timer-2 reset bug found through AccuracySNES. They update the changelog and ares host README with the affected rows, trigger condition, observed zero results, and E3.06 test ambiguity. ChangesAccuracySNES timer-2 diagnosis
Estimated code review effort: 1 (Trivial) | ~3 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 9 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (9 passed)
Comment |
Antigravity review (Gemini via Ultra)This documentation-only PR updates Blocking issuesNone found. Suggestions
Nitpicks
Automated first-pass review by |
There was a problem hiding this comment.
Pull request overview
Documentation-only update describing an AccuracySNES finding that attributes the E3.06/E8.02 ares-only divergences to an apparent ares timer-2 reset bug in the $F1 (CONTROL) handler, and records that as a notable project milestone.
Changes:
- Update the ares host README to assert a shared cause for
E3.06andE8.02and describe the suspected ares$F1timer-2 reset inversion. - Add a corresponding “Unreleased / Added” changelog entry summarizing the same finding.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| scripts/accuracysnes/ares_host/README.md | Expands the adjudication narrative for ares-only rows and documents the suspected timer-2 reset inversion. |
| CHANGELOG.md | Adds an Unreleased changelog entry capturing the same ares bug finding. |
Suppressed comments (4)
scripts/accuracysnes/ares_host/README.md:47
- This paragraph says “Nothing here is adjudicated”, but the table and the new section immediately below assert
E8.02/E3.06are explained by (and attributed to) an ares bug. That contradiction makes the README internally inconsistent; reword this sentence to clarify what is and isn’t adjudicated in this document.
This issue also appears in the following locations of the same file:
- line 58
- line 76
| `E3.06` | 2 | nobody | ares alone — **an ares bug, found by this cart** |
So ares is *corroborating snes9x* on three rows rather than standing alone, and only `E8.02` and
`E3.06` are genuinely ares-only. **Nothing here is adjudicated** — this is the shape of the
disagreement, not a verdict on it.
scripts/accuracysnes/ares_host/README.md:62
- This embeds a verbatim snippet from the ares codebase in our repo docs. Even if small, copying third‑party source into README/CHANGELOG can create licensing/copyright attribution obligations and makes the evidence easy to stale; prefer a short paraphrase plus a link/permalink to the upstream lines.
`E3.06` and `E8.02` both read **0** from every timer-2 slot they record. One mechanism explains
both, and it is visible in ares' source. `ares/sfc/smp/io.cpp`, the `$F1` (CONTROL) handler:
```cpp
if(timer0.enable.raise(data.bit(0))) { timer0.stage2 = 0; timer0.stage3 = 0; }
scripts/accuracysnes/ares_host/README.md:78
- The source reference here omits the
crates/prefix, but the actual file iscrates/rustysnes-apu/src/lib.rs. Using the correct path makes it easier for readers to find the supporting code.
stray `!` rather than intent: its own timers 0 and 1 do the un-negated version, and RustySNES,
snes9x and Mesen2 all treat the three identically (`rustysnes-apu/src/lib.rs`, the `0x01` arm,
resets `stage2`/`stage3` only when `raised`).
CHANGELOG.md:35
- The supporting RustySNES source path is missing the
crates/prefix. The file in this repo iscrates/rustysnes-apu/src/lib.rs, so updating the reference will make it directly navigable.
**ares is internally inconsistent**, which is the strongest evidence available that this is a stray
`!` rather than intent: its own timers 0 and 1 do the un-negated version, and RustySNES, snes9x and
Mesen2 all treat the three identically (`rustysnes-apu/src/lib.rs`'s `0x01` arm resets
`stage2`/`stage3` only when `raised`).
| `E3.06` and `E8.02` were the two divergences the third opinion could not attribute to a known | ||
| snes9x failure, and both read **0** from every timer-2 slot they record. One mechanism explains | ||
| both, and it is visible in `ares/sfc/smp/io.cpp`'s `$F1` (CONTROL) handler: | ||
|
|
||
| ```cpp | ||
| if(timer0.enable.raise(data.bit(0))) { timer0.stage2 = 0; timer0.stage3 = 0; } | ||
| if(timer1.enable.raise(data.bit(1))) { timer1.stage2 = 0; timer1.stage3 = 0; } | ||
| if(!timer2.enable.raise(data.bit(2))) { timer2.stage2 = 0; timer2.stage3 = 0; } | ||
| // ^ negated, and only here | ||
| ``` | ||
|
|
Completes the ares adjudication started in #305. The two divergences that could not be attributed to a known snes9x failure —
E3.06andE8.02— turn out to share one cause, and it is a bug in ares.The evidence
Both rows read 0 from every timer-2 slot they record.
ares/sfc/smp/io.cpp, the$F1(CONTROL) handler:raise()is true on a 0→1 transition. Timers 0 and 1 reset their counters on a raise — the documented behaviour. Timer 2 resets on anything except a raise, so every later$F1write clears it, including the write that stops the timer.Both rows do exactly that: enable timer 2 via
$F1, run an interval, write$F1again to stop it, then read$FF. In ares the stopping write has already zeroedT2OUT.Why this reads as a bug rather than a difference of opinion
ares is internally inconsistent. Its own timers 0 and 1 use the un-negated form. RustySNES, snes9x and Mesen2 all treat the three identically —
rustysnes-apu/src/lib.rs'''s0x01arm resetsstage2/stage3only whenraised. A stray!is the simplest explanation that fits all of it.Why it needed the third opinion
This is the first bug in a reference emulator that AccuracySNES has found, and it was invisible before today: with only snes9x and Mesen2, both rows passed everywhere and there was nothing to investigate. That is the case for having built the ares host, made concrete.
What is not claimed
E3.06'''s own weakness stands separately — its8..15band still ends one tick short ofTnOUT'''s 4-bit wrap, which is why #307 made it record its counts. Fixing ares would not fix that.Docs only; no code, no gate, no ROM change.
🤖 Generated with Claude Code
Documents an AccuracySNES finding that ares resets timer-2 counters after
$F1writes that do not enable timer 2, including the stop write, because the handler negates theraise()condition. This explains the zero timer-2 reads inE3.06andE8.02.The change adds or updates dossier assertions for
E3.06andE8.02. The coverage denominator does not move.E3.06retains a separate measurement-band weakness because the band ends one tick before timer output wraps.The claim is false if timer-2 state does not reset on those
$F1writes, or if another cause produces the zero reads. No emulator behavior changes in this change.