diff --git a/.github/release-notes/v2.2.5.md b/.github/release-notes/v2.2.5.md
new file mode 100644
index 00000000..9014cc44
--- /dev/null
+++ b/.github/release-notes/v2.2.5.md
@@ -0,0 +1,107 @@
+# RustyNES v2.2.5 — "Colophon" (provenance, licensing, and documentation integrity)
+
+A **provenance, licensing, and documentation-integrity** release. It was prompted
+by community review (NESdev forums) of the project's licensing and AI-assisted
+origins, and it corrects how RustyNES *describes its own provenance* — in source
+comments, in `NOTICE`, and in the docs — so the record matches what the code
+actually is.
+
+**Zero emulation-core behavior changes.** The deterministic `#![no_std]` chip stack,
+save-state / TAS / netplay formats, and every golden vector are byte-identical to
+v2.2.4 by construction, so **AccuracyCoin holds 141/141 (100.00%)** and nestest is
+0-diff. This was re-verified after every change (AccuracyCoin full + run-ahead,
+the nestest golden log, the dual-path differential net, and the snapshot-schema
+audit), and the whole tree passes `cargo fmt`, `clippy -D warnings` (workspace +
+feature combos), `rustdoc -D warnings`, markdownlint, and the `no_std`
+cross-compile.
+
+## Why this release exists
+
+A reviewer pointed out — correctly — that a number of in-source comments described
+implementations as "ports" of other emulators, including GPL-licensed ones, and
+that the project's AI-assisted nature was not disclosed. Rather than argue the
+point, we audited the entire tree and fixed it. The short version:
+
+- The chip, mapper, and peripheral **behaviors** are implemented from public
+ hardware documentation (the NESdev wiki, published datasheets, the documented
+ 6502 unofficial-opcode behavior) and pinned to public test ROMs. Where a
+ reference emulator was consulted, it was used as a **behavioral oracle** — to
+ observe and cross-check documented behavior — not as a source of copied code.
+- Some comments had mischaracterized that relationship as "ported from X." Those
+ were reworded to state what actually happened. **No GPL-licensed emulator source
+ is incorporated into RustyNES.**
+
+## Licensing & provenance
+
+- **In-source "port" comments corrected** across the CPU (unstable-store opcodes),
+ PPU (sprite-evaluation and OAM models), APU, and numerous mapper register
+ decoders — reframed from "ported from Mesen2 / puNES" (GPLv3 / GPLv2) to
+ independent implementations of publicly-documented hardware behavior,
+ cross-checked against reference emulators as oracles.
+- **`NOTICE` rewritten** to:
+ - disclose the behavioral-oracle use of GPL-licensed emulators
+ (Mesen2/MesenCE, higan, **GeraNES**, ares, FCEUX, Nestopia UE, puNES) with **no
+ code incorporated**;
+ - attribute the genuinely incorporated permissive components — **emu2413**
+ (Mitsutaka Okazaki, MIT), **TriCNES** (Chris Siebert, MIT), and **rcheevos**
+ (RetroAchievements.org, MIT) — with their copyright notices and the MIT text;
+ - attribute the bundled fonts (**Font Awesome**; **Press Start 2P** / OFL) and
+ the bundled **test ROMs** (AccuracyCoin MIT, Damian Yerrick Holy Mapperel zlib,
+ blargg / kevtris public domain);
+ - credit the CRT-shader / NTSC-filter **visual influences** as independent
+ reimplementations.
+- **GeraNES (GPL-3.0-only) disclosed** — it was cited as a reference across ~58
+ files but was previously absent from `NOTICE`.
+- **CRT shaders and NTSC filters** (`crt_royale`, `crt_guest`, `megatron`, the
+ Bisqwit and EMMIR NTSC filters) were reviewed at source level. Each is a
+ single-pass shader built on RustyNES's own uniform/pipeline conventions and is
+ structurally incompatible with being a translation of the upstream *multi-pass*
+ shader source; copyright protects code expression, not a visual look or a
+ rendering technique, so these are independent reimplementations. Comments were
+ reworded from "port / condensation of X" accordingly, and the one comment
+ claiming tables were "ported verbatim from Bisqwit's C" was corrected — those
+ tables encode the two-level NES composite signal documented at the NESdev wiki.
+- **`blip.rs`** no longer mislabels `blip_buf` as BSD/MIT (it is LGPL-2.1+); the
+ file is an independent band-limited-step (BLEP) implementation and now says so.
+
+## Documentation
+
+- **New `docs/originality-and-provenance.md`** — a candid account of where RustyNES
+ advances, diverges from, or independently re-derives NES emulation technique
+ (the one-clock timebase, the transistor-literal octal-latch fetch, the
+ machine-checked accuracy-honesty gates, the determinism contract, measured-and-
+ rejected optimizations), the development timeline, an oracle-versus-port
+ classification of every reference, and the full license posture — written to be
+ honest rather than triumphal, including that the project is heavily AI-assisted.
+- **README** — added an AI-assistance disclosure; removed a comparison graphic that
+ contained inaccurate details; corrected a mislabeled "sub-cycle accuracy in
+ action" screenshot caption (it was an early-development image); toned down
+ overstated language; and synced the Acknowledgments with `NOTICE`.
+- **`tests/roms/LICENSES.md`** — fixed a false exclusion claim (four Holy Mapperel
+ mapper ROMs stated as excluded are in fact committed), a stale crate path, and
+ the AccuracyCoin sub-test count, and added blanket coverage for the committed
+ directories not individually tabulated (328 committed `.nes` total, none
+ commercial).
+
+## Assets
+
+- **Press Start 2P OFL text** added to the Android app assets. The font shipped in
+ the Android app without the SIL Open Font License text that OFL 1.1 requires
+ travel with the font; the desktop and iOS builds already carried it.
+
+## Compatibility
+
+No format, save-state, or behavior changes. `.rns` save-states, `.rnm` movies, and
+netplay replays are byte-identical to v2.2.4. The libretro core's
+`display_version` advances to `v2.2.5`; there are no other functional changes to
+the RetroArch integration.
+
+## Verification
+
+- **AccuracyCoin: 141/141 (100.00%)** — full suite and through run-ahead.
+- **nestest: 0-diff** against the Nintendulator golden log.
+- **Dual-path differential net** and **snapshot-schema audit** green.
+- `cargo fmt --check`, `cargo clippy --workspace --all-targets -- -D warnings`
+ (plus the `retroachievements` / `scripting` / `hd-pack` feature combos),
+ `RUSTDOCFLAGS="-D warnings" cargo doc`, markdownlint, and the `no_std`
+ `thumbv7em-none-eabihf` cross-compile all pass.
diff --git a/.gitignore b/.gitignore
index dd292533..05fb22a9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,8 @@
/target/
/target/doc/
/target/criterion/
+# Nested per-crate target/ dirs (build output; the root /target/ rule is anchored).
+crates/*/target/
**/*.rs.bk
*.bench
# This workspace ships a binary app — commit the lock (re-include it).
diff --git a/AGENTS.md b/AGENTS.md
index cb2066ca..1f4eebdc 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -27,7 +27,7 @@
RustyNES is a cycle-accurate Nintendo Entertainment System emulator written in pure Rust. The accuracy bar is Mesen2 / higan / ares: tight lockstep scheduling at PPU-dot resolution on a master-clock-precise timebase, sub-instruction PPU events visible to subsequent CPU code, and a lookup-table non-linear audio mixer with band-limited synthesis. The frontend is pure Rust (`winit` + `wgpu` + `cpal` + `egui`).
-**Current release: v2.2.4 "Cartridge"** (2026-07-24) — a **libretro / RetroArch distribution** cut whose purpose is that the RustyNES core **builds and installs cleanly through the Libretro buildbot** () for in-RetroArch use. **Zero emulation-core changes** — the deterministic `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden vector are byte-identical to v2.2.3, so **AccuracyCoin holds 141/141 (100.00%)** and nestest is 0-diff by construction. `crates/rustynes-libretro` wraps `rustynes-core`, so it inherits every v2.2.3 change automatically (the fast-dot-path default; the `PPU_SNAPSHOT_VERSION` 8 / APU v4 save-state schema, transparent because `get_serialize_size` / `on_serialize` size and emit the *current* snapshot via `Nes::snapshot_core_into` rather than a fixed layout; the `Mapper::mix_audio` i32 widening; the Zapper model; the `mNNN_` mapper rename), and both buildbot cross-ABIs the CI early-warning gate models — `x86_64-pc-windows-gnu` and `aarch64-linux-android` — `cargo check --release -p rustynes-libretro` clean. The concrete change is a **`rustynes_libretro.info` metadata correction**: **`disk_control` `false` → `true`** (the real fix — the FDS multi-side Disk Control interface has been wired since the buildbot recipe landed but was advertised as absent, hiding multi-disk FDS swapping from RetroArch's Quick Menu), `display_version` `v1.0.0` → `v2.2.4`, and the description mapper count `168` → `172`. Libretro **core options** (region / overscan / palette / accuracy toggles) remain unexposed — `core_options = "false"` is accurate, a documented future enhancement rather than a v2.2.4 gap. The Antigravity PR reviewer standardization onto the shared template rides along. On top of **v2.2.3 "Datum"** (2026-07-23) — a **performance and accuracy-closure patch**, the product of a measure-first appraisal that profiled the emulator and acted on what the profile showed rather than on intuition. **Performance:** the specialized PPU fast dot path is promoted to the **default** and exposed to users for the first time — `Nes::set_fast_dotloop` had **no caller outside the core**, so a **−11.3%** frame-time win (fresh clean-host Criterion, reproducing v2.1.8's +12.3% by a different method; differential-tested bit-identical every frame since v2.1.8) shipped switched off and unreachable; release builds now ship **PGO-optimized** Linux binaries when the existing >3%-and-byte-identical gate passes; and CI gained a same-runner **relative** frame-time regression gate, closing a hole where a 2.5x slowdown passed the deliberately-loose absolute ceiling. **Two optimizations were measured and REJECTED** and are documented with their numbers per `docs/performance.md`'s convention — P3 (`emit_pixel` bounds-check elision) made the shipped default *slower* (+4.32% / +3.35% on the `_fast` workloads, p ≤ 0.02), and P4 (`cpu_clock`) found both textbook optimizations already implemented with the one remaining lever capped at **≤1.9%**. **Accuracy:** the **last two Holy Mapperel residuals are closed**, so all 17 ROMs report `detail=0000` (was 15/17) — MMC1's two software WRAM write-protect layers (`$E000` bit 4 + SNROM's CHR-register layer, gated on `chr_is_ram`) and FME-7's open bus on the RAM-selected-but-disabled window, both routed through the trait's existing `cpu_read_unmapped` contract. MMC1 is the change Holy Mapperel's README calls a game-compatibility hazard (FCEUX / PowerPak omit it), so it was validated before landing: **60/60** commercial ROMs including seven battery-backed MMC1 saves, plus **138/138** extended. The **Sunsoft 5B absolute level** is calibrated against Mesen2, which required widening `Mapper::mix_audio` to `i32` (the correct full-scale 5B tone `1882 * 18.471 = 34,761` does not fit `i16`). A **save-state schema gap** is fixed — `PPU_SNAPSHOT_VERSION` **8** carrying the sprite-eval FSM + OAM data-bus state, plus an APU **v4** tail — which is what made AccuracyCoin report **141/141 through run-ahead** as well as without it; a new standing field-vs-schema audit found it and the two APU gaps mechanically. A **Zapper beam-relative light model** lands opt-in / default-off (no pass-fail light-gun ROM exists to adjudicate it). **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff. Also: the eleven `sprintN.rs` mapper modules (27,631 lines, ~110 boards) are renamed for the boards they emulate with `mNNN_` mapper-number prefixes, proven content-preserving by a byte-for-byte item comparison (930 items, 0 altered) and an identical 172-ID dispatch table.
+**Current release: v2.2.5 "Colophon"** (2026-08-03) — a **provenance, licensing, and documentation-integrity** release with **zero emulation-core behavior changes** (so **AccuracyCoin holds 141/141 (100.00%)** and nestest is 0-diff by construction). It reworded in-source comments that had mischaracterized publicly-documented hardware-behavior implementations as "ports of" copyleft emulators (Mesen2 GPLv3, puNES GPLv2) into the accurate oracle framing; rewrote `NOTICE` to disclose the behavioral-oracle use of GPL emulators (Mesen2/MesenCE, higan, GeraNES, ares, FCEUX, Nestopia, puNES — no code incorporated) and to attribute the genuinely-incorporated permissive components (emu2413, TriCNES, rcheevos — all MIT), the bundled fonts and test ROMs, and the CRT-shader/NTSC-filter visual influences as independent reimplementations; disclosed **GeraNES (GPL-3.0-only)** as an oracle; added `docs/originality-and-provenance.md`; and added an AI-assistance disclosure to the README (removing a misleading comparison graphic and fixing a mislabeled screenshot caption). On top of **v2.2.4 "Cartridge"** (2026-07-24) — a **libretro / RetroArch distribution** cut whose purpose is that the RustyNES core **builds and installs cleanly through the Libretro buildbot** () for in-RetroArch use. **Zero emulation-core changes** — the deterministic `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden vector are byte-identical to v2.2.3, so **AccuracyCoin holds 141/141 (100.00%)** and nestest is 0-diff by construction. `crates/rustynes-libretro` wraps `rustynes-core`, so it inherits every v2.2.3 change automatically (the fast-dot-path default; the `PPU_SNAPSHOT_VERSION` 8 / APU v4 save-state schema, transparent because `get_serialize_size` / `on_serialize` size and emit the *current* snapshot via `Nes::snapshot_core_into` rather than a fixed layout; the `Mapper::mix_audio` i32 widening; the Zapper model; the `mNNN_` mapper rename), and both buildbot cross-ABIs the CI early-warning gate models — `x86_64-pc-windows-gnu` and `aarch64-linux-android` — `cargo check --release -p rustynes-libretro` clean. The concrete change is a **`rustynes_libretro.info` metadata correction**: **`disk_control` `false` → `true`** (the real fix — the FDS multi-side Disk Control interface has been wired since the buildbot recipe landed but was advertised as absent, hiding multi-disk FDS swapping from RetroArch's Quick Menu), `display_version` `v1.0.0` → `v2.2.4`, and the description mapper count `168` → `172`. Libretro **core options** (region / overscan / palette / accuracy toggles) remain unexposed — `core_options = "false"` is accurate, a documented future enhancement rather than a v2.2.4 gap. The Antigravity PR reviewer standardization onto the shared template rides along. On top of **v2.2.3 "Datum"** (2026-07-23) — a **performance and accuracy-closure patch**, the product of a measure-first appraisal that profiled the emulator and acted on what the profile showed rather than on intuition. **Performance:** the specialized PPU fast dot path is promoted to the **default** and exposed to users for the first time — `Nes::set_fast_dotloop` had **no caller outside the core**, so a **−11.3%** frame-time win (fresh clean-host Criterion, reproducing v2.1.8's +12.3% by a different method; differential-tested bit-identical every frame since v2.1.8) shipped switched off and unreachable; release builds now ship **PGO-optimized** Linux binaries when the existing >3%-and-byte-identical gate passes; and CI gained a same-runner **relative** frame-time regression gate, closing a hole where a 2.5x slowdown passed the deliberately-loose absolute ceiling. **Two optimizations were measured and REJECTED** and are documented with their numbers per `docs/performance.md`'s convention — P3 (`emit_pixel` bounds-check elision) made the shipped default *slower* (+4.32% / +3.35% on the `_fast` workloads, p ≤ 0.02), and P4 (`cpu_clock`) found both textbook optimizations already implemented with the one remaining lever capped at **≤1.9%**. **Accuracy:** the **last two Holy Mapperel residuals are closed**, so all 17 ROMs report `detail=0000` (was 15/17) — MMC1's two software WRAM write-protect layers (`$E000` bit 4 + SNROM's CHR-register layer, gated on `chr_is_ram`) and FME-7's open bus on the RAM-selected-but-disabled window, both routed through the trait's existing `cpu_read_unmapped` contract. MMC1 is the change Holy Mapperel's README calls a game-compatibility hazard (FCEUX / PowerPak omit it), so it was validated before landing: **60/60** commercial ROMs including seven battery-backed MMC1 saves, plus **138/138** extended. The **Sunsoft 5B absolute level** is calibrated against Mesen2, which required widening `Mapper::mix_audio` to `i32` (the correct full-scale 5B tone `1882 * 18.471 = 34,761` does not fit `i16`). A **save-state schema gap** is fixed — `PPU_SNAPSHOT_VERSION` **8** carrying the sprite-eval FSM + OAM data-bus state, plus an APU **v4** tail — which is what made AccuracyCoin report **141/141 through run-ahead** as well as without it; a new standing field-vs-schema audit found it and the two APU gaps mechanically. A **Zapper beam-relative light model** lands opt-in / default-off (no pass-fail light-gun ROM exists to adjudicate it). **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff. Also: the eleven `sprintN.rs` mapper modules (27,631 lines, ~110 boards) are renamed for the boards they emulate with `mNNN_` mapper-number prefixes, proven content-preserving by a byte-for-byte item comparison (930 items, 0 altered) and an identical 172-ID dispatch table.
The prior release, **v2.2.2 "Conduit"** (2026-07-21), was a **build, distribution, and CI-integrity patch**: the **libretro buildbot recipe from 1 of 10 jobs green to all ten building** (the last step before RustyNES lands in RetroArch's built-in core downloader), a **GitHub Actions supply-chain hardening** pass (`persist-credentials: false` on all 19 checkouts, a fail-closed release-tag check via `git/matching-refs`, `dtolnay/rust-toolchain` SHA-pinned off `@master`), and the toolchain **collapsed to one pinned source of truth** — no toolchain version literal anywhere under `.github/` and **no `nightly` on any build path**. **Zero emulation-core changes**, so AccuracyCoin held 141/141 by construction. Its one behavioural improvement in a shipped artifact: the libretro **tvOS** core built with `panic = "abort"` like every other platform.
@@ -185,7 +185,7 @@ These cross-cutting decisions span multiple files. Reading individual chip docs
- `ref-docs/` is immutable. Research updates go in dated supplemental files.
- ADRs go in `docs/adr/` (Michael Nygard format).
- `rustynes-core` re-exports the public types from the chip crates; downstream consumers (`rustynes-frontend`, `rustynes-test-harness`) should depend on `rustynes-core` rather than the chip crates directly.
-- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v2.2.4 "Cartridge"** (2026-07-24, a libretro/RetroArch distribution cut — zero emulation-core changes so AccuracyCoin holds 141/141 by construction; the libretro core is confirmed up-to-date with all recent changes and builds for the buildbot ABIs [`x86_64-pc-windows-gnu`, `aarch64-linux-android`], and `rustynes_libretro.info` is corrected: `disk_control` false→true [the FDS Disk Control interface was wired but advertised absent], `display_version` v1.0.0→v2.2.4, mapper count 168→172; core options remain a documented future enhancement; the Antigravity reviewer standardization rides along), on top of **v2.2.3 "Datum"** (2026-07-23, a performance and accuracy-closure patch — the fast PPU dot path promoted to default and exposed, PGO binaries shipped on the release path, a same-runner relative frame-time CI gate, the last two Holy Mapperel residuals closed [MMC1 WRAM write-protect + FME-7 open bus, all 17 ROMs now `detail=0000`], the Sunsoft 5B level calibrated with `Mapper::mix_audio` widened to i32, a save-state schema gap fixed at `PPU_SNAPSHOT_VERSION` 8 + an APU v4 tail, an opt-in Zapper beam-relative light model, and the eleven `sprintN.rs` mapper modules renamed to `mNNN_.rs`; two optimizations measured and REJECTED and documented as such; AccuracyCoin 141/141 — on top of **v2.2.2 "Conduit"** [2026-07-21, a build/distribution/CI-integrity patch — the libretro buildbot recipe taken from 1 of 10 jobs green to all ten building, a GitHub Actions supply-chain hardening pass, and the toolchain collapsed to one pinned source of truth with no `nightly` on any build path; zero emulation-core changes], itself on **v2.2.1** [2026-07-15, a housekeeping patch: dev-tooling archival, a zero-source-change dependency consolidation, and a gitignored FDS test-corpus addition], itself on **v2.2.0 "Capstone"** [2026-07-12], the milestone cut that closes the v2.1.5 → v2.2.0 "deepen the existing project" run — its two remaining marquees the netplay matchmaking / lobby stack and the FDS medium model, atop a peripherals + quality/security pass (Famicom `$4016`-bit-2 microphone + 3×3-aperture Zapper; cargo-fuzz targets 3 → 8 finding + fixing two `Movie::deserialize` OOM-DoS paths; a read-only Tools → ROM Info browser); every change additive or default-off, AccuracyCoin 141/141) on the v2.0.0 "Timebase" one-clock / every-cycle-bus-access scheduler rewrite + Vs. `DualSystem` dual-console support. The v2.0.x "Harbor" mobile-finalization train (v2.0.1→v2.0.9) and the entire v2.1.x "Fathom" line (v2.1.0→v2.1.10) plus the v2.2.0 "Capstone" milestone have all shipped — the run's steps being v2.1.5 "Vernier" (regression-net & residual) → v2.1.6 "Timbre" (expansion-audio fidelity) → v2.1.7 "Stepping" (opt-in PPU/2A03 die-revisions + power-on RAM/palette models; the DMA "unexpected read" frontier a documented no-op on every oracle, ADR 0033) → v2.1.8 "Tempo" (a default-OFF fast PPU dot path + SIMD blitter + wasm size pass) → v2.1.9 "Aperture" (a marquee CRT shader stack + raw NTSC composite signal-decode + GIF/WAV capture + palette editor) → v2.1.10 "Loom" (TAStudio greenzone + Lua API breadth + browser-RA auth-proxy deploy stack + Vs. `DualSystem` libretro presentation) → v2.2.0 "Capstone" (the milestone cut closing the run) → v2.2.1 (housekeeping) → **v2.2.2 "Conduit"** the build/distribution/CI-integrity patch (the prior release) — preceded by v1.10.0 "Arcade" the native Libretro / RetroArch core, the v1.9.0→v1.9.9 iOS TestFlight train, the v1.8.0→v1.8.9 "Android" train, and the desktop-feature lineage v1.1.0→v1.7.1, all on the v1.0.0 production core (see the top "Current release" block + `docs/STATUS.md`). **Never claim any version *later* than v2.2.4 is released** — in particular the joint mobile app-store launch (Google Play + Apple App Store + AltStore PAL + F-Droid) is the future **v2.3.0** (NOT v2.1.0 or v2.2.0 — the entire v2.1.x line and the v2.2.0 "Capstone" milestone have all already shipped, closing the "deepen the existing project" run; the store launch moved out to v2.3.0 — see `to-dos/ROADMAP.md`). Two distinct "v2.0"s exist and must not be conflated, **both now shipped, at different times, for different reasons**: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) — it was the *only* scheduler through v1.10.0. RustyNES's own **v2.0.0 "Timebase"** release (2026-07-03) is a *different* milestone that *replaces* that same dot-lockstep scheduler outright: the **one-clock + every-cycle-bus-access collapse** (a single canonical cycle counter + a split-around-the-access `start_cycle`/`end_cycle` PPU catch-up, mirroring Mesen2's structure), full Vs. `DualSystem` dual-console emulation (core-and-harness-only; frontend wiring deferred), and the breaking save-state / cross-version changes it entailed (ADR 0002 / ADR 0028 / ADR 0029) — the one release that broke byte-identity / save-state compatibility, by design. The R1/R2 hard-tier MMC3 IRQ-timing residual was investigated under a bounded-effort campaign and is by-design-deferred beyond v2.0.0, not closed — see ADR 0002's decision-update section for the mechanism-level finding.
+- When relabeling old engine "v2.x" narrative for users, present it as upstream lineage/history — **never as a current RustyNES release version.** The current release is **v2.2.5 "Colophon"** (2026-08-03, a provenance/licensing/documentation-integrity release — zero emulation-core behavior changes so AccuracyCoin holds 141/141 by construction; `NOTICE` rewritten for full attribution + GPL-oracle disclosure + GeraNES, in-source "port" comments reworded to the oracle framing, the CRT-shader/NTSC provenance reworded to independent reimplementations, `docs/originality-and-provenance.md` added, README AI-assistance disclosure), on top of **v2.2.4 "Cartridge"** (2026-07-24, a libretro/RetroArch distribution cut — zero emulation-core changes so AccuracyCoin holds 141/141 by construction; the libretro core is confirmed up-to-date with all recent changes and builds for the buildbot ABIs [`x86_64-pc-windows-gnu`, `aarch64-linux-android`], and `rustynes_libretro.info` is corrected: `disk_control` false→true [the FDS Disk Control interface was wired but advertised absent], `display_version` v1.0.0→v2.2.4, mapper count 168→172; core options remain a documented future enhancement; the Antigravity reviewer standardization rides along), on top of **v2.2.3 "Datum"** (2026-07-23, a performance and accuracy-closure patch — the fast PPU dot path promoted to default and exposed, PGO binaries shipped on the release path, a same-runner relative frame-time CI gate, the last two Holy Mapperel residuals closed [MMC1 WRAM write-protect + FME-7 open bus, all 17 ROMs now `detail=0000`], the Sunsoft 5B level calibrated with `Mapper::mix_audio` widened to i32, a save-state schema gap fixed at `PPU_SNAPSHOT_VERSION` 8 + an APU v4 tail, an opt-in Zapper beam-relative light model, and the eleven `sprintN.rs` mapper modules renamed to `mNNN_.rs`; two optimizations measured and REJECTED and documented as such; AccuracyCoin 141/141 — on top of **v2.2.2 "Conduit"** [2026-07-21, a build/distribution/CI-integrity patch — the libretro buildbot recipe taken from 1 of 10 jobs green to all ten building, a GitHub Actions supply-chain hardening pass, and the toolchain collapsed to one pinned source of truth with no `nightly` on any build path; zero emulation-core changes], itself on **v2.2.1** [2026-07-15, a housekeeping patch: dev-tooling archival, a zero-source-change dependency consolidation, and a gitignored FDS test-corpus addition], itself on **v2.2.0 "Capstone"** [2026-07-12], the milestone cut that closes the v2.1.5 → v2.2.0 "deepen the existing project" run — its two remaining marquees the netplay matchmaking / lobby stack and the FDS medium model, atop a peripherals + quality/security pass (Famicom `$4016`-bit-2 microphone + 3×3-aperture Zapper; cargo-fuzz targets 3 → 8 finding + fixing two `Movie::deserialize` OOM-DoS paths; a read-only Tools → ROM Info browser); every change additive or default-off, AccuracyCoin 141/141) on the v2.0.0 "Timebase" one-clock / every-cycle-bus-access scheduler rewrite + Vs. `DualSystem` dual-console support. The v2.0.x "Harbor" mobile-finalization train (v2.0.1→v2.0.9) and the entire v2.1.x "Fathom" line (v2.1.0→v2.1.10) plus the v2.2.0 "Capstone" milestone have all shipped — the run's steps being v2.1.5 "Vernier" (regression-net & residual) → v2.1.6 "Timbre" (expansion-audio fidelity) → v2.1.7 "Stepping" (opt-in PPU/2A03 die-revisions + power-on RAM/palette models; the DMA "unexpected read" frontier a documented no-op on every oracle, ADR 0033) → v2.1.8 "Tempo" (a default-OFF fast PPU dot path + SIMD blitter + wasm size pass) → v2.1.9 "Aperture" (a marquee CRT shader stack + raw NTSC composite signal-decode + GIF/WAV capture + palette editor) → v2.1.10 "Loom" (TAStudio greenzone + Lua API breadth + browser-RA auth-proxy deploy stack + Vs. `DualSystem` libretro presentation) → v2.2.0 "Capstone" (the milestone cut closing the run) → v2.2.1 (housekeeping) → **v2.2.2 "Conduit"** the build/distribution/CI-integrity patch (the prior release) — preceded by v1.10.0 "Arcade" the native Libretro / RetroArch core, the v1.9.0→v1.9.9 iOS TestFlight train, the v1.8.0→v1.8.9 "Android" train, and the desktop-feature lineage v1.1.0→v1.7.1, all on the v1.0.0 production core (see the top "Current release" block + `docs/STATUS.md`). **Never claim any version *later* than v2.2.5 is released** — in particular the joint mobile app-store launch (Google Play + Apple App Store + AltStore PAL + F-Droid) is the future **v2.3.0** (NOT v2.1.0 or v2.2.0 — the entire v2.1.x line and the v2.2.0 "Capstone" milestone have all already shipped, closing the "deepen the existing project" run; the store launch moved out to v2.3.0 — see `to-dos/ROADMAP.md`). Two distinct "v2.0"s exist and must not be conflated, **both now shipped, at different times, for different reasons**: the **engine-lineage v2.0** master-clock work shipped as the **v1.0.0** production core (2026-06-13) — it was the *only* scheduler through v1.10.0. RustyNES's own **v2.0.0 "Timebase"** release (2026-07-03) is a *different* milestone that *replaces* that same dot-lockstep scheduler outright: the **one-clock + every-cycle-bus-access collapse** (a single canonical cycle counter + a split-around-the-access `start_cycle`/`end_cycle` PPU catch-up, mirroring Mesen2's structure), full Vs. `DualSystem` dual-console emulation (core-and-harness-only; frontend wiring deferred), and the breaking save-state / cross-version changes it entailed (ADR 0002 / ADR 0028 / ADR 0029) — the one release that broke byte-identity / save-state compatibility, by design. The R1/R2 hard-tier MMC3 IRQ-timing residual was investigated under a bounded-effort campaign and is by-design-deferred beyond v2.0.0, not closed — see ADR 0002's decision-update section for the mechanism-level finding.
- **Forward plans + roadmap live in `to-dos/`.** `to-dos/ROADMAP.md` (updated in #129) is the planning entry point and frames the release line + "the path to v2.0.0 and beyond"; `to-dos/plans/` holds the per-release plan docs (through `v1.7.0-forge-plan.md` on `main`, plus the staged-forward `v1.8.0-android-plan.md` / `v1.9.0-ios-plan.md` / `v2.0.0-master-clock-plan.md`) + the `to-dos/plans/engine-lineage/` history archive + a `to-dos/plans/research/` reference-mining archive.
- The v1.0.0 release + GitHub Pages/CI + post-release record is in `docs/v1.0.0-synthesis-handoff-2026-06-13.md` — read it before touching CI, Pages, or release tooling. Full per-release history is in `CHANGELOG.md`.
- **Markdownlint is a CI gate** (pre-commit, pinned `markdownlint-cli v0.39.0`). The local `markdownlint` binary is a newer version that reports rules v0.39.0 lacks (e.g. MD060) — those are NOT gated; verify with `pre-commit run markdownlint --all-files`, not the bare binary. `.markdownlint.json` keeps `MD013`/`MD033`/`MD041` disabled by design (long technical tables, the README HTML banner/`
`, the HTML-led README). `.markdownlintignore` exempts `ref-docs/`, `ref-proj/`, the vendored `tricnes/` + upstream READMEs, and the frozen `docs/archive/` + `to-dos/archive/` trees — don't lint or reformat those.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c25205e..0a29037f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,62 @@ cycle-accurate core later replaced.
## [Unreleased]
+## [2.2.5] - 2026-08-03 - "Colophon" (provenance, licensing, and documentation integrity)
+
+A **provenance, licensing, and documentation-integrity** release, prompted by
+community review of the project's licensing and AI-assisted origins. **Zero
+emulation-core behavior changes**, so **AccuracyCoin holds 141/141 (100.00%)**, nestest is
+0-diff, and the `#![no_std]` chip stack, save-state / TAS / netplay formats, and
+every golden vector are byte-identical to v2.2.4 by construction.
+
+### Changed
+
+- **In-source "port" comments corrected.** A full-tree audit found comments that
+ described implementations of publicly-documented hardware behavior (the CPU
+ unstable-store opcodes, the PPU sprite-evaluation / OAM models, and numerous
+ mapper register decoders) as "ports of" copyleft emulators (Mesen2 — GPLv3;
+ puNES — GPLv2). Those behaviors are implemented from the NESdev wiki, published
+ datasheets, and the documented 6502 behavior, and were cross-checked against
+ reference emulators as *oracles*; the comments were reworded to say so. No
+ GPL-licensed emulator source is incorporated.
+- **CRT shaders & NTSC filters reworded.** `crt_royale` / `crt_guest` / `megatron`
+ and the Bisqwit / EMMIR NTSC filters were reviewed at source level and reframed
+ from "port / condensation of X" to independent single-pass reimplementations of
+ the *look and technique* (copyright protects code expression, not a visual look);
+ no upstream shader source is incorporated. The comment claiming tables were
+ "ported verbatim from Bisqwit's C" was corrected — those tables encode the
+ NESdev-documented NES composite signal.
+- **`blip.rs`** no longer mislabels `blip_buf` as BSD/MIT (it is LGPL-2.1+); the
+ file is an independent BLEP implementation and now says so.
+- **README** toned down and corrected: added an AI-assistance disclosure, removed
+ a comparison graphic with inaccurate details, fixed a mislabeled
+ ("sub-cycle accuracy") screenshot caption, and synced Acknowledgments with
+ `NOTICE`.
+
+### Added
+
+- **`NOTICE` rewritten** to disclose the behavioral-oracle use of GPL emulators
+ (Mesen2/MesenCE, higan, **GeraNES**, ares, FCEUX, Nestopia UE, puNES — no code
+ incorporated), attribute the incorporated permissive components (emu2413,
+ TriCNES, rcheevos — all MIT, with the MIT text), the bundled fonts (Font Awesome;
+ Press Start 2P / OFL) and test ROMs, and credit the CRT-shader / NTSC-filter
+ visual influences as independent reimplementations. GeraNES (GPL-3.0-only), cited
+ across ~58 files, was previously undisclosed.
+- **New `docs/originality-and-provenance.md`** — an honest account of where
+ RustyNES advances, diverges from, or independently re-derives NES emulation
+ technique, its development timeline, and its full license posture (including that
+ the project is heavily AI-assisted).
+- **Press Start 2P OFL text** added to the Android app assets (it shipped without
+ the required OFL text; desktop and iOS already carried it).
+
+### Fixed
+
+- **`tests/roms/LICENSES.md`** — a false exclusion claim (four Holy Mapperel mapper
+ ROMs stated as excluded were in fact committed), a stale crate path, and the
+ AccuracyCoin sub-test count; and added blanket coverage for the committed
+ directories not individually tabulated (328 committed `.nes` total, none
+ commercial).
+
## [2.2.4] - 2026-07-24 - "Cartridge" (libretro core builds/installs for RetroArch)
A **libretro / RetroArch distribution** cut. Its purpose is that the RustyNES
diff --git a/Cargo.lock b/Cargo.lock
index 922924d4..f08364f1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4290,7 +4290,7 @@ dependencies = [
[[package]]
name = "rustynes-android"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"android-activity",
"android_logger",
@@ -4308,7 +4308,7 @@ dependencies = [
[[package]]
name = "rustynes-apu"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"bitflags 2.13.1",
"criterion",
@@ -4321,7 +4321,7 @@ dependencies = [
[[package]]
name = "rustynes-cheevos"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"cc",
"ureq",
@@ -4329,7 +4329,7 @@ dependencies = [
[[package]]
name = "rustynes-core"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"bitflags 2.13.1",
"criterion",
@@ -4346,7 +4346,7 @@ dependencies = [
[[package]]
name = "rustynes-cpu"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"bitflags 2.13.1",
"criterion",
@@ -4357,7 +4357,7 @@ dependencies = [
[[package]]
name = "rustynes-frontend"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"anstyle",
"arboard",
@@ -4411,11 +4411,11 @@ dependencies = [
[[package]]
name = "rustynes-gfx-shaders"
-version = "2.2.4"
+version = "2.2.5"
[[package]]
name = "rustynes-hdpack"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"lewton",
"png",
@@ -4426,7 +4426,7 @@ dependencies = [
[[package]]
name = "rustynes-ios"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"bytemuck",
"cpal",
@@ -4440,7 +4440,7 @@ dependencies = [
[[package]]
name = "rustynes-libretro"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"libc",
"rust-libretro",
@@ -4449,7 +4449,7 @@ dependencies = [
[[package]]
name = "rustynes-mappers"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"bitflags 2.13.1",
"criterion",
@@ -4461,7 +4461,7 @@ dependencies = [
[[package]]
name = "rustynes-mobile"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"rustynes-core",
"rustynes-hdpack",
@@ -4476,14 +4476,14 @@ dependencies = [
[[package]]
name = "rustynes-monetization"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"uniffi",
]
[[package]]
name = "rustynes-netplay"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"futures-util",
"js-sys",
@@ -4499,7 +4499,7 @@ dependencies = [
[[package]]
name = "rustynes-ppu"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"bitflags 2.13.1",
"criterion",
@@ -4511,14 +4511,14 @@ dependencies = [
[[package]]
name = "rustynes-ra"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"rustynes-cheevos",
]
[[package]]
name = "rustynes-script"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"mlua",
"piccolo",
@@ -4529,7 +4529,7 @@ dependencies = [
[[package]]
name = "rustynes-test-harness"
-version = "2.2.4"
+version = "2.2.5"
dependencies = [
"insta",
"png",
diff --git a/Cargo.toml b/Cargo.toml
index d3d85516..6daaefe5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -32,7 +32,7 @@ members = [
default-members = ["crates/rustynes-libretro"]
[workspace.package]
-version = "2.2.4"
+version = "2.2.5"
edition = "2024"
rust-version = "1.96"
license = "MIT OR Apache-2.0"
diff --git a/NOTICE b/NOTICE
index 998741e8..d93fded2 100644
--- a/NOTICE
+++ b/NOTICE
@@ -15,8 +15,134 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+--------------------------------------------------------------------------------
+Hardware documentation
+--------------------------------------------------------------------------------
+
This project draws on the Nintendo Entertainment System hardware reverse-
engineering work documented at the NESdev community wiki
(https://www.nesdev.org/wiki/) and the Visual 6502 / Visual 2C02 projects
(http://www.visual6502.org/). No code from those projects is incorporated;
the documentation is referenced for hardware behavior specification.
+
+The chip, mapper, and peripheral behaviors implemented in RustyNES are written
+from that public hardware documentation (the NESdev wiki, Disch's mapper
+write-ups, Brad Taylor's 2C02 technical reference, published Xicor/Intersil I2C
+serial-EEPROM and Yamaha YM2413 datasheets, and the documented 6502 unofficial-
+opcode behavior) and pinned against public, permissively-licensed test ROMs
+(see tests/roms/LICENSES.md).
+
+--------------------------------------------------------------------------------
+Reference emulators (behavioral oracles only -- no code incorporated)
+--------------------------------------------------------------------------------
+
+During development, several existing NES emulators were consulted as behavioral
+oracles / accuracy references -- that is, to observe and cross-check documented
+hardware behavior when reconciling ambiguous test-ROM results. Among these are
+Mesen2 and MesenCE (GPLv3), higan (GPLv3), GeraNES (GPLv3), ares (ISC), and
+FCEUX and Nestopia UE and puNES (GPLv2). NO SOURCE
+CODE from any GPL-licensed emulator is incorporated into RustyNES; where an
+in-source comment references one of these projects it does so only to record
+that its runtime behavior was used as a cross-check for a behavior RustyNES
+implements independently from the hardware documentation above.
+
+--------------------------------------------------------------------------------
+Incorporated third-party components (permissively licensed)
+--------------------------------------------------------------------------------
+
+The following third-party works ARE incorporated (as a Rust port or as vendored
+source) under their permissive licenses. Their copyright notices and the MIT
+permission notice are reproduced below.
+
+* emu2413 v1.5.9 -- Yamaha YM2413 (OPLL) FM synthesizer, used for VRC7 audio.
+ Rust port in crates/rustynes-apu/src/opll.rs.
+ https://github.com/digital-sound-antiques/emu2413
+ Copyright (c) 2020 Mitsutaka Okazaki
+
+* TriCNES -- transistor-level NES emulator by the AccuracyCoin author; its
+ PPU address/data-multiplex (ALE / octal-latch), OAM-corruption, and per-cycle
+ DMA-dispatch models are ported into RustyNES (crates/rustynes-ppu,
+ rustynes-cpu, rustynes-core), and its full source is vendored as a golden
+ oracle at crates/rustynes-test-harness/golden/tricnes/tricnes-full-src/.
+ https://github.com/100thCoin/TriCNES (commit 9199870)
+ Copyright (c) 2025 Chris Siebert
+
+* rcheevos v12.3.0 -- RetroAchievements client runtime, vendored at
+ crates/rustynes-cheevos/vendor/rcheevos/ (compiled only under the optional
+ `retroachievements` feature).
+ https://github.com/RetroAchievements/rcheevos
+ Copyright (c) 2018 RetroAchievements.org
+
+The Font Awesome Free glyphs bundled with the frontend are covered by their own
+license at crates/rustynes-frontend/assets/fonts/LICENSE-FontAwesome.txt.
+
+All three components above (emu2413, TriCNES, rcheevos) are distributed under the
+MIT License:
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+
+--------------------------------------------------------------------------------
+Bundled fonts
+--------------------------------------------------------------------------------
+
+* Font Awesome Free -- Fonticons, Inc. -- see
+ crates/rustynes-frontend/assets/fonts/LICENSE-FontAwesome.txt.
+* Press Start 2P -- CodeMan38 -- SIL Open Font License 1.1. The OFL text ships
+ alongside each copy of the font: assets/RustyNES_Icon/OFL.txt (desktop),
+ ios/RustyNES/Fonts/PressStart2P-OFL.txt (iOS), and
+ android/app/src/main/assets/PressStart2P-OFL.txt (Android).
+
+--------------------------------------------------------------------------------
+Visual influences (independently reimplemented -- no code incorporated)
+--------------------------------------------------------------------------------
+
+RustyNES's optional CRT shader stack (crates/rustynes-gfx-shaders/) and NTSC
+filters (crates/rustynes-frontend/src/ntsc_bisqwit.rs, ntsc_lmp88959.rs) are
+independent, single-pass WGSL / Rust reimplementations of the *visual looks and
+techniques* of the projects below. No source code from any of them is
+incorporated: copyright protects code expression, not a visual look or a
+rendering technique, and these are from-scratch shaders on RustyNES's own
+uniform / pipeline conventions. They are credited here as the visual influences
+they reproduce:
+
+* CRT-Royale -- TroggleMonkey -- GPLv2+ (libretro) -- CRT phosphor/scanline look
+* crt-guest-advanced / guest-dr-venom -- guest.r -- GPL-family (libretro)
+* Sony Megatron Colour Video Monitor -- MajorPainInTheCactus -- GPL-family (libretro)
+* NTSC-CRT -- EMMIR (LMP88959) -- free use, credit appreciated --
+ https://github.com/LMP88959/NTSC-CRT
+* Bisqwit's NES composite-NTSC model -- the underlying two-level NES composite
+ signal is the hardware model documented at the NESdev wiki ("NTSC video"); the
+ RustyNES tables encode that documented model.
+
+--------------------------------------------------------------------------------
+Bundled test ROMs
+--------------------------------------------------------------------------------
+
+The ROMs committed under tests/roms/ are public-domain or permissively-licensed
+homebrew test programs, catalogued per-author with their individual licenses in
+tests/roms/LICENSES.md. No commercial Nintendo software is bundled. The
+permissive works whose licenses require their notices be preserved include:
+
+* AccuracyCoin -- Chris Siebert (100thCoin) -- MIT -- the upstream MIT LICENSE is
+ vendored at tests/roms/accuracycoin/LICENSE.
+* Holy Mapperel and other Damian Yerrick test ROMs -- zlib -- the upstream
+ notices are preserved with the ROMs (see tests/roms/LICENSES.md).
+
+blargg's and kevtris's suites are public domain. See tests/roms/LICENSES.md for
+the full per-ROM provenance.
diff --git a/README.md b/README.md
index e1e627e1..dfbb07f3 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-

+


@@ -24,6 +24,16 @@ scheduling at PPU-dot resolution on a master-clock-precise timebase — clearing
two newest upstream PPU tests, "ALE + Read" and "Hybrid Addresses", via the promoted
2-cycle-ALE fetch model — ADR 0030.)
+> **Development note — AI-assisted:** RustyNES is heavily AI-assisted software,
+> built with LLM tooling under a human-directed, test-driven workflow (public
+> test ROMs as the oracle, a `no_std` core, and continuous CI). See
+> [`docs/originality-and-provenance.md`](docs/originality-and-provenance.md) for
+> what that means for originality and licensing, and the
+> [Acknowledgments](#acknowledgments) for the references and components it builds
+> on. Accuracy claims are meant to be *checked* by running the public suites, not
+> taken on faith; comparisons to other emulators are comparisons, not a claim of
+> being "better."
+
Beyond reference accuracy, RustyNES is a complete, modern emulation platform:
**172 mapper families** covering the vast majority of the commercial library (plus a
UNIF `.unf` cartridge loader), the full **Famicom Disk System** (real-BIOS boot with a
@@ -676,17 +686,25 @@ Read" and "Hybrid Addresses", now pass on the shipped default.
> headline unit-test number. When a doc and a passing test ROM disagree, **the ROM
> wins** — that is the project's definition of "cycle-accurate."
-
-
-
+RustyNES's accuracy claims are meant to be *checked*, not taken on faith: run the
+public suites yourself (AccuracyCoin, nestest, blargg, Holy Mapperel — see
+[Compatibility & Accuracy](#compatibility-and-accuracy)). Any comparison to
+another emulator is exactly that — a comparison against a reference RustyNES was
+measured against (e.g. Mesen2 / higan / ares — see the [Acknowledgments](#acknowledgments)) —
+and is **not** a claim that RustyNES is "better." For an honest
+account of where the project advances, diverges from, or independently re-derives
+NES emulation technique (and its license posture), see
+[`docs/originality-and-provenance.md`](docs/originality-and-provenance.md).
-### Sub-cycle accuracy in action
+### Super Mario Bros. on RustyNES
-The screenshot below shows Super Mario Bros. at first light — correct background
-rendering, palette, and timing straight from the master-clock scheduler.
+The screenshot below is an early-milestone image — Super Mario Bros. at "first
+light," among the first commercial titles to render during development. It
+predates much of the current accuracy work and is kept as a representative
+gameplay shot, not a claim about any particular sub-system.
-
+
---
@@ -775,34 +793,46 @@ and the Material-for-MkDocs documentation handbook at
## Current Release
-RustyNES's current release is **v2.2.4 "Cartridge"**, a **libretro / RetroArch
-distribution** cut. Its purpose is that the RustyNES core builds and installs
-cleanly through the Libretro buildbot
-([git.libretro.com/libretro/RustyNES](https://git.libretro.com/libretro/RustyNES))
-so RetroArch users can pull it from the in-app core downloader.
-
-**Zero emulation-core changes**, so **AccuracyCoin holds 141/141 (100.00%)**,
-nestest is 0-diff, and the `#![no_std]` chip stack, save-state / TAS / netplay
-formats, and every golden vector are byte-identical to v2.2.3 by construction.
-`crates/rustynes-libretro` wraps `rustynes-core`, so it inherits every v2.2.3
-change automatically (the fast dot path default; the `PPU_SNAPSHOT_VERSION` 8 /
-APU v4 save-state schema, handled transparently because the serialize path sizes
-and emits the *current* snapshot via `Nes::snapshot_core_into`; the
-`Mapper::mix_audio` i32 widening; the Zapper model; the `mNNN_` mapper rename),
-and both buildbot cross-ABIs the CI gate models — `x86_64-pc-windows-gnu` and
-`aarch64-linux-android` — build clean.
-
-The concrete work is a **`rustynes_libretro.info` metadata correction** (the file
-RetroArch's core downloader reads): **`disk_control` `false` → `true`** — the
-real fix, since the FDS multi-side Disk Control interface has been wired since
-the buildbot recipe landed but was advertised as absent, hiding multi-disk FDS
-swapping from RetroArch's Quick Menu; plus `display_version` `v1.0.0` → `v2.2.4`
-and the mapper count `168` → `172`. Libretro **core options** (region / overscan
-/ palette / accuracy toggles) remain a documented future enhancement
-(`core_options = "false"` is accurate, not stale). The Antigravity PR reviewer
-standardization onto the shared template rides along.
-
-It follows **v2.2.3 "Datum"**, a performance and accuracy-closure patch: the
+RustyNES's current release is **v2.2.5 "Colophon"**, a **provenance, licensing,
+and documentation-integrity** release. It carries **zero emulation-core
+behavior changes** — so **AccuracyCoin holds 141/141 (100.00%)**, nestest is 0-diff, and
+the `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden
+vector are byte-identical to v2.2.4 by construction. Instead it corrects how the
+project *describes its own provenance*, prompted by community review:
+
+- A full-tree audit reworded in-source comments that had mischaracterized
+ implementations of publicly-documented hardware behavior as "ports of" copyleft
+ emulators (Mesen2, puNES). Those behaviors are implemented from the NESdev wiki,
+ datasheets, and the documented 6502 behavior, and cross-checked against
+ reference emulators as *oracles* — the comments now say exactly that.
+- `NOTICE` was rewritten to disclose that GPL-licensed emulators (Mesen2/MesenCE,
+ higan, GeraNES, ares, FCEUX, Nestopia UE, puNES) were used only as behavioral
+ oracles (no code incorporated); to attribute the genuinely incorporated
+ permissive components (emu2413, TriCNES, rcheevos — all MIT), the bundled fonts,
+ and the bundled test ROMs; and to credit the CRT-shader / NTSC-filter *visual
+ influences* (CRT-Royale, crt-guest-advanced, Sony Megatron, EMMIR's NTSC-CRT,
+ Bisqwit) as independent reimplementations.
+- The CRT shaders and NTSC filters were reviewed at source level and reworded from
+ "port / condensation of X" to independent single-pass reimplementations of the
+ *look and technique* — copyright protects code expression, not a visual look, and
+ no upstream shader source is incorporated.
+- A new [`docs/originality-and-provenance.md`](docs/originality-and-provenance.md)
+ gives an honest account of where RustyNES advances, diverges from, or
+ independently re-derives NES emulation technique, and its license posture —
+ including that the project is heavily AI-assisted.
+- The README (this file), `tests/roms/LICENSES.md`, and related docs were corrected
+ for accuracy: an AI-assistance disclosure was added, a comparison graphic with
+ inaccurate details was removed, and a mislabeled screenshot caption was fixed.
+
+It follows **v2.2.4 "Cartridge"**, a **libretro / RetroArch distribution** cut so
+RetroArch users can pull the core from the in-app downloader via the Libretro
+buildbot ([git.libretro.com/libretro/RustyNES](https://git.libretro.com/libretro/RustyNES));
+its concrete work was a `rustynes_libretro.info` metadata correction
+(**`disk_control` `false` → `true`**, exposing multi-disk FDS swapping in
+RetroArch's Quick Menu; `display_version` `v1.0.0` → `v2.2.4`; mapper count
+`168` → `172`), with zero emulation-core changes.
+
+Earlier, **v2.2.3 "Datum"**, a performance and accuracy-closure patch: the
specialized PPU fast dot path (**−11.3%** on rendering-heavy content,
differential-tested bit-identical since v2.1.8) promoted to the **default** and
exposed to users for the first time; PGO-optimized Linux release binaries when
@@ -959,6 +989,19 @@ RustyNES stands on the shoulders of giants:
- **[RetroAchievements](https://retroachievements.org/)** and the
**[`rcheevos`](https://github.com/RetroAchievements/rcheevos)** library that powers
the achievement integration.
+- **[emu2413](https://github.com/digital-sound-antiques/emu2413)** (Mitsutaka
+ Okazaki, MIT) — the YM2413 / OPLL model behind VRC7 audio — and
+ **[TriCNES](https://github.com/100thCoin/TriCNES)** (Chris Siebert, MIT), the
+ transistor-level emulator whose PPU / DMA models RustyNES ports and also uses as
+ a golden oracle. **GeraNES**, FCEUX, Nestopia UE, and puNES served as additional
+ behavioral oracles.
+- The community CRT shaders and NTSC filters whose *looks* RustyNES independently
+ reimplements — **CRT-Royale** (TroggleMonkey), **crt-guest-advanced** (guest.r),
+ **Sony Megatron** (MajorPainInTheCactus),
+ **[NTSC-CRT](https://github.com/LMP88959/NTSC-CRT)** (EMMIR), and **Bisqwit**'s
+ NES composite model — plus the **Press Start 2P** (OFL) and **Font Awesome**
+ fonts. Full attribution and the complete license posture are in
+ [`NOTICE`](NOTICE).
---
@@ -971,7 +1014,7 @@ If you use RustyNES in academic research, please cite:
author = {RustyNES Contributors},
title = {RustyNES: A Cycle-Accurate NES Emulator in Rust},
year = {2026},
- version = {2.2.0},
+ version = {2.2.5},
url = {https://github.com/doublegate/RustyNES},
note = {Cycle-accurate NES emulator on a master-clock-precise scheduler;
AccuracyCoin 100\% (141/141), nestest 0-diff; 172 mapper families,
diff --git a/android/app/src/main/assets/PressStart2P-OFL.txt b/android/app/src/main/assets/PressStart2P-OFL.txt
new file mode 100644
index 00000000..d836cf59
--- /dev/null
+++ b/android/app/src/main/assets/PressStart2P-OFL.txt
@@ -0,0 +1,93 @@
+Copyright 2012 The Press Start 2P Project Authors (cody@zone38.net), with Reserved Font Name "Press Start 2P".
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/crates/rustynes-apu/src/blip.rs b/crates/rustynes-apu/src/blip.rs
index 9a33ac80..448b563f 100644
--- a/crates/rustynes-apu/src/blip.rs
+++ b/crates/rustynes-apu/src/blip.rs
@@ -6,8 +6,11 @@
//! per-CPU-cycle mixer output and produces band-limited samples at the
//! host audio rate (default 44.1 kHz).
//!
-//! The technique is the same one used by Shay Green's `blip_buf` (BSD/MIT)
-//! and Mesen2's mixer:
+//! The technique is band-limited step (BLEP) synthesis — the same general
+//! approach popularized by Shay Green's `blip_buf` and used by many emulators.
+//! This is an independent implementation (our polyphase kernel in
+//! [`crate::blip_kernel`] uses a finer 32-phase resolution than `blip_buf`); no
+//! `blip_buf` code is incorporated:
//!
//! - Pre-compute a polyphase windowed-sinc kernel ([`crate::blip_kernel`])
//! keyed by `PHASES = 32` sub-output-sample fractional offsets, with
diff --git a/crates/rustynes-apu/src/opll.rs b/crates/rustynes-apu/src/opll.rs
index c7b8b220..9647e96e 100644
--- a/crates/rustynes-apu/src/opll.rs
+++ b/crates/rustynes-apu/src/opll.rs
@@ -14,16 +14,16 @@
//!
//! # Algorithmic reference
//!
-//! - `/home/parobek/Code/OSS_Public-Projects/RustyNES/ref-proj/Mesen2/Core/Shared/Utilities/emu2413.{h,cpp}`
-//! — the canonical C source (Mesen2 vendors it verbatim from upstream)
+//! - `emu2413 v1.5.9` (,
+//! MIT, Mitsutaka Okazaki) — the canonical upstream MIT C source
//! - nesdev wiki `VRC7_audio.md` — register surface + chip-level behaviour
//! - nesdev wiki `User_Ben_Boldt_YM2413_Patches.md` — patch ROM analysis
//!
//! # License posture
//!
-//! emu2413 is MIT-licensed at upstream; this Rust port is a clean-room
-//! reimplementation guided by the C source's algorithm. We preserve
-//! the upstream MIT notice in `NOTICE` at the repo root (see ADR-0005).
+//! emu2413 is MIT-licensed at upstream; this is a pure-Rust port of that C
+//! source, distributed under the same MIT license. We preserve the upstream
+//! MIT notice in `NOTICE` at the repo root (see ADR-0006).
//!
//! # Determinism
//!
diff --git a/crates/rustynes-cpu/src/cpu.rs b/crates/rustynes-cpu/src/cpu.rs
index 07565c69..9a792769 100644
--- a/crates/rustynes-cpu/src/cpu.rs
+++ b/crates/rustynes-cpu/src/cpu.rs
@@ -865,9 +865,16 @@ impl Cpu {
/// SH* unstable-store family helper (`SHA / SHX / SHY / SHS / TAS`,
/// opcodes `$9F / $93 / $9E / $9C / $9B`).
///
- /// Faithful port of Mesen2's `SyaSxaAxa` (`Core/NES/NesCpu.h` lines
- /// 716-745). Implements the canonical 6502-derivative
- /// unstable-store algorithm:
+ /// Implements the canonical 6502 unstable-store (SH*) algorithm as
+ /// documented by the `NESdev` community (the "unstable"/"highbyte" store
+ /// opcodes: `value AND (high-byte-of-address + 1)`, with the RDY/DMA
+ /// quirk) and pinned bit-for-bit by `AccuracyCoin`'s "Unofficial
+ /// Instructions: SH*" sub-test. This is an independent Rust
+ /// implementation of that documented behavior — the DMC-DMA
+ /// interruption detection below uses the emulator's own bus cycle-count
+ /// machinery. (Behavior cross-checked against reference emulators as
+ /// accuracy oracles; no third-party emulator code is incorporated.)
+ /// The algorithm:
///
/// 1. Compute the page-crossed flag against `base + index_reg`.
/// 2. Perform a dummy read at the **unfixed** address
diff --git a/crates/rustynes-frontend/src/debugger/source_map.rs b/crates/rustynes-frontend/src/debugger/source_map.rs
index 07c42e6d..e9790c38 100644
--- a/crates/rustynes-frontend/src/debugger/source_map.rs
+++ b/crates/rustynes-frontend/src/debugger/source_map.rs
@@ -22,7 +22,8 @@
//! For every `line` record we resolve each referenced span to its CPU address
//! range and record `address → (file, line)` for every byte in range. Lines
//! with no spans (e.g. macro / comment lines) carry no address and are skipped.
-//! This mirrors Mesen2's `DbgImporter`/`NesDbgImporter`.
+//! This is an independent importer for the same ca65/cc65 `.dbg` debug-info file
+//! format that other emulators' symbol importers also read.
//!
//! ## Output-only
//!
diff --git a/crates/rustynes-frontend/src/ntsc_bisqwit.rs b/crates/rustynes-frontend/src/ntsc_bisqwit.rs
index ba13eabb..1cc058a4 100644
--- a/crates/rustynes-frontend/src/ntsc_bisqwit.rs
+++ b/crates/rustynes-frontend/src/ntsc_bisqwit.rs
@@ -1,8 +1,8 @@
#![allow(
clippy::too_many_arguments,
clippy::doc_markdown,
- // Numeric tables are ported verbatim from Bisqwit's C; the integer casts
- // are intentional truncation (matching the `(int)` / `(int8_t)` casts).
+ // Numeric tables encode the documented NES composite-signal levels; the
+ // integer casts are intentional truncation (matching the fixed-point model).
clippy::cast_possible_truncation,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
@@ -12,9 +12,11 @@
//! True composite NES_NTSC filter — Bisqwit's algorithm on the GPU (T-110-A1,
//! stage 2/2).
//!
-//! Unlike the simplified [`crate::ntsc`] blur, this is a faithful port of
-//! Bisqwit's `nes_ntsc`-style composite model (as implemented by Mesen2's
-//! `BisqwitNtscFilter`): it reconstructs the analog luma+chroma **signal** from
+//! Unlike the simplified [`crate::ntsc`] blur, this is an independent
+//! implementation of the Bisqwit-style NES composite model — the two-level NES
+//! composite signal documented at the NESdev wiki ("NTSC video") page
+//! (cross-checked against reference emulators as oracles; no code incorporated):
+//! it reconstructs the analog luma+chroma **signal** from
//! the PPU's per-pixel palette index, then demodulates it back to RGB with a
//! windowed Y/I/Q filter. The genuine NTSC artifacts (chroma dot-crawl, colour
//! fringing on vertical edges, the diagonal "checkerboard" on saturated hues)
@@ -203,7 +205,7 @@ var SIGNAL_HIGH: array = array({signal_high});
var SINE: array = array({sine});
var EMPHASIS: array = array({emphasis});
-// Base YIQ matrix scalars (Bisqwit / Mesen). The live contrast / saturation
+// Base YIQ matrix scalars (the standard FCC NTSC YIQ->RGB matrix). The live contrast / saturation
// knobs scale these per frame; at knob = 0 the integer matrix below equals the
// old baked Y/IR/QR/... constants exactly (verified in f32).
const CONTRAST_BASE: f32 = 167941.0;
diff --git a/crates/rustynes-frontend/src/ntsc_lmp88959.rs b/crates/rustynes-frontend/src/ntsc_lmp88959.rs
index 88bf0f91..64052cb3 100644
--- a/crates/rustynes-frontend/src/ntsc_lmp88959.rs
+++ b/crates/rustynes-frontend/src/ntsc_lmp88959.rs
@@ -2,9 +2,11 @@
//! LMP88959-style composite NTSC/PAL filter — wgsl post-pass (v1.6.0 "Studio" I1).
//!
-//! A self-contained composite-NTSC look modelled on EMMIR's well-known
-//! `NTSC-CRT` / `LMP88959` algorithm (a single-pass encode-then-decode of the
-//! RGB image through a simulated composite signal). Unlike the Bisqwit
+//! A self-contained composite-NTSC look — an independent implementation modelled
+//! on the technique of EMMIR's (LMP88959) well-known `NTSC-CRT` project
+//! (; free use, credit appreciated; no
+//! code incorporated) — a single-pass encode-then-decode of the RGB image
+//! through a simulated composite signal. Unlike the Bisqwit
//! [`crate::ntsc_bisqwit`] filter — which consumes the `R16Uint` palette-index
//! texture and must be the *first* pass — this one is a pure **RGBA post-pass**:
//! it samples the already-rendered framebuffer, so it composes anywhere in the
diff --git a/crates/rustynes-gfx-shaders/src/bisqwit.wgsl b/crates/rustynes-gfx-shaders/src/bisqwit.wgsl
index b3180976..d33620f8 100644
--- a/crates/rustynes-gfx-shaders/src/bisqwit.wgsl
+++ b/crates/rustynes-gfx-shaders/src/bisqwit.wgsl
@@ -17,7 +17,7 @@ var SIGNAL_HIGH: array = array(38, 38, 38, 38, 38,
var SINE: array = array(0, 3, 6, 8, 6, 3, 0, -3, -6, -8, -6, -4, 0, 4, 6, 8, 6, 3, 0, -3, -6, -8, -6, -4, 0, 4, 6);
var EMPHASIS: array = array(0, 63, 1008, 1023, 3843, 3903, 4083, 4095);
-// Base YIQ matrix scalars (Bisqwit / Mesen). The live contrast / saturation
+// Base YIQ matrix scalars (the standard FCC NTSC YIQ->RGB matrix). The live contrast / saturation
// knobs scale these per frame; at knob = 0 the integer matrix below equals the
// old baked Y/IR/QR/... constants exactly (verified in f32).
const CONTRAST_BASE: f32 = 167941.0;
diff --git a/crates/rustynes-gfx-shaders/src/crt_guest.wgsl b/crates/rustynes-gfx-shaders/src/crt_guest.wgsl
index e1a447a0..a7e9ae7f 100644
--- a/crates/rustynes-gfx-shaders/src/crt_guest.wgsl
+++ b/crates/rustynes-gfx-shaders/src/crt_guest.wgsl
@@ -1,9 +1,11 @@
-// crt-guest-advanced / guest-dr-venom (single-pass WGSL port) — v2.1.9 (B6).
+// crt-guest-advanced-style look (single-pass WGSL) — v2.1.9 (B6).
//
-// A single-pass condensation of guest.r's crt-guest-advanced / guest-dr-venom
-// libretro slang shaders. Those are a large multi-pass stack (linearize, two
-// blur passes for glow + halation, the scanline/mask pass, an AfterGlow pass);
-// this port keeps guest's characteristic look in one fragment shader:
+// An independent single-pass reimplementation of the *look* popularized by
+// guest.r's crt-guest-advanced / guest-dr-venom libretro shaders (GPL-family).
+// No upstream shader source is used or incorporated: those are a large
+// multi-pass stack (linearize, two blur passes for glow + halation, the
+// scanline/mask pass, an AfterGlow pass); this is a from-scratch single
+// fragment shader implementing comparable, widely-documented techniques:
//
// * A sharp horizontal beam profile (guest's "beam shape" — a configurable
// scan-width with a controllable inner/outer falloff) rather than the pure
diff --git a/crates/rustynes-gfx-shaders/src/crt_royale.wgsl b/crates/rustynes-gfx-shaders/src/crt_royale.wgsl
index 6126a18c..345cb480 100644
--- a/crates/rustynes-gfx-shaders/src/crt_royale.wgsl
+++ b/crates/rustynes-gfx-shaders/src/crt_royale.wgsl
@@ -1,11 +1,12 @@
-// CRT-Royale (single-pass WGSL port) — v2.1.9 "Presentation & Signal" (B6).
+// CRT-Royale-style look (single-pass WGSL) — v2.1.9 "Presentation & Signal" (B6).
//
-// A faithful *single-pass* condensation of TroggleMonkey's libretro CRT-Royale
-// slang preset. The reference is a multi-pass pipeline (bloom/blur passes +
-// scanline + phosphor mask + halation + geometry); this port folds its core
-// perceptual model into one fullscreen fragment shader so it slots into the
-// existing RustyNES post-pass pipeline (same rect/crop letterbox convention as
-// CRT_WGSL). It keeps CRT-Royale's defining pieces:
+// An independent single-pass reimplementation of the *perceptual model* made
+// famous by TroggleMonkey's libretro CRT-Royale (GPLv2+). No CRT-Royale source
+// is used or incorporated: that reference is a large multi-pass pipeline
+// (bloom/blur passes + scanline + phosphor mask + halation + geometry), whereas
+// this is a from-scratch single fullscreen fragment shader on RustyNES's own
+// rect/crop uniform convention (same as CRT_WGSL), implementing the standard,
+// widely-documented CRT-simulation techniques that produce a comparable look:
//
// * Gaussian scanline beam in gamma-linear space (per-source-row beam with a
// configurable standard deviation, so bright rows bloom wider than dark
diff --git a/crates/rustynes-gfx-shaders/src/crt_stack.rs b/crates/rustynes-gfx-shaders/src/crt_stack.rs
index d089c49b..6b9d5bb7 100644
--- a/crates/rustynes-gfx-shaders/src/crt_stack.rs
+++ b/crates/rustynes-gfx-shaders/src/crt_stack.rs
@@ -14,20 +14,21 @@
//! These are opt-in: the shipped default presentation (plain blit / the existing
//! CRT) is unchanged, so the default framebuffer stays byte-identical.
-/// CRT-Royale — single-pass WGSL port.
+/// CRT-Royale-style look — independent single-pass WGSL reimplementation.
///
/// Gaussian luminance-scaled beam, selectable phosphor mask, gamma-correct
/// scanlines, barrel curvature. See the file header for the model and the
/// shared CRT-stack uniform layout.
pub const CRT_ROYALE_WGSL: &str = include_str!("crt_royale.wgsl");
-/// crt-guest-advanced / guest-dr-venom — single-pass WGSL port (power-shaped
-/// beam, halation glow, selectable mask, curvature).
+/// crt-guest-advanced-style look — independent single-pass WGSL reimplementation
+/// (power-shaped beam, halation glow, selectable mask, curvature).
pub const CRT_GUEST_WGSL: &str = include_str!("crt_guest.wgsl");
-/// Sony Megatron — single-pass WGSL port (per-subpixel phosphor lighting,
-/// selectable mask, gamma-correct beam, an HDR headroom hook with SDR Reinhard
-/// tone-map fallback).
+/// Sony-Megatron-style look — independent single-pass WGSL reimplementation.
+///
+/// Per-subpixel phosphor lighting, selectable mask, gamma-correct beam, an HDR
+/// headroom hook with SDR Reinhard tone-map fallback.
pub const MEGATRON_WGSL: &str = include_str!("megatron.wgsl");
/// Raw NTSC signal-decode pass (P4).
diff --git a/crates/rustynes-gfx-shaders/src/lib.rs b/crates/rustynes-gfx-shaders/src/lib.rs
index 2ff64fb1..84c3f451 100644
--- a/crates/rustynes-gfx-shaders/src/lib.rs
+++ b/crates/rustynes-gfx-shaders/src/lib.rs
@@ -245,8 +245,10 @@ fn fs_main(in: VsOut) -> @location(0) vec4 {
}
";
-/// The Bisqwit composite NES NTSC post-pass (a faithful port of Bisqwit's
-/// `nes_ntsc` signal model).
+/// The Bisqwit-style composite NES NTSC post-pass.
+///
+/// An independent implementation of the NES composite signal model documented at
+/// the NESdev wiki ("NTSC video"); no third-party emulator code is incorporated.
///
/// Unlike CRT/LMP it samples the **palette-index** framebuffer as an `R16Uint`
/// texture (`@group(0) @binding(0) idx_tex`), not the RGBA, plus the per-frame NTSC
diff --git a/crates/rustynes-gfx-shaders/src/megatron.wgsl b/crates/rustynes-gfx-shaders/src/megatron.wgsl
index 0e9935cd..6fd917cb 100644
--- a/crates/rustynes-gfx-shaders/src/megatron.wgsl
+++ b/crates/rustynes-gfx-shaders/src/megatron.wgsl
@@ -1,11 +1,13 @@
-// Sony Megatron (single-pass WGSL port) — v2.1.9 "Presentation & Signal" (B6).
+// Sony-Megatron-style look (single-pass WGSL) — v2.1.9 "Presentation & Signal" (B6).
//
-// A port of MajorPainInTheCactus's "Sony Megatron Colour Video Monitor" slang
-// shader. Megatron's defining idea is a physically-scaled phosphor subpixel
+// An independent reimplementation of the *approach* of MajorPainInTheCactus's
+// "Sony Megatron Colour Video Monitor" libretro shader (GPL-family); no upstream
+// shader source is used or incorporated. Megatron's defining idea is a
+// physically-scaled phosphor subpixel
// model driven for HDR displays: it lights individual R/G/B phosphors within a
// selectable mask and scales brightness to an absolute nits target so an HDR
// swapchain reproduces CRT peak brightness. WGSL/wgpu here targets an SDR
-// swapchain by default, so this port keeps Megatron's *structure* — per-subpixel
+// swapchain by default, so this reimplementation keeps Megatron's *structure* — per-subpixel
// phosphor lighting, mask selection, gamma-correct scanline beam, and an
// exposed peak/paper-white ratio — but tone-maps the result back into [0,1]
// with a Reinhard curve (`aux.w` controls the HDR headroom the tone-map
diff --git a/crates/rustynes-libretro/rustynes_libretro.info b/crates/rustynes-libretro/rustynes_libretro.info
index 58bc8f5b..a31bc986 100644
--- a/crates/rustynes-libretro/rustynes_libretro.info
+++ b/crates/rustynes-libretro/rustynes_libretro.info
@@ -5,7 +5,7 @@ supported_extensions = "nes|fds"
corename = "RustyNES"
license = "MIT OR Apache-2.0"
permissions = ""
-display_version = "v2.2.4"
+display_version = "v2.2.5"
categories = "Emulator"
# Hardware Information
diff --git a/crates/rustynes-mappers/src/fds.rs b/crates/rustynes-mappers/src/fds.rs
index 2f2419a0..2671c72a 100644
--- a/crates/rustynes-mappers/src/fds.rs
+++ b/crates/rustynes-mappers/src/fds.rs
@@ -196,7 +196,9 @@ pub const HEAD_SEEK_BYTES_PER_CYCLE: u32 = 8;
/// long so the BIOS re-read loop always observes the not-ready -> ready edge.
pub const HEAD_SEEK_SETTLE_CYCLES: u32 = 512;
-/// Per-game FDS timing quirk, modelled on `puNES` `fds.c`'s per-CRC drive table.
+/// Per-game FDS timing quirk: a per-CRC drive-timing table (the concept
+/// cross-checked against `puNES` as an oracle; no third-party emulator code is
+/// incorporated).
///
/// A small, additive set of knobs keyed off the disk-image CRC-32 (see
/// [`quirk_for_crc`]). Most titles run on the nominal timing and have no entry;
@@ -238,7 +240,7 @@ pub fn quirk_for_crc(crc: u32) -> FdsQuirk {
// position model (the [`HEAD_RESEEK_CYCLES`] re-seek window opened on every
// motor-restart rewind) is what actually closes the Kid Icarus side-B
// post-registration replay — that fix is title-independent and needs no
- // table entry. This table is the puNES-`fds.c`-style *framework* for the
+ // table entry. This table is a per-CRC *framework* (of the kind puNES uses) for the
// residual minority of titles whose replay loop wants extra not-ready slack
// beyond the nominal window.
//
diff --git a/crates/rustynes-mappers/src/kaiser.rs b/crates/rustynes-mappers/src/kaiser.rs
index e43e481b..2d35169d 100644
--- a/crates/rustynes-mappers/src/kaiser.rs
+++ b/crates/rustynes-mappers/src/kaiser.rs
@@ -606,7 +606,9 @@ kaiser_ctor!(
// Per-1 KiB CHR low/high registers ($B000-$E00C), a CHR-RAM escape (CHR reg
// value 4/5 + a force-ROM toggle on slot 0 via $88/$C8), two 8 KiB PRG selects
// ($8010/$A010), $9400 mirroring, and a /114-scaled CPU-cycle IRQ ($F000 etc.).
-// Ported from Mesen2 Waixing/Mapper253.h.
+// Register map per the NESdev wiki mapper-253 documentation (cross-checked
+// against reference emulators as accuracy oracles; no third-party emulator
+// code is incorporated).
// ===========================================================================
#[cfg(test)]
diff --git a/crates/rustynes-mappers/src/lib.rs b/crates/rustynes-mappers/src/lib.rs
index 4936a20c..28436656 100644
--- a/crates/rustynes-mappers/src/lib.rs
+++ b/crates/rustynes-mappers/src/lib.rs
@@ -1164,8 +1164,9 @@ pub fn parse(bytes: &[u8]) -> Result<(Cartridge, Box), RomError> {
// --- v1.6.0 "Studio" Workstream E, best-effort (Tier-2): J.Y. Company
// ASIC. One silicon implementation behind three iNES mapper numbers;
// 90 inhibits the ROM-nametable / extended-mirroring feature, 209
- // register-enables it, 211 forces it on. Ported from the nesdev
- // "J.Y. Company ASIC" page + Mesen2 `JyCompany`. Register-decode +
+ // register-enables it, 211 forces it on. Implemented from the nesdev
+ // "J.Y. Company ASIC" page (cross-checked against Mesen2 as an oracle).
+ // Register-decode +
// save-state unit-tested only, NOT accuracy-gated (`tier.rs`).
90 => Box::new(
JyAsic::new(prg_rom, chr_rom, h.mirroring, JyBoard::M90)
diff --git a/crates/rustynes-mappers/src/m016_bandai_fcg.rs b/crates/rustynes-mappers/src/m016_bandai_fcg.rs
index 1150c5f2..888dc3af 100644
--- a/crates/rustynes-mappers/src/m016_bandai_fcg.rs
+++ b/crates/rustynes-mappers/src/m016_bandai_fcg.rs
@@ -36,8 +36,10 @@
//! # EEPROM
//!
//! An I²C state machine ([`Eeprom`]) for the X24C01 (159) / 24C02 (16) is
-//! implemented below — a faithful port of the Mesen2 `Eeprom24C01` /
-//! `Eeprom24C02` models. It clocks bits on the SCL **rising** edge and
+//! implemented below — an independent state machine for the Xicor/Intersil
+//! X24C01 (mapper 159) / 24C02 (mapper 16) serial EEPROMs, written from the
+//! published I2C serial-EEPROM datasheet protocol. It clocks bits on the SCL
+//! **rising** edge and
//! advances the mode/ACK handshake on the **falling** edge, detects
//! START/STOP as SDA transitions while SCL is held high, and honors the two
//! chips' differing bit order (X24C01 LSB-first, 24C02 MSB-first) and
@@ -114,8 +116,9 @@ impl FcgVariant {
/// Serial I²C EEPROM (X24C01 / 24C02) state machine.
///
-/// Faithful port of the Mesen2 `Eeprom24C01` / `Eeprom24C02` models
-/// (`ref-proj/Mesen2/Core/NES/Mappers/Bandai/`). The protocol is driven on
+/// An independent I2C state machine for the X24C01 / 24C02 serial EEPROMs,
+/// implemented from the published I2C serial-EEPROM datasheet protocol. The
+/// protocol is driven on
/// **both** SCL edges: bits are clocked on the rising edge, and the
/// mode/ACK handshake advances on the falling edge — exactly how the boards
/// drive the line. START / STOP are detected as SDA transitions while SCL is
diff --git a/crates/rustynes-mappers/src/m035_jy_asic.rs b/crates/rustynes-mappers/src/m035_jy_asic.rs
index 54b8c66d..d98f97ac 100644
--- a/crates/rustynes-mappers/src/m035_jy_asic.rs
+++ b/crates/rustynes-mappers/src/m035_jy_asic.rs
@@ -299,15 +299,16 @@ impl JyAsic {
/// Apply the PRG bank-number reversal used by PRG mode 3 (`$D000` bits
/// 0-1 == 3).
///
- /// The wiki describes this as "bank numbers bits 0-6 reversed". This is a
- /// verbatim port of Mesen2's `InvertPrgBits`, which reverses the three
- /// outer bit pairs (0<->6, 1<->5, 2<->4) and notably does **not** carry
- /// bit 3 through: a faithful "reverse a 7-bit field" would leave the
- /// centre bit (3) in place, but neither Mesen2 nor Disch's original
- /// writeup preserves it, so this port drops it to match the accuracy
- /// reference bit-for-bit (no known game distinguishes the two; the JY
- /// ASIC is BestEffort tier). If a future test ROM proves bit 3 must be
- /// preserved, OR `reg & 0x08` back into the result here.
+ /// The NESdev wiki / Disch's JY-ASIC writeup describe this as "bank
+ /// numbers bits 0-6 reversed". Following that documentation we reverse the
+ /// three outer bit pairs (0<->6, 1<->5, 2<->4) and notably do **not** carry
+ /// bit 3 through: a literal "reverse a 7-bit field" would leave the centre
+ /// bit (3) in place, but Disch's writeup does not preserve it, so we drop it
+ /// to match the documented hardware bit-for-bit (no known game distinguishes
+ /// the two; the JY ASIC is BestEffort tier). If a future test ROM proves bit
+ /// 3 must be preserved, OR `reg & 0x08` back into the result here. (Behavior
+ /// cross-checked against reference emulators as accuracy oracles; no
+ /// third-party emulator code is incorporated.)
const fn invert_prg_bits(reg: u8, invert: bool) -> u8 {
if invert {
(reg & 0x01) << 6
diff --git a/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs b/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs
index 5063ef10..84058c4b 100644
--- a/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs
+++ b/crates/rustynes-mappers/src/m069_sunsoft_fme7.rs
@@ -112,10 +112,11 @@ const SUNSOFT5B_DC_BIAS: i32 = 0;
/// lets the shape stay pinned by its own unit test while the level is pinned
/// by a ROM oracle.
///
-/// **Target, derived from Mesen2 (the project's accuracy bar) rather than from
-/// our own prior numbers.** In `NesSoundMixer::GetOutputVolume` a full-volume
+/// **Target, calibrated against Mesen2 (the project's accuracy bar) as an
+/// oracle rather than against our own prior numbers.** Using the standard
+/// blargg nonlinear-mixer approximation (nesdev "APU Mixer"), a full-volume
/// 2A03 square is `(95.88 * 5000) / (8128/15 + 100) = 746.9` units, and the 5B
-/// is summed with weight `* 15` over `Sunsoft5bAudio::_volumeLut`
+/// is summed with weight `* 15` over the documented 5B log-DAC volume table
/// (`= (uint8_t)1.1885^(2i)`, so `LUT[12] = 63`, `LUT[15] = 177`). The
/// `db_5b` ROM compares a **volume-12** 5B square against that square:
///
diff --git a/crates/rustynes-mappers/src/m176_bmc_fk23c.rs b/crates/rustynes-mappers/src/m176_bmc_fk23c.rs
index ef1e0ac9..f2bf5edf 100644
--- a/crates/rustynes-mappers/src/m176_bmc_fk23c.rs
+++ b/crates/rustynes-mappers/src/m176_bmc_fk23c.rs
@@ -95,7 +95,9 @@ fn check_prg(prg: &[u8], id: u16) -> Result<(), MapperError> {
// an outer-bank / extended-MMC3 / CNROM-CHR mode. This is the
// register-decode-faithful BestEffort port: the MMC3 PRG/CHR layout plus the
// FK23C $5000 banking modes (0-2 MMC3, 3 = 32 KiB, 4 = whole-256 KiB) and the
-// $5001/$5002 outer PRG/CHR base bits. Ported from Mesen2 Waixing/Fk23C.h.
+// $5001/$5002 outer PRG/CHR base bits. Register map per the NESdev wiki FK23C /
+// mapper-176 documentation (cross-checked against reference emulators as
+// accuracy oracles; no third-party emulator code is incorporated).
// ===========================================================================
/// Waixing FK23C 8/16 Mbit BMC ASIC (mapper 176).
@@ -541,11 +543,12 @@ pub fn new_m176(
// ===========================================================================
// Coolboy (mapper 268) — COOLBOY / MINDKIDS MMC3-clone.
//
-// An MMC3 core wrapped by four $6000-$7FFF outer-bank registers (_exRegs[0..3])
-// that supply PRG/CHR base bits + a wider/narrower mask + an extended-bank mode
-// (_exRegs[3] & 0x10). This is the register-decode-faithful BestEffort port of
-// the FCEUX/Mesen2 banking transforms. Ported from
-// Mesen2 Mmc3Variants/MMC3_Coolboy.h.
+// An MMC3 core wrapped by four $6000-$7FFF outer-bank registers that supply
+// PRG/CHR base bits + a wider/narrower mask + an extended-bank mode. The
+// COOLBOY/MINDKIDS banking transforms are a register-decode BestEffort model
+// implemented from the nesdev wiki COOLBOY / mapper-268 board notes
+// (cross-checked against FCEUX/Mesen2 as behavioral oracles; no third-party
+// emulator code is incorporated).
// ===========================================================================
#[cfg(test)]
diff --git a/crates/rustynes-mappers/src/m268_bmc_coolboy.rs b/crates/rustynes-mappers/src/m268_bmc_coolboy.rs
index 7f89899c..a5244f0b 100644
--- a/crates/rustynes-mappers/src/m268_bmc_coolboy.rs
+++ b/crates/rustynes-mappers/src/m268_bmc_coolboy.rs
@@ -447,7 +447,9 @@ pub fn new_m268(
//
// A plain MMC3 core with a PRG-A19/A20 outer bank from the high two bits of
// $8001 (captured when the selected register is < 6), forced into the top of
-// the address space. CHR is RAM. Ported from Mesen2 Sachen/Sachen9602.h.
+// the address space. CHR is RAM. Register map per the NESdev wiki CoolBoy /
+// mapper-268 documentation (cross-checked against reference emulators as
+// accuracy oracles; no third-party emulator code is incorporated).
// ===========================================================================
#[cfg(test)]
diff --git a/crates/rustynes-mappers/src/m513_sachen_9602.rs b/crates/rustynes-mappers/src/m513_sachen_9602.rs
index cbfdc193..af97b171 100644
--- a/crates/rustynes-mappers/src/m513_sachen_9602.rs
+++ b/crates/rustynes-mappers/src/m513_sachen_9602.rs
@@ -340,7 +340,9 @@ pub fn new_m513(
// ===========================================================================
// TxcChip — the TXC protection accumulator (shared by Sachen 3011 / m136).
-// Ported from Mesen2 Txc/TxcChip.h (the non-JV001 variant, mask 0x07).
+// The non-JV001 variant (mask 0x07), per the NESdev wiki TXC / mapper-136
+// documentation (cross-checked against reference emulators as accuracy oracles;
+// no third-party emulator code is incorporated).
// ===========================================================================
#[cfg(test)]
diff --git a/crates/rustynes-mappers/src/mmc3_clones.rs b/crates/rustynes-mappers/src/mmc3_clones.rs
index dbcf3604..2e138d41 100644
--- a/crates/rustynes-mappers/src/mmc3_clones.rs
+++ b/crates/rustynes-mappers/src/mmc3_clones.rs
@@ -776,7 +776,9 @@ clone_ctor!(
// 8259C: shift 2, chrOr [1,2,3] (mapper 139)
// reg7 bits 1-2 select mirroring (reg7 bit 0 = "simple mode" override).
// reg5 selects the 32 KiB PRG bank; reg4 supplies the CHR high bits.
-// Ported from Mesen2 Sachen/Sachen8259.h.
+// Register map per the NESdev wiki Sachen 8259 (mappers 138/139/141)
+// documentation (cross-checked against reference emulators as accuracy oracles;
+// no third-party emulator code is incorporated).
// ===========================================================================
#[cfg(test)]
diff --git a/crates/rustynes-mappers/src/multicart_discrete.rs b/crates/rustynes-mappers/src/multicart_discrete.rs
index 6f2658ba..fe11a095 100644
--- a/crates/rustynes-mappers/src/multicart_discrete.rs
+++ b/crates/rustynes-mappers/src/multicart_discrete.rs
@@ -3797,7 +3797,9 @@ pub fn new_m204(
// `mode & 0x02` the board is in UNROM-style mode (a switchable $8000 + a fixed
// $C000), with a NROM-256 sub-case when `mode & 0x0100`; otherwise both 16 KiB
// windows mirror the same NROM bank. `mode & 0x01` flips the mirroring. CHR is a
-// single fixed 8 KiB window. Ported from Mesen2 Ntdec/Mapper221.h.
+// single fixed 8 KiB window. Register map per the NESdev wiki mapper-299 /
+// BMC-11160 documentation (cross-checked against reference emulators as
+// accuracy oracles; no third-party emulator code is incorporated).
// ===========================================================================
/// TXC/BMC-11160 multicart (mapper 299).
diff --git a/crates/rustynes-mappers/src/ntdec.rs b/crates/rustynes-mappers/src/ntdec.rs
index f97935b1..514341bd 100644
--- a/crates/rustynes-mappers/src/ntdec.rs
+++ b/crates/rustynes-mappers/src/ntdec.rs
@@ -861,8 +861,10 @@ fn chr_or_ram(chr_rom: Box<[u8]>) -> (Box<[u8]>, bool) {
// to the final three banks; $8000 is the one switchable window (register 3).
// CHR: 2 KiB pages. Register 0 selects a paired 2 KiB window into the first two
// slots ($0000 + $0800), register 1 the third ($1000), register 2 the fourth
-// ($1800). Registers live at $6000-$7FFF (addr & 3). Ported from Mesen2
-// Ntdec/NtdecTc112.h.
+// ($1800). Registers live at $6000-$7FFF (addr & 3). Register map per the
+// NESdev wiki NTDEC TC-112 / mapper-193 documentation (cross-checked against
+// reference emulators as accuracy oracles; no third-party emulator code is
+// incorporated).
// ===========================================================================
/// NTDEC TC-112 (mapper 193).
@@ -1046,8 +1048,9 @@ pub fn new_m193(
// The written *address* low bits select the layout: `bitMask = addr & 0x06`
// gives the 16 KiB PRG block, and (when bitMask != 0x06) `addr & 1` picks the
// inner half. Both PRG windows ($8000 + $C000) and the 8 KiB CHR window track
-// the decoded page; `addr & 0x10` flips the mirroring. Ported from Mesen2
-// Unlicensed/Mapper204.h.
+// the decoded page; `addr & 0x10` flips the mirroring. Register map per the
+// NESdev wiki mapper-204 documentation (cross-checked against reference
+// emulators as accuracy oracles; no third-party emulator code is incorporated).
// ===========================================================================
/// NTDEC N625092 multicart (mapper 221).
@@ -1249,7 +1252,9 @@ pub fn new_m221(
//
// One value-decoded $8000-$FFFF register: bits 4-6 select a 32 KiB PRG bank,
// the 8 KiB CHR bank is `(bank << 2) | (value & 0x03)`, and bit 7 flips the
-// mirroring (set => vertical). Ported from Mesen2 Txc/Bmc11160.h.
+// mirroring (set => vertical). Register map per the NESdev wiki mapper-299 /
+// BMC-11160 documentation (cross-checked against reference emulators as
+// accuracy oracles; no third-party emulator code is incorporated).
// ===========================================================================
#[cfg(test)]
diff --git a/crates/rustynes-mappers/src/sachen_discrete.rs b/crates/rustynes-mappers/src/sachen_discrete.rs
index 89975e22..f94d6ae4 100644
--- a/crates/rustynes-mappers/src/sachen_discrete.rs
+++ b/crates/rustynes-mappers/src/sachen_discrete.rs
@@ -445,7 +445,9 @@ impl Mapper for Sachen146 {
/// The TXC JV001 scrambling-accumulator chip (mapper 147). Distinct from the
/// non-JV001 `TxcChip` in `txc.rs` (different register/output bit positions).
-/// Ported bit-for-bit from puNES `JV001.c` / `mapper_147.c`.
+/// The JV001 pre/post-scramble is a fixed hardware bit-permutation, implemented
+/// from the nesdev wiki mapper-147 board notes and cross-checked against `puNES`
+/// as a behavioral oracle (no third-party emulator code is incorporated).
#[derive(Clone, Copy)]
struct Jv001Chip {
accumulator: u8,
@@ -1458,7 +1460,8 @@ mod tests {
#[test]
fn m147_jv001_protection_read_and_bank_decode() {
- // Ported from puNES JV001.c / mapper_147.c. The board pre-scrambles
+ // JV001 scramble per the nesdev wiki mapper-147 board notes (cross-checked
+ // against puNES as an oracle). The board pre-scrambles
// writes ((v&3)<<6)|((v&0xFC)>>2) and post-scrambles reads
// ((v&0x3F)<<2)|((v&0xC0)>>6); the chip resets with invert=0xFF.
let mut m =
diff --git a/crates/rustynes-mappers/src/unif.rs b/crates/rustynes-mappers/src/unif.rs
index ec279da4..410d5664 100644
--- a/crates/rustynes-mappers/src/unif.rs
+++ b/crates/rustynes-mappers/src/unif.rs
@@ -118,9 +118,10 @@ pub fn board_to_mapper(board: &str) -> Option {
None
}
-/// Exact (already-uppercased) board-name lookup. Ported from the
-/// `UNIF_BOARD_MAP` in `scripts/coverage/coverage.py` (Mesen2 + puNES, checked
-/// vs `docs/mappers.md`).
+/// Exact (already-uppercased) board-name lookup. This board-name -> mapper-number
+/// table is factual UNIF board-naming data compiled from `docs/mappers.md` and
+/// the nesdev UNIF board list (cross-checked against `Mesen2` / `puNES` as
+/// oracles; no third-party emulator code is incorporated).
// Arms are grouped by vendor (Nintendo / Konami / Bandai / Sachen / ...) for
// provenance and readability; some distinct board families intentionally share
// a mapper id (e.g. several boards resolve to MMC3 = 4), so identical-body arms
@@ -229,7 +230,7 @@ fn lookup_board(b: &str) -> Option {
"RET-CUFROM" => 29,
// --- v1.8.9 "Backlog" beta.6 UNIF board-map breadth: well-known board
// names mapping to families RustyNES already implements. Cross-checked
- // against Mesen2 `UnifLoader.cpp` + FCEUX `unif.cpp`.
+ // against Mesen2 / FCEUX as behavioral oracles (no code incorporated).
// NTDEC / TXC / discrete BMC families.
"11160" => 299,
"N625092" => 221,
diff --git a/crates/rustynes-ppu/src/palette_gen.rs b/crates/rustynes-ppu/src/palette_gen.rs
index dada13ea..4c7a6a1a 100644
--- a/crates/rustynes-ppu/src/palette_gen.rs
+++ b/crates/rustynes-ppu/src/palette_gen.rs
@@ -3,7 +3,8 @@
//! The hand-authored [`crate::NES_PALETTE`] is one artist's calibration of a
//! Sony PVM reference. This module instead *synthesizes* the 64-entry base
//! palette from a model of the 2C02's composite-video output, following the
-//! Bisqwit / ares (`fc/ppu/color.cpp`) approach: for each of the 64 colors,
+//! Bisqwit composite-palette method published on the nesdev wiki: for each of
+//! the 64 colors,
//! integrate the PPU's two-level chroma square wave over the 12 subcarrier
//! phases of one pixel, demodulate to YIQ, and convert to RGB through the FCC
//! matrix with a gamma correction. The result is deterministic, parameterized
@@ -28,9 +29,10 @@
//! ## Model reference
//!
//! The waveform constants (the eight composite voltage levels, the
-//! sync/black/white references, and the FCC YIQ→RGB matrix) are Bisqwit's
+//! sync/black/white references, and the FCC YIQ→RGB matrix) are from Bisqwit's
//! canonical NES palette generator as published on the nesdev wiki ("NTSC
-//! video"); ares' `PPU::Color` uses the same integration. The `hue` parameter
+//! video") and cross-checked against ares as a behavioral oracle. The `hue`
+//! parameter
//! is a global tint in subcarrier-phase units (each unit = 30°); grays are
//! hue-independent because a constant signal integrates to zero chroma.
diff --git a/crates/rustynes-ppu/src/ppu.rs b/crates/rustynes-ppu/src/ppu.rs
index 488022ee..98c32209 100644
--- a/crates/rustynes-ppu/src/ppu.rs
+++ b/crates/rustynes-ppu/src/ppu.rs
@@ -744,27 +744,29 @@ pub struct Ppu {
/// if its y is in range, else NO sprite-zero is detected.
pub(crate) sprite_eval_first_iter: bool,
- /// v2.0 Tier 1.2 — isolated OAM-data-bus model (parallel port of Mesen2's
- /// `ProcessSpriteEvaluation` + `_oamCopybuffer`). These fields exist ONLY
- /// under `ppu-oam-data-bus` and are read solely by `$2004` during
- /// rendering — the rendering / sprite-zero / overflow / MMC3 sprite-fetch
- /// FSM uses `secondary_oam` + `sprite_eval_*` + `spr_*`, all untouched.
- /// `oam_bus_copybuffer` mirrors `_oamCopybuffer` (the value `$2004`
- /// returns while the screen is drawn).
+ /// v2.0 Tier 1.2 — isolated OAM-data-bus model of the `NESdev`-documented PPU
+ /// sprite-evaluation datapath (`NESdev` wiki "PPU sprite evaluation"). These
+ /// fields exist ONLY under `ppu-oam-data-bus` and are read solely by `$2004`
+ /// during rendering — the rendering / sprite-zero / overflow / MMC3
+ /// sprite-fetch FSM uses `secondary_oam` + `sprite_eval_*` + `spr_*`, all
+ /// untouched. `oam_bus_copybuffer` is the value `$2004` returns while the
+ /// screen is drawn (the byte currently on the OAM data bus). (Behavior
+ /// cross-checked against reference emulators as accuracy oracles; no
+ /// third-party emulator code is incorporated.)
pub(crate) oam_bus_copybuffer: u8,
- /// Parallel secondary OAM (`_secondarySpriteRam`) for the bus model only.
+ /// Parallel secondary OAM (the 32-byte sprite line buffer) for the bus model only.
pub(crate) oam_bus_secondary: [u8; 32],
- /// `_spriteAddrH` (the eval pointer's sprite index, 0..=63).
+ /// Eval-pointer sprite index (0..=63) — which of the 64 primary sprites is examined.
pub(crate) oam_bus_addr_h: u8,
- /// `_spriteAddrL` (the eval pointer's byte-in-sprite, 0..=3).
+ /// Eval-pointer byte-in-sprite (0..=3) — Y / tile / attr / X.
pub(crate) oam_bus_addr_l: u8,
- /// `_secondaryOamAddr` (write index into the parallel secondary OAM).
+ /// Write index into the parallel secondary OAM.
pub(crate) oam_bus_secondary_addr: u8,
- /// `_oamCopyDone` (primary OAM fully scanned / wrapped).
+ /// Primary OAM fully scanned / wrapped for this scanline.
pub(crate) oam_bus_copy_done: bool,
- /// `_spriteInRange` (currently copying an in-range sprite).
+ /// Currently copying an in-range sprite.
pub(crate) oam_bus_sprite_in_range: bool,
- /// `_overflowBugCounter` (the 8-sprite-overflow PPU-bug countdown).
+ /// The 8-sprite-overflow PPU-bug countdown.
pub(crate) oam_bus_overflow_counter: u8,
/// OAM-corruption model — faithful port of `TriCNES`'s eval-pointer
@@ -1446,8 +1448,9 @@ impl Ppu {
/// v2.1.4 F2.3 — OAM-read decay hook. Call **immediately before** reading
/// `oam[addr]` at every primary-OAM read site (the `$2004` read and both
- /// sprite-evaluation read paths). Faithful port of Mesen2's `ReadSpriteRam`
- /// (`Core/NES/NesPpu.cpp`):
+ /// sprite-evaluation read paths). Implements the `NESdev`-documented OAM DRAM
+ /// decay-on-read behavior (`NESdev` wiki "PPU OAM" — sprite RAM is dynamic and
+ /// its cells decay; a read recharges the touched row):
///
/// - If the model is inactive (disabled or PAL), this is a no-op — `oam` and
/// the timestamps are left untouched, so the read is byte-identical to stock.
@@ -1458,7 +1461,7 @@ impl Ppu {
/// (sprAddr & 0xE3) : sprAddr` (the attribute byte keeps only its implemented
/// bits; the others read back their own low address) and leave the stale
/// timestamp (so the row keeps reading decayed until a write refreshes it,
- /// exactly like Mesen2).
+ /// matching the documented decay behavior).
///
/// The subsequent `oam[addr]` read then returns the (possibly decayed) byte.
#[inline]
@@ -1488,8 +1491,9 @@ impl Ppu {
}
/// v2.1.4 F2.3 — OAM-write decay hook. Call **after** writing `oam[addr]` at
- /// every primary-OAM write site (`$2004` / OAM DMA). Faithful port of Mesen2's
- /// `WriteSpriteRam`: a write recharges the row's DRAM cells, so refresh the
+ /// every primary-OAM write site (`$2004` / OAM DMA). Implements the documented
+ /// OAM DRAM decay-on-write refresh (`NESdev` wiki "PPU OAM"): a write recharges
+ /// the row's DRAM cells, so refresh the
/// row's last-touch timestamp. Inactive (disabled or PAL) ⇒ no-op, so the write
/// path is byte-identical to stock at the default.
#[inline]
@@ -3925,7 +3929,7 @@ impl Ppu {
// (F1.1): with rendering DISABLED and the VRAM address `v` pointing
// into palette space ($3F00-$3FFF), the palette's shared address line
// is driven by `v`, so hardware outputs the color at `v & 0x1F`
- // INSTEAD of the backdrop (NESdev "PPU palettes"; Mesen2 `NesPpu.cpp`
+ // INSTEAD of the backdrop (`NESdev` "PPU palettes"; Mesen2 `NesPpu.cpp`
// / ares output stage). This is a DISPLAY artifact only — palette RAM
// is not mutated. It cannot fire while rendering is enabled: there
// the fetch pipeline owns `v` and this branch means a transparent
@@ -4149,10 +4153,13 @@ impl Ppu {
/// v2.0 Tier 1.2 — per-dot driver for the isolated OAM-data-bus model.
///
- /// A faithful, side-effect-free port of Mesen2's
- /// `NesPpu::ProcessSpriteEvaluation` (`NesPpu.cpp:1015-1141`, default
- /// config — `EnablePpuSpriteEvalBug` off) plus the cycle-321 copybuffer
- /// reset (`NesPpu.cpp:945-951`). It maintains ONLY `oam_bus_copybuffer` +
+ /// A side-effect-free model of the `NESdev`-documented PPU sprite-evaluation
+ /// sequence (`NESdev` wiki "PPU sprite evaluation" + "PPU rendering"):
+ /// secondary-OAM clear (dots 1-64), evaluation (65-256), and sprite fetch
+ /// (257-320) in the default configuration (the optional OAMADDR sprite-eval
+ /// corruption glitch disabled; the 8-sprite overflow bug is still modeled),
+ /// plus the cycle-321 copy-buffer reset. It maintains ONLY
+ /// `oam_bus_copybuffer` +
/// the parallel `oam_bus_secondary`; it reads primary `oam` read-only and
/// NEVER touches the real sprite-eval / overflow / sprite-zero state (so
/// the existing rendering FSM is unperturbed — `$2004` reads are the sole
@@ -4166,7 +4173,7 @@ impl Ppu {
8
};
// Y-test reference: the scanline being evaluated (sprites render on
- // scanline+1). Mesen uses `_scanline` directly here.
+ // scanline+1).
let scan = self.scanline;
if cycle == 0 {
@@ -5036,7 +5043,7 @@ mod tests {
// space ($3F00-$3FFF), the palette's shared address input is driven by `v`,
// so the PPU outputs the color at `v & 0x1F` INSTEAD of the universal
// backdrop ($3F00). This is a display artifact only — palette RAM is never
- // mutated, and rendering-enabled output is unchanged. See NESdev "PPU
+ // mutated, and rendering-enabled output is unchanged. See `NESdev` "PPU
// palettes"; mirrors Mesen2 `NesPpu.cpp` / ares output-stage behavior.
#[test]
fn palette_backdrop_override_when_rendering_disabled() {
diff --git a/crates/rustynes-test-harness/src/bin/pgo_trainer.rs b/crates/rustynes-test-harness/src/bin/pgo_trainer.rs
index 2e01cb80..b373a2a2 100644
--- a/crates/rustynes-test-harness/src/bin/pgo_trainer.rs
+++ b/crates/rustynes-test-harness/src/bin/pgo_trainer.rs
@@ -1,9 +1,9 @@
//! v2.8.0 Phase 4 — the PGO training workload (see `scripts/pgo/run.sh`).
//!
-//! Adapted from Mesen2's `PGOHelper`: sweep a ROM corpus at maximum speed
+//! An independent PGO training harness: sweep a ROM corpus at maximum speed
//! (no limiter, no audio device, no display) with scripted input that
//! pushes games past their title screens (Start held on a 4-of-7-frame
-//! cycle — Mesen2's exact trick — plus a rotating d-pad/A mix so movement
+//! cycle — a common title-skip trick — plus a rotating d-pad/A mix so movement
//! and collision code paths get profiled too).
//!
//! Corpus: a committed CC0/MIT/zlib spread covering the hot configurations
diff --git a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin b/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin
deleted file mode 100644
index b2351f23..00000000
Binary files a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin and /dev/null differ
diff --git a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv b/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv
deleted file mode 100644
index 534b5eae..00000000
--- a/crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv
+++ /dev/null
@@ -1,501 +0,0 @@
-cycle,frame,scanline,dot,pc,a,x,y,p,s,opcode,op1,op2,flags
-8,1,0,25,EA5F,00,00,00,24,FD,78,4C,FA,00
-10,1,0,31,EA60,00,00,00,24,FD,4C,FA,EE,00
-13,1,0,40,EEFA,00,00,00,24,FD,A9,00,8D,00
-15,1,0,46,EEFC,00,00,00,26,FD,8D,00,20,00
-19,1,0,58,EEFF,00,00,00,26,FD,8D,01,20,00
-23,1,0,70,EF02,00,00,00,26,FD,4C,63,EA,00
-26,1,0,79,EA63,00,00,00,26,FD,78,D8,A2,00
-28,1,0,85,EA64,00,00,00,26,FD,D8,A2,FF,00
-30,1,0,91,EA65,00,00,00,26,FD,A2,FF,9A,00
-32,1,0,97,EA67,00,FF,00,A4,FD,9A,E8,8E,00
-34,1,0,103,EA68,00,FF,00,A4,FF,E8,8E,00,00
-36,1,0,109,EA69,00,00,00,26,FF,8E,00,20,00
-40,1,0,121,EA6C,00,00,00,26,FF,20,7E,EA,00
-46,1,0,139,EA7E,00,00,00,26,FD,20,36,EB,00
-52,1,0,157,EB36,00,00,00,26,FB,A9,00,A0,00
-54,1,0,163,EB38,00,00,00,26,FB,A0,20,A2,00
-56,1,0,169,EB3A,00,00,20,24,FB,A2,01,84,00
-58,1,0,175,EB3C,00,01,20,24,FB,84,0E,86,00
-61,1,0,184,EB3E,00,01,20,24,FB,86,0F,A0,00
-64,1,0,193,EB40,00,01,20,24,FB,A0,E0,A2,00
-66,1,0,199,EB42,00,01,E0,A4,FB,A2,FF,91,00
-68,1,0,205,EB44,00,FF,E0,A4,FB,91,0E,C8,00
-74,1,0,223,EB46,00,FF,E0,A4,FB,C8,D0,FB,00
-76,1,0,229,EB47,00,FF,E1,A4,FB,D0,FB,E6,00
-79,1,0,238,EB44,00,FF,E1,A4,FB,91,0E,C8,00
-85,1,0,256,EB46,00,FF,E1,A4,FB,C8,D0,FB,00
-87,1,0,262,EB47,00,FF,E2,A4,FB,D0,FB,E6,00
-90,1,0,271,EB44,00,FF,E2,A4,FB,91,0E,C8,00
-96,1,0,289,EB46,00,FF,E2,A4,FB,C8,D0,FB,00
-98,1,0,295,EB47,00,FF,E3,A4,FB,D0,FB,E6,00
-101,1,0,304,EB44,00,FF,E3,A4,FB,91,0E,C8,00
-107,1,0,322,EB46,00,FF,E3,A4,FB,C8,D0,FB,00
-109,1,0,328,EB47,00,FF,E4,A4,FB,D0,FB,E6,00
-112,1,0,337,EB44,00,FF,E4,A4,FB,91,0E,C8,00
-118,1,1,14,EB46,00,FF,E4,A4,FB,C8,D0,FB,00
-120,1,1,20,EB47,00,FF,E5,A4,FB,D0,FB,E6,00
-123,1,1,29,EB44,00,FF,E5,A4,FB,91,0E,C8,00
-129,1,1,47,EB46,00,FF,E5,A4,FB,C8,D0,FB,00
-131,1,1,53,EB47,00,FF,E6,A4,FB,D0,FB,E6,00
-134,1,1,62,EB44,00,FF,E6,A4,FB,91,0E,C8,00
-140,1,1,80,EB46,00,FF,E6,A4,FB,C8,D0,FB,00
-142,1,1,86,EB47,00,FF,E7,A4,FB,D0,FB,E6,00
-145,1,1,95,EB44,00,FF,E7,A4,FB,91,0E,C8,00
-151,1,1,113,EB46,00,FF,E7,A4,FB,C8,D0,FB,00
-153,1,1,119,EB47,00,FF,E8,A4,FB,D0,FB,E6,00
-156,1,1,128,EB44,00,FF,E8,A4,FB,91,0E,C8,00
-162,1,1,146,EB46,00,FF,E8,A4,FB,C8,D0,FB,00
-164,1,1,152,EB47,00,FF,E9,A4,FB,D0,FB,E6,00
-167,1,1,161,EB44,00,FF,E9,A4,FB,91,0E,C8,00
-173,1,1,179,EB46,00,FF,E9,A4,FB,C8,D0,FB,00
-175,1,1,185,EB47,00,FF,EA,A4,FB,D0,FB,E6,00
-178,1,1,194,EB44,00,FF,EA,A4,FB,91,0E,C8,00
-184,1,1,212,EB46,00,FF,EA,A4,FB,C8,D0,FB,00
-186,1,1,218,EB47,00,FF,EB,A4,FB,D0,FB,E6,00
-189,1,1,227,EB44,00,FF,EB,A4,FB,91,0E,C8,00
-195,1,1,245,EB46,00,FF,EB,A4,FB,C8,D0,FB,00
-197,1,1,251,EB47,00,FF,EC,A4,FB,D0,FB,E6,00
-200,1,1,260,EB44,00,FF,EC,A4,FB,91,0E,C8,00
-206,1,1,278,EB46,00,FF,EC,A4,FB,C8,D0,FB,00
-208,1,1,284,EB47,00,FF,ED,A4,FB,D0,FB,E6,00
-211,1,1,293,EB44,00,FF,ED,A4,FB,91,0E,C8,00
-217,1,1,311,EB46,00,FF,ED,A4,FB,C8,D0,FB,00
-219,1,1,317,EB47,00,FF,EE,A4,FB,D0,FB,E6,00
-222,1,1,326,EB44,00,FF,EE,A4,FB,91,0E,C8,00
-228,1,2,3,EB46,00,FF,EE,A4,FB,C8,D0,FB,00
-230,1,2,9,EB47,00,FF,EF,A4,FB,D0,FB,E6,00
-233,1,2,18,EB44,00,FF,EF,A4,FB,91,0E,C8,00
-239,1,2,36,EB46,00,FF,EF,A4,FB,C8,D0,FB,00
-241,1,2,42,EB47,00,FF,F0,A4,FB,D0,FB,E6,00
-244,1,2,51,EB44,00,FF,F0,A4,FB,91,0E,C8,00
-250,1,2,69,EB46,00,FF,F0,A4,FB,C8,D0,FB,00
-252,1,2,75,EB47,00,FF,F1,A4,FB,D0,FB,E6,00
-255,1,2,84,EB44,00,FF,F1,A4,FB,91,0E,C8,00
-261,1,2,102,EB46,00,FF,F1,A4,FB,C8,D0,FB,00
-263,1,2,108,EB47,00,FF,F2,A4,FB,D0,FB,E6,00
-266,1,2,117,EB44,00,FF,F2,A4,FB,91,0E,C8,00
-272,1,2,135,EB46,00,FF,F2,A4,FB,C8,D0,FB,00
-274,1,2,141,EB47,00,FF,F3,A4,FB,D0,FB,E6,00
-277,1,2,150,EB44,00,FF,F3,A4,FB,91,0E,C8,00
-283,1,2,168,EB46,00,FF,F3,A4,FB,C8,D0,FB,00
-285,1,2,174,EB47,00,FF,F4,A4,FB,D0,FB,E6,00
-288,1,2,183,EB44,00,FF,F4,A4,FB,91,0E,C8,00
-294,1,2,201,EB46,00,FF,F4,A4,FB,C8,D0,FB,00
-296,1,2,207,EB47,00,FF,F5,A4,FB,D0,FB,E6,00
-299,1,2,216,EB44,00,FF,F5,A4,FB,91,0E,C8,00
-305,1,2,234,EB46,00,FF,F5,A4,FB,C8,D0,FB,00
-307,1,2,240,EB47,00,FF,F6,A4,FB,D0,FB,E6,00
-310,1,2,249,EB44,00,FF,F6,A4,FB,91,0E,C8,00
-316,1,2,267,EB46,00,FF,F6,A4,FB,C8,D0,FB,00
-318,1,2,273,EB47,00,FF,F7,A4,FB,D0,FB,E6,00
-321,1,2,282,EB44,00,FF,F7,A4,FB,91,0E,C8,00
-327,1,2,300,EB46,00,FF,F7,A4,FB,C8,D0,FB,00
-329,1,2,306,EB47,00,FF,F8,A4,FB,D0,FB,E6,00
-332,1,2,315,EB44,00,FF,F8,A4,FB,91,0E,C8,00
-338,1,2,333,EB46,00,FF,F8,A4,FB,C8,D0,FB,00
-340,1,2,339,EB47,00,FF,F9,A4,FB,D0,FB,E6,00
-343,1,3,7,EB44,00,FF,F9,A4,FB,91,0E,C8,00
-349,1,3,25,EB46,00,FF,F9,A4,FB,C8,D0,FB,00
-351,1,3,31,EB47,00,FF,FA,A4,FB,D0,FB,E6,00
-354,1,3,40,EB44,00,FF,FA,A4,FB,91,0E,C8,00
-360,1,3,58,EB46,00,FF,FA,A4,FB,C8,D0,FB,00
-362,1,3,64,EB47,00,FF,FB,A4,FB,D0,FB,E6,00
-365,1,3,73,EB44,00,FF,FB,A4,FB,91,0E,C8,00
-371,1,3,91,EB46,00,FF,FB,A4,FB,C8,D0,FB,00
-373,1,3,97,EB47,00,FF,FC,A4,FB,D0,FB,E6,00
-376,1,3,106,EB44,00,FF,FC,A4,FB,91,0E,C8,00
-382,1,3,124,EB46,00,FF,FC,A4,FB,C8,D0,FB,00
-384,1,3,130,EB47,00,FF,FD,A4,FB,D0,FB,E6,00
-387,1,3,139,EB44,00,FF,FD,A4,FB,91,0E,C8,00
-393,1,3,157,EB46,00,FF,FD,A4,FB,C8,D0,FB,00
-395,1,3,163,EB47,00,FF,FE,A4,FB,D0,FB,E6,00
-398,1,3,172,EB44,00,FF,FE,A4,FB,91,0E,C8,00
-404,1,3,190,EB46,00,FF,FE,A4,FB,C8,D0,FB,00
-406,1,3,196,EB47,00,FF,FF,A4,FB,D0,FB,E6,00
-409,1,3,205,EB44,00,FF,FF,A4,FB,91,0E,C8,00
-415,1,3,223,EB46,00,FF,FF,A4,FB,C8,D0,FB,00
-417,1,3,229,EB47,00,FF,00,26,FB,D0,FB,E6,00
-419,1,3,235,EB49,00,FF,00,26,FB,E6,0F,E8,00
-424,1,3,250,EB4B,00,FF,00,24,FB,E8,D0,F6,00
-426,1,3,256,EB4C,00,00,00,26,FB,D0,F6,A0,00
-428,1,3,262,EB4E,00,00,00,26,FB,A0,00,A2,00
-430,1,3,268,EB50,00,00,00,26,FB,A2,02,84,00
-432,1,3,274,EB52,00,02,00,24,FB,84,0E,86,00
-435,1,3,283,EB54,00,02,00,24,FB,86,0F,A0,00
-438,1,3,292,EB56,00,02,00,24,FB,A0,24,A2,00
-440,1,3,298,EB58,00,02,24,24,FB,A2,FA,91,00
-442,1,3,304,EB5A,00,FA,24,A4,FB,91,0E,C8,00
-448,1,3,322,EB5C,00,FA,24,A4,FB,C8,D0,FB,00
-450,1,3,328,EB5D,00,FA,25,24,FB,D0,FB,E6,00
-453,1,3,337,EB5A,00,FA,25,24,FB,91,0E,C8,00
-459,1,4,14,EB5C,00,FA,25,24,FB,C8,D0,FB,00
-461,1,4,20,EB5D,00,FA,26,24,FB,D0,FB,E6,00
-464,1,4,29,EB5A,00,FA,26,24,FB,91,0E,C8,00
-470,1,4,47,EB5C,00,FA,26,24,FB,C8,D0,FB,00
-472,1,4,53,EB5D,00,FA,27,24,FB,D0,FB,E6,00
-475,1,4,62,EB5A,00,FA,27,24,FB,91,0E,C8,00
-481,1,4,80,EB5C,00,FA,27,24,FB,C8,D0,FB,00
-483,1,4,86,EB5D,00,FA,28,24,FB,D0,FB,E6,00
-486,1,4,95,EB5A,00,FA,28,24,FB,91,0E,C8,00
-492,1,4,113,EB5C,00,FA,28,24,FB,C8,D0,FB,00
-494,1,4,119,EB5D,00,FA,29,24,FB,D0,FB,E6,00
-497,1,4,128,EB5A,00,FA,29,24,FB,91,0E,C8,00
-503,1,4,146,EB5C,00,FA,29,24,FB,C8,D0,FB,00
-505,1,4,152,EB5D,00,FA,2A,24,FB,D0,FB,E6,00
-508,1,4,161,EB5A,00,FA,2A,24,FB,91,0E,C8,00
-514,1,4,179,EB5C,00,FA,2A,24,FB,C8,D0,FB,00
-516,1,4,185,EB5D,00,FA,2B,24,FB,D0,FB,E6,00
-519,1,4,194,EB5A,00,FA,2B,24,FB,91,0E,C8,00
-525,1,4,212,EB5C,00,FA,2B,24,FB,C8,D0,FB,00
-527,1,4,218,EB5D,00,FA,2C,24,FB,D0,FB,E6,00
-530,1,4,227,EB5A,00,FA,2C,24,FB,91,0E,C8,00
-536,1,4,245,EB5C,00,FA,2C,24,FB,C8,D0,FB,00
-538,1,4,251,EB5D,00,FA,2D,24,FB,D0,FB,E6,00
-541,1,4,260,EB5A,00,FA,2D,24,FB,91,0E,C8,00
-547,1,4,278,EB5C,00,FA,2D,24,FB,C8,D0,FB,00
-549,1,4,284,EB5D,00,FA,2E,24,FB,D0,FB,E6,00
-552,1,4,293,EB5A,00,FA,2E,24,FB,91,0E,C8,00
-558,1,4,311,EB5C,00,FA,2E,24,FB,C8,D0,FB,00
-560,1,4,317,EB5D,00,FA,2F,24,FB,D0,FB,E6,00
-563,1,4,326,EB5A,00,FA,2F,24,FB,91,0E,C8,00
-569,1,5,3,EB5C,00,FA,2F,24,FB,C8,D0,FB,00
-571,1,5,9,EB5D,00,FA,30,24,FB,D0,FB,E6,00
-574,1,5,18,EB5A,00,FA,30,24,FB,91,0E,C8,00
-580,1,5,36,EB5C,00,FA,30,24,FB,C8,D0,FB,00
-582,1,5,42,EB5D,00,FA,31,24,FB,D0,FB,E6,00
-585,1,5,51,EB5A,00,FA,31,24,FB,91,0E,C8,00
-591,1,5,69,EB5C,00,FA,31,24,FB,C8,D0,FB,00
-593,1,5,75,EB5D,00,FA,32,24,FB,D0,FB,E6,00
-596,1,5,84,EB5A,00,FA,32,24,FB,91,0E,C8,00
-602,1,5,102,EB5C,00,FA,32,24,FB,C8,D0,FB,00
-604,1,5,108,EB5D,00,FA,33,24,FB,D0,FB,E6,00
-607,1,5,117,EB5A,00,FA,33,24,FB,91,0E,C8,00
-613,1,5,135,EB5C,00,FA,33,24,FB,C8,D0,FB,00
-615,1,5,141,EB5D,00,FA,34,24,FB,D0,FB,E6,00
-618,1,5,150,EB5A,00,FA,34,24,FB,91,0E,C8,00
-624,1,5,168,EB5C,00,FA,34,24,FB,C8,D0,FB,00
-626,1,5,174,EB5D,00,FA,35,24,FB,D0,FB,E6,00
-629,1,5,183,EB5A,00,FA,35,24,FB,91,0E,C8,00
-635,1,5,201,EB5C,00,FA,35,24,FB,C8,D0,FB,00
-637,1,5,207,EB5D,00,FA,36,24,FB,D0,FB,E6,00
-640,1,5,216,EB5A,00,FA,36,24,FB,91,0E,C8,00
-646,1,5,234,EB5C,00,FA,36,24,FB,C8,D0,FB,00
-648,1,5,240,EB5D,00,FA,37,24,FB,D0,FB,E6,00
-651,1,5,249,EB5A,00,FA,37,24,FB,91,0E,C8,00
-657,1,5,267,EB5C,00,FA,37,24,FB,C8,D0,FB,00
-659,1,5,273,EB5D,00,FA,38,24,FB,D0,FB,E6,00
-662,1,5,282,EB5A,00,FA,38,24,FB,91,0E,C8,00
-668,1,5,300,EB5C,00,FA,38,24,FB,C8,D0,FB,00
-670,1,5,306,EB5D,00,FA,39,24,FB,D0,FB,E6,00
-673,1,5,315,EB5A,00,FA,39,24,FB,91,0E,C8,00
-679,1,5,333,EB5C,00,FA,39,24,FB,C8,D0,FB,00
-681,1,5,339,EB5D,00,FA,3A,24,FB,D0,FB,E6,00
-684,1,6,7,EB5A,00,FA,3A,24,FB,91,0E,C8,00
-690,1,6,25,EB5C,00,FA,3A,24,FB,C8,D0,FB,00
-692,1,6,31,EB5D,00,FA,3B,24,FB,D0,FB,E6,00
-695,1,6,40,EB5A,00,FA,3B,24,FB,91,0E,C8,00
-701,1,6,58,EB5C,00,FA,3B,24,FB,C8,D0,FB,00
-703,1,6,64,EB5D,00,FA,3C,24,FB,D0,FB,E6,00
-706,1,6,73,EB5A,00,FA,3C,24,FB,91,0E,C8,00
-712,1,6,91,EB5C,00,FA,3C,24,FB,C8,D0,FB,00
-714,1,6,97,EB5D,00,FA,3D,24,FB,D0,FB,E6,00
-717,1,6,106,EB5A,00,FA,3D,24,FB,91,0E,C8,00
-723,1,6,124,EB5C,00,FA,3D,24,FB,C8,D0,FB,00
-725,1,6,130,EB5D,00,FA,3E,24,FB,D0,FB,E6,00
-728,1,6,139,EB5A,00,FA,3E,24,FB,91,0E,C8,00
-734,1,6,157,EB5C,00,FA,3E,24,FB,C8,D0,FB,00
-736,1,6,163,EB5D,00,FA,3F,24,FB,D0,FB,E6,00
-739,1,6,172,EB5A,00,FA,3F,24,FB,91,0E,C8,00
-745,1,6,190,EB5C,00,FA,3F,24,FB,C8,D0,FB,00
-747,1,6,196,EB5D,00,FA,40,24,FB,D0,FB,E6,00
-750,1,6,205,EB5A,00,FA,40,24,FB,91,0E,C8,00
-756,1,6,223,EB5C,00,FA,40,24,FB,C8,D0,FB,00
-758,1,6,229,EB5D,00,FA,41,24,FB,D0,FB,E6,00
-761,1,6,238,EB5A,00,FA,41,24,FB,91,0E,C8,00
-767,1,6,256,EB5C,00,FA,41,24,FB,C8,D0,FB,00
-769,1,6,262,EB5D,00,FA,42,24,FB,D0,FB,E6,00
-772,1,6,271,EB5A,00,FA,42,24,FB,91,0E,C8,00
-778,1,6,289,EB5C,00,FA,42,24,FB,C8,D0,FB,00
-780,1,6,295,EB5D,00,FA,43,24,FB,D0,FB,E6,00
-783,1,6,304,EB5A,00,FA,43,24,FB,91,0E,C8,00
-789,1,6,322,EB5C,00,FA,43,24,FB,C8,D0,FB,00
-791,1,6,328,EB5D,00,FA,44,24,FB,D0,FB,E6,00
-794,1,6,337,EB5A,00,FA,44,24,FB,91,0E,C8,00
-800,1,7,14,EB5C,00,FA,44,24,FB,C8,D0,FB,00
-802,1,7,20,EB5D,00,FA,45,24,FB,D0,FB,E6,00
-805,1,7,29,EB5A,00,FA,45,24,FB,91,0E,C8,00
-811,1,7,47,EB5C,00,FA,45,24,FB,C8,D0,FB,00
-813,1,7,53,EB5D,00,FA,46,24,FB,D0,FB,E6,00
-816,1,7,62,EB5A,00,FA,46,24,FB,91,0E,C8,00
-822,1,7,80,EB5C,00,FA,46,24,FB,C8,D0,FB,00
-824,1,7,86,EB5D,00,FA,47,24,FB,D0,FB,E6,00
-827,1,7,95,EB5A,00,FA,47,24,FB,91,0E,C8,00
-833,1,7,113,EB5C,00,FA,47,24,FB,C8,D0,FB,00
-835,1,7,119,EB5D,00,FA,48,24,FB,D0,FB,E6,00
-838,1,7,128,EB5A,00,FA,48,24,FB,91,0E,C8,00
-844,1,7,146,EB5C,00,FA,48,24,FB,C8,D0,FB,00
-846,1,7,152,EB5D,00,FA,49,24,FB,D0,FB,E6,00
-849,1,7,161,EB5A,00,FA,49,24,FB,91,0E,C8,00
-855,1,7,179,EB5C,00,FA,49,24,FB,C8,D0,FB,00
-857,1,7,185,EB5D,00,FA,4A,24,FB,D0,FB,E6,00
-860,1,7,194,EB5A,00,FA,4A,24,FB,91,0E,C8,00
-866,1,7,212,EB5C,00,FA,4A,24,FB,C8,D0,FB,00
-868,1,7,218,EB5D,00,FA,4B,24,FB,D0,FB,E6,00
-871,1,7,227,EB5A,00,FA,4B,24,FB,91,0E,C8,00
-877,1,7,245,EB5C,00,FA,4B,24,FB,C8,D0,FB,00
-879,1,7,251,EB5D,00,FA,4C,24,FB,D0,FB,E6,00
-882,1,7,260,EB5A,00,FA,4C,24,FB,91,0E,C8,00
-888,1,7,278,EB5C,00,FA,4C,24,FB,C8,D0,FB,00
-890,1,7,284,EB5D,00,FA,4D,24,FB,D0,FB,E6,00
-893,1,7,293,EB5A,00,FA,4D,24,FB,91,0E,C8,00
-899,1,7,311,EB5C,00,FA,4D,24,FB,C8,D0,FB,00
-901,1,7,317,EB5D,00,FA,4E,24,FB,D0,FB,E6,00
-904,1,7,326,EB5A,00,FA,4E,24,FB,91,0E,C8,00
-910,1,8,3,EB5C,00,FA,4E,24,FB,C8,D0,FB,00
-912,1,8,9,EB5D,00,FA,4F,24,FB,D0,FB,E6,00
-915,1,8,18,EB5A,00,FA,4F,24,FB,91,0E,C8,00
-921,1,8,36,EB5C,00,FA,4F,24,FB,C8,D0,FB,00
-923,1,8,42,EB5D,00,FA,50,24,FB,D0,FB,E6,00
-926,1,8,51,EB5A,00,FA,50,24,FB,91,0E,C8,00
-932,1,8,69,EB5C,00,FA,50,24,FB,C8,D0,FB,00
-934,1,8,75,EB5D,00,FA,51,24,FB,D0,FB,E6,00
-937,1,8,84,EB5A,00,FA,51,24,FB,91,0E,C8,00
-943,1,8,102,EB5C,00,FA,51,24,FB,C8,D0,FB,00
-945,1,8,108,EB5D,00,FA,52,24,FB,D0,FB,E6,00
-948,1,8,117,EB5A,00,FA,52,24,FB,91,0E,C8,00
-954,1,8,135,EB5C,00,FA,52,24,FB,C8,D0,FB,00
-956,1,8,141,EB5D,00,FA,53,24,FB,D0,FB,E6,00
-959,1,8,150,EB5A,00,FA,53,24,FB,91,0E,C8,00
-965,1,8,168,EB5C,00,FA,53,24,FB,C8,D0,FB,00
-967,1,8,174,EB5D,00,FA,54,24,FB,D0,FB,E6,00
-970,1,8,183,EB5A,00,FA,54,24,FB,91,0E,C8,00
-976,1,8,201,EB5C,00,FA,54,24,FB,C8,D0,FB,00
-978,1,8,207,EB5D,00,FA,55,24,FB,D0,FB,E6,00
-981,1,8,216,EB5A,00,FA,55,24,FB,91,0E,C8,00
-987,1,8,234,EB5C,00,FA,55,24,FB,C8,D0,FB,00
-989,1,8,240,EB5D,00,FA,56,24,FB,D0,FB,E6,00
-992,1,8,249,EB5A,00,FA,56,24,FB,91,0E,C8,00
-998,1,8,267,EB5C,00,FA,56,24,FB,C8,D0,FB,00
-1000,1,8,273,EB5D,00,FA,57,24,FB,D0,FB,E6,00
-1003,1,8,282,EB5A,00,FA,57,24,FB,91,0E,C8,00
-1009,1,8,300,EB5C,00,FA,57,24,FB,C8,D0,FB,00
-1011,1,8,306,EB5D,00,FA,58,24,FB,D0,FB,E6,00
-1014,1,8,315,EB5A,00,FA,58,24,FB,91,0E,C8,00
-1020,1,8,333,EB5C,00,FA,58,24,FB,C8,D0,FB,00
-1022,1,8,339,EB5D,00,FA,59,24,FB,D0,FB,E6,00
-1025,1,9,7,EB5A,00,FA,59,24,FB,91,0E,C8,00
-1031,1,9,25,EB5C,00,FA,59,24,FB,C8,D0,FB,00
-1033,1,9,31,EB5D,00,FA,5A,24,FB,D0,FB,E6,00
-1036,1,9,40,EB5A,00,FA,5A,24,FB,91,0E,C8,00
-1042,1,9,58,EB5C,00,FA,5A,24,FB,C8,D0,FB,00
-1044,1,9,64,EB5D,00,FA,5B,24,FB,D0,FB,E6,00
-1047,1,9,73,EB5A,00,FA,5B,24,FB,91,0E,C8,00
-1053,1,9,91,EB5C,00,FA,5B,24,FB,C8,D0,FB,00
-1055,1,9,97,EB5D,00,FA,5C,24,FB,D0,FB,E6,00
-1058,1,9,106,EB5A,00,FA,5C,24,FB,91,0E,C8,00
-1064,1,9,124,EB5C,00,FA,5C,24,FB,C8,D0,FB,00
-1066,1,9,130,EB5D,00,FA,5D,24,FB,D0,FB,E6,00
-1069,1,9,139,EB5A,00,FA,5D,24,FB,91,0E,C8,00
-1075,1,9,157,EB5C,00,FA,5D,24,FB,C8,D0,FB,00
-1077,1,9,163,EB5D,00,FA,5E,24,FB,D0,FB,E6,00
-1080,1,9,172,EB5A,00,FA,5E,24,FB,91,0E,C8,00
-1086,1,9,190,EB5C,00,FA,5E,24,FB,C8,D0,FB,00
-1088,1,9,196,EB5D,00,FA,5F,24,FB,D0,FB,E6,00
-1091,1,9,205,EB5A,00,FA,5F,24,FB,91,0E,C8,00
-1097,1,9,223,EB5C,00,FA,5F,24,FB,C8,D0,FB,00
-1099,1,9,229,EB5D,00,FA,60,24,FB,D0,FB,E6,00
-1102,1,9,238,EB5A,00,FA,60,24,FB,91,0E,C8,00
-1108,1,9,256,EB5C,00,FA,60,24,FB,C8,D0,FB,00
-1110,1,9,262,EB5D,00,FA,61,24,FB,D0,FB,E6,00
-1113,1,9,271,EB5A,00,FA,61,24,FB,91,0E,C8,00
-1119,1,9,289,EB5C,00,FA,61,24,FB,C8,D0,FB,00
-1121,1,9,295,EB5D,00,FA,62,24,FB,D0,FB,E6,00
-1124,1,9,304,EB5A,00,FA,62,24,FB,91,0E,C8,00
-1130,1,9,322,EB5C,00,FA,62,24,FB,C8,D0,FB,00
-1132,1,9,328,EB5D,00,FA,63,24,FB,D0,FB,E6,00
-1135,1,9,337,EB5A,00,FA,63,24,FB,91,0E,C8,00
-1141,1,10,14,EB5C,00,FA,63,24,FB,C8,D0,FB,00
-1143,1,10,20,EB5D,00,FA,64,24,FB,D0,FB,E6,00
-1146,1,10,29,EB5A,00,FA,64,24,FB,91,0E,C8,00
-1152,1,10,47,EB5C,00,FA,64,24,FB,C8,D0,FB,00
-1154,1,10,53,EB5D,00,FA,65,24,FB,D0,FB,E6,00
-1157,1,10,62,EB5A,00,FA,65,24,FB,91,0E,C8,00
-1163,1,10,80,EB5C,00,FA,65,24,FB,C8,D0,FB,00
-1165,1,10,86,EB5D,00,FA,66,24,FB,D0,FB,E6,00
-1168,1,10,95,EB5A,00,FA,66,24,FB,91,0E,C8,00
-1174,1,10,113,EB5C,00,FA,66,24,FB,C8,D0,FB,00
-1176,1,10,119,EB5D,00,FA,67,24,FB,D0,FB,E6,00
-1179,1,10,128,EB5A,00,FA,67,24,FB,91,0E,C8,00
-1185,1,10,146,EB5C,00,FA,67,24,FB,C8,D0,FB,00
-1187,1,10,152,EB5D,00,FA,68,24,FB,D0,FB,E6,00
-1190,1,10,161,EB5A,00,FA,68,24,FB,91,0E,C8,00
-1196,1,10,179,EB5C,00,FA,68,24,FB,C8,D0,FB,00
-1198,1,10,185,EB5D,00,FA,69,24,FB,D0,FB,E6,00
-1201,1,10,194,EB5A,00,FA,69,24,FB,91,0E,C8,00
-1207,1,10,212,EB5C,00,FA,69,24,FB,C8,D0,FB,00
-1209,1,10,218,EB5D,00,FA,6A,24,FB,D0,FB,E6,00
-1212,1,10,227,EB5A,00,FA,6A,24,FB,91,0E,C8,00
-1218,1,10,245,EB5C,00,FA,6A,24,FB,C8,D0,FB,00
-1220,1,10,251,EB5D,00,FA,6B,24,FB,D0,FB,E6,00
-1223,1,10,260,EB5A,00,FA,6B,24,FB,91,0E,C8,00
-1229,1,10,278,EB5C,00,FA,6B,24,FB,C8,D0,FB,00
-1231,1,10,284,EB5D,00,FA,6C,24,FB,D0,FB,E6,00
-1234,1,10,293,EB5A,00,FA,6C,24,FB,91,0E,C8,00
-1240,1,10,311,EB5C,00,FA,6C,24,FB,C8,D0,FB,00
-1242,1,10,317,EB5D,00,FA,6D,24,FB,D0,FB,E6,00
-1245,1,10,326,EB5A,00,FA,6D,24,FB,91,0E,C8,00
-1251,1,11,3,EB5C,00,FA,6D,24,FB,C8,D0,FB,00
-1253,1,11,9,EB5D,00,FA,6E,24,FB,D0,FB,E6,00
-1256,1,11,18,EB5A,00,FA,6E,24,FB,91,0E,C8,00
-1262,1,11,36,EB5C,00,FA,6E,24,FB,C8,D0,FB,00
-1264,1,11,42,EB5D,00,FA,6F,24,FB,D0,FB,E6,00
-1267,1,11,51,EB5A,00,FA,6F,24,FB,91,0E,C8,00
-1273,1,11,69,EB5C,00,FA,6F,24,FB,C8,D0,FB,00
-1275,1,11,75,EB5D,00,FA,70,24,FB,D0,FB,E6,00
-1278,1,11,84,EB5A,00,FA,70,24,FB,91,0E,C8,00
-1284,1,11,102,EB5C,00,FA,70,24,FB,C8,D0,FB,00
-1286,1,11,108,EB5D,00,FA,71,24,FB,D0,FB,E6,00
-1289,1,11,117,EB5A,00,FA,71,24,FB,91,0E,C8,00
-1295,1,11,135,EB5C,00,FA,71,24,FB,C8,D0,FB,00
-1297,1,11,141,EB5D,00,FA,72,24,FB,D0,FB,E6,00
-1300,1,11,150,EB5A,00,FA,72,24,FB,91,0E,C8,00
-1306,1,11,168,EB5C,00,FA,72,24,FB,C8,D0,FB,00
-1308,1,11,174,EB5D,00,FA,73,24,FB,D0,FB,E6,00
-1311,1,11,183,EB5A,00,FA,73,24,FB,91,0E,C8,00
-1317,1,11,201,EB5C,00,FA,73,24,FB,C8,D0,FB,00
-1319,1,11,207,EB5D,00,FA,74,24,FB,D0,FB,E6,00
-1322,1,11,216,EB5A,00,FA,74,24,FB,91,0E,C8,00
-1328,1,11,234,EB5C,00,FA,74,24,FB,C8,D0,FB,00
-1330,1,11,240,EB5D,00,FA,75,24,FB,D0,FB,E6,00
-1333,1,11,249,EB5A,00,FA,75,24,FB,91,0E,C8,00
-1339,1,11,267,EB5C,00,FA,75,24,FB,C8,D0,FB,00
-1341,1,11,273,EB5D,00,FA,76,24,FB,D0,FB,E6,00
-1344,1,11,282,EB5A,00,FA,76,24,FB,91,0E,C8,00
-1350,1,11,300,EB5C,00,FA,76,24,FB,C8,D0,FB,00
-1352,1,11,306,EB5D,00,FA,77,24,FB,D0,FB,E6,00
-1355,1,11,315,EB5A,00,FA,77,24,FB,91,0E,C8,00
-1361,1,11,333,EB5C,00,FA,77,24,FB,C8,D0,FB,00
-1363,1,11,339,EB5D,00,FA,78,24,FB,D0,FB,E6,00
-1366,1,12,7,EB5A,00,FA,78,24,FB,91,0E,C8,00
-1372,1,12,25,EB5C,00,FA,78,24,FB,C8,D0,FB,00
-1374,1,12,31,EB5D,00,FA,79,24,FB,D0,FB,E6,00
-1377,1,12,40,EB5A,00,FA,79,24,FB,91,0E,C8,00
-1383,1,12,58,EB5C,00,FA,79,24,FB,C8,D0,FB,00
-1385,1,12,64,EB5D,00,FA,7A,24,FB,D0,FB,E6,00
-1388,1,12,73,EB5A,00,FA,7A,24,FB,91,0E,C8,00
-1394,1,12,91,EB5C,00,FA,7A,24,FB,C8,D0,FB,00
-1396,1,12,97,EB5D,00,FA,7B,24,FB,D0,FB,E6,00
-1399,1,12,106,EB5A,00,FA,7B,24,FB,91,0E,C8,00
-1405,1,12,124,EB5C,00,FA,7B,24,FB,C8,D0,FB,00
-1407,1,12,130,EB5D,00,FA,7C,24,FB,D0,FB,E6,00
-1410,1,12,139,EB5A,00,FA,7C,24,FB,91,0E,C8,00
-1416,1,12,157,EB5C,00,FA,7C,24,FB,C8,D0,FB,00
-1418,1,12,163,EB5D,00,FA,7D,24,FB,D0,FB,E6,00
-1421,1,12,172,EB5A,00,FA,7D,24,FB,91,0E,C8,00
-1427,1,12,190,EB5C,00,FA,7D,24,FB,C8,D0,FB,00
-1429,1,12,196,EB5D,00,FA,7E,24,FB,D0,FB,E6,00
-1432,1,12,205,EB5A,00,FA,7E,24,FB,91,0E,C8,00
-1438,1,12,223,EB5C,00,FA,7E,24,FB,C8,D0,FB,00
-1440,1,12,229,EB5D,00,FA,7F,24,FB,D0,FB,E6,00
-1443,1,12,238,EB5A,00,FA,7F,24,FB,91,0E,C8,00
-1449,1,12,256,EB5C,00,FA,7F,24,FB,C8,D0,FB,00
-1451,1,12,262,EB5D,00,FA,80,A4,FB,D0,FB,E6,00
-1454,1,12,271,EB5A,00,FA,80,A4,FB,91,0E,C8,00
-1460,1,12,289,EB5C,00,FA,80,A4,FB,C8,D0,FB,00
-1462,1,12,295,EB5D,00,FA,81,A4,FB,D0,FB,E6,00
-1465,1,12,304,EB5A,00,FA,81,A4,FB,91,0E,C8,00
-1471,1,12,322,EB5C,00,FA,81,A4,FB,C8,D0,FB,00
-1473,1,12,328,EB5D,00,FA,82,A4,FB,D0,FB,E6,00
-1476,1,12,337,EB5A,00,FA,82,A4,FB,91,0E,C8,00
-1482,1,13,14,EB5C,00,FA,82,A4,FB,C8,D0,FB,00
-1484,1,13,20,EB5D,00,FA,83,A4,FB,D0,FB,E6,00
-1487,1,13,29,EB5A,00,FA,83,A4,FB,91,0E,C8,00
-1493,1,13,47,EB5C,00,FA,83,A4,FB,C8,D0,FB,00
-1495,1,13,53,EB5D,00,FA,84,A4,FB,D0,FB,E6,00
-1498,1,13,62,EB5A,00,FA,84,A4,FB,91,0E,C8,00
-1504,1,13,80,EB5C,00,FA,84,A4,FB,C8,D0,FB,00
-1506,1,13,86,EB5D,00,FA,85,A4,FB,D0,FB,E6,00
-1509,1,13,95,EB5A,00,FA,85,A4,FB,91,0E,C8,00
-1515,1,13,113,EB5C,00,FA,85,A4,FB,C8,D0,FB,00
-1517,1,13,119,EB5D,00,FA,86,A4,FB,D0,FB,E6,00
-1520,1,13,128,EB5A,00,FA,86,A4,FB,91,0E,C8,00
-1526,1,13,146,EB5C,00,FA,86,A4,FB,C8,D0,FB,00
-1528,1,13,152,EB5D,00,FA,87,A4,FB,D0,FB,E6,00
-1531,1,13,161,EB5A,00,FA,87,A4,FB,91,0E,C8,00
-1537,1,13,179,EB5C,00,FA,87,A4,FB,C8,D0,FB,00
-1539,1,13,185,EB5D,00,FA,88,A4,FB,D0,FB,E6,00
-1542,1,13,194,EB5A,00,FA,88,A4,FB,91,0E,C8,00
-1548,1,13,212,EB5C,00,FA,88,A4,FB,C8,D0,FB,00
-1550,1,13,218,EB5D,00,FA,89,A4,FB,D0,FB,E6,00
-1553,1,13,227,EB5A,00,FA,89,A4,FB,91,0E,C8,00
-1559,1,13,245,EB5C,00,FA,89,A4,FB,C8,D0,FB,00
-1561,1,13,251,EB5D,00,FA,8A,A4,FB,D0,FB,E6,00
-1564,1,13,260,EB5A,00,FA,8A,A4,FB,91,0E,C8,00
-1570,1,13,278,EB5C,00,FA,8A,A4,FB,C8,D0,FB,00
-1572,1,13,284,EB5D,00,FA,8B,A4,FB,D0,FB,E6,00
-1575,1,13,293,EB5A,00,FA,8B,A4,FB,91,0E,C8,00
-1581,1,13,311,EB5C,00,FA,8B,A4,FB,C8,D0,FB,00
-1583,1,13,317,EB5D,00,FA,8C,A4,FB,D0,FB,E6,00
-1586,1,13,326,EB5A,00,FA,8C,A4,FB,91,0E,C8,00
-1592,1,14,3,EB5C,00,FA,8C,A4,FB,C8,D0,FB,00
-1594,1,14,9,EB5D,00,FA,8D,A4,FB,D0,FB,E6,00
-1597,1,14,18,EB5A,00,FA,8D,A4,FB,91,0E,C8,00
-1603,1,14,36,EB5C,00,FA,8D,A4,FB,C8,D0,FB,00
-1605,1,14,42,EB5D,00,FA,8E,A4,FB,D0,FB,E6,00
-1608,1,14,51,EB5A,00,FA,8E,A4,FB,91,0E,C8,00
-1614,1,14,69,EB5C,00,FA,8E,A4,FB,C8,D0,FB,00
-1616,1,14,75,EB5D,00,FA,8F,A4,FB,D0,FB,E6,00
-1619,1,14,84,EB5A,00,FA,8F,A4,FB,91,0E,C8,00
-1625,1,14,102,EB5C,00,FA,8F,A4,FB,C8,D0,FB,00
-1627,1,14,108,EB5D,00,FA,90,A4,FB,D0,FB,E6,00
-1630,1,14,117,EB5A,00,FA,90,A4,FB,91,0E,C8,00
-1636,1,14,135,EB5C,00,FA,90,A4,FB,C8,D0,FB,00
-1638,1,14,141,EB5D,00,FA,91,A4,FB,D0,FB,E6,00
-1641,1,14,150,EB5A,00,FA,91,A4,FB,91,0E,C8,00
-1647,1,14,168,EB5C,00,FA,91,A4,FB,C8,D0,FB,00
-1649,1,14,174,EB5D,00,FA,92,A4,FB,D0,FB,E6,00
-1652,1,14,183,EB5A,00,FA,92,A4,FB,91,0E,C8,00
-1658,1,14,201,EB5C,00,FA,92,A4,FB,C8,D0,FB,00
-1660,1,14,207,EB5D,00,FA,93,A4,FB,D0,FB,E6,00
-1663,1,14,216,EB5A,00,FA,93,A4,FB,91,0E,C8,00
-1669,1,14,234,EB5C,00,FA,93,A4,FB,C8,D0,FB,00
-1671,1,14,240,EB5D,00,FA,94,A4,FB,D0,FB,E6,00
-1674,1,14,249,EB5A,00,FA,94,A4,FB,91,0E,C8,00
-1680,1,14,267,EB5C,00,FA,94,A4,FB,C8,D0,FB,00
-1682,1,14,273,EB5D,00,FA,95,A4,FB,D0,FB,E6,00
-1685,1,14,282,EB5A,00,FA,95,A4,FB,91,0E,C8,00
-1691,1,14,300,EB5C,00,FA,95,A4,FB,C8,D0,FB,00
-1693,1,14,306,EB5D,00,FA,96,A4,FB,D0,FB,E6,00
-1696,1,14,315,EB5A,00,FA,96,A4,FB,91,0E,C8,00
-1702,1,14,333,EB5C,00,FA,96,A4,FB,C8,D0,FB,00
-1704,1,14,339,EB5D,00,FA,97,A4,FB,D0,FB,E6,00
-1707,1,15,7,EB5A,00,FA,97,A4,FB,91,0E,C8,00
-1713,1,15,25,EB5C,00,FA,97,A4,FB,C8,D0,FB,00
-1715,1,15,31,EB5D,00,FA,98,A4,FB,D0,FB,E6,00
-1718,1,15,40,EB5A,00,FA,98,A4,FB,91,0E,C8,00
-1724,1,15,58,EB5C,00,FA,98,A4,FB,C8,D0,FB,00
-1726,1,15,64,EB5D,00,FA,99,A4,FB,D0,FB,E6,00
-1729,1,15,73,EB5A,00,FA,99,A4,FB,91,0E,C8,00
-1735,1,15,91,EB5C,00,FA,99,A4,FB,C8,D0,FB,00
-1737,1,15,97,EB5D,00,FA,9A,A4,FB,D0,FB,E6,00
-1740,1,15,106,EB5A,00,FA,9A,A4,FB,91,0E,C8,00
-1746,1,15,124,EB5C,00,FA,9A,A4,FB,C8,D0,FB,00
-1748,1,15,130,EB5D,00,FA,9B,A4,FB,D0,FB,E6,00
-1751,1,15,139,EB5A,00,FA,9B,A4,FB,91,0E,C8,00
-1757,1,15,157,EB5C,00,FA,9B,A4,FB,C8,D0,FB,00
-1759,1,15,163,EB5D,00,FA,9C,A4,FB,D0,FB,E6,00
-1762,1,15,172,EB5A,00,FA,9C,A4,FB,91,0E,C8,00
-1768,1,15,190,EB5C,00,FA,9C,A4,FB,C8,D0,FB,00
-1770,1,15,196,EB5D,00,FA,9D,A4,FB,D0,FB,E6,00
-1773,1,15,205,EB5A,00,FA,9D,A4,FB,91,0E,C8,00
-1779,1,15,223,EB5C,00,FA,9D,A4,FB,C8,D0,FB,00
-1781,1,15,229,EB5D,00,FA,9E,A4,FB,D0,FB,E6,00
-1784,1,15,238,EB5A,00,FA,9E,A4,FB,91,0E,C8,00
-1790,1,15,256,EB5C,00,FA,9E,A4,FB,C8,D0,FB,00
-1792,1,15,262,EB5D,00,FA,9F,A4,FB,D0,FB,E6,00
-1795,1,15,271,EB5A,00,FA,9F,A4,FB,91,0E,C8,00
-1801,1,15,289,EB5C,00,FA,9F,A4,FB,C8,D0,FB,00
-1803,1,15,295,EB5D,00,FA,A0,A4,FB,D0,FB,E6,00
-1806,1,15,304,EB5A,00,FA,A0,A4,FB,91,0E,C8,00
-1812,1,15,322,EB5C,00,FA,A0,A4,FB,C8,D0,FB,00
diff --git a/docs/STATUS.md b/docs/STATUS.md
index 9ed61da9..7e922049 100644
--- a/docs/STATUS.md
+++ b/docs/STATUS.md
@@ -1,6 +1,20 @@
# RustyNES — Project Status Matrix
-> **Current release: v2.2.4** (2026-07-24) — **"Cartridge"**, a **libretro /
+> **Current release: v2.2.5** (2026-08-03) — **"Colophon"**, a **provenance,
+> licensing, and documentation-integrity** release with **zero emulation-core
+> behavior changes** (so **AccuracyCoin holds 141/141 (100.00%)**, nestest 0-diff, and the
+> `#![no_std]` chip stack, save-state / TAS / netplay formats, and every golden
+> vector are byte-identical to v2.2.4 by construction). It reworded in-source
+> comments that had mischaracterized publicly-documented hardware-behavior
+> implementations as "ports of" copyleft emulators (Mesen2, puNES); rewrote
+> `NOTICE` to disclose the behavioral-oracle use of GPL emulators (no code
+> incorporated) and to attribute the incorporated permissive components (emu2413,
+> TriCNES, rcheevos — MIT), the bundled fonts and test ROMs, and the CRT-shader /
+> NTSC-filter visual influences as independent reimplementations; added
+> `docs/originality-and-provenance.md`; and added an AI-assistance disclosure to
+> the README (with a misleading comparison graphic removed and a mislabeled
+> screenshot caption fixed). On top of **v2.2.4** (2026-07-24) — **"Cartridge"**,
+> a **libretro /
> RetroArch distribution** cut whose purpose is that the RustyNES core builds and
> installs cleanly through the Libretro buildbot
> () for in-RetroArch use. **Zero
diff --git a/docs/originality-and-provenance.md b/docs/originality-and-provenance.md
new file mode 100644
index 00000000..f51aea30
--- /dev/null
+++ b/docs/originality-and-provenance.md
@@ -0,0 +1,420 @@
+# Engineering Originality and Provenance
+
+This document explains where RustyNES advances, diverges from, or independently
+re-derives NES emulation technique; how the project was actually built (research
+first, test-driven, measured); and how it treats the licenses of the reference
+emulators and test ROMs consulted during development.
+
+It is written to be **honest rather than triumphal**. RustyNES is not a clean-room
+project that never looked at prior art, and it is not a fork or a translation of
+another emulator either. It is an independent implementation whose *architecture*
+and *engineering method* are its own, and which incorporates a small number of
+clearly-attributed components from permissively-licensed projects while using
+copyleft-licensed emulators only as behavioral oracles. The sections below spell
+out exactly which is which, with file-level and ADR-level citations so the claims
+can be checked against the tree.
+
+Authoritative companions to this document: `docs/STATUS.md` (per-suite pass
+counts and the mapper matrix), `CHANGELOG.md` (user-visible history), `docs/adr/`
+(the decision record), `NOTICE` (the legal attribution file), and
+`tests/roms/LICENSES.md` (test-ROM provenance).
+
+---
+
+## 1. Thesis: an independent build with attributed borrowings
+
+The honest claim RustyNES can make is not "no line resembles any other emulator."
+It is this:
+
+- **The architecture is original.** The scheduler substrate, the ownership model,
+ the crate/dependency topology, the determinism contract, the accuracy-honesty
+ gates, and the save-state schema discipline are RustyNES's own design decisions,
+ recorded as ADRs and implemented in its own `#![no_std]` Rust idiom.
+- **The engineering method is original and auditable.** Behaviors are implemented
+ from public hardware documentation, pinned to public test ROMs first, and every
+ performance change is measured — including the ones that were measured and
+ *rejected*. The discipline is machine-checked in CI, not asserted in prose.
+- **Specific algorithms are deliberately, transparently borrowed** from
+ permissively-licensed projects (TriCNES, emu2413, rcheevos), each attributed in
+ source and in `NOTICE` under its MIT license.
+- **Copyleft-licensed emulators were used only as oracles** — to observe and
+ cross-check documented hardware behavior — never as a source of copied code.
+
+Put differently: RustyNES's originality lives less in any single novel algorithm
+(most hardware behaviors are, by definition, shared by every accurate emulator)
+and more in the *system* that produces and guarantees that accuracy. That is the
+claim the rest of this document substantiates.
+
+**A note on AI assistance.** RustyNES is heavily AI-assisted software: much of it
+was produced with LLM tooling under a human-directed, test-driven workflow, with
+public test ROMs as the oracle, a `no_std` core as a hard baseline, and continuous
+CI as the gate. That is disclosed plainly here and in the README because it belongs
+in an honest provenance record — and because the licensing lapses this document
+corrects (comments that called hardware-behavior implementations "ports" of
+copyleft emulators) are exactly the kind of mistake AI-assisted authoring is prone
+to. The remedy is the same either way: audit against the sources, attribute
+accurately, and let the machine-checked gates — not the prose — carry the accuracy
+claims.
+
+**Not a superiority claim.** Nothing here asserts that RustyNES is "better" than
+the emulators that came before it. Where this document compares RustyNES to a
+reference, the comparison is exactly that — a comparison against a project RustyNES
+was measured against — and every accuracy figure is independently checkable by
+running the public suites (see the README Acknowledgments for the references and
+components the project builds on).
+
+---
+
+## 2. Where RustyNES advances or diverges from prior art
+
+Each subsection names the mechanism, the measurable result where one exists, the
+governing ADR, and — where relevant — the specific reference emulator RustyNES
+agrees or disagrees with.
+
+### 2.1 The one-clock, every-cycle-bus-access timebase (ADR 0029)
+
+Most NES emulators either batch subsystem work per scanline/instruction (fast,
+less accurate) or run a multi-counter dot-lockstep (accurate, complex). RustyNES's
+v2.0.0 "Timebase" rewrite collapses scheduling to a **single canonical cycle
+counter** in which *every* CPU cycle is a real bus access, and PPU catch-up is
+split around that access via paired `start_cycle` / `end_cycle` hooks. This makes
+sub-instruction PPU state visible to the very next CPU read without per-quirk
+patches — mid-scanline scroll writes, a sprite-zero hit at a precise dot, an MMC3
+IRQ at PPU dot 260 all fall out of the model rather than being special-cased.
+
+The structural choice mirrors Mesen2's cycle-stepped approach conceptually, but
+the implementation, the counter model, and the split-around-access hook design are
+RustyNES's own (`crates/rustynes-core`, `docs/scheduler.md`). It is a deliberate
+MAJOR-boundary change: the old five-counter dot-lockstep scheduler was retired
+outright, and the save-state / movie formats broke by design (see 2.9 and ADR
+0028). See ADR 0029 for the full rationale.
+
+### 2.2 The 2-cycle-ALE octal-latch PPU fetch: an independent, transistor-literal model (ADR 0030)
+
+This is a clear example of independent, evidence-led accuracy work. The PPU
+multiplexes its low VRAM address pins with the data pins; an external
+74LS373-class octal latch captures the low address bits on the address-latch-enable
+(ALE) half of each two-cycle VRAM access, and the PPU drives only the high bits on
+the read half. When those halves desync (a mid-fetch `$2006` update, or a `$2007`
+read overlapping the fetch cadence), the PPU reads a "hybrid" address it never
+coherently drove.
+
+Two AccuracyCoin tests ("ALE + Read", `$0491`; "Hybrid Addresses", `$0492`)
+exercise exactly this, and RustyNES passes both by modeling the octal latch
+explicitly. The instructive part is *how the references differ* (ADR 0030):
+Mesen2 also passes these tests, but via a persistent internal bus-address
+abstraction rather than a literal latch; higan and ares, by contrast, genuinely
+fail them (higan blocks `$2007` during rendering and models no bus latch; ares
+does not implement the `$2006` hybrid corruption). RustyNES deliberately took the
+transistor-literal modeling approach of TriCNES — the die-level emulator by the
+AccuracyCoin author — over the higher-level abstraction, because a physical
+octal-latch model is what makes the hybrid-address cases fall out of the design
+rather than being special-cased. It promoted the 2-cycle-ALE fetch to the
+unconditional default in v2.0.3 (both prior experimental flags retired). See ADR
+0030 for the campaign audit. This is independent modeling, not copying: RustyNES
+re-derived the physical mechanism from die-level evidence, converging with some
+references and diverging from others on the strength of the hardware model rather
+than by following any single one of them.
+
+### 2.3 The sprite-evaluation FSM and OAM data bus (ADR 0034)
+
+RustyNES models the PPU's sprite-evaluation datapath as an explicit per-dot state
+machine (secondary-OAM clear at dots 1-64, evaluation at 65-256, sprite fetch at
+257-320) plus an isolated OAM-data-bus model that reproduces what `$2004` returns
+while the screen is drawn. A standing field-vs-schema audit (2.4) found that this
+FSM state and the OAM data-bus latch were not fully serialized, which is what let
+AccuracyCoin regress under run-ahead; serializing them (PPU snapshot version 8)
+restored a full pass through run-ahead as well as without it. The model is
+implemented from the NESdev-documented sprite-evaluation sequence; see ADR 0034.
+
+### 2.4 Machine-checked accuracy honesty: mapper tiering and schema audits (ADR 0011)
+
+Rather than claim uniform accuracy, RustyNES classifies every mapper family into
+**Core / Curated / BestEffort** tiers and enforces, via a CI honesty gate, that
+the suite cannot advertise support or accuracy it does not actually verify against
+a test ROM or oracle. As of the v2.2.x line this covers 172 mapper families across
+the three tiers (see `docs/STATUS.md` for the current split and the authoritative
+counts). A second machine check, `snapshot_schema_audit`, parses the emulator's
+live struct fields and fails the build if any new stateful field is not covered by
+the save-state schema — the mechanism that mechanically surfaced the gap in 2.3.
+Honesty here is a build gate, not a promise. See ADR 0011.
+
+### 2.5 Determinism as a hard contract (the `#![no_std]` core)
+
+The chip stack (`rustynes-{cpu,ppu,apu,mappers,core}`) is `#![no_std]` +
+`extern crate alloc`, with a strictly one-directional dependency graph in which the
+Bus owns all mutable subsystems and each chip borrows the narrowest trait it needs.
+The contract is exact: same seed + ROM + input sequence yields a bit-identical
+framebuffer and audio stream. Power-on CPU/PPU phase alignment is drawn from a
+seeded PRNG and preserved across reset, save-state, TAS replay, and netplay
+rollback. Wall-clock, OS RNG, thread scheduling, and unordered-map iteration are
+kept out of the core by construction. This is what makes the entire test and
+regression apparatus meaningful, and it is enforced by the `no_std` cross-compile
+job (`thumbv7em-none-eabihf`, no default features) in CI. See
+`docs/architecture.md`.
+
+### 2.6 Measure-first performance, including documented rejections
+
+RustyNES treats performance as an accuracy-subordinate, evidence-gated activity: a
+change is adopted only if it is Criterion-stable above a threshold **and** proven
+byte-identical by the differential net, and it is documented in `docs/performance.md`
+*whether or not it cleared the bar*. Concrete outcomes:
+
+- The specialized fast PPU dot path was measured at roughly **-11.3%** frame time
+ on a rendering-heavy workload (clean-host Criterion, v2.2.3), differential-tested
+ bit-identical every frame, and only then promoted to the default and exposed to
+ users.
+- Two optimizations were **measured and rejected with their numbers**: an
+ `emit_pixel` bounds-check elision made the shipped default *slower*
+ (+4.32% / +3.35% on the fast workloads, p <= 0.02), and a `cpu_clock`
+ micro-optimization was capped at <= 1.9% with the textbook wins already in place.
+- Release builds ship PGO-optimized Linux binaries only when the >3%-and-byte-
+ identical gate passes; a same-runner relative frame-time regression gate closes a
+ hole the deliberately-loose absolute ceiling left open.
+
+Publishing rejected optimizations with p-values is unusual and is itself a form of
+originality: the record shows the discipline, not just the wins. See
+`docs/performance.md`.
+
+### 2.7 Signal-level video and expansion-audio calibration
+
+RustyNES includes a raw NTSC composite signal-decode path (`rustynes-ppu::raw_signal`)
+feeding a naga-validated WGSL CRT-shader stack, and a decibel oracle that asserts
+measured expansion-audio channel levels against hardware / Mesen2 targets (which,
+for the Sunsoft 5B, required widening the mapper audio-mix path to `i32` to
+represent full-scale tone without overflow). The base 2A03 NTSC output remains
+byte-identical across these additions. See `docs/performance.md`, `docs/ppu-2c02.md`,
+and the audio expansion oracle in `crates/rustynes-test-harness`.
+
+### 2.8 Rollback netplay kept out of the deterministic core
+
+Netplay's dynamic rate control, run-ahead, and snapshot-restore orchestration live
+entirely in the frontend; the core's synthesis never sees them. This is what lets
+the same deterministic core serve save-states, TAS replay, and rollback netplay
+without any of them perturbing byte-identity. Keeping timing jitter and rate
+control at the frontend boundary — never in the core — is a deliberate ownership
+decision (`docs/frontend.md`, `docs/architecture.md`).
+
+### 2.9 Explicit, versioned save-state schema (ADR 0028)
+
+Save-state and movie formats carry explicit version epochs. A pre-v2.0.0 slot
+fails to load with a clear error rather than silently misinterpreting stale bytes,
+and additive schema growth (e.g. the PPU snapshot version 8 tail in 2.3) upconverts
+older blobs where compatible. The one intentional format break is the v2.0.0
+MAJOR boundary; see ADR 0028.
+
+---
+
+## 3. How the project was built
+
+RustyNES did not begin as a copy to be modified. Its development record shows a
+research-first, test-driven, verify-last cadence, and — importantly for the "not a
+port" claim — the emulation core was **replaced wholesale** partway through the
+project rather than incrementally grown from a single seed.
+
+**Research before code.** The `ref-docs/` tree holds an immutable hardware and
+emulation reference corpus (a 60-plus-source research report plus a set of
+emulator technical studies). Behaviors were specified against this documentation
+and against public test ROMs before implementation. Corrections to the corpus land
+as new dated supplements, never in-place rewrites, so the research record stays
+auditable.
+
+**Test-as-spec.** For accuracy work the failing test-ROM expectation is pinned
+first, then code is written until it passes; where the prose docs and a passing
+test ROM disagree, the ROM wins and the docs are corrected. The suites in
+`tests/roms/` (blargg, kevtris, mmc3_test_2, AccuracyCoin, and others) are treated
+as the closed-form definition of "cycle-accurate."
+
+**A documented lineage, honestly labeled.** The current core is a synthesis, cut
+as v1.0.0 on 2026-06-13 (`docs/v1.0.0-synthesis-handoff-2026-06-13.md`), that
+replaced the earlier v0.8.x emulation core with a cycle-accurate engine developed
+through documentary stages v0.9.0-v0.9.7. Two cautions are recorded so the history
+is not misread:
+
+- The engine lineage carries its own internal "v1.x / v2.x" accuracy milestones
+ that are *not* RustyNES release versions; they are folded into the v0.9.x stages
+ and shipped as the v1.0.0 production core.
+- Consequently, **two distinct "v2.0"s exist and must not be conflated**: the
+ engine-lineage master-clock work (which shipped *as* the v1.0.0 core), and
+ RustyNES's own **v2.0.0 "Timebase"** release (2026-07-03), which *replaces* that
+ same dot-lockstep scheduler with the one-clock model of 2.1.
+
+**Then continuous, gated deepening.** After v1.0.0 came the platform ports
+(Android, iOS, the libretro/RetroArch core), the v2.0.0 Timebase rewrite, and the
+v2.1.x "Fathom" accuracy line capped by the v2.2.0 "Capstone" milestone — each
+release additive or default-off on the shipped core, verified NTSC-byte-identical
+(AccuracyCoin 141/141) except where a break was explicitly announced (v2.0.0). The
+decision record for all of this is `docs/adr/` (0001 through 0034 as of writing),
+backed by over a hundred implementation-audit logs under `docs/audit/` (about
+113 at time of writing). The
+current release is v2.2.5 "Colophon" (this release); `docs/STATUS.md` is the source of truth for
+per-suite counts.
+
+---
+
+## 4. Independence: oracle versus port
+
+The distinction that matters for the "not just a port" question is **how** each
+reference was used. RustyNES's sources fall into three categories, and the source
+tree is written so a reader can tell which applies at any given site.
+
+1. **Implemented from public hardware documentation.** The overwhelming majority
+ of chip, mapper, and peripheral behavior is written from the NESdev wiki,
+ Disch's mapper write-ups, published datasheets (e.g. the Xicor/Intersil I2C
+ serial EEPROMs, the Yamaha YM2413), the documented 6502 unofficial-opcode
+ behavior, and the Visual 6502 / Visual 2C02 die studies — then pinned to public
+ test ROMs. Hardware behavior is factual; every accurate emulator necessarily
+ agrees on it.
+2. **Ported from a permissively-licensed project, with attribution.** A small,
+ named set of components is genuinely incorporated as a Rust port under a
+ compatible (MIT) license — principally TriCNES (the PPU address/data-multiplex
+ and OAM-corruption models; see `crates/rustynes-ppu/src/ppu.rs`), the emu2413
+ OPLL synthesizer for VRC7 audio, and the rcheevos RetroAchievements runtime.
+ Each carries an in-source attribution and a `NOTICE` entry (Section 5.3).
+3. **Consulted only as a behavioral oracle.** Copyleft-licensed emulators
+ (Mesen2/MesenCE and higan and GeraNES under GPLv3; FCEUX, Nestopia UE, and
+ puNES under GPLv2) — plus ares (ISC) — were run to observe and cross-check
+ documented behavior when test-ROM results were ambiguous. No code from any of
+ them is incorporated.
+
+The octal-latch work in 2.2 illustrates the difference between categories 2 and 3:
+RustyNES took TriCNES's transistor-literal *modeling approach* for the ALE fetch
+(a permissively-licensed influence) while treating Mesen2, higan, and ares purely
+as oracles to check the result — passing `$0491` / `$0492` where higan and ares
+fail, and by a more physical model than Mesen2's abstraction. That is independent
+modeling, not copying.
+
+**A note on the provenance record.** The in-source provenance comments were
+audited to make sure they accurately reflect the categories above. A number of
+comments in the shipping crates had described hardware-behavior implementations
+(CPU unstable stores, the PPU sprite-evaluation and OAM models, and numerous
+mapper register decoders) as "ports of" a copyleft reference — Mesen2 (GPLv3), or
+FCEUX / puNES (GPLv2) — which overstated the relationship for behaviors that are,
+in fact, implemented from public hardware documentation. Those comments were
+corrected to cite the public hardware source and to record the copyleft emulator
+as a behavioral cross-check rather than a code source; GeraNES (GPLv3) was added
+to the disclosed oracle set; and `NOTICE` was extended to state the oracle-versus-
+incorporated posture explicitly and to reproduce the MIT notices for the
+incorporated components (Section 5.3). These corrections changed only comments and
+the attribution file; the emulator's behavior is byte-identical, re-verified
+against AccuracyCoin (141/141, including run-ahead), the nestest golden log
+(0-diff), and the dual-path differential net. The video shader stack and the
+NTSC-decode filters are a separate provenance matter, addressed in Section 5.6.
+
+---
+
+## 5. License compliance
+
+### 5.1 RustyNES's own license
+
+RustyNES is dual-licensed **MIT OR Apache-2.0** (author: DoubleGate), the
+conventional permissive dual-license for the Rust ecosystem. This choice is
+deliberately compatible with the permissively-licensed components it incorporates
+and deliberately does *not* subject the project to the copyleft terms of the
+reference emulators it merely consulted.
+
+### 5.2 Reference emulators: oracle use, not code reuse
+
+The projects below were used only as behavioral oracles / accuracy references. No
+source code from any of them is incorporated into RustyNES; this is stated in
+`NOTICE` and reflected in the in-source comments (Section 4).
+
+| Reference emulator | License | Use in RustyNES |
+| --- | --- | --- |
+| Mesen2 / MesenCE | GPLv3 | Behavioral oracle / accuracy cross-check only |
+| higan | GPLv3 | Accuracy reference for scheduler structure |
+| ares | ISC | Accuracy reference for scheduler structure |
+| GeraNES | GPLv3 | Behavioral oracle / cross-check for several mapper boards |
+| FCEUX | GPLv2 | Behavioral oracle for legacy-compat behaviors |
+| Nestopia UE | GPLv2 | Behavioral oracle |
+| puNES | GPLv2 | Behavioral oracle |
+
+Using a GPL-licensed program to *observe* hardware behavior, and then implementing
+that publicly-documented behavior independently, does not create a derivative work
+of that program. The point of the Section 4 audit was to make the source comments
+say precisely that, so nothing in the tree could be read as claiming a copyleft
+source was translated into this permissive project.
+
+### 5.3 Incorporated third-party components (permissive)
+
+These works are genuinely incorporated and are attributed in `NOTICE` with their
+copyright notices and the MIT permission text:
+
+| Component | License | Copyright | Where |
+| --- | --- | --- | --- |
+| emu2413 v1.5.9 | MIT | 2020 Mitsutaka Okazaki | `crates/rustynes-apu/src/opll.rs` (Rust port; VRC7 audio, ADR 0006) |
+| TriCNES (commit 9199870) | MIT | 2025 Chris Siebert | `crates/rustynes-{ppu,cpu,core}` (ported models) + vendored golden oracle |
+| rcheevos v12.3.0 | MIT | 2018 RetroAchievements.org | `crates/rustynes-cheevos/vendor/rcheevos/` (optional `retroachievements` feature) |
+| Font Awesome Free | its own license | Fonticons, Inc. | `crates/rustynes-frontend/assets/fonts/` (bundled glyphs) |
+
+The emu2413 port is a pure-Rust port of the upstream MIT C source (ADR 0006),
+distributed under that MIT license; the upstream MIT notice is now reproduced in
+`NOTICE` as that file's own comment claims. TriCNES is both a ported source (its ALE/octal-latch,
+OAM-corruption, and DMA-dispatch models) and a vendored golden oracle for the
+tests it grounds. rcheevos is compiled only when the RetroAchievements feature is
+enabled and keeps its own in-tree `LICENSE`.
+
+### 5.4 Test ROMs
+
+Every ROM committed under `tests/roms/` is a public-domain work released
+specifically for validating NES emulators, catalogued per-author in
+`tests/roms/LICENSES.md` (blargg's suites, kevtris/AccuracyCoin material, and
+others). **No commercial Nintendo software is bundled**, and none ever should be;
+users who want to test against commercial dumps they own place them in the
+gitignored `tests/roms/external/`. The AccuracyCoin battery itself is MIT-licensed
+(Chris Siebert / 100thCoin).
+
+### 5.5 Vendored and immutable trees
+
+RustyNES vendors several third-party source trees whose value depends on their
+being byte-identical to upstream (the TriCNES golden oracle, the rcheevos runtime,
+upstream test-ROM READMEs, and the `ref-docs/` / `ref-proj/` reference material).
+These are protected from accidental reformatting: `.markdownlintignore` exempts
+them from markdown linting, a shared `exclude` anchor in the pre-commit
+configuration keeps the whitespace-rewriting hooks off content the project did not
+author, and `ref-proj/` is gitignored while `ref-docs/` is treated as immutable
+(corrections land as dated supplements). This preserves both the integrity of the
+oracles and the upstream provenance of the vendored code.
+
+### 5.6 Video shaders and NTSC-decode filters
+
+The optional CRT shader stack (`crates/rustynes-gfx-shaders/`) and the NTSC-decode
+filters (`crates/rustynes-frontend/src/ntsc_bisqwit.rs`, `ntsc_lmp88959.rs`)
+reproduce the *look* of well-known community shaders and filters — CRT-Royale
+(TroggleMonkey, GPLv2+), crt-guest-advanced (guest.r), Sony Megatron
+(MajorPainInTheCactus), Bisqwit's NES composite model, and EMMIR's NTSC-CRT
+(permissive). These were reviewed at the source level. Each is a single
+fullscreen pass built on RustyNES's own uniform / pipeline conventions and is
+structurally incompatible with being a translation of the upstream *multi-pass*
+shader source. Because copyright protects code expression — not a visual look or
+a rendering technique — these are independent reimplementations, not derivative
+works of the upstream code, even where an upstream is copyleft; no upstream
+shader source is incorporated. The one comment that had implied otherwise (an
+NTSC filter reading "ported verbatim from Bisqwit's C ... as implemented by
+Mesen2") was corrected: those tables encode the two-level NES composite signal
+documented at the NESdev wiki ("NTSC video") — a hardware model, not copied code.
+The in-source comments were reworded accordingly, and `NOTICE` now credits each
+project as a "visual influence, independently reimplemented (no code
+incorporated)". All of these features are optional and default-off; none affects
+the deterministic emulation core, its `AccuracyCoin` results, or the base NTSC
+framebuffer, which are unchanged.
+
+---
+
+## 6. Conclusion
+
+RustyNES is an independent emulator, not a port. Its scheduler, ownership model,
+determinism contract, accuracy-honesty gates, and measured-performance discipline
+are its own, recorded as ADRs and enforced in CI rather than asserted. Where it
+borrows, it borrows narrowly and openly, under compatible permissive licenses,
+with attribution in both source and `NOTICE`. Where it consulted copyleft
+references, it used them as oracles to check publicly-documented hardware behavior,
+and — as the octal-latch case shows — it was willing to disagree with a leading
+reference when the transistor-level evidence pointed the other way.
+
+The strongest evidence for originality is not any single clever routine; it is the
+system that surrounds every routine: research before code, a failing test pinned
+first, a hard byte-identity contract, honesty gates that fail the build rather than
+the reader, and a decision record that documents the rejections alongside the wins.
+That system is what makes RustyNES's accuracy claims checkable — and it is what
+this project built for itself.
diff --git a/tests/roms/LICENSES.md b/tests/roms/LICENSES.md
index 2868ab67..6b93a468 100644
--- a/tests/roms/LICENSES.md
+++ b/tests/roms/LICENSES.md
@@ -117,10 +117,11 @@ These ROMs are cartridge-PCB-assembly tests that detect the mapper via
mirroring tests, then size PRG/CHR and exercise bank reachability.
Output is **visual** (on-screen text + Morse-coded audio beeps), not
the blargg `$6000` status protocol — so the integration tests in
-`crates/nes-test-harness/tests/holy_mapperel.rs` are smoke gates.
+`crates/rustynes-test-harness/tests/holy_mapperel.rs` are smoke gates.
-We exclude `M28*`, `M78.3*`, `M118*`, `M180*` because the project does
-not implement those mappers (per `docs/STATUS.md` §"Mapper coverage").
+The Holy Mapperel ROMs for mappers 28, 78.3, 118, and 180 are also committed
+under `extra/mappers/` (same Damian Yerrick zlib license) for manual
+investigation; see "Additional committed test-ROM directories" below.
## DPCM Letterbox (Damian Yerrick / Tepples)
@@ -153,7 +154,9 @@ deterministic frame-hash visual smoke (no input) in
| `AccuracyCoin/sub-tests/frame-counter-irq.nes` | derived from `AccuracyCoin.asm` (suite 13 / test 2 — `TEST_FrameCounterIRQ`) | NROM (0) | derivative of Chris Siebert | MIT (inherits) |
| `AccuracyCoin/sub-tests/apu-reg-activation.nes` | derived from `AccuracyCoin.asm` (suite 13 / test 6 — `TEST_APURegActivation`) | NROM (0) | derivative of Chris Siebert | MIT (inherits) |
-The four sub-test ROMs under `AccuracyCoin/sub-tests/` are derivative
+The sub-test ROMs under `AccuracyCoin/sub-tests/` (26 in total; the four
+with dedicated Rust regression tests are tabulated above, and the rest follow
+the identical build + MIT-inheritance pattern) are derivative
works produced by patching the upstream `AccuracyCoin.asm` source to
jump directly into a single target test at boot (bypassing both the
menu-screen and the full-battery loop). They are built by
@@ -163,7 +166,7 @@ script; the patched `AutomaticallyRunEveryTestInROM` routine is
streamlined to "set Y=suite_idx, X=test_idx, JSR RunTest, halt" and the
boot path's `InfiniteLoop` spin is redirected to enter that wrapper
immediately. Each sub-test ROM reaches its target test by frame ~30 on
-RustyNES (verified via `crates/nes-test-harness/src/bin/
+RustyNES (verified via `crates/rustynes-test-harness/src/bin/
validate_sub_test_rom.rs`), unblocking the Session-22 Mesen2 wall-time
oracle blocker for the v1.0.0-final Phase 3 / Phase 4 work.
@@ -172,15 +175,12 @@ fetched 2026-05-10). Upstream `LICENSE` file is the MIT License
("Copyright (c) 2025 Chris Siebert"); the full text is vendored
alongside the .nes file.
-AccuracyCoin is a single-NROM-cartridge battery of ~139 NES accuracy
-tests. The ROM is **interactive** — pass/fail results are reported
-visually (on-screen "PASS"/"FAIL" + hex error codes) and the user
-navigates the test menu with D-Pad + A + Start. There is no `$6000`
-status protocol, so the integration test in
-`crates/nes-test-harness/tests/accuracycoin.rs` is a boot-without-crash
-smoke gate only. v1.0.0 will need a pixel-decoding harness to extract
-the pass rate (currently un-measured; the ≥ 90% bar is documented in
-`docs/STATUS.md` §"Version policy").
+AccuracyCoin is a single-NROM-cartridge battery of NES accuracy tests. The ROM is
+**interactive** — pass/fail results are reported visually (on-screen "PASS"/"FAIL"
+plus hex error codes) with no `$6000` status protocol. The integration test in
+`crates/rustynes-test-harness/tests/accuracycoin.rs` decodes the per-test result
+state from RAM and asserts the measured pass rate, which RustyNES holds at
+**141/141 (100.00%)** (see `docs/STATUS.md`).
## "full palette" ROMs
@@ -255,6 +255,31 @@ quirk that affects mappers (like MMC3) that depend on A12 for IRQ
counter clocking. The MMC1 path is the control case (no A12-based
IRQ). Sourced from `MMC1_A12/` in `nes-test-roms`.
+## Additional committed test-ROM directories (blanket coverage)
+
+Beyond the individually-tabulated suites above, the repository commits the
+directories below under `tests/roms/`. All are public-domain or permissive
+homebrew from the same authors and aggregator already documented in this file;
+none is commercial software.
+
+- `nes-test-roms/` (89 `.nes` committed): the committed subset of the working
+ clone of `christopherpow/nes-test-roms`, the public-domain/permissive aggregator
+ named in this file's header. Each suite retains its own author and license (blargg,
+ kevtris, tepples, Damian Yerrick, NewRisingSun, bbbradsmith, and others); the
+ suites RustyNES actively gates are tabulated in the sections above.
+- `extra/` (56 `.nes`: `apu/` 19, `cpu/` 3, `mappers/` 23, `ppu/` 11): a curated
+ overflow of the same suites — additional blargg APU/CPU/PPU tests (public
+ domain), Damian Yerrick Holy Mapperel variants (zlib, including the mapper 28
+ / 78.3 / 118 / 180 boards), and related homebrew — kept out of the
+ actively-gated set but retained for manual investigation.
+- `AccuracyCoin/sub-tests/` (26 `.nes`): the boot-into-one-test derivatives of
+ `AccuracyCoin.asm` described above, all MIT (inheriting upstream).
+
+The authoritative running total is 328 committed `.nes` files under
+`tests/roms/` (per `git ls-files`; excluding the gitignored `tests/roms/external/`
+and any untracked clone contents); no commercial
+ROM is among them.
+
## Notes
- The `tests/roms/external/` directory is gitignored and reserved for
diff --git a/tests/roms/README.md b/tests/roms/README.md
index 72c303a1..47de29af 100644
--- a/tests/roms/README.md
+++ b/tests/roms/README.md
@@ -7,7 +7,7 @@ under a committed corpus is under a public-domain / CC0 / MIT / BSD /
zlib / equivalently-permissive license. Full provenance and licensing
is in [`LICENSES.md`](./LICENSES.md).
-The integration tests in `crates/nes-test-harness/tests/` consume these
+The integration tests in `crates/rustynes-test-harness/tests/` consume these
files directly. The full workspace test count (510 + 6 `#[ignore]`'d
expected-fails across 34 suites with `--features test-roms`) is gated
on this corpus.
@@ -22,7 +22,7 @@ on this corpus.
| [`holy_mapperel/`](./holy_mapperel/) | Damian Yerrick's "Holy Mapperel" cartridge-PCB-assembly tests (mapper-detection + bank-reachability). 17 ROMs covering mappers 0, 1, 2, 3, 4, 7, 9, 10, 34, 66, 69. | Damian Yerrick / tepples | zlib |
| [`mmc5/`](./mmc5/) | MMC5 (mapper 5) accuracy suite from `christopherpow/nes-test-roms`: split-screen, ExRAM modes, scanline IRQ. | Various (aggregator) | Public domain |
| [`accuracycoin/`](./accuracycoin/) | Chris Siebert's 144-test single-NROM AccuracyCoin battery — the **single source of truth** for the v0.9.x → v1.0.0 quality bar. | Chris Siebert (100thCoin) | MIT |
-| [`AccuracyCoin/`](./AccuracyCoin/) | The upstream `SOURCE_CATALOG.tsv` (144 test-name catalog parsed by the RAM-direct decoder) plus a copy of `AccuracyCoin.nes` for symmetry. The test catalog is `include_str!`ed by `nes-test-harness::accuracy_coin_catalog`. | Chris Siebert | MIT |
+| [`AccuracyCoin/`](./AccuracyCoin/) | The upstream `SOURCE_CATALOG.tsv` (144 test-name catalog parsed by the RAM-direct decoder) plus a copy of `AccuracyCoin.nes` for symmetry. The test catalog is `include_str!`ed by `rustynes-test-harness::accuracy_coin_catalog`. | Chris Siebert | MIT |
| [`audio-tests/`](./audio-tests/) | Brad Smith (`bbbradsmith`)'s `nes-audio-tests` corpus — expansion-audio relative-loudness comparisons, VRC7 / N163 / FME-7 / MMC5 audio quirks, APU DAC linearity. Covers mappers 5, 19, 24, 26, 69, 85. | Brad Smith | "Freely redistributed and modified for any purpose" (effectively PD) |
| [`m22/`](./m22/) | NewRisingSun's VRC2 (mapper 22) CHR-banking smoke test. | NewRisingSun (aggregated in `christopherpow/nes-test-roms`) | Public domain (aggregator) |
| [`mmc1_a12/`](./mmc1_a12/) | tepples's MMC1 + PPU A12 transition test (control case for the MMC3 A12-IRQ axis). | tepples (aggregated) | Public domain (aggregator) |
@@ -85,10 +85,10 @@ cargo test --workspace
cargo test --workspace --features test-roms
# Single corpus:
-cargo test -p nes-test-harness --features test-roms blargg
-cargo test -p nes-test-harness --features test-roms mmc3
-cargo test -p nes-test-harness --features test-roms accuracy_coin
-cargo test -p nes-test-harness --features test-roms,commercial-roms external_real_games
+cargo test -p rustynes-test-harness --features test-roms blargg
+cargo test -p rustynes-test-harness --features test-roms mmc3
+cargo test -p rustynes-test-harness --features test-roms accuracy_coin
+cargo test -p rustynes-test-harness --features test-roms,commercial-roms external_real_games
```
The `commercial-roms` feature is gated separately precisely because it
diff --git a/to-dos/ROADMAP.md b/to-dos/ROADMAP.md
index 156be199..785c72ea 100644
--- a/to-dos/ROADMAP.md
+++ b/to-dos/ROADMAP.md
@@ -18,13 +18,17 @@ engine + the ported desktop-UX shell + production polish) → **`v1.1.0`
(+ the `v1.4.1` patch) **→ `v1.5.0` "Lens" → `v1.6.0` "Studio" → `v1.7.0`
"Forge"** (+ the `v1.7.1` patch) **→ `v1.8.0` … `v1.8.9` "Atlas"** (the Android
platform train) **→ `v1.9.0` … `v1.9.9` "Workshop"** (the iOS/iPadOS TestFlight
-train) **→ `v1.10.0` "Arcade"** (the native Libretro core) — the additive,
-off-by-default feature/platform releases on that core, of which **`v1.10.0`
-"Arcade" is the current shipped tag**. The forward path then lands the real
-**RustyNES `v2.0.0` "Timebase"** (the one-clock/every-cycle-bus-access
-scheduler collapse, ADR 0002/0029) — **code-complete on `main` as of
-2026-07-03, tag pending** — then the **v2.0.1 → v2.1.0** mobile-finalization
-train that launches the Android + iOS apps jointly at **v2.1.0**, and beyond.
+train) **→ `v1.10.0` "Arcade"** (the native Libretro core) — all additive,
+off-by-default feature/platform releases on the v1.0.0 core. The forward path
+then landed the breaking **RustyNES `v2.0.0` "Timebase"** (the
+one-clock/every-cycle-bus-access scheduler collapse, ADR 0002/0029, shipped
+2026-07-03), the **v2.0.1 → v2.0.9 "Harbor"** mobile-finalization train, the
+**v2.1.0 → v2.1.10 "Fathom"** accuracy line, the **v2.2.0 "Capstone"** milestone
+that closed the "deepen the existing project" run, and the maintenance /
+distribution / provenance patches **v2.2.1 → v2.2.5** — of which **`v2.2.5`
+"Colophon" (provenance, licensing, and documentation integrity) is the current
+shipped tag**. The **joint Google Play + Apple App Store + AltStore PAL + F-Droid
+launch** (activating `rustynes-monetization`) is the future **`v2.3.0`**.
Where the detailed sections below carry the inbound engine's own `v1.x`/`v2.x`
tags, read them as upstream engine history (its v2.0–v2.8 line), which maps
onto the integration stages roughly as: engine v1.0.0 → RustyNES v0.9.0;