use crate::processor::memory::Address; pub(super) trait MBC { fn get(&self, address: Address) -> u8; fn set(&mut self, address: Address, data: u8); } 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) { 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 { 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; } }