diff --git a/.github/linter-service.yaml b/.github/linter-service.yaml
index bedee7b1..4d676607 100644
--- a/.github/linter-service.yaml
+++ b/.github/linter-service.yaml
@@ -1,3 +1,6 @@
+# yamllint disable rule:document-start
linters:
cargo-coupling:
min_grade: C
+ max_critical: 0
+ max_circular: 0
diff --git a/Cargo.lock b/Cargo.lock
index 488fb85a..fc5f0c01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2877,6 +2877,12 @@ version = "0.16.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39"
+[[package]]
+name = "lzma-rust2"
+version = "0.16.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce716bf1a316f47a280fc76295f6495b5bea4752bca01c3b3885e101b1c23c02"
+
[[package]]
name = "mach2"
version = "0.5.0"
@@ -3133,7 +3139,6 @@ dependencies = [
name = "nerust_console"
version = "0.1.0-dev"
dependencies = [
- "crc",
"log",
"nerust_cartridge_data",
"nerust_contract_controller_runtime",
@@ -3141,6 +3146,7 @@ dependencies = [
"nerust_contract_persistence",
"nerust_contract_rom",
"nerust_core",
+ "nerust_crc64_hasher",
"nerust_input_nes",
"nerust_screen_buffer",
"nerust_screen_filter",
@@ -3213,12 +3219,12 @@ name = "nerust_core"
version = "0.1.0-dev"
dependencies = [
"bitflags 2.12.1",
- "crc",
"hound",
"log",
"nerust_contract_mirror",
"nerust_contract_options",
"nerust_contract_rom",
+ "nerust_crc64_hasher",
"nerust_screen_video",
"nerust_serialize",
"nerust_sound_traits",
@@ -3232,6 +3238,13 @@ dependencies = [
"typetag",
]
+[[package]]
+name = "nerust_crc64_hasher"
+version = "0.1.0-dev"
+dependencies = [
+ "crc",
+]
+
[[package]]
name = "nerust_glwrap"
version = "0.1.0-dev"
@@ -3316,6 +3329,7 @@ dependencies = [
"nerust_contract_persistence",
"nerust_contract_rom",
"nerust_contract_settings",
+ "nerust_crc64_hasher",
"nerust_gui_runtime",
"nerust_gui_session",
"nerust_input_nes",
@@ -3325,6 +3339,9 @@ dependencies = [
"nerust_screen_buffer",
"nerust_screen_filter",
"nerust_screen_logical",
+ "nerust_screen_physical",
+ "nerust_snes_core",
+ "nerust_snes_render",
"nerust_sound_android",
"nerust_sound_openal",
"nerust_sound_traits",
@@ -3391,10 +3408,10 @@ name = "nerust_rom_test"
version = "0.1.0-dev"
dependencies = [
"clap",
- "crc",
"nerust_cartridge_data",
"nerust_contract_options",
"nerust_core",
+ "nerust_crc64_hasher",
"nerust_input_nes",
"nerust_input_nes_runtime",
"nerust_screen_buffer",
@@ -3496,6 +3513,39 @@ dependencies = [
"serde",
]
+[[package]]
+name = "nerust_snes_core"
+version = "0.1.0-dev"
+dependencies = [
+ "bitflags 2.12.1",
+ "nerust_sound_traits",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "nerust_snes_render"
+version = "0.1.0-dev"
+dependencies = [
+ "nerust_snes_core",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "nerust_snes_rom_test"
+version = "0.1.0-dev"
+dependencies = [
+ "clap",
+ "nerust_crc64_hasher",
+ "nerust_snes_core",
+ "nerust_snes_render",
+ "png",
+ "serde",
+ "serde_derive",
+ "serde_yaml",
+ "sevenz-rust2",
+ "thiserror 2.0.18",
+]
+
[[package]]
name = "nerust_sound_android"
version = "0.1.0-dev"
@@ -4932,6 +4982,18 @@ dependencies = [
"unsafe-libyaml",
]
+[[package]]
+name = "sevenz-rust2"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbbd24232798280d6bc896e3429a3469174de008ec8b1b591a96618b46664195"
+dependencies = [
+ "crc32fast",
+ "js-sys",
+ "lzma-rust2",
+ "wasm-bindgen",
+]
+
[[package]]
name = "shared_library"
version = "0.1.9"
diff --git a/Cargo.toml b/Cargo.toml
index d72b6c2a..9d68aeb2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -37,8 +37,12 @@ members = [
"sound/openal",
"sound/filter",
"sound/traits",
+ "snes/core",
+ "snes/render",
+ "snes/rom_test",
"sound/android",
"timer",
+ "util/crc64_hasher",
]
default-members = ["core", "persistence", "console"]
@@ -88,6 +92,7 @@ nerust_contract_persistence = { path = "contract/persistence" }
nerust_contract_rom = { path = "contract/rom" }
nerust_contract_settings = { path = "contract/settings" }
nerust_core = { path = "core" }
+nerust_crc64_hasher = { path = "util/crc64_hasher" }
nerust_gui_runtime = { path = "gui/shared/runtime" }
nerust_gui_shell = { path = "gui/shared/shell" }
nerust_gui_session = { path = "gui/shared/session" }
@@ -107,6 +112,9 @@ nerust_screen_physical = { path = "screen/physical" }
nerust_screen_rgb = { path = "screen/rgb" }
nerust_screen_wgpu = { path = "screen/wgpu" }
nerust_screen_video = { path = "screen/video" }
+nerust_snes_core = { path = "snes/core" }
+nerust_snes_render = { path = "snes/render" }
+nerust_snes_rom_test = { path = "snes/rom_test" }
nerust_serialize = { path = "serialize" }
nerust_sound_openal = { path = "sound/openal" }
nerust_sound_android = { path = "sound/android" }
@@ -123,6 +131,7 @@ serde = { version = "=1.0.228", default-features = false }
serde_bytes = { version = "=0.11.19" }
serde_derive = { version = "=1.0.228", default-features = false }
serde_yaml = { version = "=0.9.34" }
+sevenz-rust2 = { version = "=0.21.0", default-features = false }
shared_library = { version = "=0.1.9" }
simple_logger = { version = "=5.2.0" }
strum = { version = "=0.28.0" }
diff --git a/README.md b/README.md
index 9126d52a..fb701c5f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,5 @@
NSHumanReadableCopyright
- © Mitsuharu Seki. Licensed under MPL-2.0.
+ © chalharu. Licensed under MPL-2.0.
CFBundleIconFile
diff --git a/persistence/src/lib.rs b/persistence/src/lib.rs
index df759c4a..24071021 100644
--- a/persistence/src/lib.rs
+++ b/persistence/src/lib.rs
@@ -1,4 +1,4 @@
-// Copyright (c) 2024 Mitsuharu Seki
+// Copyright (c) 2024 chalharu
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
diff --git a/rom_test/Cargo.toml b/rom_test/Cargo.toml
index d40376ec..12181347 100644
--- a/rom_test/Cargo.toml
+++ b/rom_test/Cargo.toml
@@ -8,10 +8,10 @@ rust-version.workspace = true
[dependencies]
clap.workspace = true
-crc.workspace = true
nerust_cartridge_data.workspace = true
nerust_contract_options.workspace = true
nerust_core.workspace = true
+nerust_crc64_hasher.workspace = true
nerust_input_nes.workspace = true
nerust_input_nes_runtime.workspace = true
nerust_screen_buffer.workspace = true
diff --git a/rom_test/build.rs b/rom_test/build.rs
index 4b0df3db..0dad1c5c 100644
--- a/rom_test/build.rs
+++ b/rom_test/build.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use std::env;
use std::fmt::Write as _;
use std::fs;
diff --git a/rom_test/src/bin/perf.rs b/rom_test/src/bin/perf.rs
index 15bf3cd8..e47e334a 100644
--- a/rom_test/src/bin/perf.rs
+++ b/rom_test/src/bin/perf.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
fn main() {
nerust_rom_test::perf::run_cli();
}
diff --git a/rom_test/src/bin/rom_tool.rs b/rom_test/src/bin/rom_tool.rs
index 412a4f5b..4ac7e538 100644
--- a/rom_test/src/bin/rom_tool.rs
+++ b/rom_test/src/bin/rom_tool.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use clap::{Arg, ArgAction, ArgMatches, Command};
use nerust_rom_test::manifest::{RomManifest, load_default_manifest, load_manifest};
use nerust_rom_test::report::{default_output_root, write_html_report};
diff --git a/rom_test/src/error.rs b/rom_test/src/error.rs
index bdeddc7d..7df943c6 100644
--- a/rom_test/src/error.rs
+++ b/rom_test/src/error.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
diff --git a/rom_test/src/events.rs b/rom_test/src/events.rs
index 328a8319..52a8c457 100644
--- a/rom_test/src/events.rs
+++ b/rom_test/src/events.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::error::RomTestError;
use super::serde_helpers::{hex_u8, hex_u16, hex_u64};
use nerust_input_nes::frame::Buttons;
diff --git a/rom_test/src/harness.rs b/rom_test/src/harness.rs
index b4328b30..6f9bfe89 100644
--- a/rom_test/src/harness.rs
+++ b/rom_test/src/harness.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::error::RomTestError;
use super::events::{ButtonCode, ControllerPad, PadState, RomAssertion, RomEventKind};
use super::manifest::RomCase;
diff --git a/rom_test/src/lib.rs b/rom_test/src/lib.rs
index 1ca30578..0de6ff8f 100644
--- a/rom_test/src/lib.rs
+++ b/rom_test/src/lib.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
#![allow(
unused_imports,
reason = "different harness targets reuse this facade with different subsets of the shared API"
diff --git a/rom_test/src/manifest.rs b/rom_test/src/manifest.rs
index 1588f833..db2557ca 100644
--- a/rom_test/src/manifest.rs
+++ b/rom_test/src/manifest.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::error::RomTestError;
use super::events::RomEvent;
use nerust_contract_options::{CoreOptions, Mmc3IrqVariant};
diff --git a/rom_test/src/media.rs b/rom_test/src/media.rs
index 19fec86b..8f5e89a9 100644
--- a/rom_test/src/media.rs
+++ b/rom_test/src/media.rs
@@ -1,11 +1,5 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::error::RomTestError;
-use crc::{CRC_64_XZ, Crc, Digest};
+use nerust_crc64_hasher::Crc64Hasher;
use nerust_screen_buffer::screen_buffer::ScreenBuffer;
use nerust_screen_filter::FilterType;
use nerust_screen_logical::LogicalSize;
@@ -14,8 +8,6 @@ use png::{BitDepth, ColorType, Encoder};
use std::hash::{Hash, Hasher};
use std::io::Cursor;
-const CRC64_LEGACY_ECMA: Crc = Crc::::new(&CRC_64_XZ);
-
pub(crate) fn validation_screen_buffer() -> ScreenBuffer {
ScreenBuffer::new(
FilterType::None,
@@ -61,24 +53,6 @@ pub(crate) fn encode_screenshot_png(screen_buffer: &ScreenBuffer) -> Result);
-
-impl Crc64Hasher {
- fn new() -> Self {
- Self(CRC64_LEGACY_ECMA.digest())
- }
-}
-
-impl Hasher for Crc64Hasher {
- fn write(&mut self, bytes: &[u8]) {
- self.0.update(bytes);
- }
-
- fn finish(&self) -> u64 {
- self.0.clone().finalize()
- }
-}
-
#[derive(Debug, Clone)]
pub(crate) struct HashingMixer {
sample_rate: u32,
diff --git a/rom_test/src/perf.rs b/rom_test/src/perf.rs
index 0b5ef422..b4ba86cf 100644
--- a/rom_test/src/perf.rs
+++ b/rom_test/src/perf.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use crate::error::RomTestError;
use crate::events::{ButtonCode, ControllerPad, PadState, RomAssertion};
use crate::harness::{CaseHarness, apply_button_state, drive_case};
diff --git a/rom_test/src/report.rs b/rom_test/src/report.rs
index e7ab9369..0ea64c03 100644
--- a/rom_test/src/report.rs
+++ b/rom_test/src/report.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::error::RomTestError;
use super::results::CaseOutcome;
use std::fmt::Write as _;
diff --git a/rom_test/src/results.rs b/rom_test/src/results.rs
index e3f3bbf7..1a6385ba 100644
--- a/rom_test/src/results.rs
+++ b/rom_test/src/results.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::manifest::{AudioExpectation, RomCategory};
#[derive(Debug, Clone, Copy)]
diff --git a/rom_test/src/runner.rs b/rom_test/src/runner.rs
index 0f470aba..4df8ff41 100644
--- a/rom_test/src/runner.rs
+++ b/rom_test/src/runner.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
mod entry;
mod validation;
diff --git a/rom_test/src/runner/validation.rs b/rom_test/src/runner/validation.rs
index 63171925..61d75052 100644
--- a/rom_test/src/runner/validation.rs
+++ b/rom_test/src/runner/validation.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
mod artifacts;
pub(in crate::runner::validation) mod assertions;
mod harness_impl;
diff --git a/rom_test/src/runner/validation/artifacts.rs b/rom_test/src/runner/validation/artifacts.rs
index 7b7c43c7..7394c408 100644
--- a/rom_test/src/runner/validation/artifacts.rs
+++ b/rom_test/src/runner/validation/artifacts.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
mod memory;
mod screen;
mod summary;
diff --git a/rom_test/src/runner/validation/artifacts/memory.rs b/rom_test/src/runner/validation/artifacts/memory.rs
index 620c3399..165ff2ef 100644
--- a/rom_test/src/runner/validation/artifacts/memory.rs
+++ b/rom_test/src/runner/validation/artifacts/memory.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
mod cartridge_ram;
mod ppu_vram;
mod work_ram;
diff --git a/rom_test/src/runner/validation/artifacts/memory/cartridge_ram.rs b/rom_test/src/runner/validation/artifacts/memory/cartridge_ram.rs
index 0dae5e64..814db2b0 100644
--- a/rom_test/src/runner/validation/artifacts/memory/cartridge_ram.rs
+++ b/rom_test/src/runner/validation/artifacts/memory/cartridge_ram.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::super::super::{assertions::CartridgeRamAssertion, runtime::ValidationRuntime};
use super::super::ValidationArtifacts;
use crate::error::RomTestError;
diff --git a/rom_test/src/runner/validation/artifacts/memory/ppu_vram.rs b/rom_test/src/runner/validation/artifacts/memory/ppu_vram.rs
index 1d4c5fe8..ce58f2f6 100644
--- a/rom_test/src/runner/validation/artifacts/memory/ppu_vram.rs
+++ b/rom_test/src/runner/validation/artifacts/memory/ppu_vram.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::super::super::runtime::ValidationRuntime;
use super::super::ValidationArtifacts;
use crate::error::RomTestError;
diff --git a/rom_test/src/runner/validation/artifacts/memory/work_ram.rs b/rom_test/src/runner/validation/artifacts/memory/work_ram.rs
index 9eae51f2..dd950cfa 100644
--- a/rom_test/src/runner/validation/artifacts/memory/work_ram.rs
+++ b/rom_test/src/runner/validation/artifacts/memory/work_ram.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::super::super::runtime::ValidationRuntime;
use super::super::ValidationArtifacts;
use crate::error::RomTestError;
diff --git a/rom_test/src/runner/validation/artifacts/screen.rs b/rom_test/src/runner/validation/artifacts/screen.rs
index e2a83997..d42589c0 100644
--- a/rom_test/src/runner/validation/artifacts/screen.rs
+++ b/rom_test/src/runner/validation/artifacts/screen.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::super::runtime::ValidationRuntime;
use super::ValidationArtifacts;
use crate::error::RomTestError;
diff --git a/rom_test/src/runner/validation/artifacts/summary.rs b/rom_test/src/runner/validation/artifacts/summary.rs
index d2e0fa94..5e9ec6d3 100644
--- a/rom_test/src/runner/validation/artifacts/summary.rs
+++ b/rom_test/src/runner/validation/artifacts/summary.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::super::runtime::ValidationRuntime;
use super::ValidationArtifacts;
use crate::manifest::RomCase;
diff --git a/rom_test/src/runner/validation/assertions.rs b/rom_test/src/runner/validation/assertions.rs
index b8c70dd1..65a0e9e4 100644
--- a/rom_test/src/runner/validation/assertions.rs
+++ b/rom_test/src/runner/validation/assertions.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
#[derive(Clone, Copy)]
pub(in crate::runner::validation) struct CartridgeRamAssertion {
pub(in crate::runner::validation) frame: u64,
diff --git a/rom_test/src/runner/validation/harness_impl.rs b/rom_test/src/runner/validation/harness_impl.rs
index 3e61a744..d7187721 100644
--- a/rom_test/src/runner/validation/harness_impl.rs
+++ b/rom_test/src/runner/validation/harness_impl.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::runner::ValidationRunner;
use crate::error::RomTestError;
use crate::events::{MemoryAssertionSpace, RomAssertion};
diff --git a/rom_test/src/runner/validation/runner.rs b/rom_test/src/runner/validation/runner.rs
index 8852422f..73ee6a85 100644
--- a/rom_test/src/runner/validation/runner.rs
+++ b/rom_test/src/runner/validation/runner.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::artifacts::ValidationArtifacts;
use super::assertions::CartridgeRamAssertion;
use super::runtime::ValidationRuntime;
diff --git a/rom_test/src/runner/validation/runtime.rs b/rom_test/src/runner/validation/runtime.rs
index db6922b1..bc9edd02 100644
--- a/rom_test/src/runner/validation/runtime.rs
+++ b/rom_test/src/runner/validation/runtime.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
mod bootstrap;
mod controller;
mod execution;
diff --git a/rom_test/src/runner/validation/runtime/bootstrap.rs b/rom_test/src/runner/validation/runtime/bootstrap.rs
index 832fe636..eca4cf50 100644
--- a/rom_test/src/runner/validation/runtime/bootstrap.rs
+++ b/rom_test/src/runner/validation/runtime/bootstrap.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::ValidationRuntime;
use crate::error::RomTestError;
use crate::manifest::RomCase;
diff --git a/rom_test/src/runner/validation/runtime/controller.rs b/rom_test/src/runner/validation/runtime/controller.rs
index 67066ec9..634084cc 100644
--- a/rom_test/src/runner/validation/runtime/controller.rs
+++ b/rom_test/src/runner/validation/runtime/controller.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::ValidationRuntime;
use crate::events::{ButtonCode, ControllerPad, PadState};
use crate::harness::apply_button_state;
diff --git a/rom_test/src/runner/validation/runtime/execution.rs b/rom_test/src/runner/validation/runtime/execution.rs
index 70819f28..7a29b0c7 100644
--- a/rom_test/src/runner/validation/runtime/execution.rs
+++ b/rom_test/src/runner/validation/runtime/execution.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::ValidationRuntime;
impl ValidationRuntime {
diff --git a/rom_test/src/runner/validation/runtime/inspection.rs b/rom_test/src/runner/validation/runtime/inspection.rs
index 8eca38c5..86872002 100644
--- a/rom_test/src/runner/validation/runtime/inspection.rs
+++ b/rom_test/src/runner/validation/runtime/inspection.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::ValidationRuntime;
use crate::error::RomTestError;
use crate::media::{encode_screenshot_png, screen_hash};
diff --git a/rom_test/src/serde_helpers.rs b/rom_test/src/serde_helpers.rs
index a9b3ebb9..9183f378 100644
--- a/rom_test/src/serde_helpers.rs
+++ b/rom_test/src/serde_helpers.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use serde::de::{self, Visitor};
use std::fmt;
diff --git a/rom_test/src/tests.rs b/rom_test/src/tests.rs
index 7c6f2018..f28b053c 100644
--- a/rom_test/src/tests.rs
+++ b/rom_test/src/tests.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use super::error::RomTestError;
use super::events::{
ButtonCode, ControllerPad, MemoryAssertionSpace, PadState, RomAssertion, RomEvent, RomEventKind,
diff --git a/rom_test/tests/lib.rs b/rom_test/tests/lib.rs
index bac6a191..760cd413 100644
--- a/rom_test/tests/lib.rs
+++ b/rom_test/tests/lib.rs
@@ -1,9 +1,3 @@
-// Copyright (c) 2018 Mitsuharu Seki
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
use nerust_rom_test::manifest::{RomManifest, load_default_manifest};
use nerust_rom_test::results::{CaseOutcome, ValidationOptions};
use nerust_rom_test::runner::validate_case;
diff --git a/roms/snes-apu-tests/dsp-register-smoke/README.md b/roms/snes-apu-tests/dsp-register-smoke/README.md
new file mode 100644
index 00000000..2001759b
--- /dev/null
+++ b/roms/snes-apu-tests/dsp-register-smoke/README.md
@@ -0,0 +1,17 @@
+# SNES APU DSP Register Smoke Test ROM
+
+A small self-authored SNES APU test ROM for the nerust `rom_test` harness.
+
+The S-CPU uploads a tiny SPC700 program through the IPL protocol. The SPC700
+program disables the IPL overlay, writes and reads APU DSP register data through
+`$F2/$F3`, exercises the auxiliary APU IO bytes at `$F8/$F9`, and reports the
+observed values back through CPU-visible APU ports. The S-CPU copies the final
+ports into WRAM `$7E:0000-$7E:0003` for manifest assertions, while the manifest
+also checks the APU RAM result bytes.
+
+The ROM leaves display output blank; `rom_test` still captures a deterministic
+final screen hash and screenshot.
+
+## License
+
+Public domain. Use freely for emulator testing, development, and validation.
diff --git a/roms/snes-apu-tests/dsp-register-smoke/build/ApuDspRegisterSmoke.sfc b/roms/snes-apu-tests/dsp-register-smoke/build/ApuDspRegisterSmoke.sfc
new file mode 100644
index 00000000..be1f8c22
Binary files /dev/null and b/roms/snes-apu-tests/dsp-register-smoke/build/ApuDspRegisterSmoke.sfc differ
diff --git a/roms/snes-apu-tests/dsp-register-smoke/generate_rom.py b/roms/snes-apu-tests/dsp-register-smoke/generate_rom.py
new file mode 100644
index 00000000..dcd8c5ed
--- /dev/null
+++ b/roms/snes-apu-tests/dsp-register-smoke/generate_rom.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python3
+
+from pathlib import Path
+
+ROM_SIZE = 64 * 1024
+HEADER_OFFSET = 0x7FC0
+RESET_VECTOR_OFFSET = 0x7FFC
+PROGRAM_ADDRESS = 0x8000
+PROGRAM_OFFSET = PROGRAM_ADDRESS - 0x8000
+SPC_ENTRY = 0x0300
+OUTPUT_PATH = Path(__file__).resolve().parent / "build" / "ApuDspRegisterSmoke.sfc"
+
+
+def u16(value):
+ return [value & 0xFF, (value >> 8) & 0xFF]
+
+
+def lda_imm(value):
+ return [0xA9, value & 0xFF]
+
+
+def lda_abs(address):
+ return [0xAD, *u16(address)]
+
+
+def sta_abs(address):
+ return [0x8D, *u16(address)]
+
+
+def sta_long(bank, address):
+ return [0x8F, address & 0xFF, (address >> 8) & 0xFF, bank & 0xFF]
+
+
+def cmp_imm(value):
+ return [0xC9, value & 0xFF]
+
+
+def wait_abs_eq(address, value):
+ return [*lda_abs(address), *cmp_imm(value), 0xD0, 0xF9]
+
+
+def store_imm_abs(value, address):
+ return [*lda_imm(value), *sta_abs(address)]
+
+
+def copy_abs_to_wram(source, destination):
+ return [*lda_abs(source), *sta_long(0x7E, destination)]
+
+
+def build_spc_program():
+ return bytes(
+ [
+ 0x8F,
+ 0x00,
+ 0xF1, # MOV $F1,#$00: disable IPL ROM overlay
+ 0x8F,
+ 0x0C,
+ 0xF2, # MOV $F2,#$0C: select MVOLL
+ 0x8F,
+ 0x7F,
+ 0xF3, # MOV $F3,#$7F
+ 0xE4,
+ 0xF3, # MOV A,$F3
+ 0xC4,
+ 0x20, # MOV $20,A
+ 0xC4,
+ 0xF5, # MOV $F5,A
+ 0x8F,
+ 0x2C,
+ 0xF2, # MOV $F2,#$2C: select EVOLL
+ 0x8F,
+ 0x40,
+ 0xF3, # MOV $F3,#$40
+ 0xE4,
+ 0xF3, # MOV A,$F3
+ 0xC4,
+ 0x21, # MOV $21,A
+ 0xC4,
+ 0xF6, # MOV $F6,A
+ 0xE4,
+ 0xF2, # MOV A,$F2
+ 0xC4,
+ 0x22, # MOV $22,A
+ 0xC4,
+ 0xF7, # MOV $F7,A
+ 0x8F,
+ 0x12,
+ 0xF8, # MOV $F8,#$12
+ 0x8F,
+ 0x34,
+ 0xF9, # MOV $F9,#$34
+ 0xE4,
+ 0xF8, # MOV A,$F8
+ 0xC4,
+ 0x23, # MOV $23,A
+ 0xE4,
+ 0xF9, # MOV A,$F9
+ 0xC4,
+ 0x24, # MOV $24,A
+ 0x8F,
+ 0xA5,
+ 0xF4, # MOV $F4,#$A5: success marker
+ 0xFF, # STOP
+ ]
+ )
+
+
+def build_program():
+ spc_program = build_spc_program()
+ if len(spc_program) > 0xFD:
+ raise ValueError("SPC program is too large for the one-page IPL uploader")
+
+ code = []
+ code += wait_abs_eq(0x2140, 0xAA)
+ code += wait_abs_eq(0x2141, 0xBB)
+ code += store_imm_abs(SPC_ENTRY & 0xFF, 0x2142)
+ code += store_imm_abs(SPC_ENTRY >> 8, 0x2143)
+ code += store_imm_abs(0x01, 0x2141)
+ code += store_imm_abs(0xCC, 0x2140)
+ code += wait_abs_eq(0x2140, 0xCC)
+
+ for index, value in enumerate(spc_program):
+ code += store_imm_abs(value, 0x2141)
+ code += store_imm_abs(index, 0x2140)
+ code += wait_abs_eq(0x2140, index)
+
+ kick = (len(spc_program) + 2) | 1
+ code += store_imm_abs(SPC_ENTRY & 0xFF, 0x2142)
+ code += store_imm_abs(SPC_ENTRY >> 8, 0x2143)
+ code += store_imm_abs(0x00, 0x2141)
+ code += store_imm_abs(kick, 0x2140)
+ code += wait_abs_eq(0x2140, kick)
+ code += wait_abs_eq(0x2140, 0xA5)
+
+ for port in range(4):
+ code += copy_abs_to_wram(0x2140 + port, port)
+
+ code += [0x80, 0xFE]
+ return bytes(code)
+
+
+def write_header(rom):
+ title = b"NERUST APU DSP SMOKE "
+ rom[HEADER_OFFSET : HEADER_OFFSET + len(title)] = title
+ rom[HEADER_OFFSET + 0x15] = 0x20 # LoROM map mode
+ rom[HEADER_OFFSET + 0x16] = 0x00 # ROM only
+ rom[HEADER_OFFSET + 0x17] = 0x06 # 64 KiB ROM
+ rom[HEADER_OFFSET + 0x18] = 0x00 # no cartridge RAM
+ rom[HEADER_OFFSET + 0x19] = 0x01 # NTSC
+ rom[HEADER_OFFSET + 0x1A] = 0x33 # maker code
+ rom[HEADER_OFFSET + 0x1B] = 0x00 # version
+
+ for vector_offset in (0x7FEA, 0x7FEC, 0x7FEE, 0x7FFA, RESET_VECTOR_OFFSET, 0x7FFE):
+ rom[vector_offset : vector_offset + 2] = bytes(u16(PROGRAM_ADDRESS))
+
+ rom[HEADER_OFFSET + 0x1C : HEADER_OFFSET + 0x20] = b"\x00\x00\x00\x00"
+ checksum = sum(rom) & 0xFFFF
+ complement = checksum ^ 0xFFFF
+ rom[HEADER_OFFSET + 0x1C : HEADER_OFFSET + 0x1E] = bytes(u16(complement))
+ rom[HEADER_OFFSET + 0x1E : HEADER_OFFSET + 0x20] = bytes(u16(checksum))
+
+
+def main():
+ rom = bytearray([0xFF] * ROM_SIZE)
+ program = build_program()
+ rom[PROGRAM_OFFSET : PROGRAM_OFFSET + len(program)] = program
+ write_header(rom)
+
+ OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
+ OUTPUT_PATH.write_bytes(rom)
+ print(f"wrote {OUTPUT_PATH} ({len(rom)} bytes)")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/roms/snes-coprocessor-tests/cx4-smoke/.gitignore b/roms/snes-coprocessor-tests/cx4-smoke/.gitignore
new file mode 100644
index 00000000..7a60b85e
--- /dev/null
+++ b/roms/snes-coprocessor-tests/cx4-smoke/.gitignore
@@ -0,0 +1,2 @@
+__pycache__/
+*.pyc
diff --git a/roms/snes-coprocessor-tests/cx4-smoke/BUILD.md b/roms/snes-coprocessor-tests/cx4-smoke/BUILD.md
new file mode 100644
index 00000000..86508d0c
--- /dev/null
+++ b/roms/snes-coprocessor-tests/cx4-smoke/BUILD.md
@@ -0,0 +1,20 @@
+# Building
+
+## Prerequisites
+
+- Python 3
+
+No external packages or assembler toolchains are required.
+
+## Build
+
+```bash
+python3 generate_rom.py
+```
+
+Output:
+
+- `build/Cx4Smoke.sfc` - 64 KiB LoROM CX4 test ROM
+
+The generator writes the 65C816 machine code and SNES header directly so the
+artifact is deterministic and easy to reproduce in CI or locally.
diff --git a/roms/snes-coprocessor-tests/cx4-smoke/README.md b/roms/snes-coprocessor-tests/cx4-smoke/README.md
new file mode 100644
index 00000000..1735ebdc
--- /dev/null
+++ b/roms/snes-coprocessor-tests/cx4-smoke/README.md
@@ -0,0 +1,19 @@
+# SNES CX4 Smoke Test ROM
+
+A small self-authored SNES CX4 test ROM for the nerust `rom_test` harness.
+
+The program runs on the S-CPU with a CX4 cartridge header and verifies a
+minimal set of host-visible CX4 behavior:
+
+- CX4 cartridge header detection (`map mode $20`, chipset `$F3`, subtype `$10`)
+- CX4 24-bit multiply command `$25`
+- CX4 identification command `$89`
+- CX4 busy-status reads
+
+Results are copied into WRAM `$7E:0000-$7E:0006` for manifest assertions. The
+ROM leaves display output blank; `rom_test` still captures a deterministic
+final screen hash and screenshot.
+
+## License
+
+Public domain. Use freely for emulator testing, development, and validation.
diff --git a/roms/snes-coprocessor-tests/cx4-smoke/UPSTREAM.md b/roms/snes-coprocessor-tests/cx4-smoke/UPSTREAM.md
new file mode 100644
index 00000000..74d0083f
--- /dev/null
+++ b/roms/snes-coprocessor-tests/cx4-smoke/UPSTREAM.md
@@ -0,0 +1,11 @@
+# Provenance
+
+This ROM is self-authored for nerust and is generated from `generate_rom.py` in
+this directory.
+
+- No external ROM image or commercial/proprietary content is used.
+- No git subtree import is required because there is no upstream repository.
+- The generated artifact committed for the `rom_test` harness is:
+ - `build/Cx4Smoke.sfc`
+ - SHA-256:
+ `e5a13f04959d84f7cef6614bb56248401c5d3b3afe8364d3a88bd787878bd3b1`
diff --git a/roms/snes-coprocessor-tests/cx4-smoke/build/Cx4Smoke.sfc b/roms/snes-coprocessor-tests/cx4-smoke/build/Cx4Smoke.sfc
new file mode 100644
index 00000000..04628931
Binary files /dev/null and b/roms/snes-coprocessor-tests/cx4-smoke/build/Cx4Smoke.sfc differ
diff --git a/roms/snes-coprocessor-tests/cx4-smoke/generate_rom.py b/roms/snes-coprocessor-tests/cx4-smoke/generate_rom.py
new file mode 100644
index 00000000..195f4294
--- /dev/null
+++ b/roms/snes-coprocessor-tests/cx4-smoke/generate_rom.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+
+from pathlib import Path
+
+ROM_SIZE = 64 * 1024
+HEADER_OFFSET = 0x7FC0
+RESET_VECTOR_OFFSET = 0x7FFC
+PROGRAM_ADDRESS = 0x8000
+PROGRAM_OFFSET = PROGRAM_ADDRESS - 0x8000
+OUTPUT_PATH = Path(__file__).resolve().parent / "build" / "Cx4Smoke.sfc"
+
+
+def u16(value):
+ return [value & 0xFF, (value >> 8) & 0xFF]
+
+
+def lda_imm(value):
+ return [0xA9, value & 0xFF]
+
+
+def lda_abs(address):
+ return [0xAD, *u16(address)]
+
+
+def sta_abs(address):
+ return [0x8D, *u16(address)]
+
+
+def sta_long(bank, address):
+ return [0x8F, address & 0xFF, (address >> 8) & 0xFF, bank & 0xFF]
+
+
+def store_imm_abs(value, address):
+ return [*lda_imm(value), *sta_abs(address)]
+
+
+def copy_abs_to_wram(source, destination):
+ return [*lda_abs(source), *sta_long(0x7E, destination)]
+
+
+def build_program():
+ code = []
+
+ # CX4 command $25 multiplies two little-endian 24-bit inputs at $7F80.
+ for value, address in (
+ (0x23, 0x7F80),
+ (0x01, 0x7F81),
+ (0x00, 0x7F82),
+ (0x04, 0x7F83),
+ (0x00, 0x7F84),
+ (0x00, 0x7F85),
+ ):
+ code += store_imm_abs(value, address)
+ code += store_imm_abs(0x25, 0x7F4F)
+ code += copy_abs_to_wram(0x7F80, 0x0000)
+ code += copy_abs_to_wram(0x7F81, 0x0001)
+ code += copy_abs_to_wram(0x7F82, 0x0002)
+
+ # CX4 command $89 returns the CX4 identifier bytes 36 43 05.
+ code += store_imm_abs(0x89, 0x7F4F)
+ code += copy_abs_to_wram(0x7F80, 0x0003)
+ code += copy_abs_to_wram(0x7F81, 0x0004)
+ code += copy_abs_to_wram(0x7F82, 0x0005)
+ code += copy_abs_to_wram(0x7F5E, 0x0006)
+
+ # Stay alive for benchmark frame loops.
+ code += [0x80, 0xFE]
+ return bytes(code)
+
+
+def write_header(rom):
+ title = b"NERUST CX4 SMOKE "
+ rom[HEADER_OFFSET - 1] = 0x10 # CX4 expansion subtype
+ rom[HEADER_OFFSET : HEADER_OFFSET + len(title)] = title
+ rom[HEADER_OFFSET + 0x15] = 0x20 # LoROM map mode
+ rom[HEADER_OFFSET + 0x16] = 0xF3 # coprocessor family with enhancement subtype
+ rom[HEADER_OFFSET + 0x17] = 0x06 # 64 KiB ROM
+ rom[HEADER_OFFSET + 0x18] = 0x00 # no cartridge RAM
+ rom[HEADER_OFFSET + 0x19] = 0x01 # NTSC
+ rom[HEADER_OFFSET + 0x1A] = 0x33 # maker code
+ rom[HEADER_OFFSET + 0x1B] = 0x00 # version
+
+ for vector_offset in (0x7FEA, 0x7FEC, 0x7FEE, 0x7FFA, RESET_VECTOR_OFFSET, 0x7FFE):
+ rom[vector_offset : vector_offset + 2] = bytes(u16(PROGRAM_ADDRESS))
+
+ rom[HEADER_OFFSET + 0x1C : HEADER_OFFSET + 0x20] = b"\x00\x00\x00\x00"
+ checksum = sum(rom) & 0xFFFF
+ complement = checksum ^ 0xFFFF
+ rom[HEADER_OFFSET + 0x1C : HEADER_OFFSET + 0x1E] = bytes(u16(complement))
+ rom[HEADER_OFFSET + 0x1E : HEADER_OFFSET + 0x20] = bytes(u16(checksum))
+
+
+def main():
+ rom = bytearray([0xFF] * ROM_SIZE)
+ program = build_program()
+ rom[PROGRAM_OFFSET : PROGRAM_OFFSET + len(program)] = program
+ write_header(rom)
+
+ OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True)
+ OUTPUT_PATH.write_bytes(rom)
+ print(f"wrote {OUTPUT_PATH} ({len(rom)} bytes)")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/.gitignore b/roms/snes-coprocessor-tests/dsp1-smoke/.gitignore
new file mode 100644
index 00000000..7a60b85e
--- /dev/null
+++ b/roms/snes-coprocessor-tests/dsp1-smoke/.gitignore
@@ -0,0 +1,2 @@
+__pycache__/
+*.pyc
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/BUILD.md b/roms/snes-coprocessor-tests/dsp1-smoke/BUILD.md
new file mode 100644
index 00000000..e9afc09d
--- /dev/null
+++ b/roms/snes-coprocessor-tests/dsp1-smoke/BUILD.md
@@ -0,0 +1,25 @@
+# Building
+
+## Prerequisites
+
+- Python 3
+
+No external packages or assembler toolchains are required.
+
+## Build
+
+```bash
+python3 generate_rom.py
+```
+
+Output:
+
+- `build/Dsp1Smoke.sfc` - 64 KiB LoROM DSP-1 test ROM
+- `build/Dsp1aSmoke.sfc` - 64 KiB LoROM DSP-1A-compatible test ROM
+- `build/Dsp1bSmoke.sfc` - 64 KiB HiROM DSP-1B test ROM
+- `build/Dsp1GeometrySmoke.sfc` - 64 KiB LoROM DSP-1 geometry test ROM
+- `build/Dsp1aGeometrySmoke.sfc` - 64 KiB LoROM DSP-1A-compatible geometry test ROM
+- `build/Dsp1bGeometrySmoke.sfc` - 64 KiB HiROM DSP-1B geometry test ROM
+
+The generator writes the 65C816 machine code and SNES headers directly so the
+artifacts are deterministic and easy to reproduce in CI or locally.
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/README.md b/roms/snes-coprocessor-tests/dsp1-smoke/README.md
new file mode 100644
index 00000000..064efc52
--- /dev/null
+++ b/roms/snes-coprocessor-tests/dsp1-smoke/README.md
@@ -0,0 +1,30 @@
+# SNES DSP-1 Family Smoke Test ROMs
+
+Small self-authored SNES DSP-1 family test ROMs for the nerust `rom_test`
+harness.
+
+The generated ROMs run on the S-CPU and verify minimal host-visible DSP-1
+behavior through the cartridge DSP data/status ports:
+
+- DSP-1 LoROM header detection (`map mode $20`, chipset `$03`)
+- DSP-1A-compatible LoROM header detection (`map mode $30`, chipset `$05`)
+- DSP-1B HiROM header detection (`map mode $21`, chipset `$05`)
+- DSP command `$00` fixed-point multiply
+- DSP command `$27` memory-size/ROM-version response
+- DSP geometry/scalar commands:
+ - `$04` sine/cosine
+ - `$0C` 2D rotation
+ - `$1C` 3D rotation
+ - `$28` vector length
+ - `$08` squared radius
+ - `$10` inverse
+- reset-ready status after command completion
+
+Basic command results are copied into WRAM `$7E:0000-$7E:0004`; geometry command
+results are copied into WRAM `$7E:0010-$7E:0028` for manifest assertions. The
+ROMs leave display output blank; `rom_test` still captures deterministic final
+screen hashes and screenshots.
+
+## License
+
+Public domain. Use freely for emulator testing, development, and validation.
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/UPSTREAM.md b/roms/snes-coprocessor-tests/dsp1-smoke/UPSTREAM.md
new file mode 100644
index 00000000..56edc900
--- /dev/null
+++ b/roms/snes-coprocessor-tests/dsp1-smoke/UPSTREAM.md
@@ -0,0 +1,26 @@
+# Provenance
+
+These ROMs are self-authored for nerust and are generated from
+`generate_rom.py` in this directory.
+
+- No external ROM image or commercial/proprietary content is used.
+- No git subtree import is required because there is no upstream repository.
+- The generated artifacts committed for the `rom_test` harness are:
+ - `build/Dsp1Smoke.sfc`
+ - SHA-256:
+ `bec587f10ebddb06538b3a236a5c9bc24772206ffd10af44662040fcfd3a7209`
+ - `build/Dsp1aSmoke.sfc`
+ - SHA-256:
+ `864d99726ad6aff58292d4bacc3129baf155d0712f69f062e70667d24360bcab`
+ - `build/Dsp1bSmoke.sfc`
+ - SHA-256:
+ `72af15130ee0149a664cbeb703906be49ca936beaf44817aeefb4fbdc4abaee6`
+ - `build/Dsp1GeometrySmoke.sfc`
+ - SHA-256:
+ `e47e81f2a28ee06368870135d430b7578473fa584e5fa7c6ec5018d26160a898`
+ - `build/Dsp1aGeometrySmoke.sfc`
+ - SHA-256:
+ `8877dfc5af18674fc6dc4100e411388b2a7236a2b0f8d8e89b943afbf5541c40`
+ - `build/Dsp1bGeometrySmoke.sfc`
+ - SHA-256:
+ `920eda8025d5ed3acf3444c697b8dc9d0837fcf7715ecfc8edf6575f1b187968`
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1GeometrySmoke.sfc b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1GeometrySmoke.sfc
new file mode 100644
index 00000000..28c2595e
Binary files /dev/null and b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1GeometrySmoke.sfc differ
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1Smoke.sfc b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1Smoke.sfc
new file mode 100644
index 00000000..f635ddf2
Binary files /dev/null and b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1Smoke.sfc differ
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1aGeometrySmoke.sfc b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1aGeometrySmoke.sfc
new file mode 100644
index 00000000..7ff8be48
Binary files /dev/null and b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1aGeometrySmoke.sfc differ
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1aSmoke.sfc b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1aSmoke.sfc
new file mode 100644
index 00000000..199f80b4
Binary files /dev/null and b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1aSmoke.sfc differ
diff --git a/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1bGeometrySmoke.sfc b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1bGeometrySmoke.sfc
new file mode 100644
index 00000000..faa912f4
--- /dev/null
+++ b/roms/snes-coprocessor-tests/dsp1-smoke/build/Dsp1bGeometrySmoke.sfc
@@ -0,0 +1 @@
+ ` ` ` ` @ ` ` ~ ` ~ ` ~ ` ~ ` ` ` # ` ` ` ` ` ~ ` ~ ` ~ ` ~ ` ` ` ` ` ` ` ` ` ` ` ` ` ` ~ ` ~ ` ~ ` ~ ` ~ ` ~( ` ` ` ` ` ` ` ` ~ ` ~ ` ` ` ` ` ` ` ` ~ ` ! ~ ` " ~ ` # ~ ` ` @ ` ` ` ` $ ~ ` % ~ ` &