feat: implement fetch, decode, execute loop

This commit is contained in:
Rekai Nyangadzayi Musuka 2020-12-23 03:25:16 -06:00
parent e540c86c7e
commit 4d2e0e33f2
6 changed files with 120 additions and 77 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
/target
/.idea
/.vscode
/bin

View File

@ -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!()
}
}

View File

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

View File

@ -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,20 +1726,20 @@ 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, 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,
@ -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;

View File

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

View File

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