diff options
| -rw-r--r-- | crates/readrom/src/main.rs | 29 | ||||
| -rw-r--r-- | lib/header/src/lib.rs | 68 | ||||
| -rw-r--r-- | tests/expected/readrom/header.txt | 20 |
3 files changed, 95 insertions, 22 deletions
diff --git a/crates/readrom/src/main.rs b/crates/readrom/src/main.rs index f7411e3..d3701d0 100644 --- a/crates/readrom/src/main.rs +++ b/crates/readrom/src/main.rs @@ -175,47 +175,50 @@ fn print_header(header: &Header) { println!("Header:"); match header.kind { - Kind::INes => println!(" Kind:\t\t\tiNES"), - Kind::Nes20 => println!(" Kind:\t\t\tNES 2.0"), + Kind::INes => println!(" Kind:\t\t\t\tiNES"), + Kind::Nes20 => println!(" Kind:\t\t\t\tNES 2.0"), } println!( - " PRG ROM size:\t\t{} bytes ({}KB)", + " PRG ROM size:\t\t\t{} bytes ({}KB)", header.prg_rom_size * 16 * 1024, header.prg_rom_size * 16 ); println!( - " CHR ROM size:\t\t{} bytes ({}KB)", + " CHR ROM size:\t\t\t{} bytes ({}KB)", header.chr_rom_size * 8 * 1024, header.chr_rom_size * 8 ); if let Some(def) = &header.prg_ram_definition { println!( - " PRG-RAM size:\t\t{} bytes ({}KB); {}", + " PRG-RAM size:\t\t\t{} bytes ({}KB); {}", def.size, def.size / 1024, def.kind ); } else if header.has_persistent_memory { - println!(" PRG-RAM size:\t\t8192 bytes (8KB)"); + println!(" PRG-RAM size:\t\t\t8192 bytes (8KB)"); } if let Some(def) = &header.chr_ram_definition { println!( - " CHR-RAM size:\t\t{} bytes ({}KB); {}", + " CHR-RAM size:\t\t\t{} bytes ({}KB); {}", def.size, def.size / 1024, def.kind ); } - println!(" Mapper:\t\t{}", header.mapper); + println!(" Mapper:\t\t\t{}", header.mapper); println!( - " Mirroring:\t\t{}", + " Mirroring:\t\t\t{}", capitalize(header.nametable_arrangement.mirroring()) ); - println!(" CPU/PPU timing:\t{}", header.timing); + println!(" CPU/PPU timing:\t\t{}", header.timing); + if let Some(ded) = header.default_expansion_device.as_ref() { + println!(" Default Expansion Device:\t{}", ded); + } } fn print_vectors(addrs: &[u8]) { @@ -226,15 +229,15 @@ fn print_vectors(addrs: &[u8]) { } println!( - " NMI:\t\t\t{:#04x}", + " NMI:\t\t\t\t{:#04x}", u16::from_le_bytes([addrs[0], addrs[1]]) ); println!( - " Reset:\t\t{:#04x}", + " Reset:\t\t\t{:#04x}", u16::from_le_bytes([addrs[2], addrs[3]]) ); println!( - " IRQ:\t\t\t{:#04x}", + " IRQ:\t\t\t\t{:#04x}", u16::from_le_bytes([addrs[4], addrs[5]]) ); } diff --git a/lib/header/src/lib.rs b/lib/header/src/lib.rs index cf0b736..3a62e35 100644 --- a/lib/header/src/lib.rs +++ b/lib/header/src/lib.rs @@ -146,6 +146,48 @@ impl std::fmt::Display for Timing { } } +/// The default expansion device available on the NES 2.0 header. +#[derive(Debug)] +pub enum DefaultExpansionDevice { + Unknown, + StandardControllers, + FourScoreSatellite, + FamicomFourPlayersAdapter, + VsSystem4016, + VsSystem4017, + VsZapper, + Zapper, + TwoZappers, + PowerPadA, + PowerPadB, +} + +impl std::fmt::Display for DefaultExpansionDevice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Self::Unknown => write!(f, "Unknown"), + Self::StandardControllers => write!(f, "Standard NES/Famicom controllers"), + Self::FourScoreSatellite => write!( + f, + "NES Four Score/Satellite with two additional standard controllers" + ), + Self::FamicomFourPlayersAdapter => { + write!( + f, + "Famicom Four Players Adapter with two additional standard controllers using the \"simple\" protocol" + ) + } + Self::VsSystem4016 => write!(f, "Vs. System (1P via $4016)"), + Self::VsSystem4017 => write!(f, "Vs. System (1P via $4017)"), + Self::VsZapper => write!(f, "Vs. Zapper"), + Self::Zapper => write!(f, "Zapper ($4017)"), + Self::TwoZappers => write!(f, "Two Zappers"), + Self::PowerPadA => write!(f, "Power Pad Side A"), + Self::PowerPadB => write!(f, "Power Pad Side B"), + } + } +} + /// Information that has been parsed from an NES/Famicom ROM header. The /// `Header` struct implements the `TryFrom` trait. Hence, use /// `Header::try_from` in order to parse a header. @@ -190,6 +232,10 @@ pub struct Header { /// The kind of the header. pub kind: Kind, + + /// The default expansion device that can be specified in the NES 2.0 + /// header. Used to specify the default way to experience a game. + pub default_expansion_device: Option<DefaultExpansionDevice>, } impl TryFrom<&[u8]> for Header { @@ -219,11 +265,33 @@ impl TryFrom<&[u8]> for Header { prg_ram_definition: parse_ram_definition(&kind, bytes.get(10), has_persistent_memory), chr_ram_definition: parse_ram_definition(&kind, bytes.get(11), has_persistent_memory), timing: parse_timing(&kind, bytes.get(12)), + default_expansion_device: if matches!(kind, Kind::Nes20) { + Some(parse_default_expansion_device(bytes.get(15).unwrap_or(&0))) + } else { + None + }, kind, }) } } +// Returns the DefaultExpansionDevice for the given byte value. +fn parse_default_expansion_device(byte: &u8) -> DefaultExpansionDevice { + match byte { + 1 => DefaultExpansionDevice::StandardControllers, + 2 => DefaultExpansionDevice::FourScoreSatellite, + 3 => DefaultExpansionDevice::FamicomFourPlayersAdapter, + 4 => DefaultExpansionDevice::VsSystem4016, + 5 => DefaultExpansionDevice::VsSystem4017, + 7 => DefaultExpansionDevice::VsZapper, + 8 => DefaultExpansionDevice::Zapper, + 9 => DefaultExpansionDevice::TwoZappers, + 11 => DefaultExpansionDevice::PowerPadA, + 12 => DefaultExpansionDevice::PowerPadB, + _ => DefaultExpansionDevice::Unknown, + } +} + // Parse the given `byte` by assuming it's a RAM definition. It will just return // `None` if `has_persistent_memory` is false or the `kind` is not NES 2.0. fn parse_ram_definition( diff --git a/tests/expected/readrom/header.txt b/tests/expected/readrom/header.txt index a2d4a81..fc963e0 100644 --- a/tests/expected/readrom/header.txt +++ b/tests/expected/readrom/header.txt @@ -1,11 +1,13 @@ Header: - Kind: NES 2.0 - PRG ROM size: 32768 bytes (32KB) - CHR ROM size: 8192 bytes (8KB) - Mapper: NROM - Mirroring: Horizontal - CPU/PPU timing: NTSC + Kind: NES 2.0 + PRG ROM size: 32768 bytes (32KB) + CHR ROM size: 8192 bytes (8KB) + Mapper: NROM + Mirroring: Horizontal + CPU/PPU timing: NTSC + Default Expansion Device: Unknown + Raw: [ 4E 45 53 1A 02 01 00 08 00 00 00 00 00 00 00 00 ] Vectors: - NMI: 0xa2f8 - Reset: 0xa3c2 - IRQ: 0xa3c1 + NMI: 0xa2f8 + Reset: 0xa3c2 + IRQ: 0xa3c1 |
