tetanes_core/
mapper.rs

1//! Memory Mappers for cartridges.
2//!
3//! <https://wiki.nesdev.org/w/index.php/Mapper>
4
5use crate::{
6    common::{Clock, Regional, Reset, Sram},
7    mem,
8    ppu::Mirroring,
9};
10use enum_dispatch::enum_dispatch;
11use serde::{Deserialize, Serialize};
12
13pub use bandai_fcg::BandaiFCG; // m016, m153, m157, m159
14pub use m000_nrom::Nrom;
15pub use m001_sxrom::{Revision as Mmc1Revision, Sxrom};
16pub use m002_uxrom::Uxrom;
17pub use m003_cnrom::Cnrom;
18pub use m004_txrom::{Revision as Mmc3Revision, Txrom};
19pub use m005_exrom::Exrom;
20pub use m007_axrom::Axrom;
21pub use m009_pxrom::Pxrom;
22pub use m010_fxrom::Fxrom;
23pub use m011_color_dreams::ColorDreams;
24pub use m018_jalecoss88006::JalecoSs88006;
25pub use m019_namco163::Namco163;
26pub use m024_m026_vrc6::Vrc6;
27pub use m034_bnrom::Bnrom;
28pub use m034_nina001::Nina001;
29pub use m066_gxrom::Gxrom;
30pub use m069_sunsoft_fme7::SunsoftFme7;
31pub use m071_bf909x::{Bf909x, Revision as Bf909Revision};
32pub use m076_dxrom::Dxrom as Dxrom76;
33pub use m079_nina003_006::Nina003006;
34pub use m088_dxrom::Dxrom as Dxrom88;
35pub use m095_dxrom::Dxrom as Dxrom95;
36pub use m154_dxrom::Dxrom as Dxrom154;
37pub use m206_dxrom::Dxrom as Dxrom206;
38
39pub mod bandai_fcg;
40pub mod m000_nrom;
41pub mod m001_sxrom;
42pub mod m002_uxrom;
43pub mod m003_cnrom;
44pub mod m004_txrom;
45pub mod m005_exrom;
46pub mod m007_axrom;
47pub mod m009_pxrom;
48pub mod m010_fxrom;
49pub mod m011_color_dreams;
50pub mod m018_jalecoss88006;
51pub mod m019_namco163;
52pub mod m024_m026_vrc6;
53pub mod m034_bnrom;
54pub mod m034_nina001;
55pub mod m066_gxrom;
56pub mod m069_sunsoft_fme7;
57pub mod m071_bf909x;
58pub mod m076_dxrom;
59pub mod m079_nina003_006;
60pub mod m088_dxrom;
61pub mod m095_dxrom;
62pub mod m154_dxrom;
63pub mod m206_dxrom;
64pub mod vrc_irq;
65
66/// Errors that mappers can return.
67#[derive(thiserror::Error, Debug)]
68#[must_use]
69pub enum Error {
70    /// A mapper banking error.
71    #[error(transparent)]
72    Bank(#[from] mem::Error),
73}
74
75/// Allow user-controlled mapper revision for mappers that are difficult to auto-detect correctly.
76#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
77#[must_use]
78pub enum MapperRevision {
79    // Mmc1 and Vrc6 should be properly detected by the mapper number
80    /// No known detection except DB lookup
81    Mmc3(Mmc3Revision),
82    /// Can compare to submapper 1, if header is correct
83    Bf909(Bf909Revision),
84}
85
86impl std::fmt::Display for MapperRevision {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        let s = match self {
89            Self::Mmc3(rev) => match rev {
90                Mmc3Revision::A => "MMC3A",
91                Mmc3Revision::BC => "MMC3B/C",
92                Mmc3Revision::Acc => "MMC3Acc",
93            },
94            Self::Bf909(rev) => match rev {
95                Bf909Revision::Bf909x => "BF909x",
96                Bf909Revision::Bf9097 => "BF9097",
97            },
98        };
99        write!(f, "{s}")
100    }
101}
102
103#[enum_dispatch]
104#[derive(Debug, Clone, Serialize, Deserialize)]
105#[allow(clippy::large_enum_variant)]
106#[must_use]
107pub enum Mapper {
108    None,
109    /// `NROM` (Mapper 000)
110    Nrom,
111    /// `SxROM`/`MMC1` (Mapper 001)
112    Sxrom,
113    /// `UxROM` (Mapper 002)
114    Uxrom,
115    /// `CNROM` (Mapper 003)
116    Cnrom,
117    /// `TxROM`/`MMC3` (Mapper 004)
118    Txrom,
119    /// `ExROM`/`MMC5` (Mapper 5)
120    Exrom,
121    /// `AxROM` (Mapper 007)
122    Axrom,
123    /// `PxROM`/`MMC2` (Mapper 009)
124    Pxrom,
125    /// `FxROM`/`MMC4` (Mapper 010)
126    Fxrom,
127    /// `Color Dreams` (Mapper 011)
128    ColorDreams,
129    /// `Bandai FCG` (Mappers 016, 153, 157, and 159)
130    BandaiFCG,
131    /// `Jaleco SS88006` (Mapper 018)
132    JalecoSs88006,
133    /// `Namco163` (Mapper 019)
134    Namco163,
135    /// `VRC6` (Mapper 024).
136    Vrc6,
137    /// `BNROM` (Mapper 034).
138    Bnrom,
139    /// `NINA-001` (Mapper 034).
140    Nina001,
141    /// `GxROM` (Mapper 066).
142    Gxrom,
143    /// `Sunsoft FME7` (Mapper 069).
144    SunsoftFme7,
145    /// `Bf909x` (Mapper 071).
146    Bf909x,
147    /// `DxROM`/`NAMCOT-3446` (Mapper 076).
148    Dxrom76,
149    /// `NINA-003`/`NINA-006` (Mapper 079).
150    Nina003006,
151    /// `DxROM`/`Namco 108` (Mapper 088).
152    Dxrom88,
153    /// `DxROM`/`NAMCOT-3425` (Mapper 095).
154    Dxrom95,
155    /// `DxROM`/`NAMCOT-3453` (Mapper 154).
156    Dxrom154,
157    /// `DxROM`/`Namco 108` (Mapper 206).
158    Dxrom206,
159}
160
161impl Mapper {
162    pub fn none() -> Self {
163        None.into()
164    }
165
166    pub const fn is_none(&self) -> bool {
167        matches!(self, Self::None(_))
168    }
169}
170
171impl Default for Mapper {
172    fn default() -> Self {
173        Self::none()
174    }
175}
176
177/// Type of read operation for an address for a given Mapper.
178#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
179#[must_use]
180pub enum MappedRead {
181    /// Defer to default data bus behavior for this read. Primarily used to read from
182    /// a mirrored Console-Internal RAM (i.e Nametable) address.
183    Bus,
184    /// Read from a CHR ROM or RAM address.
185    Chr(usize),
186    /// Read from a non-mirrored Console-Internal RAM (i.e. Nameteable) address for Mappers that
187    /// support custom Nametable Mirroring.
188    CIRam(usize),
189    /// Read from an External RAM address for Mappers that support EXRAM.
190    ExRam(usize),
191    /// Read from a PRG ROM address.
192    PrgRom(usize),
193    /// Read from a PRG ROM address.
194    PrgRam(usize),
195    /// Provide data directly for this read (i.e. from an internal Mapper register).
196    Data(u8),
197}
198
199/// Type of write operation for an address for a given Mapper.
200#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
201#[must_use]
202pub enum MappedWrite {
203    /// Do nothing with this write.
204    None,
205    /// Defer to default data bus behavior for this write.
206    Bus,
207    /// Write value to CHR RAM address.
208    ChrRam(usize, u8),
209    /// Write value to a non-mirrored Console-Internal RAM (i.e. Nametable) address for Mappers
210    /// that support custom Nametable Mirroring.
211    CIRam(usize, u8),
212    /// Write value to an External RAM address for Mappers that support EXRAM.
213    ExRam(usize, u8),
214    /// Write value to a PRG RAM address.
215    PrgRam(usize, u8),
216    /// Toggle PRG RAM write protection.
217    PrgRamProtect(bool),
218}
219
220#[enum_dispatch(Mapper)]
221pub trait MapRead {
222    fn map_read(&mut self, addr: u16) -> MappedRead {
223        self.map_peek(addr)
224    }
225
226    fn map_peek(&self, _addr: u16) -> MappedRead {
227        MappedRead::Bus
228    }
229}
230
231#[enum_dispatch(Mapper)]
232pub trait MapWrite {
233    fn map_write(&mut self, _addr: u16, _val: u8) -> MappedWrite {
234        MappedWrite::Bus
235    }
236}
237
238#[derive(Debug, Copy, Clone, PartialEq, Eq)]
239#[must_use]
240pub enum BusKind {
241    Cpu,
242    Ppu,
243}
244
245#[enum_dispatch(Mapper)]
246pub trait OnBusRead {
247    fn on_bus_read(&mut self, _addr: u16, _kind: BusKind) {}
248}
249
250#[enum_dispatch(Mapper)]
251pub trait OnBusWrite {
252    fn on_bus_write(&mut self, _addr: u16, _val: u8, _kind: BusKind) {}
253}
254
255#[enum_dispatch(Mapper)]
256pub trait Mirrored {
257    fn mirroring(&self) -> Mirroring {
258        Mirroring::default()
259    }
260    fn set_mirroring(&mut self, _mirroring: Mirroring) {}
261}
262
263#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
264#[must_use]
265pub struct None;
266
267impl MapRead for None {}
268impl MapWrite for None {}
269impl OnBusRead for None {}
270impl OnBusWrite for None {}
271impl Mirrored for None {}
272impl Clock for None {}
273impl Regional for None {}
274impl Reset for None {}
275impl Sram for None {}