nestadia/cartridge/
mod.rs

1mod 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>, // program ROM, used by CPU
67    chr_memory: Vec<u8>, // character ROM, used by PPU
68    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        // PRG memory
118        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        // CHR memory
123        // Don't parse if it's RAM
124        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}