aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/readrom/src/main.rs29
-rw-r--r--lib/header/src/lib.rs68
-rw-r--r--tests/expected/readrom/header.txt20
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