feat: implement ability to boot straigt to cartridge

This commit is contained in:
Rekai Nyangadzayi Musuka 2020-12-23 19:39:37 -06:00
parent 1502cc3ec2
commit 2a234f4d14
6 changed files with 212 additions and 23 deletions

View File

@ -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<Cartridge>,
}
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)
}
}

26
src/cartridge.rs Normal file
View File

@ -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<u8>,
}
impl Cartridge {
pub fn new<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<Self> {
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
}
}

View File

@ -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 {

View File

@ -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),
}
}
}

View File

@ -1,3 +1,4 @@
mod bus;
mod cartridge;
pub mod cpu;
mod instruction;

View File

@ -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
);