tetanes_core/mapper/
m000_nrom.rs

1//! `NROM` (Mapper 000).
2//!
3//! <https://wiki.nesdev.org/w/index.php/NROM>
4
5use crate::{
6    cart::Cart,
7    common::{Clock, Regional, Reset, Sram},
8    mapper::{
9        self, MapRead, MapWrite, MappedRead, MappedWrite, Mapper, Mirrored, OnBusRead, OnBusWrite,
10    },
11    ppu::Mirroring,
12};
13use serde::{Deserialize, Serialize};
14
15/// `NROM` (Mapper 000).
16#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
17#[must_use]
18pub struct Nrom {
19    pub mirroring: Mirroring,
20    pub mirror_prg_rom: bool,
21}
22
23impl Nrom {
24    const PRG_RAM_SIZE: usize = 8 * 1024;
25    const CHR_RAM_SIZE: usize = 8 * 1024;
26
27    pub fn load(cart: &mut Cart) -> Result<Mapper, mapper::Error> {
28        // Family Basic supported 2-4K of PRG-RAM, but we'll provide 8K by default.
29        cart.add_prg_ram(Self::PRG_RAM_SIZE);
30        // NROM doesn't have CHR-RAM - but a lot of homebrew games use Mapper 000 with CHR-RAM, so
31        // we'll provide some.
32        if !cart.has_chr_rom() && cart.chr_ram.is_empty() {
33            cart.add_chr_ram(Self::CHR_RAM_SIZE);
34        };
35        let nrom = Self {
36            mirroring: cart.mirroring(),
37            mirror_prg_rom: cart.prg_rom.len() <= 0x4000,
38        };
39        Ok(nrom.into())
40    }
41}
42
43impl Mirrored for Nrom {
44    fn mirroring(&self) -> Mirroring {
45        self.mirroring
46    }
47
48    fn set_mirroring(&mut self, mirroring: Mirroring) {
49        self.mirroring = mirroring;
50    }
51}
52
53impl MapRead for Nrom {
54    // PPU $0000..=$1FFF 8K Fixed CHR-ROM Bank
55    // CPU $6000..=$7FFF 2K or 4K PRG-RAM Family Basic only. 8K is provided by default.
56    // CPU $8000..=$BFFF 16K PRG-ROM Bank 1 for NROM128 or NROM256
57    // CPU $C000..=$FFFF 16K PRG-ROM Bank 2 for NROM256 or Bank 1 Mirror for NROM128
58
59    fn map_peek(&self, addr: u16) -> MappedRead {
60        match addr {
61            0x0000..=0x1FFF => MappedRead::Chr(addr.into()),
62            0x6000..=0x7FFF => MappedRead::PrgRam((addr & 0x1FFF).into()),
63            0x8000..=0xBFFF => MappedRead::PrgRom((addr & 0x3FFF).into()),
64            0xC000..=0xFFFF => {
65                let mirror = if self.mirror_prg_rom { 0x3FFF } else { 0x7FFF };
66                MappedRead::PrgRom((addr & mirror).into())
67            }
68            _ => MappedRead::Bus,
69        }
70    }
71}
72
73impl MapWrite for Nrom {
74    fn map_write(&mut self, addr: u16, val: u8) -> MappedWrite {
75        match addr {
76            0x0000..=0x1FFF => MappedWrite::ChrRam(addr.into(), val),
77            0x6000..=0x7FFF => MappedWrite::PrgRam((addr & 0x1FFF).into(), val),
78            _ => MappedWrite::Bus,
79        }
80    }
81}
82
83impl OnBusRead for Nrom {}
84impl OnBusWrite for Nrom {}
85impl Reset for Nrom {}
86impl Clock for Nrom {}
87impl Regional for Nrom {}
88impl Sram for Nrom {}