added tests and all pass with this implementation
This commit is contained in:
+76
-1
@@ -1,6 +1,7 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub enum Mirroring { Horizontal, Vertical, FourScreen }
|
pub enum Mirroring { Horizontal, Vertical, FourScreen }
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Cartridge {
|
pub struct Cartridge {
|
||||||
prg_rom: Vec<u8>,
|
prg_rom: Vec<u8>,
|
||||||
chr_rom: Vec<u8>,
|
chr_rom: Vec<u8>,
|
||||||
@@ -14,6 +15,10 @@ pub struct Cartridge {
|
|||||||
impl Cartridge {
|
impl Cartridge {
|
||||||
pub fn from_file(path: &str) -> Result<Cartridge, String> {
|
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}"))?;
|
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 {
|
if data.len() < 16 {
|
||||||
return Err("file is too short to be an iNES ROM".to_string())
|
return Err("file is too short to be an iNES ROM".to_string())
|
||||||
}
|
}
|
||||||
@@ -82,4 +87,74 @@ impl Cartridge {
|
|||||||
debug_assert!(address < 0x2000, "CHR writes outside $0000-$1FFF; Address from CPU: {address:#06X}");
|
debug_assert!(address < 0x2000, "CHR writes outside $0000-$1FFF; Address from CPU: {address:#06X}");
|
||||||
self.chr_rom[address as usize] = value
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user