added tests and all pass with this implementation

This commit is contained in:
2026-08-07 22:16:35 -05:00
parent 09b66ccda1
commit 445d9dcdef
+76 -1
View File
@@ -1,6 +1,7 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Mirroring { Horizontal, Vertical, FourScreen }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Cartridge {
prg_rom: Vec<u8>,
chr_rom: Vec<u8>,
@@ -14,6 +15,10 @@ pub struct Cartridge {
impl Cartridge {
pub fn from_file(path: &str) -> Result<Cartridge, String> {
let data: Vec<u8> = std::fs::read(path).map_err(|e| format!("couldn't read {path}: {e}"))?;
Self::parse_data(data)
}
pub fn parse_data(data: Vec<u8>) -> Result<Cartridge,String> {
if data.len() < 16 {
return Err("file is too short to be an iNES ROM".to_string())
}
@@ -83,3 +88,73 @@ impl Cartridge {
self.chr_rom[address as usize] = value
}
}
#[cfg(test)]
mod tests {
use super::*;
fn load(name: &str) -> Cartridge {
let path = format!("{}/roms/{}", env!("CARGO_MANIFEST_DIR"), name);
Cartridge::from_file(&path).expect("test ROM should load")
}
// Header parsing requires real test ROMs or actual game backups
#[test]
fn smb_header() {
let cart = load("backup/Super Mario Bros. (Japan, USA).nes");
assert_eq!(cart.prg_rom.len(), 32 * 1024);
assert_eq!(cart.chr_rom.len(), 8 * 1024);
assert_eq!(cart.mapper, 0);
assert_eq!(cart.mirroring, Mirroring::Vertical);
}
#[test]
fn nestest_header() {
let cart = load("test/cpu/nestest/nestest.nes");
assert_eq!(cart.prg_rom.len(), 16 * 1024);
assert_eq!(cart.mirroring, Mirroring::Horizontal);
}
// PRG ROM mappings
#[test]
fn nestest_prg_mirrors() {
let cart = load("test/cpu/nestest/nestest.nes"); // 16KiB - Mirrored
assert_eq!(cart.read_prg(0x8000), cart.read_prg(0xC000));
}
#[test]
fn smb_prg_no_mirror() {
let cart = load("backup/Super Mario Bros. (Japan, USA).nes");
assert_eq!(cart.read_prg(0x8000), cart.prg_rom[0]);
assert_eq!(cart.read_prg(0xC000), cart.prg_rom[0x4000]);
}
// Errors via parse_data
#[test]
fn rejects_bad_magic() {
assert!(Cartridge::parse_data(vec![0x00; 16]).is_err());
}
#[test]
fn rejects_short_file() {
assert!(Cartridge::parse_data(vec![b'N', b'E', b'S', 0x1A]).is_err());
}
#[test]
fn rejects_mmc1() {
let mut hdr = vec![0u8; 16];
hdr[..4].copy_from_slice(b"NES\x1a");
hdr[4] = 0x10; // 16 PRG banks
hdr[6] = 0x10; // mapper low nibble = 1
let err = Cartridge::parse_data(hdr).unwrap_err();
assert!(err.contains("mapper 1"), "got: {err}");
}
// CHR-RAM roundtrip (coredump has 0 CHR banks we make CHR-RAM)
#[test]
fn chr_ram_roundtrip() {
let mut cart = load("test/cpu/coredump-v1.3.nes");
cart.write_chr(0x1234, 0xAB);
assert_eq!(cart.read_chr(0x1234), 0xAB);
}
}