Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added dxrom #340

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading