Skip to content

docs(accuracysnes): AccuracySNES found an ares bug — $F1's timer-2 reset is inverted - #308

Merged
doublegate merged 1 commit into
mainfrom
docs/ares-timer2-bug
Aug 1, 2026
Merged

docs(accuracysnes): AccuracySNES found an ares bug — $F1's timer-2 reset is inverted#308
doublegate merged 1 commit into
mainfrom
docs/ares-timer2-bug

Conversation

@doublegate

@doublegate doublegate commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Completes the ares adjudication started in #305. The two divergences that could not be attributed to a known snes9x failure — E3.06 and E8.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:

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

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 $F1 write clears it, including the write that stops the timer.

Both rows do exactly that: enable timer 2 via $F1, run an interval, write $F1 again to stop it, then read $FF. In ares the stopping write has already zeroed T2OUT.

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'''s 0x01 arm resets stage2/stage3 only when raised. 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 — its 8..15 band still ends one tick short of TnOUT'''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 $F1 writes that do not enable timer 2, including the stop write, because the handler negates the raise() condition. This explains the zero timer-2 reads in E3.06 and E8.02.

The change adds or updates dossier assertions for E3.06 and E8.02. The coverage denominator does not move. E3.06 retains 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 $F1 writes, or if another cause produces the zero reads. No emulator behavior changes in this change.

…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>
Copilot AI review requested due to automatic review settings August 1, 2026 14:11
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 77bfbb64-348d-4d86-8faf-b0e41e0987e5

📥 Commits

Reviewing files that changed from the base of the PR and between 122ef84 and 0e73b34.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • scripts/accuracysnes/ares_host/README.md
📜 Recent review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: test-light
  • GitHub Check: accuracysnes
  • GitHub Check: lint
  • GitHub Check: copilot-pull-request-reviewer
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{rs,md}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Chip-behavior changes must update both the chip implementation and the corresponding docs/<subsystem>.md documentation.

A chip change must update both the chip implementation and its corresponding docs/<chip>.md documentation in the same change.

Files:

  • CHANGELOG.md
  • scripts/accuracysnes/ares_host/README.md
CHANGELOG.md

📄 CodeRabbit inference engine (CONTRIBUTING.md)

User-visible changes must be recorded under the [Unreleased] section.

For the full pull request diff against its base branch, modify CHANGELOG.md when user-visible behavior changes, including emulator output, frontend features, CLI flags, public APIs, or AccuracySNES cartridge contents. Do not require it for purely internal changes, tests, comments, or CI configuration.

Files:

  • CHANGELOG.md
**/*

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*: Do not commit or vendor the generated snesdev_wiki/ mirror; it is gitignored and intended only as a local reference.
Keep commits focused and use Conventional Commits: <type>(<scope>): <subject>, with an imperative subject of at most 72 characters.
Do not use emojis in code, comments, or commit messages.
Before opening a PR, ensure formatting, Clippy, workspace tests, the core embedded build, rustdoc with warnings denied, documentation coverage, and changelog requirements pass.
Ticket completion must be reflected in the relevant to-dos/ sprint file.

**/*: Preserve the one-directional crate graph: chip crates must not depend on one another; rustysnes-core ties them together.
Never commit commercial ROMs; only commit derived screenshots and hashes.
Keep docs/STATUS.md as the authoritative per-subsystem status and update project documentation in the same PR as code changes.
Do not treat RustyNES v2.0 or engine-lineage anchors as project releases.

Files:

  • CHANGELOG.md
  • scripts/accuracysnes/ares_host/README.md
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: Docs are the spec, not a changelog. Flag prose that has drifted from the code it describes
rather than style nits. The markdownlint gate is pinned to v0.39.0 via pre-commit —
do not report rules that version does not have (MD060 in particular).

Files:

  • CHANGELOG.md
  • scripts/accuracysnes/ares_host/README.md
scripts/accuracysnes/**

⚙️ CodeRabbit configuration file

scripts/accuracysnes/**: The cross-validation harness: the same AccuracySNES image is run on snes9x (through a
libretro host in C) and on Mesen2 (through its test runner and a Lua script), and their
verdicts are compared with the cart's. Its integrity is the whole argument for the
battery, so flag anything that could make a reference appear to agree — a verdict parsed
loosely, a missing-file path that degrades to success, a scene comparison that skips
rather than fails when the golden is absent. A known reference divergence belongs in
SNES9X_KNOWN_FAILURES with a source citation, never in a widened match.

Files:

  • scripts/accuracysnes/ares_host/README.md
🪛 LanguageTool
scripts/accuracysnes/ares_host/README.md

[grammar] ~73-~73: Ensure spelling is correct
Context: ...again to stop it, then read$FF. In ares the stopping write has already zeroed ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🔇 Additional comments (2)
CHANGELOG.md (1)

14-39: LGTM!

scripts/accuracysnes/ares_host/README.md (1)

42-43: LGTM!

Also applies to: 56-86


Walkthrough

The 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.

Changes

AccuracySNES timer-2 diagnosis

Layer / File(s) Summary
Document timer-2 reset behavior
CHANGELOG.md, scripts/accuracysnes/ares_host/README.md
The documentation identifies the negated $F1 transition check that clears timer 2 on non-raise writes, including stop writes. It records the resulting E8.02 and E3.06 divergences and the inability of E3.06 to distinguish a zero result from a timer wrap.

Estimated code review effort: 1 (Trivial) | ~3 minutes

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 9 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title uses a valid Conventional Commits prefix and accurately describes the documentation change, but its subject is not in imperative mood. Rewrite the subject in imperative mood, for example: "docs(accuracysnes): document ares timer-2 reset bug".
✅ Passed checks (9 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Changelog Entry ✅ Passed The full diff against origin/main adds a 26-line CHANGELOG.md entry under Unreleased; the PR changes only documentation files.
Docs-As-Spec ✅ Passed Against origin/main, the PR changes only CHANGELOG.md and scripts/accuracysnes/ares_host/README.md; it changes no crates/** behavior and needs no matching docs/.md edit.
Accuracysnes Bookkeeping ✅ Passed Against main/origin/main, the PR changes only CHANGELOG.md and scripts/accuracysnes/ares_host/README.md; it adds or removes no AccuracySNES test or scene, so the conditional bookkeeping checks do n...
No Panic On Untrusted Input ✅ Passed The PR changes only CHANGELOG.md and a README; no executable code or new .unwrap(), .expect(), or panic!() calls were added.
Safety Comment On New Unsafe ✅ Passed The patch changes only CHANGELOG.md and ares_host/README.md; the added lines contain no new unsafe block or unsafe fn, so no SAFETY comment is required.

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

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This documentation-only PR updates CHANGELOG.md and scripts/accuracysnes/ares_host/README.md to document an inverted timer-2 reset logic bug ($F1 control register) identified in the ares reference emulator during AccuracySNES cross-validation.

Blocking issues

None found.

Suggestions

  • PR title length: The PR title docs(accuracysnes): AccuracySNES found an ares bug — $F1's timer-2 reset is inverted is 82 characters long, violating the project convention requiring subject lines to be 72 characters or shorter.
  • scripts/accuracysnes/ares_host/README.md: The quoted ares/sfc/smp/io.cpp snippet does not state the targeted upstream ares version or commit SHA. Pin the commit hash so readers can verify the upstream source state.

Nitpicks

  • CHANGELOG.md: Investigative notes regarding third-party emulators are categorized under ### Added. Consider moving research findings under a ### Documentation heading to keep user-facing release notes focused on crate changes.
  • CHANGELOG.md: Inconsistent spelling ("behaviour" vs project standard "behavior").

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

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

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.06 and E8.02 and describe the suspected ares $F1 timer-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.06 are 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 is crates/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 is crates/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`).

Comment thread CHANGELOG.md
Comment on lines +15 to +25
`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
```

@doublegate
doublegate merged commit 121f666 into main Aug 1, 2026
12 checks passed
@doublegate
doublegate deleted the docs/ares-timer2-bug branch August 1, 2026 14:57
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