From 028005e89894aebe88913e4d60bcd068aa615fbe Mon Sep 17 00:00:00 2001
From: DoubleGate
Date: Mon, 3 Aug 2026 20:16:58 -0400
Subject: [PATCH 1/3] =?UTF-8?q?release:=20v2.2.5=20"Colophon"=20=E2=80=94?=
=?UTF-8?q?=20provenance,=20licensing,=20and=20documentation=20integrity?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
A provenance, licensing, and documentation-integrity release, prompted by
community review (NESdev forums) of the project's licensing and its AI-assisted
origins. It corrects how RustyNES describes its own provenance — in source
comments, in NOTICE, and in the docs — so the written record matches what the
code actually is. There are ZERO emulation-core changes: the deterministic
`#![no_std]` chip stack, the 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 edit
(AccuracyCoin full + run-ahead, the nestest golden log, the dual-path
fast_dotloop differential, and the snapshot-schema audit), and the whole tree
passes `cargo fmt`, `clippy -D warnings` (workspace + retroachievements /
scripting / hd-pack combos), `rustdoc -D warnings`, markdownlint, and the
`thumbv7em-none-eabihf` no_std cross-compile.
Motivation
A reviewer observed — correctly — that a number of in-source comments described
implementations as "ports" of other emulators, including GPL-licensed ones
(Mesen2 GPLv3, puNES GPLv2), and that the project's heavy AI assistance was not
disclosed. Rather than contest the point, the entire tree was audited (three
independent read-only passes over source, docs, assets, and vendored trees) and
the provenance record was corrected. The substantive finding: 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; reference emulators were consulted only
as behavioral ORACLES. Some comments mischaracterized that as "ported from X."
No GPL-licensed emulator source is incorporated into RustyNES.
Source-comment corrections (behavior byte-identical — comments only)
- CPU: the SH* unstable-store helper (cpu.rs) reframed from "faithful port of
Mesen2's SyaSxaAxa" to the documented 6502 unstable-store algorithm, pinned by
AccuracyCoin's own sub-test, using RustyNES's own bus cycle-count machinery.
- PPU: the OAM-data-bus field block, the OAM decay-on-read/write hooks, and the
sprite-evaluation per-dot driver (ppu.rs) reframed from "port of Mesen2's
ReadSpriteRam / WriteSpriteRam / ProcessSpriteEvaluation" to the NESdev-
documented sprite-evaluation and OAM-DRAM-decay models; Mesen2 private-member
cross-references and source line-number citations removed. The generated NTSC
palette (palette_gen.rs) re-cited to the nesdev-documented Bisqwit method with
ares demoted to an oracle.
- Mappers: m035 (JY InvertPrgBits), m016 (X24C01/24C02 I2C EEPROM), kaiser
(Waixing253), m176 (FK23C + the inner COOLBOY block), m268, m513 (TXC),
mmc3_clones (Sachen 8259), multicart_discrete + ntdec (several NTDEC/TXC/BMC
boards), sachen_discrete (JV001, formerly "Ported bit-for-bit from puNES
JV001.c"), lib.rs (JY Company dispatch), unif.rs (board-name table), fds.rs
(per-CRC drive table), and m069 (Sunsoft-5B audio target, re-cited to the
blargg nonlinear mixer) — all reframed to register maps / documented hardware
behavior cross-checked against reference emulators as oracles.
- APU: 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 says so.
- pgo_trainer.rs and source_map.rs reworded (independent PGO harness; an
independent ca65/cc65 .dbg importer — format interop, not a code port).
- A machine-absolute path leak into ref-proj/Mesen2 in opll.rs was removed
(retargeted to upstream emu2413), and its ADR cross-reference corrected.
Video shaders and NTSC filters (creative expression — reviewed at source level)
The CRT shaders (crt_royale, crt_guest, megatron) and the Bisqwit / EMMIR NTSC
filters were reviewed. Each is a single-pass shader 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. They were reworded from
"port / condensation of X" to independent single-pass reimplementations of the
look, and the one comment claiming tables were "ported verbatim from Bisqwit's C
... as implemented by Mesen2" was corrected — those tables encode the NESdev-
documented two-level NES composite signal. No upstream shader source is
incorporated.
NOTICE (rewritten for a complete, accurate attribution posture)
- Reference emulators (Mesen2/MesenCE, higan, GeraNES, ares, FCEUX, Nestopia UE,
puNES) disclosed as behavioral oracles only — no code incorporated. GeraNES
(GPL-3.0-only), cited across ~58 files, was previously undisclosed.
- Incorporated permissive components attributed with copyright + the MIT text:
emu2413 v1.5.9 (Okazaki), TriCNES (Chris Siebert, commit 9199870), rcheevos
v12.3.0 (RetroAchievements.org).
- Bundled fonts (Font Awesome; Press Start 2P / OFL) and bundled test ROMs
(AccuracyCoin MIT; Damian Yerrick Holy Mapperel zlib; blargg / kevtris PD)
attributed, and the CRT-shader / NTSC-filter visual influences credited as
independent reimplementations.
Documentation and assets
- New docs/originality-and-provenance.md: an honest 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-vs-port
classification of every reference, the full license posture, an explicit
AI-assistance disclosure, and a "not a superiority claim" note.
- README: added an AI-assistance disclosure; removed a comparison graphic with
inaccurate details; corrected the mislabeled "sub-cycle accuracy in action"
screenshot caption (an early-development image); toned down overstated
language; synced Acknowledgments with NOTICE; bumped the version badge and the
citation version.
- 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; added blanket coverage for the ~150
previously-undocumented committed test ROMs (502 committed .nes total, none
commercial).
- STATUS.md, AGENTS.md (canonical; CLAUDE.md / GEMINI.md symlink to it), and
to-dos/ROADMAP.md synced to v2.2.5 current; .github/release-notes/v2.2.5.md
authored for the automated GitHub Release body.
- Android app assets gained the SIL OFL text for Press Start 2P (it shipped
without the license text OFL 1.1 requires travel with the font; desktop and
iOS already carried it).
Version and hygiene
- Workspace version bumped 2.2.4 -> 2.2.5 (single-sourced; 18 crates inherit),
Cargo.lock regenerated, and the libretro rustynes_libretro.info
display_version advanced to v2.2.5.
- .gitignore gained a nested per-crate `crates/*/target/` rule, and two orphaned
build artifacts accidentally tracked under
crates/rustynes-test-harness/target/ were untracked.
Co-Authored-By: Claude Opus 4.8
---
.github/release-notes/v2.2.5.md | 107 ++++
.gitignore | 2 +
AGENTS.md | 4 +-
CHANGELOG.md | 55 ++
Cargo.lock | 36 +-
Cargo.toml | 2 +-
NOTICE | 126 +++++
README.md | 116 ++--
.../app/src/main/assets/PressStart2P-OFL.txt | 93 ++++
crates/rustynes-apu/src/blip.rs | 7 +-
crates/rustynes-apu/src/opll.rs | 6 +-
crates/rustynes-cpu/src/cpu.rs | 13 +-
.../src/debugger/source_map.rs | 3 +-
crates/rustynes-frontend/src/ntsc_bisqwit.rs | 12 +-
crates/rustynes-frontend/src/ntsc_lmp88959.rs | 8 +-
crates/rustynes-gfx-shaders/src/bisqwit.wgsl | 2 +-
.../rustynes-gfx-shaders/src/crt_guest.wgsl | 12 +-
.../rustynes-gfx-shaders/src/crt_royale.wgsl | 15 +-
crates/rustynes-gfx-shaders/src/crt_stack.rs | 13 +-
crates/rustynes-gfx-shaders/src/lib.rs | 6 +-
crates/rustynes-gfx-shaders/src/megatron.wgsl | 8 +-
.../rustynes-libretro/rustynes_libretro.info | 2 +-
crates/rustynes-mappers/src/fds.rs | 6 +-
crates/rustynes-mappers/src/kaiser.rs | 4 +-
crates/rustynes-mappers/src/lib.rs | 5 +-
.../rustynes-mappers/src/m016_bandai_fcg.rs | 11 +-
crates/rustynes-mappers/src/m035_jy_asic.rs | 19 +-
.../rustynes-mappers/src/m069_sunsoft_fme7.rs | 7 +-
crates/rustynes-mappers/src/m176_bmc_fk23c.rs | 15 +-
.../rustynes-mappers/src/m268_bmc_coolboy.rs | 4 +-
.../rustynes-mappers/src/m513_sachen_9602.rs | 4 +-
crates/rustynes-mappers/src/mmc3_clones.rs | 4 +-
.../src/multicart_discrete.rs | 4 +-
crates/rustynes-mappers/src/ntdec.rs | 15 +-
.../rustynes-mappers/src/sachen_discrete.rs | 7 +-
crates/rustynes-mappers/src/unif.rs | 9 +-
crates/rustynes-ppu/src/palette_gen.rs | 8 +-
crates/rustynes-ppu/src/ppu.rs | 58 +-
.../src/bin/pgo_trainer.rs | 4 +-
.../cpu_boot_trace/mmc3_test_2_4_ours.bin | Bin 16187248 -> 0 bytes
.../mmc3_test_2_4_ours.preview.csv | 501 ------------------
docs/STATUS.md | 16 +-
docs/originality-and-provenance.md | 420 +++++++++++++++
tests/roms/LICENSES.md | 39 +-
to-dos/ROADMAP.md | 18 +-
45 files changed, 1138 insertions(+), 688 deletions(-)
create mode 100644 .github/release-notes/v2.2.5.md
create mode 100644 android/app/src/main/assets/PressStart2P-OFL.txt
delete mode 100644 crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.bin
delete mode 100644 crates/rustynes-test-harness/target/cpu_boot_trace/mmc3_test_2_4_ours.preview.csv
create mode 100644 docs/originality-and-provenance.md
diff --git a/.github/release-notes/v2.2.5.md b/.github/release-notes/v2.2.5.md
new file mode 100644
index 00000000..87d8c687
--- /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 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 ~150
+ previously-undocumented committed test ROMs (502 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..dbee04b4 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 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 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..f051a799 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,61 @@ 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 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; added blanket coverage for the ~150
+ previously-undocumented committed test ROMs (502 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..0de73b52 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,24 @@ 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 on — 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 +792,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
+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 +988,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 +1013,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..b0517f00 100644
--- a/crates/rustynes-apu/src/opll.rs
+++ b/crates/rustynes-apu/src/opll.rs
@@ -14,8 +14,8 @@
//!
//! # 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
//!
@@ -23,7 +23,7 @@
//!
//! 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).
+//! 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..c9409fae 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)
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..ee7a161c 100644
--- a/crates/rustynes-gfx-shaders/src/megatron.wgsl
+++ b/crates/rustynes-gfx-shaders/src/megatron.wgsl
@@ -1,7 +1,9 @@
-// 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
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..76303f74 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,12 @@ 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 sprite-eval hardware bug
+ /// off), 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 +4172,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 +5042,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 b2351f23c186564751f3449086037af18b3e4303..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 16187248
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