From 2a234f4d14fe6089b5668ea20308f6816f1db68d Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 23 Dec 2020 19:39:37 -0600 Subject: [PATCH] feat: implement ability to boot straigt to cartridge --- src/bus.rs | 151 +++++++++++++++++++++++++++++++++++++++------ src/cartridge.rs | 26 ++++++++ src/cpu.rs | 25 +++++++- src/instruction.rs | 28 ++++++++- src/lib.rs | 1 + src/main.rs | 4 +- 6 files changed, 212 insertions(+), 23 deletions(-) create mode 100644 src/cartridge.rs diff --git a/src/bus.rs b/src/bus.rs index b18280d..1cc4ac6 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,48 +1,161 @@ -#[derive(Debug, Copy, Clone)] +use super::cartridge::Cartridge; + +#[derive(Debug, Clone)] pub struct Bus { - boot: [u8; 256], + boot: Option<[u8; 256]>, + cartridge: Option, } impl Default for Bus { fn default() -> Self { Self { - boot: include_bytes!("../bin/DMG_ROM.bin").to_owned(), + boot: Some(include_bytes!("../bin/DMG_ROM.bin").to_owned()), + cartridge: None, } } } +impl Bus { + pub fn with_boot() -> Self { + Default::default() + } + + pub fn without_boot() -> Self { + Self { + boot: None, + cartridge: None, + } + } + + pub fn load_cartridge(&mut self, path: &str) { + self.cartridge = Some(Cartridge::new(path).unwrap()); + } +} + impl Bus { pub fn read_byte(&self, addr: u16) -> u8 { match addr { - 0x0000..=0x00FF => { - // Restart and Interrupt Vectors - self.boot[addr as usize] + 0x0000..=0x3FFF => { + // 16KB ROM bank 00 + if addr <= 0x00FF && self.boot.is_some() { + let boot = self.boot.unwrap(); + boot[addr as usize] + } else { + match &self.cartridge { + Some(cart) => cart.read_byte(addr), + None => panic!("Tried to read from a non-existant cartridge"), + } + } + } + 0x4000..=0x7FFF => match &self.cartridge { + // 16KB ROM Bank 01 -> NN (switchable via MB) + Some(cart) => cart.read_byte(addr), + None => panic!("Tried to read from a non-existant cartridge"), + }, + 0x8000..=0x9FFF => { + // 8KB Video RAM + unimplemented!("Unable to read {:#06X} in Video RAM", addr); + } + 0xA000..=0xBFFF => { + // 8KB External RAM + unimplemented!("Unable to read {:#06X} in Extermal RAM", addr); + } + 0xC000..=0xCFFF => { + // 4KB Work RAM Bank 0 + unimplemented!("Unable to read {:#06X} in Work RAM Bank 0", addr); + } + 0xD000..=0xDFFF => { + // 4KB Work RAM Bank 1 -> N + unimplemented!("Unable to read {:#06X} in Work RAM Bank N", addr); + } + 0xE000..=0xFDFF => { + // Mirror of 0xC000 to 0xDDFF + unimplemented!("Unable to read {:#06X} in Restricted Mirror", addr); + } + 0xFE00..=0xFE9F => { + // Sprite Attrbute Table + unimplemented!("Unable to read {:#06X} in the Sprite Attribute Table", addr); + } + 0xFEA0..=0xFEFF => unimplemented!("{:#06X} is not allowed to be used", addr), + 0xFF00..=0xFF7F => { + // IO Registers + unimplemented!("Unable to read {:#06X} in I/O Registers", addr); + } + 0xFF80..=0xFFFE => { + // High RAM + unimplemented!("Unable to read {:#06X} in High RAM", addr); + } + 0xFFFF => { + // Interupts Enable Register + unimplemented!("Unable to read Interrupt Enable Register {:#06X} ", addr); } - _ => unimplemented!("Can't read byte from {:#06x}", addr), } } pub fn write_byte(&mut self, addr: u16, byte: u8) { - match addr { - 0x000..=0x0FF => { - // Restart and Itterupt Vectors - self.boot[addr as usize] = byte; - } - _ => unimplemented!("Can't write {:#04x} to {:#06x}", byte, addr), - } + unimplemented!("Can't write {:#04x} to {:#06X}", byte, addr) } pub fn read_word(&self, addr: u16) -> u16 { match addr { - 0x0000..=0x00FF => { - // Restart and Interrupt Vectors - (self.boot[(addr + 1) as usize] as u16) << 8 | self.boot[addr as usize] as u16 + 0x0000..=0x3FFF => { + // 16KB ROM bank 00 + if addr <= 0x00FF && self.boot.is_some() { + let boot = self.boot.unwrap(); + (boot[(addr + 1) as usize] as u16) << 8 | boot[addr as usize] as u16 + } else { + match &self.cartridge { + Some(cart) => cart.read_word(addr), + None => panic!("Tried to read from a non-existant cartridge"), + } + } + } + 0x4000..=0x7FFF => match &self.cartridge { + // 16KB ROM Bank 01 -> NN (switchable via MB) + Some(cart) => cart.read_word(addr), + None => panic!("Tried to read from a non-existant cartridge"), + }, + 0x8000..=0x9FFF => { + // 8KB Video RAM + unimplemented!("Unable to read {:#06X} in Video RAM", addr); + } + 0xA000..=0xBFFF => { + // 8KB External RAM + unimplemented!("Unable to read {:#06X} in Extermal RAM", addr); + } + 0xC000..=0xCFFF => { + // 4KB Work RAM Bank 0 + unimplemented!("Unable to read {:#06X} in Work RAM Bank 0", addr); + } + 0xD000..=0xDFFF => { + // 4KB Work RAM Bank 1 -> N + unimplemented!("Unable to read {:#06X} in Work RAM Bank N", addr); + } + 0xE000..=0xFDFF => { + // Mirror of 0xC000 to 0xDDFF + unimplemented!("Unable to read {:#06X} in Restricted Mirror", addr); + } + 0xFE00..=0xFE9F => { + // Sprite Attrbute Table + unimplemented!("Unable to read {:#06X} in the Sprite Attribute Table", addr); + } + 0xFEA0..=0xFEFF => unimplemented!("{:#06X} is not allowed to be used", addr), + 0xFF00..=0xFF7F => { + // IO Registers + unimplemented!("Unable to read {:#06X} in I/O Registers", addr); + } + 0xFF80..=0xFFFE => { + // High RAM + unimplemented!("Unable to read {:#06X} in High RAM", addr); + } + 0xFFFF => { + // Interupts Enable Register + unimplemented!("Unable to read Interrupt Enable Register {:#06X} ", addr); } - _ => unimplemented!("Can't read word from {:#06x}", addr), } } pub fn write_word(&mut self, addr: u16, word: u16) { - unimplemented!("Can't write {:#06x} to {:#06x}", word, addr) + unimplemented!("Can't write {:#06X} to {:#06X}", word, addr) } } diff --git a/src/cartridge.rs b/src/cartridge.rs new file mode 100644 index 0000000..e1d643f --- /dev/null +++ b/src/cartridge.rs @@ -0,0 +1,26 @@ +use std::fs::File; +use std::io::{self, Read}; +use std::path::Path; + +#[derive(Debug, Clone)] +pub struct Cartridge { + memory: Vec, +} + +impl Cartridge { + pub fn new + ?Sized>(path: &P) -> io::Result { + let mut memory = vec![]; + let mut rom = File::open(path)?; + rom.read_to_end(&mut memory)?; + + Ok(Self { memory }) + } + + pub fn read_byte(&self, addr: u16) -> u8 { + self.memory[addr as usize] + } + + pub fn read_word(&self, addr: u16) -> u16 { + (self.memory[(addr + 1) as usize] as u16) << 8 | self.memory[addr as usize] as u16 + } +} diff --git a/src/cpu.rs b/src/cpu.rs index 7645046..4bfac7e 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,7 +1,7 @@ use super::bus::Bus; use super::instruction::Instruction; -#[derive(Debug, Copy, Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct Cpu { bus: Bus, reg: Registers, @@ -15,6 +15,25 @@ impl Cpu { Default::default() } + pub fn new_without_boot() -> Self { + Self { + bus: Bus::without_boot(), + reg: Registers { + a: 0x01, + b: 0x00, + c: 0x13, + d: 0x00, + e: 0xD8, + h: 0x01, + l: 0x4D, + sp: 0xFFFE, + pc: 0x0100, + }, + flags: 0xb0.into(), + ..Default::default() + } + } + pub fn ime(&self) -> bool { self.ime } @@ -26,6 +45,10 @@ impl Cpu { pub fn inc_pc(&mut self) { self.reg.pc += 1; } + + pub fn load_cartridge(&mut self, path: &str) { + self.bus.load_cartridge(path); + } } impl Cpu { diff --git a/src/instruction.rs b/src/instruction.rs index 7d7b5db..3541e1c 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -49,7 +49,7 @@ pub enum Instruction { SET(u8, InstrRegister), } -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] pub enum JPTarget { RegisterPair(RegisterPair), ImmediateWord(u16), @@ -70,7 +70,7 @@ pub enum MATHTarget { ImmediateByte(u8), } -#[derive(Debug, Copy, Clone)] +#[derive(Copy, Clone)] pub enum LDTarget { IndirectC, Register(InstrRegister), @@ -1993,3 +1993,27 @@ impl Table { } } } + +impl std::fmt::Debug for JPTarget { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + JPTarget::RegisterPair(pair) => write!(f, "{:?}", pair), + JPTarget::ImmediateWord(word) => write!(f, "{:#06X}", word), + } + } +} + +impl std::fmt::Debug for LDTarget { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + LDTarget::IndirectC => f.write_str("IndirectC"), + LDTarget::Register(reg) => write!(f, "{:?}", reg), + LDTarget::IndirectRegister(pair) => write!(f, "{:?}", pair), + LDTarget::ByteAtAddress(addr) => write!(f, "{:#06X}", addr), + LDTarget::ImmediateWord(word) => write!(f, "{:#06X}", word), + LDTarget::ImmediateByte(byte) => write!(f, "{:04X}", byte), + LDTarget::RegisterPair(pair) => write!(f, "{:?}", pair), + LDTarget::ByteAtAddressWithOffset(byte) => write!(f, "{:#04X}", byte), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index ddc88db..c10e6ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ mod bus; +mod cartridge; pub mod cpu; mod instruction; diff --git a/src/main.rs b/src/main.rs index 7b27a37..56ac559 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,15 @@ use gb::cpu::Cpu as LR35902; fn main() { let mut game_boy = LR35902::new(); + game_boy.load_cartridge("bin/cpu_instrs.gb"); + loop { let pc = game_boy.register_pair(gb::cpu::RegisterPair::PC); let opcode = game_boy.fetch(); let instruction = game_boy.decode(opcode); println!( - "Addr: {:#06x} | Opcode: {:#x} | Instr: {:x?}", + "Addr: {:#06X} | Opcode: {:#04X} | Instr: {:X?}", pc, opcode, instruction );