diff --git a/src/cpu/cartridge.rs b/src/cpu/cartridge.rs new file mode 100644 index 0000000..c8ea20c --- /dev/null +++ b/src/cpu/cartridge.rs @@ -0,0 +1,172 @@ +use cpu::constants::*; + +/// The things that are constant between all types of cartridges +/// This also includes things like video ram +/// Thus this struct is best understood as dealing with any and all things +/// addressable +/// +/// TODO: memory locking during certain periods (i.e. the rest of the virtual +/// memory system...) +pub struct Cartridge { + /// top 16kb + memory_bank0: [byte; 0x4000], + cart_sub: CartridgeSubType, + /// 8kb video ram + video_ram: [byte; 0x2000], + /// 8kb internal ram + internal_ram: [byte; 0x2000], + /// sprite attribute memory + oam: [byte; 0xA0], + + /// 0xFF80-0xFFFF + internal_ram2: [byte; 0x80], + interrupt_flag: byte, +} + +pub enum CartridgeSubType { + ROM_only { + memory_bank1: [byte; 0x4000], + }, + MBC1 { + /* + MBC1 has two modes: + * 16mbit ROM (with 128 banks), 8KB RAM (1 bank) + * 4mbit (with 32 banks), 32KB RAM (4 banks) + */ + //13 bits for 8KB addressing + //addressing 16mbit = 2MB, (1kb = 10) (8kb = 13) (16kb = 14) + //(2mb = 21) + //21bits to index fully, because first 0x4000 address are sep + memory_model: MBC1_type, + memory_banks: [byte; (2 << 13) + (2 << 21) - 0x4000], + ram_active: bool, + //top two bits (21 & 22?) used for selecting RAM in 4_32 mode + mem_bank_selector: u32, + }, +} + +pub enum MBC1_type { + sixteen_eight, + four_thirtytwo, +} + +impl Cartridge { + //for reading + pub fn index(&self, ind: u16) -> byte { + match ind { + 0x0000...0x3FFF => { + self.memory_bank0[ind as usize] + } + 0x4000...0x7FFF => { + match self.cart_sub { + CartridgeSubType::ROM_only {memory_bank1: membank1} => { + membank1[(ind - 0x4000) as usize] + }, + CartridgeSubType::MBC1 {memory_model: MBC1_type::sixteen_eight, + memory_banks: mb, + ram_active: ra, + mem_bank_selector: index} => { + mb[((ind - 0x4000) as usize) + ((index * 0x4000) as usize)] + }, + CartridgeSubType::MBC1 {memory_model: MBC1_type::four_thirtytwo, + memory_banks: mb, + ram_active: ra, + mem_bank_selector: index} => { + unimplemented!() + }, + + _ => unimplemented!(), + } + }, + // Video RAM: + 0x8000...0x9FFF => { + //TODO: block reads if reads should be blocked + unimplemented!() + }, + // switchable RAM bank + 0xA000...0xBFFF => { + unimplemented!() + }, + //internal ram + 0xC000...0xDFFF => { + self.internal_ram[(ind - 0xC000) as usize] + }, + //echo of internal ram + 0xE000...0xFDFF => { + self.internal_ram[(ind - 0xE000) as usize] + }, + // OAM + 0xFE00...0xFF9F => { + self.oam[(ind - 0xFE00) as usize] + }, + // IO ports + 0xFF00...0xFF4B => { + //TODO: + unimplemented!() + }, + //more internal RAM + 0xFF80...0xFFFE => { + self.internal_ram2[(ind - 0xFF80) as usize] + }, + //interrupt flag + 0xFFFF => { + //TODO: + self.interrupt_flag + } + _ => { + error!("Address 0x{:X} cannot be read from", ind); + 0 + }, + } + } + + pub fn index_set(&mut self, ind: u16, val: u8) { + match self.cart_sub { + CartridgeSubType::ROM_only {memory_bank1: membank1} => { + match ind as usize { + 0xFF80...0xFFFE => { + self.internal_ram2[(ind - 0xFF80) as usize] = val; + } + //internal ram + 0xC000...0xDFFF => { + self.internal_ram[(ind - 0xC000) as usize] = val; + }, + //echo of internal ram + 0xE000...0xFDFF => { + self.internal_ram[(ind - 0xE000) as usize] = val; + }, + 0x0000...0x7FFF => { + error!("Cannot write to address 0x{:X} of a ROM-only cartridge", ind); + } + addr => { + unimplemented!(); + } + } + }, + CartridgeSubType::MBC1 {memory_model: MBC1_type::sixteen_eight, + memory_banks: mb, + ram_active: ra, + mem_bank_selector: mut index} => { + match ind as usize { + 0x2000...0x3FFF => { + //take the lower 5 bits to select the 2nd ROM bank + let bank_select = if (val & 0x1F) == 0 {1} else {val & 0x1F}; + index = bank_select as u32; + debug!("MBC1 switching second ROM bank to ROM bank {}", bank_select); + } + 0x6000...0x7FFF => { + if (val & 0x1) == 1 { + debug!("MBC1 switching to 4-32 mode"); + unimplemented!(); + } else { + debug!("MBC1: already in 16-8 mode"); + } + } + _ => unimplemented!(), + } + }, + + _ => unimplemented!(), + } + } +} diff --git a/src/cpu/mod.rs b/src/cpu/mod.rs index a6e3eae..84aea96 100644 --- a/src/cpu/mod.rs +++ b/src/cpu/mod.rs @@ -5,12 +5,14 @@ #[macro_use] mod macros; mod tests; pub mod constants; +pub mod cartridge; use std::collections::VecDeque; use std::num::Wrapping; use disasm::*; use self::constants::*; +use self::cartridge::*; pub trait CpuEventLogger { fn new(mem: Option<&[u8]>) -> Self; @@ -191,6 +193,9 @@ pub struct Cpu { memory_banks: Vec<[byte; 0x4000]>, + /// Whether or not the RAM (included in some carts is writable) + ram_writable: bool, + /// Whether or not the CPU is running, waiting for input, or stopped pub state: CpuState, @@ -223,6 +228,7 @@ impl Clone for Cpu { cartridge_type: None, mbc_type: None, memory_banks: vec![], + ram_writable: false, state: self.state, input_state: self.input_state, @@ -257,6 +263,7 @@ impl Cpu { cartridge_type: None, mbc_type: None, memory_banks: vec![], + ram_writable: false, state: CpuState::Normal, input_state: 0xFF, @@ -284,6 +291,7 @@ impl Cpu { self.sp = 0xFFFE; self.pc = 0x100; self.cycles = 0; + self.ram_writable = false; // if let Some(ref mut el) = self.event_logger { // el.events_deq.clear(); // } @@ -1026,6 +1034,14 @@ impl Cpu { if let Some(cart_type) = self.cartridge_type { match address { + 0...0x1FFF if cart_type == CartridgeType::RomRam + || cart_type == CartridgeType::RomRamBatt + || cart_type == CartridgeType::RomMBC1Ram + || cart_type == CartridgeType::RomMBC1RamBatt => { + self.ram_writable = (value & 0xF) == 0b1010; + debug!("Setting RAM to {}", + if self.ram_writable {"on"} else {"off"}); + }, 0...0x7FFF if cart_type == CartridgeType::RomOnly || cart_type == CartridgeType::RomRam || cart_type == CartridgeType::RomRamBatt => { @@ -2498,6 +2514,16 @@ impl Cpu { } else { // to_cartridge_type failed error!("Could not find a cartridge type!"); } + + debug!("Cart loaded with {} ram banks", + match self.mem[0x149] { + 0 => 0, + 1 => 1, + 2 => 1, + 3 => 4, + 4 => 16, + _ => {error!("Undefined value at 0x149 in ROM"); -1}, + }); } }