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
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 ,
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
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 ,
_ = > panic! ( " oh no " ) ,
}
} ,
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 ]
} ,
_ = > {
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 ( ) ,
0xC000 ..= 0xDFFF = > self . irq_counter = 0 , // 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.
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 ) {
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 ;
}
}
fn check_irq ( & mut self ) -> bool {
if self . trigger_irq {
self . trigger_irq = false ;
true
} else {
false
}
}
2020-01-16 01:27:53 +00:00
}