nestadia/cartridge/
mod.rs1mod ines_header;
2mod mapper_000;
3mod mapper_001;
4mod mapper_002;
5mod mapper_003;
6mod mapper_004;
7mod mapper_066;
8
9use alloc::boxed::Box;
10use alloc::vec;
11use alloc::vec::Vec;
12use core::convert::TryFrom as _;
13
14use self::ines_header::{Flags6, INesHeader};
15use self::mapper_000::Mapper000;
16use self::mapper_002::Mapper002;
17use self::mapper_003::Mapper003;
18use self::mapper_004::Mapper004;
19use self::mapper_066::Mapper066;
20use crate::cartridge::mapper_001::Mapper001;
21
22#[derive(Debug, Clone, Copy)]
23pub enum Mirroring {
24 Horizontal,
25 Vertical,
26 FourScreen,
27 OneScreenLower,
28 OneScreenUpper,
29}
30
31#[derive(Debug, Clone, Copy)]
32pub enum RomParserError {
33 TooShort,
34 InvalidMagicBytes,
35 MapperNotImplemented,
36}
37
38impl core::fmt::Display for RomParserError {
39 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
40 write!(f, "{:?}", &self)
41 }
42}
43
44enum CartridgeReadTarget {
45 PrgRam(u8),
46 PrgRom(usize),
47}
48
49trait Mapper: Send + Sync {
50 fn cpu_map_read(&self, addr: u16) -> CartridgeReadTarget;
51 fn cpu_map_write(&mut self, addr: u16, data: u8);
52 fn ppu_map_read(&self, addr: u16) -> usize;
53 fn ppu_map_write(&self, addr: u16) -> Option<usize>;
54 fn mirroring(&self) -> Mirroring;
55 fn get_sram(&self) -> Option<&[u8]>;
56
57 fn irq_state(&self) -> bool {
58 false
59 }
60 fn irq_clear(&mut self) {}
61 fn irq_scanline(&mut self) {}
62}
63
64pub struct Cartridge {
65 chr_ram: bool,
66 prg_memory: Vec<u8>, chr_memory: Vec<u8>, mapper: Box<dyn Mapper>,
69}
70
71impl Cartridge {
72 pub fn load(rom: &[u8], save_data: Option<&[u8]>) -> Result<Self, RomParserError> {
73 const PRG_BANK_SIZE: usize = 16384;
74 const CHR_BANK_SIZE: usize = 8192;
75
76 let header: INesHeader = INesHeader::try_from(rom)?;
77
78 log::info!("ROM info: {:?}", &header);
79
80 let mirroring = if header.flags6.contains(Flags6::FOUR_SCREEN) {
81 Mirroring::FourScreen
82 } else if header.flags6.contains(Flags6::MIRRORING) {
83 Mirroring::Vertical
84 } else {
85 Mirroring::Horizontal
86 };
87
88 let mapper: Box<dyn Mapper> = match header.mapper_id {
89 0 => Box::new(Mapper000::new(header.prg_size, mirroring)),
90 1 => Box::new(Mapper001::new(header.prg_size, mirroring, save_data)),
91 2 => Box::new(Mapper002::new(header.prg_size, mirroring)),
92 3 => Box::new(Mapper003::new(header.prg_size, mirroring)),
93 4 => Box::new(Mapper004::new(header.prg_size, mirroring)),
94 66 => Box::new(Mapper066::new(mirroring)),
95 _ => return Err(RomParserError::MapperNotImplemented),
96 };
97
98 let chr_memory_len = CHR_BANK_SIZE * (header.chr_size as usize);
99 let prg_memory_len = PRG_BANK_SIZE * (header.prg_size as usize);
100
101 let prg_start = if header.flags6.contains(Flags6::TRAINER) {
102 512 + 16
103 } else {
104 16
105 };
106
107 let expected_rom_size = prg_start + prg_memory_len + chr_memory_len;
108 if rom.len() < expected_rom_size {
109 log::error!(
110 "Invalid ROM size: expected {} bytes of memory, but ROM has {}",
111 expected_rom_size,
112 rom.len()
113 );
114 return Err(RomParserError::TooShort);
115 }
116
117 let prg_end = prg_start + prg_memory_len;
119 let prg_memory = rom[prg_start..prg_end].to_vec();
120 assert_eq!(prg_memory.len(), prg_memory_len);
121
122 let chr_ram = header.chr_size == 0;
125 let chr_memory = if !chr_ram {
126 let chr_start = prg_end;
127 let chr_end = prg_end + chr_memory_len;
128 rom[chr_start..chr_end].to_vec()
129 } else {
130 vec![0u8; CHR_BANK_SIZE]
131 };
132
133 Ok(Cartridge {
134 chr_ram,
135 prg_memory,
136 chr_memory,
137 mapper,
138 })
139 }
140
141 pub fn mirroring(&self) -> Mirroring {
142 self.mapper.mirroring()
143 }
144
145 pub fn read_prg_mem(&self, addr: u16) -> u8 {
146 match self.mapper.cpu_map_read(addr) {
147 CartridgeReadTarget::PrgRom(rom_addr) => self.prg_memory[rom_addr],
148 CartridgeReadTarget::PrgRam(data) => data,
149 }
150 }
151
152 pub fn write_prg_mem(&mut self, addr: u16, data: u8) {
153 self.mapper.cpu_map_write(addr, data);
154 }
155
156 pub fn read_chr_mem(&self, addr: u16) -> u8 {
157 let addr = self.mapper.ppu_map_read(addr);
158 if addr < self.chr_memory.len() {
159 self.chr_memory[addr]
160 } else {
161 0
162 }
163 }
164
165 pub fn write_chr_mem(&mut self, addr: u16, data: u8) {
166 if self.chr_ram {
167 if let Some(addr) = self.mapper.ppu_map_write(addr) {
168 self.chr_memory[addr] = data;
169 } else {
170 log::warn!(
171 "attempted to write on CHR memory at {}, but this is not supported by this mapper",
172 addr
173 );
174 }
175 } else {
176 log::warn!(
177 "attempted to write on CHR memory at {}, but this ROM uses CHR ROM",
178 addr
179 );
180 };
181 }
182
183 pub fn get_save_data(&self) -> Option<&[u8]> {
184 self.mapper.get_sram()
185 }
186
187 pub fn take_irq_set_state(&mut self) -> bool {
188 let state = self.mapper.irq_state();
189 self.mapper.irq_clear();
190 state
191 }
192
193 pub fn irq_scanline(&mut self) {
194 self.mapper.irq_scanline()
195 }
196
197 #[cfg(feature = "debugger")]
198 pub fn disassemble(&self) -> Vec<(u16, alloc::string::String)> {
199 let mut disas1 = crate::cpu::disassembler::disassemble(&self.prg_memory, 0x4000);
200 let disas2 = crate::cpu::disassembler::disassemble(&self.prg_memory, 0x8000);
201 let disas3 = crate::cpu::disassembler::disassemble(&self.prg_memory, 0xc000);
202
203 disas1.extend(disas2);
204 disas1.extend(disas3);
205 disas1
206 }
207}