tetanes_core/mapper/
m003_cnrom.rs

1//! `CNROM` (Mapper 003).
2//!
3//! <https://wiki.nesdev.org/w/index.php/CNROM>
4//! <https://wiki.nesdev.org/w/index.php/INES_Mapper_003>
5
6use crate::{
7    cart::Cart,
8    common::{Clock, Regional, Reset, Sram},
9    mapper::{
10        self, MapRead, MapWrite, MappedRead, MappedWrite, Mapper, Mirrored, OnBusRead, OnBusWrite,
11    },
12    mem::Banks,
13    ppu::Mirroring,
14};
15use serde::{Deserialize, Serialize};
16
17/// `CNROM` (Mapper 003).
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[must_use]
20pub struct Cnrom {
21    pub mirroring: Mirroring,
22    pub chr_banks: Banks,
23    pub mirror_prg_rom: bool,
24}
25
26impl Cnrom {
27    const CHR_ROM_WINDOW: usize = 8 * 1024;
28
29    pub fn load(cart: &mut Cart) -> Result<Mapper, mapper::Error> {
30        let cnrom = Self {
31            mirroring: cart.mirroring(),
32            chr_banks: Banks::new(0x0000, 0x1FFFF, cart.chr_rom.len(), Self::CHR_ROM_WINDOW)?,
33            mirror_prg_rom: cart.prg_rom.len() <= 0x4000,
34        };
35        Ok(cnrom.into())
36    }
37}
38
39impl Mirrored for Cnrom {
40    fn mirroring(&self) -> Mirroring {
41        self.mirroring
42    }
43
44    fn set_mirroring(&mut self, mirroring: Mirroring) {
45        self.mirroring = mirroring;
46    }
47}
48
49impl MapRead for Cnrom {
50    // PPU $0000..=$1FFF 8K CHR-ROM Banks Switchable
51    // CPU $8000..=$BFFF 16K PRG-ROM Bank Fixed
52    // CPU $C000..=$FFFF 16K PRG-ROM Bank Fixed or Bank 1 Mirror if only 16 KB PRG-ROM
53
54    fn map_peek(&self, addr: u16) -> MappedRead {
55        match addr {
56            0x0000..=0x1FFF => MappedRead::Chr(self.chr_banks.translate(addr)),
57            0x8000..=0xBFFF => MappedRead::PrgRom((addr & 0x3FFF).into()),
58            0xC000..=0xFFFF => {
59                let mirror = if self.mirror_prg_rom { 0x3FFF } else { 0x7FFF };
60                MappedRead::PrgRom((addr & mirror).into())
61            }
62            _ => MappedRead::Bus,
63        }
64    }
65}
66
67impl MapWrite for Cnrom {
68    fn map_write(&mut self, addr: u16, val: u8) -> MappedWrite {
69        if matches!(addr, 0x8000..=0xFFFF) {
70            self.chr_banks.set(0, val.into());
71        }
72        MappedWrite::Bus
73    }
74}
75
76impl OnBusRead for Cnrom {}
77impl OnBusWrite for Cnrom {}
78impl Reset for Cnrom {}
79impl Clock for Cnrom {}
80impl Regional for Cnrom {}
81impl Sram for Cnrom {}