use crate::processor::memory::Address; pub(super) trait Mbc { fn get(&self, address: Address) -> u8; fn get_ram(&self, address: Address) -> u8; fn set(&mut self, address: Address, data: u8); fn set_ram(&mut self, address: Address, data: u8); fn mbc_type(&self) -> String; } 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 get_ram(&self, _address: Address) -> u8 { 0xFF } fn set_ram(&mut self, _address: Address, _data: u8) {} fn set(&mut self, _address: Address, _data: u8) {} fn mbc_type(&self) -> String { String::from("None") } } #[derive(Clone, Copy)] enum BankingMode { Simple, Advanced, } pub(super) struct Mbc1 { pub(super) data: Vec, rom_len: usize, rom_bank: u8, ram_enabled: bool, ram: Option>, ram_bank: u8, upper_banks: u8, bank_mode: BankingMode, } const KB: usize = 1024; const ROM_BANK_SIZE: usize = 16 * KB; const RAM_BANK_SIZE: usize = 8 * KB; impl Mbc1 { pub(super) fn init( data: Vec, rom_size: u8, ram_size: u8, _save_file: Option>, ) -> Self { let rom_len = 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"), } * ROM_BANK_SIZE; // in kb let ram = match ram_size { 0x00 => None, 0x01 => Some(vec![0; 2 * KB]), 0x02 => Some(vec![0; 8 * KB]), 0x03 => Some(vec![0; 32 * KB]), 0x04 => Some(vec![0; 128 * KB]), 0x05 => Some(vec![0; 64 * KB]), _ => panic!("unacceptable ram size"), }; Self { data, rom_len, rom_bank: 0x1, ram_enabled: false, ram, ram_bank: 0, upper_banks: 0, bank_mode: BankingMode::Simple, } } } impl Mbc for Mbc1 { fn get(&self, address: Address) -> u8 { self.data[self.get_rom_addr(address)] } fn get_ram(&self, address: Address) -> u8 { if self.ram_enabled && let Some(ram) = &self.ram { let addr = self.get_ram_addr(address)%ram.len(); return ram[addr]; } 0xFF } fn set_ram(&mut self, address: Address, data: u8) { let mut addr = self.get_ram_addr(address); if self.ram_enabled && let Some(ram) = &mut self.ram { addr %= ram.len(); ram[addr] = data; } } 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 = set_data; } 0x4000..0x6000 => { // ram bank OR upper bits 5 & 6 of rom bank self.upper_banks = data & 0b11; } 0x6000..0x8000 => { // mode select self.bank_mode = if data == 0x1 { BankingMode::Advanced } else if data == 0x0 { BankingMode::Simple } else { self.bank_mode }; } _ => {} } } fn mbc_type(&self) -> String { if let Some(ram) = &self.ram { format!("{}KB MBC1 with {}KB RAM", self.rom_len / KB, ram.len() / KB) } else { format!("{}KB MBC1", self.rom_len / KB) } } } impl Mbc1 { fn get_rom_addr(&self, address: Address) -> usize { (match address { 0x0..0x4000 => match self.bank_mode { BankingMode::Simple => address as usize, BankingMode::Advanced => { (address as usize) + (self.upper_banks as usize * 512 * KB) } }, 0x4000..0x8000 => { (address - 0x4000) as usize + (ROM_BANK_SIZE * self.rom_bank as usize) + (self.upper_banks as usize * 512 * KB) } 0xA000..0xC000 => panic!("passed ram address to rom address function"), _ => panic!("address {address} incompatible with MBC1"), } % self.rom_len) } fn get_ram_addr(&self, address: Address) -> usize { match address { 0x0..0x8000 => panic!("passed rom address to ram address function"), 0xA000..0xC000 => match self.bank_mode { BankingMode::Simple => { (address - 0xA000) as usize + (RAM_BANK_SIZE * self.ram_bank as usize) } BankingMode::Advanced => { (address - 0xA000) as usize + (RAM_BANK_SIZE * self.ram_bank as usize) + (self.upper_banks as usize * 16 * KB) } }, _ => panic!("address {address} incompatible with MBC1"), } } }