1pub(crate) mod action52;
2pub(crate) mod bandai;
3pub(crate) mod konami;
4pub(crate) mod mmc1;
5pub(crate) mod mmc2;
6pub(crate) mod mmc3;
7pub(crate) mod mmc5;
8pub(crate) mod namco163;
9pub(crate) mod namco175;
10pub(crate) mod nrom;
11pub(crate) mod sunsoft;
12pub(crate) mod unrom512;
13
14use crate::bus::cartridge::Cartridge;
15use bincode::{Decode, Encode};
16
17#[allow(clippy::upper_case_acronyms)]
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
19pub(crate) enum ChrType {
20    ROM,
21    RAM,
22}
23
24impl ChrType {
25    fn to_map_result(self, address: u32) -> PpuMapResult {
26        match self {
27            Self::ROM => PpuMapResult::ChrROM(address),
28            Self::RAM => PpuMapResult::ChrRAM(address),
29        }
30    }
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
34pub(crate) enum NametableMirroring {
35    Horizontal,
36    Vertical,
37    SingleScreenBank0,
38    SingleScreenBank1,
39}
40
41impl NametableMirroring {
42    fn map_to_vram(self, address: u16) -> u16 {
43        assert!((0x2000..=0x3EFF).contains(&address));
44
45        let relative_addr = address & 0x0FFF;
46
47        match self {
48            Self::Horizontal => ((relative_addr & 0x0800) >> 1) | (relative_addr & 0x03FF),
49            Self::Vertical => relative_addr & 0x07FF,
50            Self::SingleScreenBank0 => relative_addr & 0x03FF,
51            Self::SingleScreenBank1 => 0x0400 | (relative_addr & 0x03FF),
52        }
53    }
54}
55
56#[derive(Debug, Clone, Copy)]
57pub(crate) enum CpuMapResult {
58    PrgROM(u32),
59    PrgRAM(u32),
60    None,
61}
62
63impl CpuMapResult {
64    fn read(self, cartridge: &Cartridge) -> Option<u8> {
65        match self {
66            Self::PrgROM(address) => Some(cartridge.get_prg_rom(address)),
67            Self::PrgRAM(address) => Some(cartridge.get_prg_ram(address)),
68            Self::None => None,
69        }
70    }
71
72    fn write(self, value: u8, cartridge: &mut Cartridge) {
73        if let Self::PrgRAM(address) = self {
74            cartridge.set_prg_ram(address, value);
75        }
76    }
77}
78
79#[derive(Debug, Clone, Copy)]
80pub(crate) enum PpuMapResult {
81    ChrROM(u32),
82    ChrRAM(u32),
83    Vram(u16),
84}
85
86impl PpuMapResult {
87    pub(super) fn read(self, cartridge: &Cartridge, vram: &[u8; 2048]) -> u8 {
88        match self {
89            Self::ChrROM(address) => cartridge.get_chr_rom(address),
90            Self::ChrRAM(address) => cartridge.get_chr_ram(address),
91            Self::Vram(address) => vram[address as usize],
92        }
93    }
94
95    pub(super) fn write(self, value: u8, cartridge: &mut Cartridge, vram: &mut [u8; 2048]) {
96        match self {
97            Self::ChrROM(_) => {}
98            Self::ChrRAM(address) => {
99                cartridge.set_chr_ram(address, value);
100            }
101            Self::Vram(address) => {
102                vram[address as usize] = value;
103            }
104        }
105    }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
109enum BankSizeKb {
110    One,
111    Two,
112    Four,
113    Eight,
114    Sixteen,
115    ThirtyTwo,
116}
117
118impl BankSizeKb {
119    fn shift(self) -> u32 {
120        match self {
121            Self::One => 10,
122            Self::Two => 11,
123            Self::Four => 12,
124            Self::Eight => 13,
125            Self::Sixteen => 14,
126            Self::ThirtyTwo => 15,
127        }
128    }
129
130    fn address_mask(self) -> u16 {
131        match self {
132            Self::One => 0x03FF,
133            Self::Two => 0x07FF,
134            Self::Four => 0x0FFF,
135            Self::Eight => 0x1FFF,
136            Self::Sixteen => 0x3FFF,
137            Self::ThirtyTwo => 0x7FFF,
138        }
139    }
140
141    fn to_absolute_address<N: Into<u32>>(self, bank_number: N, address: u16) -> u32 {
142        (bank_number.into() << self.shift()) | u32::from(address & self.address_mask())
143    }
144
145    fn to_absolute_address_from_end<N: Into<u32>>(
146        self,
147        inverse_bank_number: N,
148        memory_len: u32,
149        address: u16,
150    ) -> u32 {
151        (((memory_len >> self.shift()) - inverse_bank_number.into()) << self.shift())
152            | u32::from(address & self.address_mask())
153    }
154
155    fn to_absolute_address_last_bank(self, memory_len: u32, address: u16) -> u32 {
156        self.to_absolute_address_from_end(1_u32, memory_len, address)
157    }
158}
159
160#[cfg(test)]
161pub(crate) fn new_mmc1(prg_rom: Vec<u8>) -> super::Mapper {
162    use super::mappers::mmc1::Mmc1;
163    use super::{Mapper, MapperImpl, TimingMode};
164
165    Mapper::Mmc1(MapperImpl {
166        cartridge: Cartridge {
167            timing_mode: TimingMode::Ntsc,
168            prg_rom,
169            prg_ram: vec![0; 8192],
170            has_ram_battery: false,
171            prg_ram_dirty_bit: false,
172            chr_rom: vec![0; 8192],
173            chr_ram: Vec::new(),
174        },
175        data: Mmc1::new(ChrType::ROM),
176    })
177}