tetanes_core/mapper/
m076_dxrom.rs

1//! `DxROM`/`NAMCOT-3446` (Mapper 076).
2//!
3//! <https://www.nesdev.org/wiki/INES_Mapper_076>
4//! <https://www.nesdev.org/wiki/DxROM>
5
6use crate::{
7    cart::Cart,
8    common::{Clock, NesRegion, Regional, Reset, ResetKind, Sram},
9    fs,
10    mapper::{
11        self, BusKind, Dxrom206, MapRead, MapWrite, MappedRead, MappedWrite, Mapper, Mirrored,
12        OnBusRead, OnBusWrite,
13    },
14    ppu::Mirroring,
15};
16use serde::{Deserialize, Serialize};
17use std::path::Path;
18
19/// `DxROM`/`NAMCOT-3446` (Mapper 076).
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[must_use]
22pub struct Dxrom {
23    pub inner: Dxrom206,
24}
25
26impl Dxrom {
27    const CHR_WINDOW: usize = 2048;
28
29    pub fn load(cart: &mut Cart) -> Result<Mapper, mapper::Error> {
30        let dxrom = Self {
31            inner: Dxrom206::new(cart, Self::CHR_WINDOW)?,
32        };
33        Ok(dxrom.into())
34    }
35
36    pub fn update_chr_banks(&mut self) {
37        self.inner.set_chr_banks(|banks, regs| {
38            banks.set(0, regs[2] as usize);
39            banks.set(1, regs[3] as usize);
40            banks.set(2, regs[4] as usize);
41            banks.set(3, regs[5] as usize);
42        });
43    }
44}
45
46impl Mirrored for Dxrom {
47    fn mirroring(&self) -> Mirroring {
48        self.inner.mirroring()
49    }
50
51    fn set_mirroring(&mut self, mirroring: Mirroring) {
52        self.inner.set_mirroring(mirroring);
53    }
54}
55
56impl OnBusRead for Dxrom {
57    fn on_bus_read(&mut self, addr: u16, kind: BusKind) {
58        self.inner.on_bus_read(addr, kind)
59    }
60}
61
62impl OnBusWrite for Dxrom {
63    fn on_bus_write(&mut self, addr: u16, val: u8, kind: BusKind) {
64        self.inner.on_bus_write(addr, val, kind)
65    }
66}
67
68impl MapRead for Dxrom {
69    // PPU $0000..=$07FF (or $1000..=$17FF) 2K CHR-ROM/RAM Bank 1 Switchable
70    // PPU $0800..=$0FFF (or $1800..=$1FFF) 2K CHR-ROM/RAM Bank 2 Switchable
71    // PPU $1000..=$17FF (or $0000..=$07FF) 2K CHR-ROM/RAM Bank 3 Switchable
72    // PPU $1800..=$1FFF (or $0800..=$0FFF) 2K CHR-ROM/RAM Bank 4 Switchable
73
74    // CPU $8000..=$9FFF (or $C000..=$DFFF) 8K PRG-ROM Bank 1 Switchable
75    // CPU $A000..=$BFFF 8K PRG-ROM Bank 2 Switchable
76    // CPU $C000..=$DFFF (or $8000..=$9FFF) 8K PRG-ROM Bank 3 Fixed to second-to-last Bank
77    // CPU $E000..=$FFFF 8K PRG-ROM Bank 4 Fixed to Last
78
79    fn map_read(&mut self, addr: u16) -> MappedRead {
80        self.inner.map_read(addr)
81    }
82
83    fn map_peek(&self, addr: u16) -> MappedRead {
84        self.inner.map_peek(addr)
85    }
86}
87
88impl MapWrite for Dxrom {
89    fn map_write(&mut self, addr: u16, val: u8) -> MappedWrite {
90        let write = self.inner.map_write(addr, val);
91        if matches!(addr, 0x8000..=0x8001) {
92            self.update_chr_banks();
93        }
94        write
95    }
96}
97
98impl Reset for Dxrom {
99    fn reset(&mut self, kind: ResetKind) {
100        self.inner.reset(kind);
101        self.update_chr_banks();
102    }
103}
104impl Clock for Dxrom {
105    fn clock(&mut self) -> u64 {
106        self.inner.clock()
107    }
108}
109impl Regional for Dxrom {
110    fn region(&self) -> NesRegion {
111        self.inner.region()
112    }
113
114    fn set_region(&mut self, region: NesRegion) {
115        self.inner.set_region(region)
116    }
117}
118impl Sram for Dxrom {
119    fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
120        self.inner.save(path)
121    }
122
123    fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
124        self.inner.load(path)
125    }
126}