From 4d2e0e33f20ce16d770219f0d93d066b93ea55ea Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Wed, 23 Dec 2020 03:25:16 -0600 Subject: [PATCH] feat: implement fetch, decode, execute loop --- .gitignore | 1 + src/bus.rs | 24 +++++++++--- src/cpu.rs | 53 ++++++++++++++++++------- src/instruction.rs | 98 +++++++++++++++++++++------------------------- src/lib.rs | 4 +- src/main.rs | 17 +++++++- 6 files changed, 120 insertions(+), 77 deletions(-) diff --git a/.gitignore b/.gitignore index 06f5fec..64b0854 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /.idea /.vscode +/bin \ No newline at end of file diff --git a/src/bus.rs b/src/bus.rs index 9be451d..6fadbc5 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -1,20 +1,34 @@ -#[derive(Debug, Copy, Clone)] +const ROM: &[u8; 256] = include_bytes!("../bin/DMG_ROM.bin"); + +#[derive(Debug, Copy, Clone, Default)] pub struct Bus {} impl Bus { pub fn read_byte(&self, addr: u16) -> u8 { - unimplemented!() + match addr { + 0x0000..=0x00FF => { + // Restart and Interrupt Vectors + ROM[addr as usize] + } + _ => unimplemented!(), + } } - pub fn write_byte(&mut self, addr: u16, byte: u8) { + pub fn write_byte(&mut self, _addr: u16, _byte: u8) { unimplemented!() } pub fn read_word(&self, addr: u16) -> u16 { - unimplemented!() + match addr { + 0x0000..=0x00FF => { + // Restart and Interrupt Vectors + (ROM[(addr + 1) as usize] as u16) << 8 | ROM[addr as usize] as u16 + } + _ => unimplemented!(), + } } - pub fn write_word(&mut self, addr: u16, word: u16) { + pub fn write_word(&mut self, _addr: u16, _word: u16) { unimplemented!() } } diff --git a/src/cpu.rs b/src/cpu.rs index d6b8cbc..7645046 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)] +#[derive(Debug, Copy, Clone, Default)] pub struct Cpu { bus: Bus, reg: Registers, @@ -11,6 +11,10 @@ pub struct Cpu { } impl Cpu { + pub fn new() -> Self { + Default::default() + } + pub fn ime(&self) -> bool { self.ime } @@ -18,34 +22,47 @@ impl Cpu { pub fn set_ime(&mut self, enabled: bool) { self.ime = enabled; } + + pub fn inc_pc(&mut self) { + self.reg.pc += 1; + } } impl Cpu { - fn fetch(&self) -> u8 { - self.bus.read_byte(self.reg.pc) - // TODO: Figure out where to increment the program counter. + pub fn fetch(&mut self) -> u8 { + let opcode = self.bus.read_byte(self.reg.pc); + self.inc_pc(); + + opcode } - fn decode(&self, opcode: u8) -> Instruction { + pub fn decode(&mut self, opcode: u8) -> Instruction { Instruction::from_byte(self, opcode) } - fn execute(&mut self, instruction: Instruction) { + pub fn execute(&mut self, instruction: Instruction) { Instruction::execute(self, instruction); } } impl Cpu { - pub fn read_byte(&self, addr: u16) -> u8 { - self.bus.read_byte(addr) + pub fn read_byte(&mut self, addr: u16) -> u8 { + let byte = self.bus.read_byte(addr); + self.inc_pc(); + + byte } pub fn write_byte(&mut self, addr: u16, byte: u8) { self.bus.write_byte(addr, byte) } - pub fn read_word(&self, addr: u16) -> u16 { - self.bus.read_word(addr) + pub fn read_word(&mut self, addr: u16) -> u16 { + let word = self.bus.read_word(addr); + self.inc_pc(); + self.inc_pc(); + + word } pub fn write_word(&mut self, addr: u16, word: u16) { @@ -56,8 +73,14 @@ impl Cpu { #[derive(Debug, Copy, Clone)] enum State { Execute, - Halt, - Stop, + // Halt, + // Stop, +} + +impl Default for State { + fn default() -> Self { + Self::Execute + } } impl Cpu { @@ -70,7 +93,7 @@ impl Cpu { Register::E => self.reg.e = value, Register::H => self.reg.h = value, Register::L => self.reg.l = value, - Register::Flag => self.reg.a = value.into(), + Register::Flag => self.reg.a = value, } } @@ -147,7 +170,7 @@ pub enum RegisterPair { PC, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] struct Registers { a: u8, b: u8, @@ -160,7 +183,7 @@ struct Registers { pc: u16, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Default)] pub struct Flags { pub z: bool, // Zero Flag pub n: bool, // Negative Flag diff --git a/src/instruction.rs b/src/instruction.rs index a352825..7d7b5db 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -106,19 +106,6 @@ pub enum InstrRegister { IndirectHL, // (HL) } -#[derive(Debug, Clone, Copy)] -enum InstrRegisterWithC { - A, - B, - C, - D, - E, - H, - L, - IndirectHL, // (HL) - IndirectC, // (0xFF00 + C) -} - #[derive(Debug, Copy, Clone)] pub enum JumpCondition { NotZero, @@ -150,9 +137,7 @@ impl Instruction { RegisterPair::BC | RegisterPair::DE | RegisterPair::HL - | RegisterPair::SP => { - cpu.set_register_pair(RegisterPair::try_from(pair).unwrap(), nn) - } + | RegisterPair::SP => cpu.set_register_pair(pair, nn), _ => unreachable!(), } Cycles(12) @@ -190,19 +175,22 @@ impl Instruction { // LD A, (BC) | Put value at address BC into A // LD A, (DE) | Put value at address DE into A let addr = cpu.register_pair(RegisterPair::try_from(pair).unwrap()); - cpu.set_register(Register::A, cpu.read_byte(addr)); + let byte = cpu.read_byte(addr); + cpu.set_register(Register::A, byte); } InstrRegisterPair::IncrementHL => { // LD A, (HL+) | Put value at address HL into A, then increment HL let addr = cpu.register_pair(RegisterPair::HL); - cpu.set_register(Register::A, cpu.read_byte(addr)); + let byte = cpu.read_byte(addr); + cpu.set_register(Register::A, byte); cpu.set_register_pair(RegisterPair::HL, addr + 1); } InstrRegisterPair::DecrementHL => { // LD A, (HL-) | Put value at address HL into A, then increment HL let addr = cpu.register_pair(RegisterPair::HL); - cpu.set_register(Register::A, cpu.read_byte(addr)); + let byte = cpu.read_byte(addr); + cpu.set_register(Register::A, byte); cpu.set_register_pair(RegisterPair::HL, addr - 1); } @@ -238,7 +226,8 @@ impl Instruction { } (LDTarget::Register(InstrRegister::A), LDTarget::IndirectC) => { let addr = 0xFF00 + cpu.register(Register::C) as u16; - cpu.set_register(Register::A, cpu.read_byte(addr)); + let byte = cpu.read_byte(addr); + cpu.set_register(Register::A, byte); Cycles(8) } (LDTarget::Register(lhs), LDTarget::Register(rhs)) => { @@ -256,7 +245,8 @@ impl Instruction { } (LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddressWithOffset(n)) => { // LD A, (0xFF00 + n) | Store value at address (0xFF00 + n) in register A - cpu.set_register(Register::A, cpu.read_byte(0xFF00 + (n as u16))); + let byte = cpu.read_byte(0xFF00 + (n as u16)); + cpu.set_register(Register::A, byte); Cycles(12) } ( @@ -272,7 +262,8 @@ impl Instruction { Cycles(16) } (LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddress(nn)) => { - cpu.set_register(Register::A, cpu.read_byte(nn)); + let byte = cpu.read_byte(nn); + cpu.set_register(Register::A, byte); Cycles(16) } _ => unreachable!(), @@ -332,7 +323,7 @@ impl Instruction { | RegisterPair::HL | RegisterPair::SP => { let hl_value = cpu.register_pair(RegisterPair::HL); - let value = cpu.register_pair(RegisterPair::try_from(pair).unwrap()); + let value = cpu.register_pair(pair); let sum = Self::add_u16s(hl_value, value, &mut flags); cpu.set_register_pair(RegisterPair::HL, sum); @@ -413,10 +404,8 @@ impl Instruction { } InstrRegister::IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte( - addr, - Self::inc_register(cpu.read_byte(addr), &mut flags), - ); + let byte = Self::inc_register(cpu.read_byte(addr), &mut flags); + cpu.write_byte(addr, byte); cycles = Cycles(12) } } @@ -471,7 +460,8 @@ impl Instruction { } InstrRegister::IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, Self::dec_register(cpu.read_byte(addr), &mut flags)); + let byte = cpu.read_byte(addr); + cpu.write_byte(addr, Self::dec_register(byte, &mut flags)); cycles = Cycles(12); } } @@ -1058,7 +1048,7 @@ impl Instruction { // RST n | Push current address onto the stack, jump to 0x0000 + n let addr = cpu.register_pair(RegisterPair::PC); Self::push(cpu, addr); - cpu.set_register_pair(RegisterPair::PC, 0x0000 + (n as u16)); + cpu.set_register_pair(RegisterPair::PC, n as u16); Cycles(16) } Instruction::RLC(reg) => { @@ -1589,7 +1579,7 @@ impl Instruction { fn u16_half_carry(left: u16, right: u16) -> bool { // Self::u8_half_carry((left >> 8) as u8, (right >> 8) as u8) - ((left & 0xFFFF) + (right & 0xFFFF)) > 0xFFF // Thanks @Nectar Boy#1003 + left + right > 0xFFF // Thanks @Nectar Boy#1003 } fn u8_half_carry(left: u8, right: u8) -> bool { @@ -1619,7 +1609,7 @@ impl Instruction { } impl Instruction { - pub fn from_byte(cpu: &Cpu, byte: u8) -> Self { + pub fn from_byte(cpu: &mut Cpu, byte: u8) -> Self { if byte == 0xCB { Self::from_prefixed_byte(cpu) } else { @@ -1627,7 +1617,7 @@ impl Instruction { } } - fn from_unprefixed_byte(cpu: &Cpu, opcode: u8) -> Self { + fn from_unprefixed_byte(cpu: &mut Cpu, opcode: u8) -> Self { // https://gb-archive.github.io/salvage/decoding_gbz80_opcodes/Decoding%20Gamboy%20Z80%20Opcodes.html let x = (opcode >> 6) & 0b00000011; let y = (opcode >> 3) & 0b00000111; @@ -1635,23 +1625,22 @@ impl Instruction { let p = y >> 1; let q = y & 0b00000001; - let n = cpu.read_byte(cpu.register_pair(RegisterPair::PC) + 1); - let nn = cpu.read_word(cpu.register_pair(RegisterPair::PC) + 1); + let pc = cpu.register_pair(RegisterPair::PC); match (x, z, q, y, p) { (0, 0, _, 0, _) => Self::NOP, // NOP (0, 0, _, 1, _) => Self::LD( // LD (nn), SP - LDTarget::ByteAtAddress(nn), + LDTarget::ByteAtAddress(cpu.read_word(pc)), LDTarget::RegisterPair(RegisterPair::SP), ), (0, 0, _, 2, _) => Self::STOP, // STOP - (0, 0, _, 3, _) => Self::JR(JumpCondition::Always, n as i8), // JR d - (0, 0, _, 4..=7, _) => Self::JR(Table::cc(y - 4), n as i8), // JR cc[y - 4], d + (0, 0, _, 3, _) => Self::JR(JumpCondition::Always, cpu.read_byte(pc) as i8), // JR d + (0, 0, _, 4..=7, _) => Self::JR(Table::cc(y - 4), cpu.read_byte(pc) as i8), // JR cc[y - 4], d (0, 1, 0, _, _) => Self::LD( // LD rp[p], nn LDTarget::RegisterPair(Table::rp(p)), - LDTarget::ImmediateWord(nn), + LDTarget::ImmediateWord(cpu.read_word(pc)), ), (0, 1, 1, _, _) => Self::ADD( // ADD HL, rp[p] @@ -1717,7 +1706,7 @@ impl Instruction { (0, 6, _, _, _) => Self::LD( // LD r[y], n LDTarget::Register(Table::r(y)), - LDTarget::ImmediateByte(n), + LDTarget::ImmediateByte(cpu.read_byte(pc)), ), (0, 7, _, 0, _) => Self::RLCA, (0, 7, _, 1, _) => Self::RRCA, @@ -1737,22 +1726,22 @@ impl Instruction { (3, 0, _, 0..=3, _) => Self::RET(Table::cc(y)), // RET cc[y] (3, 0, _, 4, _) => Self::LD( // LD (0xFF00 + n), A - LDTarget::ByteAtAddressWithOffset(n), + LDTarget::ByteAtAddressWithOffset(cpu.read_byte(pc)), LDTarget::Register(InstrRegister::A), ), (3, 0, _, 5, _) => Self::ADD( // ADD SP, d MATHTarget::RegisterPair(RegisterPair::SP), - MATHTarget::ImmediateByte(n), + MATHTarget::ImmediateByte(cpu.read_byte(pc)), ), (3, 0, _, 6, _) => Self::LD( // LD A, (0xFF00 + n) LDTarget::Register(InstrRegister::A), - LDTarget::ByteAtAddressWithOffset(n), + LDTarget::ByteAtAddressWithOffset(cpu.read_byte(pc)), ), - (3, 0, _, 7, _) => Self::LDHL(n as i8), // LD HL, SP + d - (3, 1, 0, _, _) => Self::POP(Table::rp2(p)), // POP rp2[p] - (3, 1, 1, _, 0) => Self::RET(JumpCondition::Always), // RET + (3, 0, _, 7, _) => Self::LDHL(cpu.read_byte(pc) as i8), // LD HL, SP + d + (3, 1, 0, _, _) => Self::POP(Table::rp2(p)), // POP rp2[p] + (3, 1, 1, _, 0) => Self::RET(JumpCondition::Always), // RET (3, 1, 1, _, 1) => Self::RETI, (3, 1, 1, _, 2) => Self::JP( // JP HL @@ -1767,7 +1756,7 @@ impl Instruction { (3, 2, _, 0..=3, _) => Self::JP( // JP cc[y], nn Table::cc(y), - JPTarget::ImmediateWord(nn), + JPTarget::ImmediateWord(cpu.read_word(pc)), ), (3, 2, _, 4, _) => Self::LD( // LD (0xFF00 + C) ,A @@ -1776,7 +1765,7 @@ impl Instruction { ), (3, 2, _, 5, _) => Self::LD( // LD (nn), A - LDTarget::ByteAtAddress(nn), + LDTarget::ByteAtAddress(cpu.read_word(pc)), LDTarget::Register(InstrRegister::A), ), (3, 2, _, 6, _) => Self::LD( @@ -1787,12 +1776,12 @@ impl Instruction { (3, 2, _, 7, _) => Self::LD( // LD A, (nn) LDTarget::Register(InstrRegister::A), - LDTarget::ByteAtAddress(nn), + LDTarget::ByteAtAddress(cpu.read_word(pc)), ), (3, 3, _, 0, _) => Self::JP( // JP nn JumpCondition::Always, - JPTarget::ImmediateWord(nn), + JPTarget::ImmediateWord(cpu.read_word(pc)), ), (3, 3, _, 1, _) => unreachable!("This is the 0xCB Prefix"), // (3, 3, _, 2, _) => unreachable!(), ("removed" in documentation) @@ -1801,12 +1790,12 @@ impl Instruction { // (3, 3, _, 5, _) => unreachable!(), ("removed" in documentation) (3, 3, _, 6, _) => Self::DI, (3, 3, _, 7, _) => Self::EI, - (3, 4, _, 0..=3, _) => Self::CALL(Table::cc(y), nn), // CALL cc[y], nn + (3, 4, _, 0..=3, _) => Self::CALL(Table::cc(y), cpu.read_word(pc)), // CALL cc[y], nn // (3, 4, _, 4..=7, _) => unreachable!(), ("removed" in documentation) (3, 5, 0, _, _) => Self::PUSH(Table::rp2(p)), // PUSH rp2[p] - (3, 5, 1, _, 0) => Self::CALL(JumpCondition::Always, nn), // CALL nn + (3, 5, 1, _, 0) => Self::CALL(JumpCondition::Always, cpu.read_word(pc)), // CALL nn // (3, 5, 1, _, 1..=3) => unreachable!(), ("removed" in documentation) - (3, 6, _, _, _) => Table::x3_alu(y, n), + (3, 6, _, _, _) => Table::x3_alu(y, cpu.read_byte(pc)), (3, 7, _, _, _) => Self::RST(y * 8), // RST n _ => panic!( "Unknown Opcode: {:#x?}\n x: {}, z: {}, q: {}, y: {}, p: {}", @@ -1815,8 +1804,9 @@ impl Instruction { } } - fn from_prefixed_byte(cpu: &Cpu) -> Self { - let opcode = cpu.read_byte(cpu.register_pair(RegisterPair::PC) + 1); + fn from_prefixed_byte(cpu: &mut Cpu) -> Self { + let pc = cpu.register_pair(RegisterPair::PC); + let opcode = cpu.read_byte(pc); // FIXME: Should the PC be incremented here? // https://gb-archive.github.io/salvage/decoding_gbz80_opcodes/Decoding%20Gamboy%20Z80%20Opcodes.html let x = (opcode >> 6) & 0b00000011; diff --git a/src/lib.rs b/src/lib.rs index 2b822e6..ddc88db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ -mod cpu; mod bus; -mod instruction; \ No newline at end of file +pub mod cpu; +mod instruction; diff --git a/src/main.rs b/src/main.rs index 47ad8c6..7b27a37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,18 @@ +use gb::cpu::Cpu as LR35902; + fn main() { - println!("Hello World!"); + let mut game_boy = LR35902::new(); + + 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?}", + pc, opcode, instruction + ); + + game_boy.execute(instruction); + } }