2020-01-16 01:27:53 +00:00
use super ::{ Cartridge , Mapper , Mirror } ;
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
2020-01-18 20:52:09 +00:00
2020-01-17 02:57:17 +00:00
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 ;
2020-01-16 01:27:53 +00:00
Mmc3 {
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
self . prg_rom_bank_mode = value & ( 1 < < 6 ) ! = 0 ;
self . chr_rom_bank_mode = value & ( 1 < < 7 ) ! = 0 ;
}
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 {
fn read ( & mut self , address : usize ) -> u8 {
2020-01-17 02:57:17 +00:00
let val = match address {
0x0000 ..= 0x1FFF = > { // reading from CHR-ROM
let offset_1k = address % 0x400 ;
let offset_2k = address % 0x800 ;
let bank_reg_num = match self . chr_rom_bank_mode {
true = > {
match address {
0x0000 ..= 0x03FF = > 2 ,
0x0400 ..= 0x07FF = > 3 ,
0x0800 ..= 0x0BFF = > 4 ,
0x0C00 ..= 0x0FFF = > 5 ,
0x1000 ..= 0x17FF = > 0 ,
0x1800 ..= 0x1FFF = > 1 ,
_ = > panic! ( " oh no " ) ,
}
} ,
false = > {
match address {
0x0000 ..= 0x07FF = > 0 ,
0x0800 ..= 0x0FFF = > 1 ,
0x1000 ..= 0x13FF = > 2 ,
0x1400 ..= 0x17FF = > 3 ,
0x1800 ..= 0x1BFF = > 4 ,
0x1C00 ..= 0x1FFF = > 5 ,
_ = > panic! ( " oh no " ) ,
}
} ,
} ;
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 ;
2020-01-17 02:57:17 +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 ]
2020-01-17 02:57:17 +00:00
} else { // dealing with 1K banks of 8K chunks
self . cart . chr_rom [ chunk_num ] [ chunk_eighth + offset_1k ]
}
} ,
0x6000 ..= 0x7FFF = > self . prg_ram_bank [ address % 0x2000 ] , // PRG-RAM
2020-01-30 06:00:12 +00:00
2020-01-17 02:57:17 +00:00
0x8000 ..= 0xFFFF = > { // reading from PRG ROM, dealing with 8K banks of 16K chunks
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 {
2020-01-17 02:57:17 +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 ,
2020-01-30 06:00:12 +00:00
_ = > panic! ( " oh no " ) ,
2020-01-17 02:57:17 +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 " ) ,
}
} ,
} ;
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 ]
2020-01-30 06:00:12 +00:00
2020-01-17 02:57:17 +00:00
} ,
_ = > {
println! ( " bad address read from MMC3: 0x {:X} " , address ) ;
0
} ,
} ;
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 ;
}
return
}
2020-01-16 01:27:53 +00:00
match address % 2 = = 0 {
true = > { // even
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 ) ,
2020-01-16 01:27:53 +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 ,
0xE000 ..= 0xFFFF = > self . irq_enable = false ,
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
}
} ,
false = > { // odd
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
}
} ,
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 get_mirroring ( & mut self ) -> Mirror {
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
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 ;
}
// "Should reload and set IRQ every clock when reload is 0."
if self . irq_latch = = 0 & & self . irq_enable {
self . irq_counter = self . irq_latch ;
self . trigger_irq = true ;
}
2020-01-17 02:57:17 +00:00
if self . irq_counter = = 0 {
self . irq_counter = self . irq_latch ;
2020-01-25 21:40:52 +00:00
return ;
2020-01-17 02:57:17 +00:00
} else {
self . irq_counter - = 1 ;
}
if self . irq_counter = = 0 & & self . irq_enable {
self . trigger_irq = true ;
}
}
fn check_irq ( & mut self ) -> bool {
2020-01-30 03:22:30 +00:00
// if self.trigger_irq {
// self.trigger_irq = false;
// true
// } else {
// false
// }
2020-01-17 02:57:17 +00:00
if self . trigger_irq {
self . trigger_irq = false ;
2020-01-30 03:22:30 +00:00
self . irq_delay = 15 ;
}
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-01-16 01:27:53 +00:00
}