Modeling the Bus and writing first test for NOP instruction

This commit is contained in:
Franco Colmenarez 2021-10-13 19:38:37 -05:00
parent 2afa2a1ff8
commit 5d04b7c346
7 changed files with 133 additions and 18 deletions

View File

@ -1,7 +1,10 @@
use rust_boy::rom::ROM; use rust_boy::rom::ROM;
use rust_boy::console::Console;
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
let myrom = ROM::load_file("roms/cpu_instrs.gb".to_string())?; /* let myrom = ROM::load_file("roms/cpu_instrs.gb".to_string())?;
myrom.print_content(); myrom.print_content(); */
let mut console = Console::new();
console.cpu_run();
Ok(()) Ok(())
} }

View File

@ -1,3 +1,5 @@
use crate::rom::ROM;
pub enum MemoryMap { pub enum MemoryMap {
BankZero, BankZero,
BankSwitchable, BankSwitchable,
@ -15,18 +17,50 @@ pub enum MemoryMap {
impl MemoryMap { impl MemoryMap {
pub fn get_map(address: u16) -> Self { pub fn get_map(address: u16) -> Self {
if address <= 0x3FFF {return Self::BankZero;} match address {
if address >= 0x4000 && address <= 0x7FFF {return Self::BankSwitchable;} 0x0000..=0x3FFF => Self::BankZero,
if address >= 0x8000 && address <= 0x9FFF {return Self::VideoRAM;} 0x4000..=0x7FFF => Self::BankSwitchable,
if address >= 0xA000 && address <= 0xBFFF {return Self::ExternalRAM;} 0x8000..=0x9FFF => Self::VideoRAM,
if address >= 0xC000 && address <= 0xCFFF {return Self::WorkRAM1;} 0xA000..=0xBFFF => Self::ExternalRAM,
if address >= 0xD000 && address <= 0xDFFF {return Self::WorkRAM2;} 0xC000..=0xCFFF => Self::WorkRAM1,
if address >= 0xE000 && address <= 0xFDFF {return Self::EchoRAM;} // Mirror of C000~DDFF 0xD000..=0xDFFF => Self::WorkRAM2,
if address >= 0xFE00 && address <= 0xFE9F {return Self::SpriteAttributeTable;} 0xE000..=0xFDFF => Self::EchoRAM, // Mirror of C000~DDFF
if address >= 0xFEA0 && address <= 0xFEFF {return Self::NotUsable;} 0xFE00..=0xFE9F => Self::SpriteAttributeTable,
if address >= 0xFF00 && address <= 0xFF7F {return Self::IORegisters;} 0xFEA0..=0xFEFF => Self::NotUsable,
if address >= 0xFF80 && address <= 0xFFFE {return Self::HighRAM;} 0xFF00..=0xFF7F => Self::IORegisters,
if address == 0xFFFF {return Self::InterruptEnableRegister;} 0xFF80..=0xFFFE => Self::HighRAM,
Self::BankZero 0xFFFF => Self::InterruptEnableRegister,
_ => Self::BankZero,
}
}
}
pub struct Bus {
game_rom: ROM,
data: [u8; 0xFFFF],
}
impl Bus {
pub fn new() -> Self {
let game_rom = match ROM::load_file("roms/cpu_instrs.gb".to_string()) {
Ok(rom) => rom,
_ => ROM::from_bytes(&[0; 0xFFFF])
};
game_rom.print_content(Some(0x102));
Self {
data: [0; 0xFFFF],
game_rom,
}
}
pub fn read(&self, address: u16) -> u8 {
match MemoryMap::get_map(address) {
MemoryMap::BankZero => self.game_rom.read(address),
_ => self.data[address as usize],
}
}
pub fn write(&mut self, address: u16, data: u8) {
self.data[address as usize] = data;
} }
} }

23
src/console.rs Normal file
View File

@ -0,0 +1,23 @@
use crate::cpu::CPU;
use crate::ppu::PPU;
use crate::bus::Bus;
pub struct Console {
cpu: CPU,
ppu: PPU,
bus: Bus,
}
impl Console {
pub fn new() -> Self {
Self {
cpu: CPU::new(),
ppu: PPU::new(),
bus: Bus::new(),
}
}
pub fn cpu_run(&mut self) {
self.cpu.run(&mut self.bus);
}
}

View File

@ -1,4 +1,5 @@
use crate::utils::{BitIndex, get_bit, set_bit}; use crate::utils::{BitIndex, get_bit, set_bit};
use crate::bus::Bus;
pub enum Register { pub enum Register {
A(u8), // Accumulator A(u8), // Accumulator
@ -43,7 +44,7 @@ impl Registers {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
a: 0, a: 0,
f: 0b11110000, // The first 4 lower bits are always set to 0 f: 0b00000000, // The first 4 lower bits are always set to 0
b: 0, b: 0,
c: 0, c: 0,
d: 0, d: 0,
@ -111,6 +112,10 @@ impl Registers {
} }
} }
pub fn increment_pc(&mut self) {
self.pc += 1;
}
fn get_af(&self) -> u16 { fn get_af(&self) -> u16 {
((self.a as u16) << 8) | (self.f as u16) ((self.a as u16) << 8) | (self.f as u16)
} }
@ -249,6 +254,28 @@ pub struct CPU {
} }
impl CPU { impl CPU {
pub fn new() -> Self {
Self {
registers: Registers::new(),
}
}
// Get the program counter
pub fn get_register(&self, register: Register) -> u16 {
self.registers.get(register)
}
pub fn run(&mut self, bus: &mut Bus) {
println!("Opcode: {:02X}", bus.read(self.registers.get(Register::PC(0))));
}
pub fn exec(&mut self, opcode: CpuOpcode) {
match opcode {
CpuOpcode::NOP => self.registers.increment_pc(),
_ => println!("Illegal instruction"),
};
}
pub fn parse_opcode(opcode: u8) -> CpuOpcode { pub fn parse_opcode(opcode: u8) -> CpuOpcode {
match opcode { match opcode {
0x06 => CpuOpcode::LD(OpcodeParameter::Register_U8(Register::B(0))), 0x06 => CpuOpcode::LD(OpcodeParameter::Register_U8(Register::B(0))),
@ -572,5 +599,8 @@ mod tests {
#[test] #[test]
fn test_cpu_instructions() { fn test_cpu_instructions() {
let mut cpu = CPU::new();
cpu.exec(CpuOpcode::NOP);
assert_eq!(cpu.registers.get(Register::PC(0)), 0x101);
} }
} }

View File

@ -1,4 +1,6 @@
pub mod utils; pub mod utils;
pub mod cpu; pub mod cpu;
pub mod ppu;
pub mod rom; pub mod rom;
pub mod bus; pub mod bus;
pub mod console;

7
src/ppu.rs Normal file
View File

@ -0,0 +1,7 @@
pub struct PPU;
impl PPU {
pub fn new() -> Self {
Self{}
}
}

View File

@ -15,7 +15,23 @@ impl ROM {
}) })
} }
pub fn print_content(&self) { pub fn read(&self, address: u16) -> u8 {
println!("{:02X?}", self.bytes); match self.bytes.get(address as usize) {
Some(val) => *val,
None => 0xFF,
}
}
pub fn from_bytes(bytes: &[u8]) -> Self {
Self {
bytes: bytes.to_vec(),
}
}
pub fn print_content(&self, address: Option<u16>) {
match address {
Some(address) => println!("{:02X?}", self.read(address)),
None => println!("{:02X?}", self.bytes),
};
} }
} }