Skip to content

Commit

Permalink
feat: added dxrom (#340)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukexor authored Oct 14, 2024
1 parent 153094d commit 906af59
Show file tree
Hide file tree
Showing 23 changed files with 773 additions and 94 deletions.
51 changes: 28 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,29 +208,34 @@ Support for the following mappers is currently implemented or in development:

<!-- markdownlint-disable line-length -->

| # | Name | Example Games | # of Games<sup>1</sup> | % of Games<sup>1</sup> |
| --- | -------------------- | ----------------------------------------- | ---------------------- | ---------------------- |
| 000 | NROM | Bomberman, Donkey Kong, Super Mario Bros. | ~247 | ~10% |
| 001 | SxROM/MMC1B/C | Metroid, Legend of Zelda, Tetris | ~680 | ~28% |
| 002 | UxROM | Castlevania, Contra, Mega Man | ~270 | ~11% |
| 003 | CNROM | Arkanoid, Paperboy, Pipe Dream | ~155 | ~6% |
| 004 | TxROM/MMC3/MMC6 | Kirby's Adventure, Super Mario Bros. 2/3 | ~599 | ~24% |
| 005 | ExROM/MMC5 | Castlevania 3, Laser Invasion | ~24 | &lt;0.01% |
| 007 | AxROM | Battletoads, Marble Madness | ~75 | ~3% |
| 009 | PxROM/MMC2 | Punch Out!! | 1 | &lt;0.01% |
| 010 | FxROM/MMC4 | Fire Emblem Gaiden | 3 | &lt;0.01% |
| 011 | Color Dreams | Crystal Mines, Metal Fighter | 15 | ~1% |
| 016 | Bandai FCG | Dragon Ball: Daimaou Fukkatsu | 14 | ~1% |
| 024 | VRC6a | Akumajou Densetsu | 1 | &lt;0.01% |
| 026 | VRC6b | Madara, Esper Dream 2 | 2 | &lt;0.01% |
| 034 | BNROM/NINA-001 | Deadly Towers, Impossible Mission II | 3 | &lt;0.01% |
| 066 | GxROM/MxROM | Super Mario Bros. + Duck Hunt | ~17 | &lt;0.01% |
| 071 | Camerica/Codemasters | Firehawk, Bee 52, MiG 29 - Soviet Fighter | ~15 | &lt;0.01% |
| 153 | Bandai FCG | Famicom Jump II: Saikyou no 7-nin | 1 | &lt;0.01% |
| 157 | Bandai FCG | SD Gundam Wars | 7 | &lt;0.01% |
| 155 | SxROM/MMC1A | Tatakae!! Ramen Man: Sakuretsu Choujin | 2 | &lt;0.01% |
| 159 | Bandai FCG | Dragon Ball Z: Kyoushuu! Saiya-jin | 4 | &lt;0.01% |
| | | | ~2155 / 2447 | ~88.0% |
| # | Name | Example Games | # of Games<sup>1</sup> | % of Games<sup>1</sup> |
| --- | --------------------- | ------------------------------------------ | ---------------------- | ---------------------- |
| 000 | NROM | Bomberman, Donkey Kong, Super Mario Bros. | ~247 | ~10% |
| 001 | SxROM/MMC1B/C | Metroid, Legend of Zelda, Tetris | ~680 | ~28% |
| 002 | UxROM | Castlevania, Contra, Mega Man | ~270 | ~11% |
| 003 | CNROM | Arkanoid, Paperboy, Pipe Dream | ~155 | ~6% |
| 004 | TxROM/MMC3/MMC6 | Kirby's Adventure, Super Mario Bros. 2/3 | ~599 | ~24% |
| 005 | ExROM/MMC5 | Castlevania 3, Laser Invasion | ~24 | &lt;0.01% |
| 007 | AxROM | Battletoads, Marble Madness | ~75 | ~3% |
| 009 | PxROM/MMC2 | Punch Out!! | 1 | &lt;0.01% |
| 010 | FxROM/MMC4 | Fire Emblem Gaiden | 3 | &lt;0.01% |
| 011 | Color Dreams | Crystal Mines, Metal Fighter | 15 | ~1% |
| 016 | Bandai FCG | Dragon Ball: Daimaou Fukkatsu | 14 | ~1% |
| 024 | VRC6a | Akumajou Densetsu | 1 | &lt;0.01% |
| 026 | VRC6b | Madara, Esper Dream 2 | 2 | &lt;0.01% |
| 034 | BNROM/NINA-001 | Deadly Towers, Impossible Mission II | 3 | &lt;0.01% |
| 066 | GxROM/MxROM | Super Mario Bros. + Duck Hunt | ~17 | &lt;0.01% |
| 071 | Camerica/Codemasters | Firehawk, Bee 52, MiG 29 - Soviet Fighter | ~15 | &lt;0.01% |
| 076 | DxROM/Namco 108 | Megami Tensei: Digital Devil Story | 1 | &lt;0.01% |
| 088 | DxROM/Namco 108 | Quinty, Dragon Spirit - Aratanaru Densetsu | 3 | &lt;0.01% |
| 095 | DxROM/Namco 108 | Dragon Buster | 1 | &lt;0.01% |
| 153 | Bandai FCG | Famicom Jump II: Saikyou no 7-nin | 1 | &lt;0.01% |
| 154 | DxROM/Namco 108 | Devil Man | 1 | &lt;0.01% |
| 157 | Bandai FCG/Datach | SD Gundam Wars | 7 | &lt;0.01% |
| 155 | SxROM/MMC1A | Tatakae!! Ramen Man: Sakuretsu Choujin | 2 | &lt;0.01% |
| 159 | Bandai FCG | Dragon Ball Z: Kyoushuu! Saiya-jin | 4 | &lt;0.01% |
| 206 | DxROM/Namco 108 | Fantasy Zone, Gauntlet | 45 | ~2% |
| | | | ~2186 / 2447 | ~89.0% |

<!-- markdownlint-enable line-length -->

Expand Down
9 changes: 7 additions & 2 deletions tetanes-core/src/cart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::{
fs,
mapper::{
self, m024_m026_vrc6::Revision as Vrc6Revision, m034_nina001::Nina001, Axrom, BandaiFCG,
Bf909x, Bnrom, Cnrom, ColorDreams, Exrom, Fxrom, Gxrom, Mapper, Mmc1Revision, Nrom, Pxrom,
Sxrom, Txrom, Uxrom, Vrc6,
Bf909x, Bnrom, Cnrom, ColorDreams, Dxrom154, Dxrom206, Dxrom76, Dxrom88, Dxrom95, Exrom,
Fxrom, Gxrom, Mapper, Mmc1Revision, Nrom, Pxrom, Sxrom, Txrom, Uxrom, Vrc6,
},
mem::RamState,
ppu::Mirroring,
Expand Down Expand Up @@ -219,6 +219,11 @@ impl Cart {
}
66 => Gxrom::load(&mut cart)?,
71 => Bf909x::load(&mut cart)?,
76 => Dxrom76::load(&mut cart)?,
88 => Dxrom88::load(&mut cart)?,
95 => Dxrom95::load(&mut cart)?,
154 => Dxrom154::load(&mut cart)?,
206 => Dxrom206::load(&mut cart)?,
155 => Sxrom::load(&mut cart, Mmc1Revision::A)?,
_ => Mapper::none(),
};
Expand Down
15 changes: 15 additions & 0 deletions tetanes-core/src/mapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ pub use m034_bnrom::Bnrom;
pub use m034_nina001::Nina001;
pub use m066_gxrom::Gxrom;
pub use m071_bf909x::{Bf909x, Revision as Bf909Revision};
pub use m076_dxrom::Dxrom as Dxrom76;
pub use m088_dxrom::Dxrom as Dxrom88;
pub use m095_dxrom::Dxrom as Dxrom95;
pub use m154_dxrom::Dxrom as Dxrom154;
pub use m206_dxrom::Dxrom as Dxrom206;

pub mod bandai_fcg;
pub mod m000_nrom;
Expand All @@ -43,6 +48,11 @@ pub mod m034_bnrom;
pub mod m034_nina001;
pub mod m066_gxrom;
pub mod m071_bf909x;
pub mod m076_dxrom;
pub mod m088_dxrom;
pub mod m095_dxrom;
pub mod m154_dxrom;
pub mod m206_dxrom;
pub mod vrc_irq;

#[derive(thiserror::Error, Debug)]
Expand Down Expand Up @@ -100,6 +110,11 @@ pub enum Mapper {
Nina001,
Gxrom,
Bf909x,
Dxrom76,
Dxrom88,
Dxrom95,
Dxrom154,
Dxrom206,
}

impl Mapper {
Expand Down
2 changes: 1 addition & 1 deletion tetanes-core/src/mapper/bandai_fcg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! `Bandai FCG` (Mapper 016)
//! `Bandai FCG` (Mappers 016, 153, 157, and 159)
//!
//! <https://www.nesdev.org/wiki/INES_Mapper_016>
Expand Down
22 changes: 11 additions & 11 deletions tetanes-core/src/mapper/m000_nrom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ impl Nrom {
}
}

impl Mapped for Nrom {
fn mirroring(&self) -> Mirroring {
self.mirroring
}

fn set_mirroring(&mut self, mirroring: Mirroring) {
self.mirroring = mirroring;
}
}

impl MemMap for Nrom {
// PPU $0000..=$1FFF 8K Fixed CHR-ROM Bank
// CPU $6000..=$7FFF 2K or 4K PRG-RAM Family Basic only. 8K is provided by default.
Expand Down Expand Up @@ -65,17 +75,7 @@ impl MemMap for Nrom {
}
}

impl Mapped for Nrom {
fn mirroring(&self) -> Mirroring {
self.mirroring
}

fn set_mirroring(&mut self, mirroring: Mirroring) {
self.mirroring = mirroring;
}
}

impl Reset for Nrom {}
impl Clock for Nrom {}
impl Regional for Nrom {}
impl Reset for Nrom {}
impl Sram for Nrom {}
18 changes: 9 additions & 9 deletions tetanes-core/src/mapper/m001_sxrom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,6 @@ impl MemMap for Sxrom {
}
}

impl Clock for Sxrom {
fn clock(&mut self) -> usize {
if self.regs.write_just_occurred > 0 {
self.regs.write_just_occurred -= 1;
}
1
}
}

impl Reset for Sxrom {
fn reset(&mut self, kind: ResetKind) {
self.regs.shift_register = Self::DEFAULT_SHIFT_REGISTER;
Expand All @@ -296,6 +287,15 @@ impl Reset for Sxrom {
}
}

impl Clock for Sxrom {
fn clock(&mut self) -> usize {
if self.regs.write_just_occurred > 0 {
self.regs.write_just_occurred -= 1;
}
1
}
}

impl Regional for Sxrom {}
impl Sram for Sxrom {}

Expand Down
2 changes: 1 addition & 1 deletion tetanes-core/src/mapper/m002_uxrom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl Mapped for Uxrom {
}
}

impl Reset for Uxrom {}
impl Clock for Uxrom {}
impl Regional for Uxrom {}
impl Reset for Uxrom {}
impl Sram for Uxrom {}
2 changes: 1 addition & 1 deletion tetanes-core/src/mapper/m003_cnrom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ impl Mapped for Cnrom {
}
}

impl Reset for Cnrom {}
impl Clock for Cnrom {}
impl Regional for Cnrom {}
impl Reset for Cnrom {}
impl Sram for Cnrom {}
41 changes: 34 additions & 7 deletions tetanes-core/src/mapper/m004_txrom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub struct Regs {
pub struct Txrom {
pub regs: Regs,
pub mirroring: Mirroring,
pub mapper_num: u16,
pub submapper_num: u8,
pub revision: Revision,
pub chr_banks: Banks,
pub prg_ram_banks: Banks,
Expand All @@ -67,7 +69,7 @@ pub struct Txrom {

impl Txrom {
const PRG_WINDOW: usize = 8 * 1024;
const CHR_WINDOW: usize = 1024;
pub(super) const CHR_WINDOW: usize = 1024;

const FOUR_SCREEN_RAM_SIZE: usize = 4 * 1024;
const PRG_RAM_SIZE: usize = 8 * 1024;
Expand All @@ -76,7 +78,7 @@ impl Txrom {
const PRG_MODE_MASK: u8 = 0x40; // Bit 6 of bank select
const CHR_INVERSION_MASK: u8 = 0x80; // Bit 7 of bank select

pub fn load(cart: &mut Cart) -> Result<Mapper, mapper::Error> {
pub fn new(cart: &mut Cart, chr_window: usize) -> Result<Self, mapper::Error> {
cart.add_prg_ram(Self::PRG_RAM_SIZE);
if cart.mirroring() == Mirroring::FourScreen {
cart.add_exram(Self::FOUR_SCREEN_RAM_SIZE);
Expand All @@ -92,22 +94,32 @@ impl Txrom {
let mut txrom = Self {
regs: Regs::default(),
mirroring: cart.mirroring(),
mapper_num: cart.mapper_num(),
submapper_num: cart.submapper_num(),
revision: Revision::BC, // TODO compare to known games
chr_banks: Banks::new(0x0000, 0x1FFF, chr_len, Self::CHR_WINDOW)?,
chr_banks: Banks::new(0x0000, 0x1FFF, chr_len, chr_window)?,
prg_ram_banks: Banks::new(0x6000, 0x7FFF, cart.prg_ram.len(), Self::PRG_WINDOW)?,
prg_rom_banks: Banks::new(0x8000, 0xFFFF, cart.prg_rom.len(), Self::PRG_WINDOW)?,
};
let last_bank = txrom.prg_rom_banks.last();
txrom.prg_rom_banks.set(2, last_bank - 1);
txrom.prg_rom_banks.set(3, last_bank);
Ok(txrom.into())
Ok(txrom)
}

pub fn load(cart: &mut Cart) -> Result<Mapper, mapper::Error> {
Ok(Self::new(cart, Self::CHR_WINDOW)?.into())
}

pub const fn bank_register(&self, index: usize) -> u8 {
self.regs.bank_values[index]
}

pub fn set_revision(&mut self, rev: Revision) {
self.revision = rev;
}

pub fn update_banks(&mut self) {
pub fn update_prg_banks(&mut self) {
let prg_last = self.prg_rom_banks.last();
let prg_lo = self.regs.bank_values[6] as usize;
let prg_hi = self.regs.bank_values[7] as usize;
Expand All @@ -121,7 +133,13 @@ impl Txrom {
self.prg_rom_banks.set(2, prg_last - 1);
}
self.prg_rom_banks.set(3, prg_last);
}

pub fn set_chr_banks(&mut self, f: impl Fn(&mut Banks, &mut [u8])) {
f(&mut self.chr_banks, &mut self.regs.bank_values)
}

pub fn update_chr_banks(&mut self) {
// 1: two 2K banks at $1000-$1FFF, four 1 KB banks at $0000-$0FFF
// 0: two 2K banks at $0000-$0FFF, four 1 KB banks at $1000-$1FFF
let chr = self.regs.bank_values;
Expand All @@ -142,6 +160,14 @@ impl Txrom {
}
}

pub fn update_banks(&mut self) {
self.update_prg_banks();
// Allow mappers to override chr banks with `set_chr_banks`
if !matches!(self.mapper_num, 76 | 88) {
self.update_chr_banks();
};
}

pub fn clock_irq(&mut self, addr: u16) {
if addr < 0x2000 {
let next_clock = (addr >> 12) & 1;
Expand Down Expand Up @@ -249,6 +275,7 @@ impl MemMap for Txrom {
// 1: two 2K banks at $1000-$1FFF,
// four 1K banks at $0000-$0FFF)
//

// Match only $8000/1, $A000/1, $C000/1, and $E000/1
match addr & 0xE001 {
0x8000 => {
Expand All @@ -262,11 +289,11 @@ impl MemMap for Txrom {
}
0xA000 => {
if self.mirroring != Mirroring::FourScreen {
self.mirroring = match val & 0x01 {
self.set_mirroring(match val & 0x01 {
0 => Mirroring::Vertical,
1 => Mirroring::Horizontal,
_ => unreachable!("impossible mirroring"),
};
});
self.update_banks();
}
}
Expand Down
44 changes: 22 additions & 22 deletions tetanes-core/src/mapper/m005_exrom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -554,16 +554,6 @@ impl Mapped for Exrom {
}
}

impl Regional for Exrom {
fn region(&self) -> NesRegion {
self.dmc.region()
}

fn set_region(&mut self, region: NesRegion) {
self.dmc.set_region(region);
}
}

impl MemMap for Exrom {
// CHR mode 0
// PPU $0000..=$1FFF 8K switchable CHR bank
Expand Down Expand Up @@ -1008,14 +998,10 @@ impl MemMap for Exrom {
}
}

impl Sample for Exrom {
#[must_use]
fn output(&self) -> f32 {
let pulse1 = self.pulse1.output();
let pulse2 = self.pulse2.output();
let pulse = PULSE_TABLE[(pulse1 + pulse2) as usize];
let dmc = TND_TABLE[self.dmc.output() as usize];
-(pulse + dmc)
impl Reset for Exrom {
fn reset(&mut self, _kind: ResetKind) {
self.regs.prg_mode = PrgMode::Bank8k;
self.regs.chr_mode = ChrMode::Bank1k;
}
}

Expand Down Expand Up @@ -1052,15 +1038,29 @@ impl Clock for Exrom {
}
}

impl Reset for Exrom {
fn reset(&mut self, _kind: ResetKind) {
self.regs.prg_mode = PrgMode::Bank8k;
self.regs.chr_mode = ChrMode::Bank1k;
impl Regional for Exrom {
fn region(&self) -> NesRegion {
self.dmc.region()
}

fn set_region(&mut self, region: NesRegion) {
self.dmc.set_region(region);
}
}

impl Sram for Exrom {}

impl Sample for Exrom {
#[must_use]
fn output(&self) -> f32 {
let pulse1 = self.pulse1.output();
let pulse2 = self.pulse2.output();
let pulse = PULSE_TABLE[(pulse1 + pulse2) as usize];
let dmc = TND_TABLE[self.dmc.output() as usize];
-(pulse + dmc)
}
}

impl std::fmt::Debug for Exrom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Exrom")
Expand Down
Loading

0 comments on commit 906af59

Please sign in to comment.