2021-11-22 07:20:04 +00:00
use super ::{ serialize ::* , Cartridge , Mapper , Mirror } ;
2020-01-16 01:27:53 +00:00
pub struct Mmc3 {
cart : Cartridge ,
2020-01-17 02:57:17 +00:00
mirroring : Mirror ,
2020-01-16 01:27:53 +00:00
2020-01-17 02:57:17 +00:00
bank_registers : Vec < usize > ,
next_bank : u8 ,
2020-01-16 01:27:53 +00:00
irq_latch : u8 ,
2020-01-17 02:57:17 +00:00
irq_counter : u8 ,
irq_enable : bool ,
trigger_irq : bool , // signal to send to CPU
2020-01-25 21:40:52 +00:00
reload_counter : bool ,
2020-01-30 03:22:30 +00:00
irq_delay : u8 ,
2020-01-17 02:57:17 +00:00
prg_ram_bank : Vec < u8 > , // CPU $6000-$7FFF
// 0: $8000-$9FFF swappable, $C000-$DFFF fixed to second-last bank
// 1: $C000-$DFFF swappable, $8000-$9FFF fixed to second-last bank
prg_rom_bank_mode : bool ,
// 0: two 2 KB banks at $0000-$0FFF, four 1 KB banks at $1000-$1FFF
// 1: two 2 KB banks at $1000-$1FFF, four 1 KB banks at $0000-$0FFF
chr_rom_bank_mode : bool ,
2020-01-18 20:52:09 +00:00
chr_ram_bank : Vec < u8 > , // used if cartridge doesn't have any CHR-ROM, 8KB, $0000-$1FFF
2020-01-16 01:27:53 +00:00
}
impl Mmc3 {
pub fn new ( cart : Cartridge ) -> Self {
2020-01-17 02:57:17 +00:00
let m = cart . mirroring ;
2021-11-22 07:20:04 +00:00
Mmc3 {
2020-01-16 01:27:53 +00:00
cart : cart ,
2020-01-17 02:57:17 +00:00
mirroring : m ,
bank_registers : vec ! [ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] ,
next_bank : 0 ,
irq_latch : 0 ,
irq_counter : 0 ,
irq_enable : false ,
trigger_irq : false ,
2020-01-25 21:40:52 +00:00
reload_counter : false ,
2020-01-30 03:22:30 +00:00
irq_delay : 0 ,
2020-01-17 02:57:17 +00:00
prg_ram_bank : vec ! [ 0 ; 0x2000 ] ,
prg_rom_bank_mode : false ,
chr_rom_bank_mode : false ,
2020-01-18 20:52:09 +00:00
chr_ram_bank : vec ! [ 0 ; 0x2000 ] ,
2020-01-16 01:27:53 +00:00
}
}
2020-01-17 02:57:17 +00:00
fn bank_select ( & mut self , value : u8 ) {
self . next_bank = value & 0b111 ;
// ?? = value & (1<<5); // Nothing on the MMC3, see MMC6
2021-11-22 07:20:04 +00:00
self . prg_rom_bank_mode = value & ( 1 < < 6 ) ! = 0 ;
self . chr_rom_bank_mode = value & ( 1 < < 7 ) ! = 0 ;
2020-01-17 02:57:17 +00:00
}
2020-01-16 01:27:53 +00:00
2020-01-17 02:57:17 +00:00
fn bank_data ( & mut self , value : u8 ) {
// R6 and R7 will ignore the top two bits, as the MMC3 has only 6 PRG ROM address lines.
// R0 and R1 ignore the bottom bit, as the value written still counts banks in 1KB units but odd numbered banks can't be selected.
self . bank_registers [ self . next_bank as usize ] = match self . next_bank {
2020-01-18 20:52:09 +00:00
0 | 1 = > value & 0b1111_1110 ,
6 | 7 = > value & 0b0011_1111 ,
2020-01-17 02:57:17 +00:00
_ = > value ,
} as usize ;
}
fn prg_ram_protect ( & mut self ) { }
2020-01-16 01:27:53 +00:00
}
impl Mapper for Mmc3 {
2020-02-01 20:49:30 +00:00
fn read ( & self , address : usize ) -> u8 {
2020-01-17 02:57:17 +00:00
let val = match address {
2021-11-22 07:20:04 +00:00
0x0000 ..= 0x1FFF = > {
// reading from CHR-ROM
2020-01-17 02:57:17 +00:00
let offset_1k = address % 0x400 ;
let offset_2k = address % 0x800 ;
let bank_reg_num = match self . chr_rom_bank_mode {
2021-11-22 07:20:04 +00:00
true = > match address {
0x0000 ..= 0x03FF = > 2 ,
0x0400 ..= 0x07FF = > 3 ,
0x0800 ..= 0x0BFF = > 4 ,
0x0C00 ..= 0x0FFF = > 5 ,
0x1000 ..= 0x17FF = > 0 ,
0x1800 ..= 0x1FFF = > 1 ,
_ = > panic! ( " oh no " ) ,
2020-01-17 02:57:17 +00:00
} ,
2021-11-22 07:20:04 +00:00
false = > match address {
0x0000 ..= 0x07FF = > 0 ,
0x0800 ..= 0x0FFF = > 1 ,
0x1000 ..= 0x13FF = > 2 ,
0x1400 ..= 0x17FF = > 3 ,
0x1800 ..= 0x1BFF = > 4 ,
0x1C00 ..= 0x1FFF = > 5 ,
_ = > panic! ( " oh no " ) ,
2020-01-17 02:57:17 +00:00
} ,
} ;
let bank_num = self . bank_registers [ bank_reg_num ] ;
2020-01-18 20:52:09 +00:00
let chunk_num = bank_num / 8 ;
let chunk_eighth = ( bank_num % 8 ) * 0x400 ;
2021-11-22 07:20:04 +00:00
if bank_reg_num = = 0 | | bank_reg_num = = 1 {
// dealing with 2K banks of 8K chunks
2020-01-18 20:52:09 +00:00
self . cart . chr_rom [ chunk_num ] [ chunk_eighth + offset_2k ]
2021-11-22 07:20:04 +00:00
} else {
// dealing with 1K banks of 8K chunks
2020-01-17 02:57:17 +00:00
self . cart . chr_rom [ chunk_num ] [ chunk_eighth + offset_1k ]
}
2021-11-22 07:20:04 +00:00
}
2020-01-17 02:57:17 +00:00
0x6000 ..= 0x7FFF = > self . prg_ram_bank [ address % 0x2000 ] , // PRG-RAM
2020-01-30 06:00:12 +00:00
2021-11-22 07:20:04 +00:00
0x8000 ..= 0xFFFF = > {
// reading from PRG ROM, dealing with 8K banks of 16K chunks
2020-01-17 02:57:17 +00:00
let offset_8k = address % 0x2000 ;
let num_banks = self . cart . prg_rom_size * 2 ;
2020-01-18 20:52:09 +00:00
let bank_num = match self . prg_rom_bank_mode {
2021-11-22 07:20:04 +00:00
true = > match address {
0x8000 ..= 0x9FFF = > num_banks - 2 ,
0xA000 ..= 0xBFFF = > self . bank_registers [ 7 ] ,
0xC000 ..= 0xDFFF = > self . bank_registers [ 6 ] ,
0xE000 ..= 0xFFFF = > num_banks - 1 ,
_ = > panic! ( " oh no " ) ,
2020-01-17 02:57:17 +00:00
} ,
2021-11-22 07:20:04 +00:00
false = > match address {
0x8000 ..= 0x9FFF = > self . bank_registers [ 6 ] ,
0xA000 ..= 0xBFFF = > self . bank_registers [ 7 ] ,
0xC000 ..= 0xDFFF = > num_banks - 2 ,
0xE000 ..= 0xFFFF = > num_banks - 1 ,
_ = > panic! ( " oh no " ) ,
2020-01-17 02:57:17 +00:00
} ,
} ;
let chunk_num = bank_num / 2 ;
2020-01-18 20:52:09 +00:00
let chunk_half = ( bank_num % 2 ) * 0x2000 ;
2020-01-17 02:57:17 +00:00
self . cart . prg_rom [ chunk_num ] [ chunk_half + offset_8k ]
2021-11-22 07:20:04 +00:00
}
2020-01-17 02:57:17 +00:00
_ = > {
println! ( " bad address read from MMC3: 0x {:X} " , address ) ;
0
2021-11-22 07:20:04 +00:00
}
2020-01-17 02:57:17 +00:00
} ;
val
2020-01-16 01:27:53 +00:00
}
fn write ( & mut self , address : usize , value : u8 ) {
2020-01-18 20:52:09 +00:00
if ( 0 ..= 0x1FFF ) . contains ( & address ) {
if self . cart . chr_rom_size = = 0 {
self . chr_ram_bank [ address ] = value ;
}
2021-11-22 07:20:04 +00:00
return ;
2020-01-18 20:52:09 +00:00
}
2020-01-16 01:27:53 +00:00
match address % 2 = = 0 {
2021-11-22 07:20:04 +00:00
true = > {
// even
2020-01-16 01:27:53 +00:00
match address {
2020-01-17 02:57:17 +00:00
0x6000 ..= 0x7FFF = > self . prg_ram_bank [ address % 0x2000 ] = value , // PRG-RAM
0x8000 ..= 0x9FFF = > self . bank_select ( value ) ,
2021-11-22 07:20:04 +00:00
0xA000 ..= 0xBFFF = > {
self . mirroring = if value & 1 = = 0 {
Mirror ::Vertical
} else {
Mirror ::Horizontal
}
}
2020-01-17 02:57:17 +00:00
0xC000 ..= 0xDFFF = > self . irq_latch = value ,
2021-11-22 07:20:04 +00:00
0xE000 ..= 0xFFFF = > {
self . irq_enable = false ;
self . trigger_irq = false
} // Writing any value to this register will disable MMC3 interrupts AND acknowledge any pending interrupts.
2020-01-18 20:52:09 +00:00
_ = > println! ( " bad address written to MMC3: 0x {:X} " , address ) ,
2020-01-16 01:27:53 +00:00
}
2021-11-22 07:20:04 +00:00
}
false = > {
// odd
2020-01-16 01:27:53 +00:00
match address {
2020-01-17 02:57:17 +00:00
0x6000 ..= 0x7FFF = > self . prg_ram_bank [ address % 0x2000 ] = value , // PRG-RAM
2020-01-16 01:27:53 +00:00
0x8000 ..= 0x9FFF = > self . bank_data ( value ) ,
2020-01-17 02:57:17 +00:00
0xA000 ..= 0xBFFF = > self . prg_ram_protect ( ) ,
2020-01-25 21:40:52 +00:00
0xC000 ..= 0xDFFF = > self . reload_counter = true , // Writing any value to this register reloads the MMC3 IRQ counter at the NEXT rising edge of the PPU address, presumably at PPU cycle 260 of the current scanline.
2020-01-17 02:57:17 +00:00
0xE000 ..= 0xFFFF = > self . irq_enable = true ,
2020-01-18 20:52:09 +00:00
_ = > println! ( " bad address written to MMC3: 0x {:X} " , address ) ,
2020-01-16 01:27:53 +00:00
}
2021-11-22 07:20:04 +00:00
}
2020-01-17 02:57:17 +00:00
}
2020-01-16 01:27:53 +00:00
}
2020-02-02 03:01:49 +00:00
fn get_mirroring ( & self ) -> Mirror {
2020-01-17 02:57:17 +00:00
if self . cart . four_screen_vram {
Mirror ::FourScreen
} else {
self . mirroring
}
}
2020-01-16 01:27:53 +00:00
fn load_battery_backed_ram ( & mut self ) { }
fn save_battery_backed_ram ( & self ) { }
2020-01-17 02:57:17 +00:00
2020-01-31 02:33:29 +00:00
// This function is called by the PPU when the A12 address line changes.
// It's supposed to only be called when A12 goes from 0 to 1, but that doesn't work
// for my emulator for some reason.
2020-01-17 02:57:17 +00:00
fn clock ( & mut self ) {
2020-01-25 21:40:52 +00:00
if self . reload_counter {
self . irq_counter = self . irq_latch ;
self . reload_counter = false ;
}
2020-01-17 02:57:17 +00:00
if self . irq_counter = = 0 {
self . irq_counter = self . irq_latch ;
} else {
self . irq_counter - = 1 ;
}
if self . irq_counter = = 0 & & self . irq_enable {
self . trigger_irq = true ;
}
}
2020-01-31 02:33:29 +00:00
// This function is called by the CPU every step (which takes more than one CPU clock cycle).
// I think I'm supposed to be tracking IRQ delays by the PPU, not letting an IRQ fire if
// there was one within the last 15 PPU cycles, but that didn't work and this does.
2020-01-17 02:57:17 +00:00
fn check_irq ( & mut self ) -> bool {
if self . trigger_irq {
self . trigger_irq = false ;
2020-01-31 02:33:29 +00:00
if self . irq_delay = = 0 {
self . irq_delay = 5 ;
}
2020-01-30 03:22:30 +00:00
}
if self . irq_delay > 0 {
self . irq_delay - = 1 ;
if self . irq_delay = = 0 {
return true ;
}
2020-01-17 02:57:17 +00:00
}
2020-01-30 03:22:30 +00:00
false
2020-01-17 02:57:17 +00:00
}
2020-03-03 01:01:00 +00:00
fn save_state ( & self ) -> MapperData {
2021-11-22 07:20:04 +00:00
MapperData ::Mmc3 ( Mmc3Data {
cart : self . cart . clone ( ) ,
mirroring : self . mirroring ,
bank_registers : self . bank_registers . clone ( ) ,
next_bank : self . next_bank ,
irq_latch : self . irq_latch ,
irq_counter : self . irq_counter ,
irq_enable : self . irq_enable ,
trigger_irq : self . trigger_irq ,
reload_counter : self . reload_counter ,
irq_delay : self . irq_delay ,
prg_ram_bank : self . prg_ram_bank . clone ( ) ,
prg_rom_bank_mode : self . prg_rom_bank_mode ,
chr_rom_bank_mode : self . chr_rom_bank_mode ,
chr_ram_bank : self . chr_ram_bank . clone ( ) ,
} )
2020-03-03 01:01:00 +00:00
}
fn load_state ( & mut self , mapper_data : MapperData ) {
if let MapperData ::Mmc3 ( mmc3_data ) = mapper_data {
self . cart = mmc3_data . cart ;
self . mirroring = mmc3_data . mirroring ;
self . bank_registers = mmc3_data . bank_registers ;
self . next_bank = mmc3_data . next_bank ;
self . irq_latch = mmc3_data . irq_latch ;
self . irq_counter = mmc3_data . irq_counter ;
self . irq_enable = mmc3_data . irq_enable ;
self . trigger_irq = mmc3_data . trigger_irq ;
self . reload_counter = mmc3_data . reload_counter ;
self . irq_delay = mmc3_data . irq_delay ;
self . prg_ram_bank = mmc3_data . prg_ram_bank ;
self . prg_rom_bank_mode = mmc3_data . prg_rom_bank_mode ;
self . chr_rom_bank_mode = mmc3_data . chr_rom_bank_mode ;
self . chr_ram_bank = mmc3_data . chr_ram_bank ;
}
}
2020-01-16 01:27:53 +00:00
}