diff --git a/src/processor/memory/rom.rs b/src/processor/memory/rom.rs index fbd2ef4..8a809cf 100644 --- a/src/processor/memory/rom.rs +++ b/src/processor/memory/rom.rs @@ -22,11 +22,11 @@ impl ROM { let title = unsafe { from_utf8_unchecked(&data[0x134..title_length]).to_string() }; let _gbc_flag = data[0x143]; let _sgb_flag = data[0x146]; - let _rom_size = data[0x148]; + let rom_size = data[0x148]; let _ram_size = data[0x149]; let mbc: Box = match data[0x147] { - 0x00 => Box::new(NONE { data }), - 0x01 => Box::new(MBC1 { data }), + 0x00 => Box::new(NONE::init(data)), + 0x01 => Box::new(MBC1::init(data, rom_size)), _ => panic!("unimplemented mbc: {:#X}", data[0x147]), }; Self { title, mbc } diff --git a/src/processor/memory/rom/mbcs.rs b/src/processor/memory/rom/mbcs.rs index 8e09a73..1b5bfea 100644 --- a/src/processor/memory/rom/mbcs.rs +++ b/src/processor/memory/rom/mbcs.rs @@ -9,26 +9,102 @@ pub(super) struct NONE { pub(super) data: Vec, } +impl NONE { + pub(super) fn init(data: Vec) -> Self { + Self { data } + } +} + impl MBC for NONE { fn get(&self, address: Address) -> u8 { self.data[address as usize] } - fn set(&mut self, address: Address, data: u8) { + fn set(&mut self, _address: Address, _data: u8) { return; } } +#[derive(Clone, Copy)] +enum BankingMode { + RomBanking, + RamBanking, +} + pub(super) struct MBC1 { pub(super) data: Vec, + num_banks: usize, + bank_mode: BankingMode, + ram_enabled: bool, + rom_bank: u8, +} + +impl MBC1 { + pub(super) fn init(data: Vec, rom_size: u8) -> Self { + let num_banks = match rom_size { + 0x00 => 2, + 0x01 => 4, + 0x02 => 8, + 0x03 => 16, + 0x04 => 32, + 0x05 => 64, + 0x06 => 128, + 0x07 => 256, + 0x08 => 512, + 0x52 => 72, + 0x53 => 80, + 0x54 => 96, + _ => panic!("unacceptable rom size"), + }; + Self { + data, + num_banks, + bank_mode: BankingMode::RomBanking, + ram_enabled: false, + rom_bank: 0x1, + } + } } impl MBC for MBC1 { fn get(&self, address: Address) -> u8 { - self.data[address as usize] + match address { + 0x0..0x4000 => self.data[address as usize], + 0x4000..0x8000 => self.data[address as usize - (self.rom_bank as usize * 0x4000)], + _ => panic!("address too big for rom!"), + } } fn set(&mut self, address: Address, data: u8) { + match address { + 0x0..0x2000 => { + // enable/disable ram + self.ram_enabled = (data & 0x0F) == 0xA; + } + 0x2000..0x4000 => { + // rom bank number - lower 5 bits + let mut set_data = data & 0b00011111; + if set_data == 0 { + set_data = 1; + } + self.rom_bank = (self.rom_bank & 0b11100000) | set_data; + } + 0x4000..0x6000 => { + // ram bank OR upper bits 5 & 6 of rom bank + self.rom_bank = (self.rom_bank & 0b00011111) | ((data & 0b00000011) << 5); + } + 0x6000..0x8000 => { + // mode select + self.bank_mode = if data == 0x1 { + BankingMode::RamBanking + } else if data == 0x0 { + BankingMode::RomBanking + } else { + self.bank_mode + }; + } + _ => {} + } return; } }