From 2ed5fdb82365169c876040d44e5f296a0f81949c Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Mon, 15 Nov 2021 18:52:44 -0500 Subject: [PATCH] Refactor MBCs --- src/bus.rs | 6 +- src/rom.rs | 194 ++++++++++++++++++++++++++++++----------------------- 2 files changed, 113 insertions(+), 87 deletions(-) diff --git a/src/bus.rs b/src/bus.rs index 68a040f..ae608ef 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -2,7 +2,7 @@ use std::ops::RangeInclusive; use crate::utils::{ join_bytes }; -use crate::rom::ROM; +use crate::rom::{ROM, load_rom}; use crate::ppu::{ PPU, DMA_ADDRESS, @@ -27,7 +27,7 @@ pub const INTERRUPT_ENABLE_ADDRESS: u16 = 0xFFFF; pub const INTERRUPT_FLAG_ADDRESS: u16 = 0xFF0F; pub struct Bus { - game_rom: ROM, + game_rom: Box, data: [u8; 0x10000], pub ppu: PPU, pub joypad: Joypad, @@ -41,7 +41,7 @@ impl Bus { println!("Please, specify a ROM file"); std::process::exit(1); } - let game_rom = match ROM::load_file(&args[1]) { + let game_rom = match load_rom(&args[1]) { Ok(rom) => rom, Err(err) => { println!("Could not read ROM: {}", err); diff --git a/src/rom.rs b/src/rom.rs index d4b15ac..0e70fc8 100644 --- a/src/rom.rs +++ b/src/rom.rs @@ -14,10 +14,17 @@ pub const RAM_SIZE_ADDRESS: u16 = 0x0149; pub const ROM_SIZE_ADDRESS: u16 = 0x0148; pub const DESTINATION_CODE_ADDRESS: u16 = 0x014A; -#[derive(Debug)] -enum Region { - Japanese, - NonJapanese, +pub fn load_rom(filename: &str) -> std::io::Result> { + let mut file = File::open(filename)?; + let mut data = vec![]; + file.read_to_end(&mut data)?; + let info = ROMInfo::from_bytes(&data); + + Ok(match info.mbc { + MBC::NoMBC => Box::new(NoMBC::new(data, info)), + MBC::MBC1 => Box::new(MBC1::new(data, info)), + _ => unimplemented!(), + }) } #[derive(Debug, Copy, Clone)] @@ -36,6 +43,12 @@ enum MBC { BandaiTIMA5, } +#[derive(Debug)] +enum Region { + Japanese, + NonJapanese, +} + #[derive(Debug)] enum BankingMode { Simple, @@ -144,7 +157,41 @@ impl ROMInfo { } } -pub struct ROM { +pub trait ROM { + fn read(&self, address: u16) -> u8; + fn write(&mut self, address: u16, data: u8); +} + +pub struct NoMBC { + data: Vec, + info: ROMInfo, +} + +impl NoMBC { + pub fn new(data: Vec, info: ROMInfo) -> Self { + let rom = Self { + data, + info, + }; + println!("MBC {:?}", rom.info.mbc); + println!("Region {:?}", rom.info.region); + + rom + } +} + +impl ROM for NoMBC { + fn read(&self, address: u16) -> u8 { + match self.data.get(address as usize) { + Some(byte) => *byte, + None => 0xFF, + } + } + + fn write(&mut self, _address: u16, _data: u8) {} +} + +pub struct MBC1 { data: Vec, info: ROMInfo, ram: Vec, @@ -154,21 +201,15 @@ pub struct ROM { banking_mode: BankingMode, } -impl ROM { - pub fn load_file(filename: &str) -> std::io::Result { - let mut file = File::open(filename)?; - let mut data = vec![]; - file.read_to_end(&mut data)?; - - let info = ROMInfo::from_bytes(&data); +impl MBC1 { + fn new(data: Vec, info: ROMInfo) -> Self { println!("MBC {:?}", info.mbc); + println!("Region {:?}", info.region); println!("Has RAM {}", info.has_ram); println!("ROM banks {}", info.rom_banks); println!("RAM banks {}", info.ram_banks); - println!("Region {:?}", info.region); let ram = Vec::with_capacity(info.ram_size() as usize); - - Ok(Self { + Self { data, info, ram, @@ -176,78 +217,10 @@ impl ROM { ram_bank: 0, ram_enable: false, banking_mode: BankingMode::Simple, - }) - } - - pub fn read(&self, address: u16) -> u8 { - match self.info.mbc { - MBC::NoMBC => { - return match self.data.get(address as usize) { - Some(data) => *data, - None => 0xFF, - }; - }, - MBC::MBC1 => { - if BANK_ZERO.contains(&address) { - return self.data[address as usize]; - } else if BANK_SWITCHABLE.contains(&address) { - return self.data[((self.rom_bank as usize * 0x4000) + (address as usize & 0x3FFF)) as usize]; - } else if EXTERNAL_RAM.contains(&address) { - if !self.info.has_ram { - return 0xFF; - } - return match self.ram.get((address - EXTERNAL_RAM.min().unwrap() + (0x2000 * self.ram_bank as u16)) as usize) { - Some(data) => *data, - None => 0xFF, - }; - } - unreachable!("ROM read: Address {} not valid", address); - }, - _ => unimplemented!(), } } - pub fn write(&mut self, address: u16, data: u8) { - match self.info.mbc { - MBC::NoMBC => {}, - MBC::MBC1 => { - if address <= 0x1FFF { // RAM enable register - if !self.info.has_ram { - return; - } - self.ram_enable = match data & 0x0F { - 0x0A => true, - _ => false, - }; - return; - } else if address >= 0x2000 && address <= 0x3FFF { // ROM bank number register - // println!("Switch bank to {:02X}", data); - self.switch_rom_bank(data as u16 & 0b00011111); - } else if address >= 0x4000 && address <= 0x5FFF { // ROM and RAM bank number register - // println!("RAM bank {:02X}", data); - self.ram_bank = data & 0b11; - } else if address >= 0x6000 && address <= 0x7FFF { // Banking mode select - self.banking_mode = match data & 1 { - 0 => BankingMode::Simple, - 1 => BankingMode::Advanced, - _ => unreachable!(), - } - } else if EXTERNAL_RAM.contains(&address) { - if !self.ram_enable || !self.info.has_ram { - return; - } - let address = address as usize - EXTERNAL_RAM.min().unwrap() as usize + (EXTERNAL_RAM.min().unwrap() as usize * self.ram_bank as usize); - if let Some(elem) = self.ram.get_mut(address) { - *elem = data; - } - self.switch_rom_bank(self.rom_bank + (data as u16 >> 5)); - } - }, - _ => unimplemented!(), - } - } - - pub fn switch_rom_bank(&mut self, bank: u16) { + fn switch_rom_bank(&mut self, bank: u16) { self.rom_bank = bank; if self.rom_bank > self.info.rom_banks.saturating_sub(1) { self.rom_bank = self.info.rom_banks.saturating_sub(1); @@ -257,3 +230,56 @@ impl ROM { } } } + +impl ROM for MBC1 { + fn read(&self, address: u16) -> u8 { + if BANK_ZERO.contains(&address) { + return self.data[address as usize]; + } else if BANK_SWITCHABLE.contains(&address) { + return self.data[((self.rom_bank as usize * 0x4000) + (address as usize & 0x3FFF)) as usize]; + } else if EXTERNAL_RAM.contains(&address) { + if !self.info.has_ram { + return 0xFF; + } + return match self.ram.get((address - EXTERNAL_RAM.min().unwrap() + (0x2000 * self.ram_bank as u16)) as usize) { + Some(data) => *data, + None => 0xFF, + }; + } + unreachable!("ROM read: Address {} not valid", address); + } + + fn write(&mut self, address: u16, data: u8) { + if address <= 0x1FFF { // RAM enable register + if !self.info.has_ram { + return; + } + self.ram_enable = match data & 0x0F { + 0x0A => true, + _ => false, + }; + return; + } else if address >= 0x2000 && address <= 0x3FFF { // ROM bank number register + // println!("Switch bank to {:02X}", data); + self.switch_rom_bank(data as u16 & 0b00011111); + } else if address >= 0x4000 && address <= 0x5FFF { // ROM and RAM bank number register + // println!("RAM bank {:02X}", data); + self.ram_bank = data & 0b11; + } else if address >= 0x6000 && address <= 0x7FFF { // Banking mode select + self.banking_mode = match data & 1 { + 0 => BankingMode::Simple, + 1 => BankingMode::Advanced, + _ => unreachable!(), + } + } else if EXTERNAL_RAM.contains(&address) { + if !self.ram_enable || !self.info.has_ram { + return; + } + let address = address as usize - EXTERNAL_RAM.min().unwrap() as usize + (EXTERNAL_RAM.min().unwrap() as usize * self.ram_bank as usize); + if let Some(elem) = self.ram.get_mut(address) { + *elem = data; + } + self.switch_rom_bank(self.rom_bank + (data as u16 >> 5)); + } + } +}