diff --git a/src/processor/memory/rom.rs b/src/processor/memory/rom.rs index e11c4ce..e325e8e 100644 --- a/src/processor/memory/rom.rs +++ b/src/processor/memory/rom.rs @@ -1,7 +1,7 @@ use crate::processor::memory::Address; use std::str::from_utf8_unchecked; -use self::mbcs::{Mbc, Mbc1, Mbc5, None}; +use self::mbcs::{Mbc, Mbc1, Mbc3, Mbc5, None}; mod mbcs; @@ -32,6 +32,14 @@ impl Rom { println!("MBC1 w/battery - battery not implemented!"); Box::new(Mbc1::init(data, rom_size, ram_size, None)) } + 0x0F => panic!("MBC3 + RTC"), + 0x1F => panic!("MBC3 + RTC + RAM"), + 0x11 => Box::new(Mbc3::init(data, rom_size, 0)), + 0x12 => Box::new(Mbc3::init(data, rom_size, ram_size)), + 0x13 => { + println!("MBC3 w/battery - battery not implemented!"); + Box::new(Mbc3::init(data, rom_size, 0)) + } 0x19 => Box::new(Mbc5::init(data, rom_size, 0)), 0x1A => Box::new(Mbc5::init(data, rom_size, ram_size)), 0x1B => { diff --git a/src/processor/memory/rom/mbcs.rs b/src/processor/memory/rom/mbcs.rs index 9270f27..e1a4e2f 100644 --- a/src/processor/memory/rom/mbcs.rs +++ b/src/processor/memory/rom/mbcs.rs @@ -1,9 +1,11 @@ use crate::processor::memory::Address; mod mbc1; +mod mbc3; mod mbc5; mod none; pub use mbc1::Mbc1; +pub use mbc3::Mbc3; pub use mbc5::Mbc5; pub use none::None; diff --git a/src/processor/memory/rom/mbcs/mbc3.rs b/src/processor/memory/rom/mbcs/mbc3.rs new file mode 100644 index 0000000..d0a2536 --- /dev/null +++ b/src/processor/memory/rom/mbcs/mbc3.rs @@ -0,0 +1,102 @@ +use crate::processor::memory::Address; + +use super::{ram_size_kb, rom_banks, Mbc, KB, RAM_BANK_SIZE, ROM_BANK_SIZE}; + +pub struct Mbc3 { + data: Vec, + rom_bank: u8, + rom_size: usize, + ram: Option>, + ram_bank: u8, + ram_size: usize, + ram_enabled: bool, +} + +impl Mbc3 { + pub fn init(data: Vec, rom_size: u8, ram_size: u8) -> Self { + let ram = ram_size_kb(ram_size).map(|s| vec![0; s * KB]); + Self { + data, + rom_bank: 1, + rom_size: rom_banks(rom_size) * ROM_BANK_SIZE, + ram, + ram_bank: 0, + ram_size: ram_size_kb(ram_size).map_or(1, |s| s * KB), + ram_enabled: false, + } + } + + fn get_rom_addr(&self, address: Address) -> usize { + (match address { + 0x0..0x4000 => address as usize, + 0x4000..0x8000 => { + let internal_addr = address as usize - 0x4000; + internal_addr + (ROM_BANK_SIZE * self.rom_bank as usize) + } + _ => panic!("address {address} incompatible with MBC5"), + } % self.rom_size) + } + + fn get_ram_addr(&self, address: Address) -> usize { + ((address as usize - 0xA000) + (RAM_BANK_SIZE * self.ram_bank as usize)) % self.ram_size + } +} + +impl Mbc for Mbc3 { + 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 { + ram[self.get_ram_addr(address)] + } else { + 0xFF + } + } + + fn set(&mut self, address: Address, data: u8) { + match address { + 0x0..0x2000 => { + if data & 0xF == 0xA { + self.ram_enabled = true; + } + } + 0x2000..0x4000 => { + self.rom_bank = data & 0b01111111; + if self.rom_bank == 0 { + self.rom_bank = 1; + } + } + 0x4000..0x6000 => match data { + 0x0..=0x03 => self.ram_bank = data, + 0x08..=0x0C => panic!("rtc bank map: {data:#X}"), + _ => panic!("ram/rtc bank error: tried to map {data:#X}"), + }, + 0x6000..0x8000 => { + // RTC + } + + _ => panic!("unsupported addr"), + } + } + + fn set_ram(&mut self, address: Address, data: u8) { + let real_addr = self.get_ram_addr(address); + if self.ram_enabled && let Some(ram) = &mut self.ram { + ram[real_addr] = data; + } + } + + fn mbc_type(&self) -> String { + if let Some(ram) = &self.ram { + format!( + "{}KB MBC3 with {}KB RAM", + self.rom_size / KB, + ram.len() / KB, + ) + } else { + format!("{}KB MBC3", self.rom_size / KB) + } + } +}