From 17d69fcf0a5abc1a89a2e50479105d5710d46ce2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Magalh=C3=A3es?= <joamag@gmail.com> Date: Mon, 7 Aug 2023 22:41:41 +0100 Subject: [PATCH] chore: MBC handling in state --- src/rom.rs | 54 +++++++++++++++++++++++++- src/state.rs | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 154 insertions(+), 4 deletions(-) diff --git a/src/rom.rs b/src/rom.rs index a781cf1d..c1de9209 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -12,6 +12,18 @@ use wasm_bindgen::prelude::*; pub const ROM_BANK_SIZE: usize = 16384; pub const RAM_BANK_SIZE: usize = 8192; +#[cfg_attr(feature = "wasm", wasm_bindgen)] +pub enum MbcType { + NoMbc = 0x00, + Mbc1 = 0x01, + Mbc2 = 0x02, + Mbc3 = 0x03, + Mbc5 = 0x04, + Mbc6 = 0x05, + Mbc7 = 0x06, + Unknown = 0x07, +} + #[cfg_attr(feature = "wasm", wasm_bindgen)] pub enum RomType { RomOnly = 0x00, @@ -79,6 +91,28 @@ impl RomType { RomType::Unknown => "Unknown", } } + + pub fn mbc_type(&self) -> MbcType { + match self { + RomType::RomOnly => MbcType::NoMbc, + RomType::Mbc1 | RomType::Mbc1Ram | RomType::Mbc1RamBattery => MbcType::Mbc1, + RomType::Mbc2 | RomType::Mbc2Battery => MbcType::Mbc2, + RomType::Mbc3 + | RomType::Mbc3Ram + | RomType::Mbc3RamBattery + | RomType::Mbc3TimerBattery + | RomType::Mbc3TimerRamBattery => MbcType::Mbc3, + RomType::Mbc5 + | RomType::Mbc5Ram + | RomType::Mbc5RamBattery + | RomType::Mbc5Rumble + | RomType::Mbc5RumbleRam + | RomType::Mbc5RumbleRamBattery => MbcType::Mbc5, + RomType::Mbc6 => MbcType::Mbc6, + RomType::Mbc7SensorRumbleRamBattery => MbcType::Mbc7, + _ => MbcType::Unknown, + } + } } impl Display for RomType { @@ -380,14 +414,30 @@ impl Cartridge { ) } - pub fn set_rom_bank(&mut self, rom_bank: u8) { - self.rom_offset = rom_bank as usize * ROM_BANK_SIZE; + pub fn ram_enabled(&self) -> bool { + self.ram_enabled + } + + pub fn set_ram_enabled(&mut self, ram_enabled: bool) { + self.ram_enabled = ram_enabled + } + + pub fn ram_bank(&self) -> u8 { + (self.ram_offset / RAM_BANK_SIZE) as u8 } pub fn set_ram_bank(&mut self, ram_bank: u8) { self.ram_offset = ram_bank as usize * RAM_BANK_SIZE; } + pub fn rom_bank(&self) -> u8 { + (self.rom_offset / ROM_BANK_SIZE) as u8 + } + + pub fn set_rom_bank(&mut self, rom_bank: u8) { + self.rom_offset = rom_bank as usize * ROM_BANK_SIZE; + } + pub fn set_rumble_cb(&mut self, rumble_cb: fn(active: bool)) { self.rumble_cb = rumble_cb; } diff --git a/src/state.rs b/src/state.rs index b1c5b3b6..d86abbc7 100644 --- a/src/state.rs +++ b/src/state.rs @@ -24,6 +24,7 @@ pub struct BeesState { name: BeesName, info: BeesInfo, core: BeesCore, + mbc: BeesMbc, end: BeesBlock, } @@ -85,6 +86,7 @@ impl Serialize for BeesState { self.name.save(buffer); self.info.save(buffer); self.core.save(buffer); + self.mbc.save(buffer); self.end.save(buffer); self.footer.save(buffer); } @@ -103,20 +105,23 @@ impl Serialize for BeesState { // reads the block information and then moves the cursor // back to the original position to be able to re-read the // block data - // @TODO: may read only the Block header that should ufice + // @TODO: may read only the Block header that should suffice // fo the verification we're doing and should save us some cycles let block = BeesBlock::from_data(data); let offset = -((block.header.size as usize + size_of::<u32>() * 2) as i64); data.seek(SeekFrom::Current(offset)).unwrap(); + println!("{}", block.magic().as_str()); + match block.magic().as_str() { "NAME" => self.name = BeesName::from_data(data), "INFO" => self.info = BeesInfo::from_data(data), "CORE" => self.core = BeesCore::from_data(data), + "MBC " => self.mbc = BeesMbc::from_data(data), "END " => self.end = BeesBlock::from_data(data), _ => (), } - println!("{}", block.magic().as_str()); + if block.is_end() { break; } @@ -132,6 +137,7 @@ impl State for BeesState { info: BeesInfo::from_gb(gb), core: BeesCore::from_gb(gb), end: BeesBlock::from_magic(String::from("END ")), + mbc: BeesMbc::from_gb(gb), } } @@ -140,6 +146,7 @@ impl State for BeesState { self.name.to_gb(gb)?; self.info.to_gb(gb)?; self.core.to_gb(gb)?; + self.mbc.to_gb(gb)?; Ok(()) } } @@ -750,6 +757,99 @@ impl Default for BeesCore { } } +pub struct BeesMbrRegister { + address: u16, + value: u8, +} + +impl BeesMbrRegister { + pub fn new(address: u16, value: u8) -> Self { + Self { address, value } + } +} + +pub struct BeesMbc { + header: BeesBlockHeader, + registers: Vec<BeesMbrRegister>, +} + +impl BeesMbc { + pub fn new(registers: Vec<BeesMbrRegister>) -> Self { + Self { + header: BeesBlockHeader::new( + String::from("MBC "), + ((size_of::<u8>() + size_of::<u16>()) * registers.len()) as u32, + ), + registers, + } + } + + pub fn from_data(data: &mut Cursor<Vec<u8>>) -> Self { + let mut instance = Self::default(); + instance.load(data); + instance + } +} + +impl Serialize for BeesMbc { + fn save(&mut self, buffer: &mut Vec<u8>) { + self.header.save(buffer); + for register in self.registers.iter() { + buffer.write_all(®ister.address.to_le_bytes()).unwrap(); + buffer.write_all(®ister.value.to_le_bytes()).unwrap(); + } + } + + fn load(&mut self, data: &mut Cursor<Vec<u8>>) { + self.header.load(data); + for _ in 0..(self.header.size / 3) { + let mut buffer = [0x00; 2]; + data.read_exact(&mut buffer).unwrap(); + let address = u16::from_le_bytes(buffer); + let mut buffer = [0x00; 1]; + data.read_exact(&mut buffer).unwrap(); + let value = u8::from_le_bytes(buffer); + self.registers.push(BeesMbrRegister::new(address, value)); + } + } +} + +impl State for BeesMbc { + fn from_gb(gb: &mut GameBoy) -> Self { + let mut registers = vec![]; + match gb.cartridge().rom_type().mbc_type() { + crate::rom::MbcType::NoMbc => todo!(), + crate::rom::MbcType::Mbc1 => { + registers.push(BeesMbrRegister::new(0x0000, if gb.rom().ram_enabled() { 0x0a_u8 } else { 0x00_u8 })); + registers.push(BeesMbrRegister::new(0x2000, gb.rom().rom_bank())); + registers.push(BeesMbrRegister::new(0x4000, gb.rom().ram_bank())); + registers.push(BeesMbrRegister::new(0x6000, 0x00_u8)); + } + crate::rom::MbcType::Mbc2 => todo!(), + crate::rom::MbcType::Mbc3 => todo!(), + crate::rom::MbcType::Mbc5 => todo!(), + crate::rom::MbcType::Mbc6 => todo!(), + crate::rom::MbcType::Mbc7 => todo!(), + crate::rom::MbcType::Unknown => todo!(), + } + + Self::new(registers) + } + + fn to_gb(&self, gb: &mut GameBoy) -> Result<(), String> { + for register in self.registers.iter() { + gb.mmu().write(register.address, register.value); + } + Ok(()) + } +} + +impl Default for BeesMbc { + fn default() -> Self { + Self::new(vec![]) + } +} + pub fn save_state_file(file_path: &str, gb: &mut GameBoy) { let mut file = File::create(file_path).unwrap(); let data = save_state(gb); -- GitLab