use crate::{
common::{Clock, NesRegion, Regional, Reset, ResetKind, Sample, Sram},
fs, mem,
ppu::{CIRam, Mirroring},
};
use serde::{Deserialize, Serialize};
use std::path::Path;
pub use bandai_fcg::BandaiFCG; pub use m000_nrom::Nrom;
pub use m001_sxrom::{Revision as Mmc1Revision, Sxrom};
pub use m002_uxrom::Uxrom;
pub use m003_cnrom::Cnrom;
pub use m004_txrom::{Revision as Mmc3Revision, Txrom};
pub use m005_exrom::Exrom;
pub use m007_axrom::Axrom;
pub use m009_pxrom::Pxrom;
pub use m010_fxrom::Fxrom;
pub use m011_color_dreams::ColorDreams;
pub use m018_jalecoss88006::JalecoSs88006;
pub use m019_namco163::Namco163;
pub use m024_m026_vrc6::Vrc6;
pub use m034_bnrom::Bnrom;
pub use m034_nina001::Nina001;
pub use m066_gxrom::Gxrom;
pub use m069_sunsoft_fme7::SunsoftFme7;
pub use m071_bf909x::{Bf909x, Revision as Bf909Revision};
pub use m079_nina003_006::Nina003006;
pub mod bandai_fcg;
pub mod m000_nrom;
pub mod m001_sxrom;
pub mod m002_uxrom;
pub mod m003_cnrom;
pub mod m004_txrom;
pub mod m005_exrom;
pub mod m007_axrom;
pub mod m009_pxrom;
pub mod m010_fxrom;
pub mod m011_color_dreams;
pub mod m018_jalecoss88006;
pub mod m019_namco163;
pub mod m024_m026_vrc6;
pub mod m034_bnrom;
pub mod m034_nina001;
pub mod m066_gxrom;
pub mod m069_sunsoft_fme7;
pub mod m071_bf909x;
pub mod m079_nina003_006;
pub mod vrc_irq;
#[derive(thiserror::Error, Debug)]
#[must_use]
pub enum Error {
#[error(transparent)]
Bank(#[from] mem::Error),
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[must_use]
pub enum MapperRevision {
Mmc3(Mmc3Revision),
Bf909(Bf909Revision),
}
impl std::fmt::Display for MapperRevision {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match self {
Self::Mmc3(rev) => match rev {
Mmc3Revision::A => "MMC3A",
Mmc3Revision::BC => "MMC3B/C",
Mmc3Revision::Acc => "MMC3Acc",
},
Self::Bf909(rev) => match rev {
Bf909Revision::Bf909x => "BF909x",
Bf909Revision::Bf9097 => "BF9097",
},
};
write!(f, "{s}")
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub enum Mapper {
None(()),
Nrom(Nrom),
Sxrom(Sxrom),
Uxrom(Uxrom),
Cnrom(Cnrom),
Txrom(Txrom),
Exrom(Box<Exrom>),
Axrom(Axrom),
Pxrom(Pxrom),
Fxrom(Fxrom),
ColorDreams(ColorDreams),
BandaiFCG(Box<BandaiFCG>),
JalecoSs88006(JalecoSs88006),
Namco163(Box<Namco163>),
Vrc6(Box<Vrc6>),
Bnrom(Bnrom),
Nina001(Nina001),
Gxrom(Gxrom),
SunsoftFme7(SunsoftFme7),
Bf909x(Bf909x),
Nina003006(Nina003006),
}
macro_rules! impl_from_board {
(@impl $variant:ident, $board:ident) => {
impl From<$board> for Mapper {
fn from(board: $board) -> Self {
Self::$variant(board)
}
}
};
(@impl $variant:ident, Box<$board:ident>) => {
impl From<$board> for Mapper {
fn from(board: $board) -> Self {
Self::$variant(Box::new(board))
}
}
impl From<Box<$board>> for Mapper {
fn from(board: Box<$board>) -> Self {
Self::$variant(board)
}
}
};
($($variant:ident($($tt:tt)+)),+ $(,)?) => {
$(impl_from_board!(@impl $variant, $($tt)+);)+
};
}
impl_from_board!(
Nrom(Nrom),
Sxrom(Sxrom),
Uxrom(Uxrom),
Cnrom(Cnrom),
Txrom(Txrom),
Exrom(Box<Exrom>),
Axrom(Axrom),
Pxrom(Pxrom),
Fxrom(Fxrom),
ColorDreams(ColorDreams),
BandaiFCG(Box<BandaiFCG>),
JalecoSs88006(JalecoSs88006),
Namco163(Box<Namco163>),
Vrc6(Box<Vrc6>),
Bnrom(Bnrom),
Nina001(Nina001),
Gxrom(Gxrom),
SunsoftFme7(SunsoftFme7),
Bf909x(Bf909x),
Nina003006(Nina003006),
);
macro_rules! impl_map {
($self:expr, $fn:ident$(,)? $($args:expr),*$(,)?) => {
match $self {
Mapper::None(m) => m.$fn($($args),*),
Mapper::Nrom(m) => m.$fn($($args),*),
Mapper::Sxrom(m) => m.$fn($($args),*),
Mapper::Uxrom(m) => m.$fn($($args),*),
Mapper::Cnrom(m) => m.$fn($($args),*),
Mapper::Txrom(m) => m.$fn($($args),*),
Mapper::Exrom(m) => m.$fn($($args),*),
Mapper::Axrom(m) => m.$fn($($args),*),
Mapper::Pxrom(m) => m.$fn($($args),*),
Mapper::Fxrom(m) => m.$fn($($args),*),
Mapper::ColorDreams(m) => m.$fn($($args),*),
Mapper::BandaiFCG(m) => m.$fn($($args),*),
Mapper::JalecoSs88006(m) => m.$fn($($args),*),
Mapper::Namco163(m) => m.$fn($($args),*),
Mapper::Vrc6(m) => m.$fn($($args),*),
Mapper::Bnrom(m) => m.$fn($($args),*),
Mapper::Nina001(m) => m.$fn($($args),*),
Mapper::Gxrom(m) => m.$fn($($args),*),
Mapper::SunsoftFme7(m) => m.$fn($($args),*),
Mapper::Bf909x(m) => m.$fn($($args),*),
Mapper::Nina003006(m) => m.$fn($($args),*),
}
};
}
impl Map for Mapper {
#[inline(always)]
fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
impl_map!(self, chr_read, addr, ciram)
}
#[inline(always)]
fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
impl_map!(self, chr_peek, addr, ciram)
}
#[inline(always)]
fn prg_read(&mut self, addr: u16) -> u8 {
impl_map!(self, prg_read, addr)
}
#[inline(always)]
fn prg_peek(&self, addr: u16) -> u8 {
impl_map!(self, prg_peek, addr)
}
#[inline(always)]
fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
impl_map!(self, chr_write, addr, val, ciram)
}
#[inline(always)]
fn prg_write(&mut self, addr: u16, val: u8) {
impl_map!(self, prg_write, addr, val)
}
fn ppu_read(&mut self, addr: u16) {
impl_map!(self, ppu_read, addr)
}
fn ppu_write(&mut self, addr: u16, val: u8) {
impl_map!(self, ppu_write, addr, val)
}
fn irq_pending(&self) -> bool {
impl_map!(self, irq_pending)
}
fn dma_pending(&self) -> bool {
impl_map!(self, dma_pending)
}
fn clear_dma_pending(&mut self) {
impl_map!(self, clear_dma_pending)
}
#[inline(always)]
fn mirroring(&self) -> Mirroring {
impl_map!(self, mirroring)
}
}
impl Sample for Mapper {
#[inline]
fn output(&self) -> f32 {
match self {
Self::Exrom(exrom) => exrom.output(),
Self::Namco163(namco163) => namco163.output(),
Self::Vrc6(vrc6) => vrc6.output(),
Self::SunsoftFme7(sunsoft_fme7) => sunsoft_fme7.output(),
_ => 0.0,
}
}
}
impl Reset for Mapper {
fn reset(&mut self, kind: ResetKind) {
impl_map!(self, reset, kind)
}
}
impl Clock for Mapper {
#[inline]
fn clock(&mut self) {
impl_map!(self, clock)
}
}
impl Regional for Mapper {
fn region(&self) -> NesRegion {
impl_map!(self, region)
}
fn set_region(&mut self, region: NesRegion) {
impl_map!(self, set_region, region)
}
}
impl Sram for Mapper {
fn save(&self, path: impl AsRef<Path>) -> fs::Result<()> {
impl_map!(self, save, path)
}
fn load(&mut self, path: impl AsRef<Path>) -> fs::Result<()> {
impl_map!(self, load, path)
}
}
impl Mapper {
pub const fn none() -> Self {
Self::None(())
}
pub const fn is_none(&self) -> bool {
matches!(self, Self::None(_))
}
}
impl Default for Mapper {
fn default() -> Self {
Self::none()
}
}
pub trait Map: Clock + Regional + Reset + Sram {
#[inline(always)]
fn chr_read(&mut self, addr: u16, ciram: &CIRam) -> u8 {
self.chr_peek(addr, ciram)
}
fn chr_peek(&self, _addr: u16, _ciram: &CIRam) -> u8;
#[inline(always)]
fn prg_read(&mut self, addr: u16) -> u8 {
self.prg_peek(addr)
}
fn prg_peek(&self, _addr: u16) -> u8;
#[inline(always)]
fn chr_write(&mut self, addr: u16, val: u8, ciram: &mut CIRam) {
if let 0x2000..=0x3EFF = addr {
ciram.write(addr, val, self.mirroring());
}
}
fn prg_write(&mut self, _addr: u16, _val: u8) {}
fn ppu_read(&mut self, _addr: u16) {}
fn ppu_write(&mut self, _addr: u16, _val: u8) {}
fn irq_pending(&self) -> bool {
false
}
fn clear_dma_pending(&mut self) {}
fn dma_pending(&self) -> bool {
false
}
fn mirroring(&self) -> Mirroring;
}
impl Map for () {
fn chr_peek(&self, addr: u16, ciram: &CIRam) -> u8 {
match addr {
0x2000..=0x3EFF => ciram.peek(addr, self.mirroring()),
_ => 0,
}
}
fn prg_peek(&self, _addr: u16) -> u8 {
0
}
fn mirroring(&self) -> Mirroring {
Mirroring::default()
}
}
impl Sample for () {}
impl Reset for () {}
impl Clock for () {}
impl Regional for () {}
impl Sram for () {}