1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! NES Memory Mappers for Cartridges
//!
//! [http://wiki.nesdev.com/w/index.php/Mapper]()

use crate::{
    cartridge::Cartridge,
    common::{Addr, Byte, Clocked, Powered},
    memory::{MemRead, MemWrite},
    serialization::Savable,
    {nes_err, NesResult},
};
use enum_dispatch::enum_dispatch;
use std::{
    fmt::Debug,
    io::{Read, Write},
};

use m000_nrom::Nrom; // Mapper 0
use m001_sxrom::Sxrom; // Mapper 1
use m002_uxrom::Uxrom; // Mapper 2
use m003_cnrom::Cnrom; // Mapper 3
use m004_txrom::Txrom; // Mapper 4
use m005_exrom::Exrom; // Mapper 5
use m007_axrom::Axrom; // Mapper 7
use m009_pxrom::Pxrom; // Mapper 9

mod m000_nrom;
mod m001_sxrom;
mod m002_uxrom;
mod m003_cnrom;
mod m004_txrom;
mod m005_exrom;
mod m007_axrom;
mod m009_pxrom;

/// Nametable Mirroring Mode
///
/// [http://wiki.nesdev.com/w/index.php/Mirroring#Nametable_Mirroring]()
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Mirroring {
    Horizontal,
    Vertical,
    SingleScreenA,
    SingleScreenB,
    FourScreen,
}

#[derive(Debug, Clone)]
pub struct NullMapper {}

#[allow(clippy::large_enum_variant)]
#[enum_dispatch]
#[derive(Debug, Clone)]
pub enum MapperType {
    NullMapper,
    Nrom,
    Sxrom,
    Uxrom,
    Cnrom,
    Txrom,
    Exrom,
    Axrom,
    Pxrom,
}

#[enum_dispatch(MapperType)]
pub trait Mapper: MemRead + MemWrite + Savable + Clocked + Powered {
    fn irq_pending(&mut self) -> bool {
        false
    }
    fn mirroring(&self) -> Mirroring {
        Mirroring::Horizontal
    }
    fn vram_change(&mut self, _addr: Addr) {}
    fn battery_backed(&self) -> bool {
        false
    }
    fn save_sram<F: Write>(&self, _fh: &mut F) -> NesResult<()> {
        Ok(())
    }
    fn load_sram<F: Read>(&mut self, _fh: &mut F) -> NesResult<()> {
        Ok(())
    }
    fn use_ciram(&self, _addr: Addr) -> bool {
        true
    }
    fn nametable_page(&self, _addr: Addr) -> Addr {
        0
    }
    fn ppu_write(&mut self, _addr: Addr, _val: Byte) {}
    fn open_bus(&mut self, _addr: Addr, _val: Byte) {}
}

/// Attempts to return a valid Mapper for the given rom.
pub fn load_rom<F: Read>(name: &str, rom: &mut F) -> NesResult<MapperType> {
    let cart = Cartridge::from_rom(name, rom)?;
    let mapper = match cart.header.mapper_num {
        0 => Nrom::load(cart),
        1 => Sxrom::load(cart),
        2 => Uxrom::load(cart),
        3 => Cnrom::load(cart),
        4 => Txrom::load(cart),
        5 => Exrom::load(cart),
        7 => Axrom::load(cart),
        9 => Pxrom::load(cart),
        71 => Uxrom::load(cart), // TODO: Mapper 71 has slight differences from Uxrom
        _ => nes_err!("unsupported mapper number: {}", cart.header.mapper_num)?,
    };
    Ok(mapper)
}

impl Mapper for NullMapper {}
impl MemRead for NullMapper {}
impl MemWrite for NullMapper {}
impl Savable for NullMapper {}
impl Clocked for NullMapper {}
impl Powered for NullMapper {}

pub fn null() -> MapperType {
    let null = NullMapper {};
    null.into()
}

impl Savable for Mirroring {
    fn save<F: Write>(&self, fh: &mut F) -> NesResult<()> {
        (*self as u8).save(fh)
    }
    fn load<F: Read>(&mut self, fh: &mut F) -> NesResult<()> {
        let mut val = 0u8;
        val.load(fh)?;
        *self = match val {
            0 => Mirroring::Horizontal,
            1 => Mirroring::Vertical,
            2 => Mirroring::SingleScreenA,
            3 => Mirroring::SingleScreenB,
            4 => Mirroring::FourScreen,
            _ => panic!("invalid Mirroring value {}", val),
        };
        Ok(())
    }
}

impl Default for Mirroring {
    fn default() -> Self {
        Mirroring::Horizontal
    }
}