diff --git a/src/bus.rs b/src/bus.rs index 7d3f9ef..be78019 100644 --- a/src/bus.rs +++ b/src/bus.rs @@ -66,6 +66,10 @@ impl Bus { self.cartridge.as_ref()?.title() } + pub(crate) fn boot_mapped(&self) -> bool { + self.boot.is_some() + } + pub(crate) fn clock(&mut self) { self.ppu.clock(); self.timer.clock(); diff --git a/src/cpu.rs b/src/cpu.rs index ad15da7..5523f30 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,6 +1,7 @@ use crate::apu::Apu; use crate::bus::{Bus, BusIo}; -use crate::instruction::{Cycle, Instruction}; +use crate::instruction::cycle::Cycle; +use crate::instruction::Instruction; use crate::interrupt::{InterruptEnable, InterruptFlag}; use crate::joypad::Joypad; use crate::ppu::Ppu; @@ -82,8 +83,24 @@ impl Cpu { self.bus.read_byte(self.reg.pc) } + pub(crate) fn imm_byte(&mut self) -> u8 { + let byte = self.bus.read_byte(self.reg.pc); + self.reg.pc += 1; + byte + } + + pub(crate) fn imm_word(&mut self) -> u16 { + let word = self.bus.read_word(self.reg.pc); + self.reg.pc += 2; + word + } + pub(crate) fn decode(&mut self, opcode: u8) -> Instruction { - Instruction::from_byte(self, opcode) + if opcode == 0xCB { + Instruction::decode(self.imm_byte(), true) + } else { + Instruction::decode(opcode, false) + } } fn execute(&mut self, instruction: Instruction) -> Cycle { @@ -91,6 +108,13 @@ impl Cpu { } pub fn step(&mut self) -> Cycle { + // Log instructions + // if !self.bus.boot_mapped() { + // let out = std::io::stdout(); + // let _ = self._log_state(out.lock()); + // } + + // FIXME: The Halt instruction takes less cycles than it should in Blargg's 2nd cpu_instrs test let cycles = match self.halted() { Some(state) => { use HaltState::*; @@ -105,10 +129,6 @@ impl Cpu { self.inc_pc(); let instr = self.decode(opcode); - - // let out = std::io::stdout(); - // let _ = self._debug_log(out.lock(), &instr); - let cycles = self.execute(instr); self.check_ime(); @@ -122,6 +142,7 @@ impl Cpu { self.bus.clock(); } + // TODO: This is in the wrong place self.handle_interrupts(); cycles @@ -139,17 +160,6 @@ impl BusIo for Cpu { } impl Cpu { - pub(crate) fn read_imm_byte(&mut self, addr: u16) -> u8 { - self.inc_pc(); // NB: the addr read in the line below will be equal to PC - 1 after this function call - self.bus.read_byte(addr) - } - - pub(crate) fn read_imm_word(&mut self, addr: u16) -> u16 { - self.inc_pc(); - self.inc_pc(); // NB: the addr read in the line below will be equal to PC - 2 after this function call - self.bus.read_word(addr) - } - pub(crate) fn write_word(&mut self, addr: u16, word: u16) { self.bus.write_word(addr, word) } @@ -340,6 +350,13 @@ impl Cpu { &self.flags } + pub(crate) fn update_flags(&mut self, z: bool, n: bool, h: bool, c: bool) { + self.flags.set_z(z); + self.flags.set_n(n); + self.flags.set_h(h); + self.flags.set_c(c); + } + pub(crate) fn set_flags(&mut self, flags: Flags) { self.flags = flags; } @@ -347,8 +364,9 @@ impl Cpu { impl Cpu { fn _debug_log(&self, mut w: impl std::io::Write, instr: &Instruction) -> std::io::Result<()> { + let pc = self.reg.pc - 1; write!(w, "A: {:02X} ", self.reg.a)?; - write!(w, "F: {:02X} ", u8::from(self.flags))?; + write!(w, "F: {:04b} ", u8::from(self.flags) >> 4)?; write!(w, "B: {:02X} ", self.reg.b)?; write!(w, "C: {:02X} ", self.reg.c)?; write!(w, "D: {:02X} ", self.reg.d)?; @@ -356,11 +374,11 @@ impl Cpu { write!(w, "H: {:02X} ", self.reg.h)?; write!(w, "L: {:02X} ", self.reg.l)?; write!(w, "SP: {:04X} ", self.reg.sp)?; - write!(w, "PC: 00:{:04X} ", self.reg.pc)?; - write!(w, "({:02X} ", self.read_byte(self.reg.pc))?; - write!(w, "{:02X} ", self.read_byte(self.reg.pc + 1))?; - write!(w, "{:02X} ", self.read_byte(self.reg.pc + 2))?; - write!(w, "{:02X}) ", self.read_byte(self.reg.pc + 3))?; + write!(w, "PC: 00:{:04X} ", pc)?; + write!(w, "({:02X} ", self.read_byte(pc))?; + write!(w, "{:02X} ", self.read_byte(pc + 1))?; + write!(w, "{:02X} ", self.read_byte(pc + 2))?; + write!(w, "{:02X}) ", self.read_byte(pc + 3))?; writeln!(w, "| {:?}", instr)?; w.flush() } diff --git a/src/emu.rs b/src/emu.rs index 5d87195..8465067 100644 --- a/src/emu.rs +++ b/src/emu.rs @@ -1,5 +1,5 @@ use crate::cpu::Cpu as SM83; -use crate::instruction::Cycle; +use crate::instruction::cycle::Cycle; use crate::joypad; use crate::ppu::Ppu; use anyhow::Result; diff --git a/src/instruction.rs b/src/instruction.rs index 5d622e1..a803305 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -1,17 +1,26 @@ +use self::add::{Source as AddSource, Target as AddTarget}; +use self::alu::Source as AluSource; +use self::cycle::Cycle; +use self::jump::{JumpCondition, JumpLocation}; +use self::load::{Source as LDSource, Target as LDTarget}; +use self::table::{ + alu_imm_instr, alu_reg_instr, flag_instr, group1, group2, group3, jump_cond, prefix_alu, + register, +}; +use self::table::{Group1RegisterPair, Group2RegisterPair, Group3RegisterPair, Register}; use crate::bus::BusIo; -use crate::cpu::{Cpu, Flags, HaltState, ImeState, Register, RegisterPair}; -use std::{convert::TryFrom, fmt::Debug}; +use crate::cpu::{Cpu, Flags, HaltState, ImeState, Register as CpuRegister, RegisterPair}; -#[derive(Clone, Copy)] #[allow(clippy::upper_case_acronyms)] pub(crate) enum Instruction { NOP, - LD(LDTarget, LDTarget), STOP, - JR(JumpCondition, i8), - ADD(MATHTarget, MATHTarget), - INC(Registers), - DEC(Registers), + JR(JumpCondition), + LD(LDTarget, LDSource), + ADD(AddTarget, AddSource), + LDHL, + INC(AllRegisters), + DEC(AllRegisters), RLCA, RRCA, RLA, @@ -21,222 +30,194 @@ pub(crate) enum Instruction { SCF, CCF, HALT, - ADC(MATHTarget), // ADC A, MATHTarget - SUB(MATHTarget), // SUB A, MATHTarget - SBC(MATHTarget), - AND(MATHTarget), // AND A, MATHTarget - XOR(MATHTarget), // XOR A, MATHTarget - OR(MATHTarget), // OR A, MATHTarget - CP(MATHTarget), // CP A, MATHTarget + ADC(AluSource), + SUB(AluSource), + SBC(AluSource), + AND(AluSource), + XOR(AluSource), + OR(AluSource), + CP(AluSource), RET(JumpCondition), - LDHL(i8), // LD HL, SP + d - POP(RegisterPair), + POP(Group3RegisterPair), RETI, - JP(JumpCondition, JPTarget), + JP(JumpCondition, JumpLocation), DI, EI, - CALL(JumpCondition, u16), - PUSH(RegisterPair), + CALL(JumpCondition), + PUSH(Group3RegisterPair), RST(u8), - RLC(InstrRegister), - RRC(InstrRegister), - RL(InstrRegister), - RR(InstrRegister), - SLA(InstrRegister), - SRA(InstrRegister), - SWAP(InstrRegister), - SRL(InstrRegister), - BIT(u8, InstrRegister), - RES(u8, InstrRegister), - SET(u8, InstrRegister), + RLC(Register), + RRC(Register), + RL(Register), + RR(Register), + SLA(Register), + SRA(Register), + SWAP(Register), + SRL(Register), + BIT(u8, Register), + RES(u8, Register), + SET(u8, Register), } -#[derive(Clone, Copy)] -pub(crate) enum JPTarget { - RegisterPair(RegisterPair), - ImmediateWord(u16), +impl std::fmt::Debug for Instruction { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Instruction::*; + + match self { + NOP => f.write_str("NOP"), + STOP => f.write_str("STOP"), + JR(c) => write!(f, "JR {:?} i8", c), + LD(t, s) => write!(f, "LD {:?}, {:?}", t, s), + ADD(t, s) => write!(f, "ADD {:?}, {:?}", t, s), + LDHL => f.write_str("LD HL, SP + i8"), + INC(rs) => write!(f, "INC {:?}", rs), + DEC(rs) => write!(f, "DEC {:?}", rs), + RLCA => f.write_str("RLCA"), + RRCA => f.write_str("RRCA"), + RLA => f.write_str("RLA"), + RRA => f.write_str("RRA"), + DAA => f.write_str("DAA"), + CPL => f.write_str("CPL"), + SCF => f.write_str("SCF"), + CCF => f.write_str("CCF"), + HALT => f.write_str("HALT"), + ADC(s) => write!(f, "ADC {:?}", s), + SUB(s) => write!(f, "SUB {:?}", s), + SBC(s) => write!(f, "SBC {:?}", s), + AND(s) => write!(f, "AND {:?}", s), + XOR(s) => write!(f, "XOR {:?}", s), + OR(s) => write!(f, "OR {:?}", s), + CP(s) => write!(f, "CP {:?}", s), + RET(c) => write!(f, "RET {:?}", c), + POP(rp) => write!(f, "POP: {:?}", rp), + RETI => f.write_str("RETI"), + JP(c, l) => write!(f, "JP {:?} {:?}", c, l), + DI => f.write_str("DI"), + EI => f.write_str("EI"), + CALL(c) => write!(f, "CALL {:?}", c), + PUSH(rp) => write!(f, "PUSH {:?}", rp), + RST(v) => write!(f, "RST {:#04X}", v), + RLC(r) => write!(f, "RLC {:?}", r), + RRC(r) => write!(f, "RRC {:?}", r), + RL(r) => write!(f, "RL {:?}", r), + RR(r) => write!(f, "RR {:?}", r), + SLA(r) => write!(f, "SLA {:?}", r), + SRA(r) => write!(f, "SRA {:?}", r), + SWAP(r) => write!(f, "SWAP {:?}", r), + SRL(r) => write!(f, "SRL {:?}", r), + BIT(b, r) => write!(f, "BIT {}, {:?}", b, r), + RES(b, r) => write!(f, "RES {}, {:?}", b, r), + SET(b, r) => write!(f, "SET {}, {:?}", b, r), + } + } } -#[derive(Clone, Copy)] -pub(crate) enum Registers { - Byte(InstrRegister), - Word(RegisterPair), -} - -#[derive(Clone, Copy)] -pub(crate) enum MATHTarget { - Register(InstrRegister), - RegisterPair(RegisterPair), - ImmediateByte(u8), -} - -#[derive(Clone, Copy)] -pub(crate) enum LDTarget { - IndirectC, - Register(InstrRegister), - IndirectRegister(InstrRegisterPair), - ByteAtAddress(u16), - ImmediateWord(u16), - ImmediateByte(u8), - RegisterPair(RegisterPair), - ByteAtAddressWithOffset(u8), -} - -#[derive(Clone, Copy)] -pub(crate) enum InstrRegisterPair { - AF, - BC, - DE, - HL, - SP, - PC, - IncrementHL, - DecrementHL, -} - -#[derive(Clone, Copy)] -pub(crate) enum InstrRegister { - A, - B, - C, - D, - E, - H, - L, - IndirectHL, // (HL) -} - -#[derive(Clone, Copy)] -pub(crate) enum JumpCondition { - NotZero, - Zero, - NotCarry, - Carry, - Always, -} - -#[derive(Debug)] -struct Table; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] -#[repr(transparent)] -pub struct Cycle(u32); - impl Instruction { pub(crate) fn execute(cpu: &mut Cpu, instruction: Self) -> Cycle { match instruction { Instruction::NOP => Cycle::new(4), - Instruction::LD(lhs, rhs) => match (lhs, rhs) { - (LDTarget::ByteAtAddress(nn), LDTarget::RegisterPair(RegisterPair::SP)) => { - // LD (nn), SP | Put Stack Pointer at address nn - cpu.write_word(nn, cpu.register_pair(RegisterPair::SP)); + Instruction::LD(target, src) => match (target, src) { + (LDTarget::IndirectImmediateWord, LDSource::SP) => { + // LD (u16), SP | Store stack pointer in byte at 16-bit register + let word = cpu.imm_word(); + cpu.write_word(word, cpu.register_pair(RegisterPair::SP)); Cycle::new(20) } - (LDTarget::RegisterPair(pair), LDTarget::ImmediateWord(nn)) => { - // LD rp[p], nn | Put value nn into register pair - use RegisterPair::*; + (LDTarget::Group1(pair), LDSource::ImmediateWord) => { + // LD r16, u16 | Store u16 in 16-bit register + use Group1RegisterPair::*; + let word = cpu.imm_word(); match pair { - BC | DE | HL | SP => cpu.set_register_pair(pair, nn), - _ => unreachable!("There is no \"LD {:?}, nn\" instruction", pair), + BC | DE | HL | SP => cpu.set_register_pair(pair.as_register_pair(), word), } Cycle::new(12) } - (LDTarget::IndirectRegister(pair), LDTarget::Register(InstrRegister::A)) => { - let a = cpu.register(Register::A); - match pair { - InstrRegisterPair::BC | InstrRegisterPair::DE => { - // LD (BC), A | Put A into memory address BC - // LD (DE), A | Put A into memory address DE - let addr = cpu.register_pair(pair.to_register_pair()); - cpu.write_byte(addr, a); - } - InstrRegisterPair::IncrementHL => { - // LD (HL+), A | Put A into byte at address HL, then increment HL - let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, a); + (LDTarget::IndirectGroup2(pair), LDSource::A) => { + // LD (r16), A | Store accumulator in byte at 16-bit register + let acc = cpu.register(CpuRegister::A); + match pair { + Group2RegisterPair::BC | Group2RegisterPair::DE => { + let addr = cpu.register_pair(pair.as_register_pair()); + cpu.write_byte(addr, acc); + } + Group2RegisterPair::IncrementHL => { + let addr = cpu.register_pair(RegisterPair::HL); + cpu.write_byte(addr, acc); cpu.set_register_pair(RegisterPair::HL, addr + 1); } - InstrRegisterPair::DecrementHL => { - // LD (HL-), A | Put A into byte at address HL, then decrement HL + Group2RegisterPair::DecrementHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, a); - + cpu.write_byte(addr, acc); cpu.set_register_pair(RegisterPair::HL, addr - 1); } - _ => unreachable!("There is no \"LD ({:?}), A\" instruction", pair), } Cycle::new(8) } - (LDTarget::Register(InstrRegister::A), LDTarget::IndirectRegister(pair)) => { + (LDTarget::A, LDSource::IndirectGroup2(pair)) => { + // LD A, (r16) | Store byte at 16-bit register in accumulator match pair { - InstrRegisterPair::BC | InstrRegisterPair::DE => { - // 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(pair.to_register_pair()); + Group2RegisterPair::BC | Group2RegisterPair::DE => { + let addr = cpu.register_pair(pair.as_register_pair()); let byte = cpu.read_byte(addr); - cpu.set_register(Register::A, byte); + cpu.set_register(CpuRegister::A, byte); } - InstrRegisterPair::IncrementHL => { - // LD A, (HL+) | Put value at address HL into A, then increment HL + Group2RegisterPair::IncrementHL => { let addr = cpu.register_pair(RegisterPair::HL); let byte = cpu.read_byte(addr); - - cpu.set_register(Register::A, byte); + cpu.set_register(CpuRegister::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 + Group2RegisterPair::DecrementHL => { let addr = cpu.register_pair(RegisterPair::HL); let byte = cpu.read_byte(addr); - cpu.set_register(Register::A, byte); - + cpu.set_register(CpuRegister::A, byte); cpu.set_register_pair(RegisterPair::HL, addr - 1); } - _ => unreachable!("There is no \"LD A, ({:?})\" instruction", pair), } Cycle::new(8) } - (LDTarget::Register(reg), LDTarget::ImmediateByte(n)) => { - // LD r[y], n | Store n in Register - use InstrRegister::*; + (LDTarget::Register(reg), LDSource::ImmediateByte) => { + // LD r8, u8 | Store u8 in 8-bit register + use Register::*; + let right = cpu.imm_byte(); match reg { A | B | C | D | E | H | L => { - cpu.set_register(reg.to_register(), n); + cpu.set_register(reg.cpu_register(), right); Cycle::new(8) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - cpu.write_byte(addr, n); + cpu.write_byte(addr, right); Cycle::new(12) } } } - (LDTarget::IndirectC, LDTarget::Register(InstrRegister::A)) => { - // LD (0xFF00 + C), A | Store value of register A at address 0xFF00 + C - let addr = 0xFF00 + cpu.register(Register::C) as u16; - cpu.write_byte(addr, cpu.register(Register::A)); + (LDTarget::IoWithC, LDSource::A) => { + // LD (0xFF00 + C), A | Store accumulator in byte at address 0xFF00 + C + let addr = 0xFF00 + cpu.register(CpuRegister::C) as u16; + cpu.write_byte(addr, cpu.register(CpuRegister::A)); Cycle::new(8) } - (LDTarget::Register(InstrRegister::A), LDTarget::IndirectC) => { - let addr = 0xFF00 + cpu.register(Register::C) as u16; - let byte = cpu.read_byte(addr); - cpu.set_register(Register::A, byte); + (LDTarget::A, LDSource::IoWithC) => { + // LD A, (0xFF00 + C) | Store byte at 0xFF00 + C in register A + let byte = cpu.read_byte(0xFF00 + cpu.register(CpuRegister::C) as u16); + cpu.set_register(CpuRegister::A, byte); Cycle::new(8) } - (LDTarget::Register(lhs), LDTarget::Register(rhs)) => { - // LD r[y], r[z] | Store value of RHS Register in LHS Register - use InstrRegister::*; + (LDTarget::Register(target), LDSource::Register(source)) => { + // LD r8, r8 | Store 8-bit register in 8-bit register + use Register::*; - match rhs { + match source { B | C | D | E | H | L | A => { - let right = cpu.register(rhs.to_register()); + let right = cpu.register(source.cpu_register()); - match lhs { + match target { B | C | D | E | H | L | A => { - cpu.set_register(lhs.to_register(), right); + cpu.set_register(target.cpu_register(), right); Cycle::new(4) } IndirectHL => { @@ -250,65 +231,62 @@ impl Instruction { let addr = cpu.register_pair(RegisterPair::HL); let right = cpu.read_byte(addr); - match lhs { + match target { B | C | D | E | H | L | A => { - cpu.set_register(lhs.to_register(), right); + cpu.set_register(target.cpu_register(), right); Cycle::new(8) } IndirectHL => { - unreachable!( - "There is no \"LD ({:?}), ({:?})\" instruction", - lhs, rhs - ) + unreachable!("LD (HL), (HL) is an illegal instruction") } } } } } - (LDTarget::ByteAtAddressWithOffset(n), LDTarget::Register(InstrRegister::A)) => { - // LD (0xFF00 + n), A | Store register A at address (0xFF00 + n) - cpu.write_byte(0xFF00 + (n as u16), cpu.register(Register::A)); + (LDTarget::IoWithImmediateOffset, LDSource::A) => { + // LD (0xFF00 + u8), A | Store accumulator in byte at address 0xFF00 + u8 + let offset = cpu.imm_byte(); + cpu.write_byte(0xFF00 + (offset as u16), cpu.register(CpuRegister::A)); Cycle::new(12) } - (LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddressWithOffset(n)) => { - // LD A, (0xFF00 + n) | Store value at address (0xFF00 + n) in register A - let byte = cpu.read_byte(0xFF00 + (n as u16)); - cpu.set_register(Register::A, byte); + (LDTarget::A, LDSource::IoWithImmediateOffset) => { + // LD A, (0xFF00 + u8) | Store byte at address 0xFF00 + u8 in accumulator + let offset = cpu.imm_byte(); + let byte = cpu.read_byte(0xFF00 + (offset as u16)); + cpu.set_register(CpuRegister::A, byte); Cycle::new(12) } - ( - LDTarget::RegisterPair(RegisterPair::SP), - LDTarget::RegisterPair(RegisterPair::HL), - ) => { - // LD SP, HL | Load Register HL into Register SP + (LDTarget::SP, LDSource::HL) => { + // LD SP, HL | Store HL in stack pointer cpu.set_register_pair(RegisterPair::SP, cpu.register_pair(RegisterPair::HL)); Cycle::new(8) } - (LDTarget::ByteAtAddress(nn), LDTarget::Register(InstrRegister::A)) => { - cpu.write_byte(nn, cpu.register(Register::A)); + (LDTarget::IndirectImmediateWord, LDSource::A) => { + // LD (u16), A | Store accumulator in byte at 16-bit register + let word = cpu.imm_word(); + cpu.write_byte(word, cpu.register(CpuRegister::A)); Cycle::new(16) } - (LDTarget::Register(InstrRegister::A), LDTarget::ByteAtAddress(nn)) => { - let byte = cpu.read_byte(nn); - cpu.set_register(Register::A, byte); + (LDTarget::A, LDSource::IndirectImmediateWord) => { + // LD A, (u16) | Store byte at 16-bit register in accumulator + let addr = cpu.imm_word(); + let byte = cpu.read_byte(addr); + cpu.set_register(CpuRegister::A, byte); Cycle::new(16) } - _ => unreachable!("There is no \"LD {:?}, {:?}\" instruction", lhs, rhs), + _ => unreachable!("LD {:?}, {:?} is an illegal instruction", target, src), }, - Instruction::STOP => Cycle::new(4), - Instruction::JR(cond, offset) => { - // JR cc[y - 4], d | If condition is true, then add d to current address and jump - // JR d | Add d to current address and jump - let flags: &Flags = cpu.flags(); + Instruction::STOP => todo!("SM83 Instruction STOP executed"), + Instruction::JR(cond) => { + // JR cond i8 | Add i8 bytes from program counter if condition is true + // JR i8 | Add i8 bytes from program counter + let flags: Flags = *cpu.flags(); - let prev = cpu.register_pair(RegisterPair::PC); - let addr = prev.wrapping_add(offset as u16); + let byte = cpu.imm_byte(); // Note: This modifies the PC we access immediately after + let pc = cpu.register_pair(RegisterPair::PC); + let addr = pc.wrapping_add(byte as i8 as u16); match cond { - JumpCondition::Always => { - cpu.set_register_pair(RegisterPair::PC, addr); - Cycle::new(12) - } JumpCondition::NotZero => { if !flags.z() { cpu.set_register_pair(RegisterPair::PC, addr); @@ -341,115 +319,109 @@ impl Instruction { Cycle::new(8) } } + JumpCondition::Always => { + cpu.set_register_pair(RegisterPair::PC, addr); + Cycle::new(12) + } } } - Instruction::ADD(lhs, rhs) => match (lhs, rhs) { - (MATHTarget::RegisterPair(RegisterPair::HL), MATHTarget::RegisterPair(pair)) => { - // ADD HL, rp[p] | add register pair to HL. - use RegisterPair::*; - + Instruction::ADD(target, src) => match (target, src) { + (AddTarget::HL, AddSource::Group1(pair)) => { + // ADD HL, r16 | Add 16-bit register to HL + use Group1RegisterPair::*; let mut flags: Flags = *cpu.flags(); match pair { BC | DE | HL | SP => { - let hl_value = cpu.register_pair(RegisterPair::HL); - let value = cpu.register_pair(pair); - let sum = Self::add_u16(hl_value, value, &mut flags); - - cpu.set_register_pair(RegisterPair::HL, sum); + let left = cpu.register_pair(RegisterPair::HL); + let right = cpu.register_pair(pair.as_register_pair()); + cpu.set_register_pair( + RegisterPair::HL, + Self::add_u16(left, right, &mut flags), + ); } - _ => unreachable!("There is no \"ADD HL, {:?}\" instruction", pair), } cpu.set_flags(flags); Cycle::new(8) } - (MATHTarget::Register(InstrRegister::A), MATHTarget::Register(reg)) => { - // ADD A, r[z] | Add (A + r[z]) to register A - use InstrRegister::*; - + (AddTarget::A, AddSource::Register(reg)) => { + // ADD A, r8 | Add 8-bit register to accumulator + use Register::*; let mut flags: Flags = *cpu.flags(); - let a_value = cpu.register(Register::A); + + let left = cpu.register(CpuRegister::A); let (cycles, sum) = match reg { B | C | D | E | H | L | A => { - let value = cpu.register(reg.to_register()); - - let sum = Self::add(a_value, value, &mut flags); - (Cycle::new(4), sum) + let right = cpu.register(reg.cpu_register()); + (Cycle::new(4), Self::add(left, right, &mut flags)) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let sum = Self::add(a_value, value, &mut flags); - (Cycle::new(8), sum) + let right = cpu.read_byte(addr); + (Cycle::new(8), Self::add(left, right, &mut flags)) } }; - cpu.set_register(Register::A, sum); + cpu.set_register(CpuRegister::A, sum); cpu.set_flags(flags); cycles } - (MATHTarget::RegisterPair(RegisterPair::SP), MATHTarget::ImmediateByte(d)) => { - // ADD SP, d | Add d (is signed) to register pair SP. + (AddTarget::SP, AddSource::ImmediateSignedByte) => { + // ADD SP, i8 | Add i8 to stack pointer let mut flags: Flags = *cpu.flags(); - let d = d as i8; - let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags); - - cpu.set_flags(flags); + let left = cpu.register_pair(RegisterPair::SP); + let sum = Self::add_u16_i8(left, cpu.imm_byte() as i8, &mut flags); cpu.set_register_pair(RegisterPair::SP, sum); + cpu.set_flags(flags); Cycle::new(16) } - (MATHTarget::Register(InstrRegister::A), MATHTarget::ImmediateByte(n)) => { - // ADD A, n | Add n to register A + (AddTarget::A, AddSource::ImmediateByte) => { + // ADD A, u8 | Add u8 to accumulator let mut flags: Flags = *cpu.flags(); - let sum = Self::add(cpu.register(Register::A), n, &mut flags); - cpu.set_register(Register::A, sum); + let left = cpu.register(CpuRegister::A); + let sum = Self::add(left, cpu.imm_byte(), &mut flags); + cpu.set_register(CpuRegister::A, sum); cpu.set_flags(flags); Cycle::new(8) } - _ => unreachable!("There is no \"ADD {:?}, {:?}\" instruction", lhs, rhs), + _ => unreachable!("ADD {:?}, {:?} is an illegal instruction", target, src), }, Instruction::INC(registers) => { match registers { - Registers::Byte(reg) => { - // INC r[y] | Increment Register - use InstrRegister::*; - + AllRegisters::Register(reg) => { + // INC r8 | Increment 8-bit register + use Register::*; let mut flags: Flags = *cpu.flags(); let cycles = match reg { B | C | D | E | H | L | A => { - let reg = reg.to_register(); - - let value = cpu.register(reg); - cpu.set_register(reg, Self::inc_register(value, &mut flags)); + let reg = reg.cpu_register(); + cpu.set_register(reg, Self::inc(cpu.register(reg), &mut flags)); Cycle::new(4) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = Self::inc_register(cpu.read_byte(addr), &mut flags); - cpu.write_byte(addr, byte); + cpu.write_byte(addr, Self::inc(cpu.read_byte(addr), &mut flags)); Cycle::new(12) } }; - cpu.set_flags(flags); cycles } - Registers::Word(pair) => { - // INC rp[p] | Increment Register Pair - // Note: According to RGBDS, no flags are set here. - use RegisterPair::*; + AllRegisters::Group1(pair) => { + // INC r16 | Increment 16-bit register + // Note: No flags are set with this version of the INC instruction + use Group1RegisterPair::*; match pair { BC | DE | HL | SP => { - let value = cpu.register_pair(pair); - cpu.set_register_pair(pair, value.wrapping_add(1)); + let pair = pair.as_register_pair(); + let left = cpu.register_pair(pair); + cpu.set_register_pair(pair, left.wrapping_add(1)); } - _ => unreachable!("There is no \"INC {:?}\" instruction", pair), } Cycle::new(8) } @@ -457,189 +429,176 @@ impl Instruction { } Instruction::DEC(registers) => { match registers { - Registers::Byte(reg) => { - // DEC r[y] | Decrement Register - use InstrRegister::*; - + AllRegisters::Register(reg) => { + // DEC r8 | Decrement 8-bit register + use Register::*; let mut flags: Flags = *cpu.flags(); let cycles = match reg { B | C | D | E | H | L | A => { - let reg = reg.to_register(); - - let value = cpu.register(reg); - cpu.set_register(reg, Self::dec_register(value, &mut flags)); + let reg = reg.cpu_register(); + cpu.set_register(reg, Self::dec(cpu.register(reg), &mut flags)); Cycle::new(4) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let byte = cpu.read_byte(addr); - cpu.write_byte(addr, Self::dec_register(byte, &mut flags)); + cpu.write_byte(addr, Self::dec(cpu.read_byte(addr), &mut flags)); Cycle::new(12) } }; - cpu.set_flags(flags); cycles } - Registers::Word(pair) => { - // DEC rp[p] | Decrement Register Pair - use RegisterPair::*; + AllRegisters::Group1(pair) => { + // DEC r16 | Decrement Register Pair + use Group1RegisterPair::*; match pair { BC | DE | HL | SP => { - let value = cpu.register_pair(pair); - cpu.set_register_pair(pair, value.wrapping_sub(1)); + let pair = pair.as_register_pair(); + let left = cpu.register_pair(pair); + cpu.set_register_pair(pair, left.wrapping_sub(1)); } - _ => unreachable!("There is no \"DEC {:?}\" instruction", pair), }; Cycle::new(8) } } } Instruction::RLCA => { - // Rotate Register A left - let mut flags: Flags = *cpu.flags(); - - let a = cpu.register(Register::A); - let msb = a >> 7; - let rot_a = a.rotate_left(1); - - flags.update(false, false, false, msb == 0x01); - cpu.set_flags(flags); - cpu.set_register(Register::A, rot_a); + // RLCA | Rotate accumulator left + let acc = cpu.register(CpuRegister::A); + let most_sgfnt = acc >> 7; + let acc_rotated = acc.rotate_left(1); + cpu.set_register(CpuRegister::A, acc_rotated); + cpu.update_flags(false, false, false, most_sgfnt == 0x01); Cycle::new(4) } Instruction::RRCA => { - // Rotate Register A right - let mut flags: Flags = *cpu.flags(); - - let a = cpu.register(Register::A); - let lsb = a & 0x01; - let rot_a = a.rotate_right(1); - - flags.update(false, false, false, lsb == 0x01); - cpu.set_flags(flags); - cpu.set_register(Register::A, rot_a); + // RRCA | Rotate accumulator right + let acc = cpu.register(CpuRegister::A); + let least_sgfnt = acc & 0x01; + let acc_rotated = acc.rotate_right(1); + cpu.set_register(CpuRegister::A, acc_rotated); + cpu.update_flags(false, false, false, least_sgfnt == 0x01); Cycle::new(4) } Instruction::RLA => { - // Rotate register A left through carry - let mut flags: Flags = *cpu.flags(); + // RLA | Rotate accumulator left through carry + let flags: Flags = *cpu.flags(); - let a = cpu.register(Register::A); - let (rot_a, carry) = Self::rl_thru_carry(a, flags.c()); - - flags.update(false, false, false, carry); - cpu.set_flags(flags); - cpu.set_register(Register::A, rot_a); + let acc = cpu.register(CpuRegister::A); + let (acc_rotated, carry) = Self::rl_thru_carry(acc, flags.c()); + cpu.set_register(CpuRegister::A, acc_rotated); + cpu.update_flags(false, false, false, carry); Cycle::new(4) } Instruction::RRA => { - // Rotate register A right through carry - let mut flags: Flags = *cpu.flags(); + // RRA | Rotate accumulator right through carry + let flags: Flags = *cpu.flags(); - let a = cpu.register(Register::A); - let (rot_a, carry) = Self::rr_thru_carry(a, flags.c()); - - flags.update(false, false, false, carry); - cpu.set_flags(flags); - cpu.set_register(Register::A, rot_a); + let acc = cpu.register(CpuRegister::A); + let (acc_rotated, carry) = Self::rr_thru_carry(acc, flags.c()); + cpu.set_register(CpuRegister::A, acc_rotated); + cpu.update_flags(false, false, false, carry); Cycle::new(4) } Instruction::DAA => { - // source: https://ehaskins.com/2018-01-30%20Z80%20DAA/ - // TODO: Maybe i16 isn't the right choice here? - - let mut correction: i16 = 0; - let mut value = cpu.register(Register::A) as i16; + // DAA | Change accumulator into its BCD representation + // resource: https://ehaskins.com/2018-01-30%20Z80%20DAA/ + // https://github.com/mamedev/mame/blob/master/src/devices/cpu/lr35902/opc_main.hxx#L354 let mut flags = *cpu.flags(); + let mut tmp = cpu.register(CpuRegister::A) as i16; - if flags.h() || (!flags.n() && (value & 0xF) > 9) { - correction |= 0x06; + if !flags.n() { + // Positive + if flags.h() || tmp & 0x0F > 0x09 { + tmp += 0x06; + } + + if flags.c() || tmp > 0x9F { + tmp += 0x60; + } + } else { + // Negative + if flags.h() { + tmp -= 6; + + if !flags.c() { + tmp &= 0xFF; + } + } + + if flags.c() { + tmp -= 0x60; + } } - if flags.c() || (!flags.n() && value > 0x99) { - correction |= 0x60; + if tmp & 0x100 != 0 { flags.set_c(true); } - value += if flags.n() { -correction } else { correction }; - let result = value as u8; - - flags.set_z(result == 0); + cpu.set_register(CpuRegister::A, tmp as u8); + flags.set_z(tmp as u8 == 0); flags.set_h(false); - cpu.set_flags(flags); - cpu.set_register(Register::A, value as u8); - Cycle::new(4) } Instruction::CPL => { - // Compliment A register (inverse) + // CPL | Compliment accumulator let mut flags: Flags = *cpu.flags(); - let a = cpu.register(Register::A); + let acc = cpu.register(CpuRegister::A); + cpu.set_register(CpuRegister::A, !acc); // Bitwise not is ! instead of ~ flags.set_n(true); flags.set_h(true); - cpu.set_flags(flags); - cpu.set_register(Register::A, !a); // Bitwise not is ! instead of ~ Cycle::new(4) } Instruction::SCF => { - // Set Carry Flag + // SCF | Set Carry Flag let mut flags: Flags = *cpu.flags(); flags.set_n(false); flags.set_h(false); flags.set_c(true); - cpu.set_flags(flags); Cycle::new(4) } Instruction::CCF => { - // Compliment Carry Flag (inverse) + // CCF | Compliment Carry Flag let mut flags: Flags = *cpu.flags(); flags.set_n(false); flags.set_h(false); flags.set_c(!flags.c()); - cpu.set_flags(flags); Cycle::new(4) } Instruction::HALT => { - // Enter CPU low power consumption mode until interrupt occurs + // HALT | Enter CPU low power consumption mode until interrupt occurs use HaltState::*; - let req = cpu.read_byte(0xFF0F); let enabled = cpu.read_byte(0xFFFF); - let halt_state = if let ImeState::Enabled = cpu.ime() { - ImeEnabled - } else if req & enabled != 0 { - SomePending - } else { - NonePending + let halt_state = match *cpu.ime() { + ImeState::Enabled => ImeEnabled, + _ if req & enabled != 0 => SomePending, + _ => NonePending, }; - cpu.halt(halt_state); - - // Though this can actually last forever Cycle::new(4) } - Instruction::ADC(target) => match target { - MATHTarget::Register(reg) => { - // ADC A, r[z] | Add register r[z] plus the Carry flag to A - use InstrRegister::*; - + Instruction::ADC(source) => match source { + AluSource::Register(reg) => { + // ADC A, r8 | Add 8-bit register to accumulator with carry + use Register::*; let mut flags: Flags = *cpu.flags(); - let left = cpu.register(Register::A); + + let left = cpu.register(CpuRegister::A); let (cycles, sum) = match reg { B | C | D | E | H | L | A => { - let right = cpu.register(reg.to_register()); + let right = cpu.register(reg.cpu_register()); let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); (Cycle::new(4), sum) } @@ -649,244 +608,216 @@ impl Instruction { (Cycle::new(8), sum) } }; - - cpu.set_register(Register::A, sum); + cpu.set_register(CpuRegister::A, sum); cpu.set_flags(flags); cycles } - MATHTarget::ImmediateByte(n) => { - // ADC A, n | Add immediate byte plus the carry flag to A + AluSource::ImmediateByte => { + // ADC A, u8 | Add u8 to accumulator with carry let mut flags: Flags = *cpu.flags(); - let value = cpu.register(Register::A); - - let sum = Self::add_with_carry_bit(value, n, flags.c(), &mut flags); + let left = cpu.register(CpuRegister::A); + let right = cpu.imm_byte(); + let sum = Self::add_with_carry_bit(left, right, flags.c(), &mut flags); + cpu.set_register(CpuRegister::A, sum); cpu.set_flags(flags); - cpu.set_register(Register::A, sum); Cycle::new(8) } - _ => unreachable!("There is no \"ADC {:?}\" instruction", target), }, - Instruction::SUB(target) => match target { - MATHTarget::Register(reg) => { - // SUB r[z] | Subtract the value in register r[z] from register A, then store in A - use InstrRegister::*; - + Instruction::SUB(source) => match source { + AluSource::Register(reg) => { + // SUB r8 | Subtract 8-bit register from accumulator + use Register::*; let mut flags: Flags = *cpu.flags(); - let a_value = cpu.register(Register::A); + + let left = cpu.register(CpuRegister::A); let (cycles, diff) = match reg { B | C | D | E | H | L | A => { - let value = cpu.register(reg.to_register()); - let diff = Self::sub(a_value, value, &mut flags); - (Cycle::new(4), diff) + let right = cpu.register(reg.cpu_register()); + (Cycle::new(4), Self::sub(left, right, &mut flags)) } IndirectHL => { - let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); - let diff = Self::sub(a_value, value, &mut flags); - (Cycle::new(8), diff) + let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + (Cycle::new(8), Self::sub(left, right, &mut flags)) } }; - - cpu.set_register(Register::A, diff); + cpu.set_register(CpuRegister::A, diff); cpu.set_flags(flags); cycles } - MATHTarget::ImmediateByte(n) => { - // SUB n | Subtract the immediate byte from register A, then store in A + AluSource::ImmediateByte => { + // SUB u8 | Subtract u8 from accumulator let mut flags: Flags = *cpu.flags(); - let diff = Self::sub(cpu.register(Register::A), n, &mut flags); + let left = cpu.register(CpuRegister::A); + let right = cpu.imm_byte(); + cpu.set_register(CpuRegister::A, Self::sub(left, right, &mut flags)); cpu.set_flags(flags); - cpu.set_register(Register::A, diff); Cycle::new(8) } - _ => unreachable!("There is no \"SUB {:?}\" instruction", target), }, Instruction::SBC(target) => match target { - MATHTarget::Register(reg) => { - // SBC A, r[z] | Subtract the value from register r[z] from A, add the Carry flag and then store in A - use InstrRegister::*; - + AluSource::Register(reg) => { + // SBC r8 | Subtract 8-bit register from accumulator with carry + use Register::*; let mut flags: Flags = *cpu.flags(); - let left = cpu.register(Register::A); + + let left = cpu.register(CpuRegister::A); let (cycles, diff) = match reg { B | C | D | E | H | L | A => { - let right = cpu.register(reg.to_register()); + let right = cpu.register(reg.cpu_register()); let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); (Cycle::new(4), diff) } IndirectHL => { let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); let diff = Self::sub_with_carry(left, right, flags.c(), &mut flags); - (Cycle::new(8), diff) } }; - - cpu.set_register(Register::A, diff); + cpu.set_register(CpuRegister::A, diff); cpu.set_flags(flags); cycles } - MATHTarget::ImmediateByte(n) => { - // SBC A, n | Subtract the value from immediate byte from A, add the carry flag and then store in A + AluSource::ImmediateByte => { + // SBC u8 | Subtract u8 from accumulator with carry let mut flags: Flags = *cpu.flags(); - let value = cpu.register(Register::A); - - let diff = Self::sub_with_carry(value, n, flags.c(), &mut flags); + let left = cpu.register(CpuRegister::A); + let diff = Self::sub_with_carry(left, cpu.imm_byte(), flags.c(), &mut flags); + cpu.set_register(CpuRegister::A, diff); cpu.set_flags(flags); - cpu.set_register(Register::A, diff); Cycle::new(8) } - _ => unreachable!("There is no \"SBC {:?}\" instruction", target), }, Instruction::AND(target) => match target { - MATHTarget::Register(reg) => { - // AND r[z] | Bitwise AND register r[z] and register A, store in register A - use InstrRegister::*; + AluSource::Register(reg) => { + // AND r8 | Perform bitwise AND on accumulator and 8-bit register + use Register::*; + let left = cpu.register(CpuRegister::A); - let mut flags: Flags = *cpu.flags(); - let a_value = cpu.register(Register::A); - - let (cycles, result) = match reg { + let (cycles, acc) = match reg { B | C | D | E | H | L | A => { - let value = cpu.register(reg.to_register()); - (Cycle::new(4), a_value & value) + (Cycle::new(4), left & cpu.register(reg.cpu_register())) } IndirectHL => { - let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); - (Cycle::new(8), a_value & value) + let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + (Cycle::new(8), left & right) } }; - - flags.update(result == 0, false, true, false); - cpu.set_register(Register::A, result); - cpu.set_flags(flags); + cpu.set_register(CpuRegister::A, acc); + cpu.update_flags(acc == 0, false, true, false); cycles } - MATHTarget::ImmediateByte(n) => { - // AND n | Bitwise AND immediate byte and register A, store in register A - let mut flags: Flags = *cpu.flags(); - let result = cpu.register(Register::A) & n; - - flags.update(result == 0, false, true, false); - cpu.set_register(Register::A, result); - cpu.set_flags(flags); + AluSource::ImmediateByte => { + // AND u8 | Perform bitwise AND on accumulator and u8 + let acc = cpu.register(CpuRegister::A) & cpu.imm_byte(); + cpu.set_register(CpuRegister::A, acc); + cpu.update_flags(acc == 0, false, true, false); Cycle::new(8) } - _ => unreachable!("There is no \"AND {:?}\" instruction", target), }, - Instruction::XOR(target) => match target { - MATHTarget::Register(reg) => { - // XOR r[z] | Bitwise XOR register r[z] and register A, store in register A - use InstrRegister::*; + Instruction::XOR(source) => match source { + AluSource::Register(reg) => { + // XOR r8 | Perform bitwise XOR on accumulator and 8-bit register + use Register::*; + let left = cpu.register(CpuRegister::A); - let mut flags: Flags = *cpu.flags(); - let a_value = cpu.register(Register::A); - - let (cycles, result) = match reg { + let (cycles, acc) = match reg { B | C | D | E | H | L | A => { - let value = cpu.register(reg.to_register()); - (Cycle::new(4), a_value ^ value) + (Cycle::new(4), left ^ cpu.register(reg.cpu_register())) } IndirectHL => { - let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); - (Cycle::new(8), a_value ^ value) + let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + (Cycle::new(8), left ^ right) } }; - - flags.update(result == 0, false, false, false); - cpu.set_flags(flags); - cpu.set_register(Register::A, result); + cpu.set_register(CpuRegister::A, acc); + cpu.update_flags(acc == 0, false, false, false); cycles } - MATHTarget::ImmediateByte(n) => { - // XOR n | Bitwise XOR immediate byte and register A, store in register A - let mut flags: Flags = *cpu.flags(); - let result = cpu.register(Register::A) ^ n; - - flags.update(result == 0, false, false, false); - cpu.set_flags(flags); - cpu.set_register(Register::A, result); + AluSource::ImmediateByte => { + // XOR u8 | Perform bitwise XOR on accumulator and u8 + let acc = cpu.register(CpuRegister::A) ^ cpu.imm_byte(); + cpu.set_register(CpuRegister::A, acc); + cpu.update_flags(acc == 0, false, false, false); Cycle::new(8) } - _ => unreachable!("There is no \"XOR {:?}\" instruction", target), }, Instruction::OR(target) => match target { - MATHTarget::Register(reg) => { - // OR r[z] | Bitwise OR register r[z] and register A, store in register A - use InstrRegister::*; + AluSource::Register(reg) => { + // OR r8 | Perform bitwise OR on accumulator and 8-bit register + use Register::*; + let left = cpu.register(CpuRegister::A); - let mut flags: Flags = *cpu.flags(); - let a_value = cpu.register(Register::A); - - let (cycles, result) = match reg { + let (cycles, acc) = match reg { B | C | D | E | H | L | A => { - let value = cpu.register(reg.to_register()); - (Cycle::new(4), a_value | value) + (Cycle::new(4), left | cpu.register(reg.cpu_register())) } IndirectHL => { - let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); - (Cycle::new(8), a_value | value) + let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + (Cycle::new(8), left | right) } }; - - flags.update(result == 0, false, false, false); - cpu.set_flags(flags); - cpu.set_register(Register::A, result); + cpu.set_register(CpuRegister::A, acc); + cpu.update_flags(acc == 0, false, false, false); cycles } - MATHTarget::ImmediateByte(n) => { - // OR n | Bitwise OR on immediate byte n and register A, store in register A - let mut flags: Flags = *cpu.flags(); - let result = cpu.register(Register::A) | n; - - flags.update(result == 0, false, false, false); - cpu.set_flags(flags); - cpu.set_register(Register::A, result); + AluSource::ImmediateByte => { + // OR u8 | Perform bitwise OR on accumulator and u8 + let acc = cpu.register(CpuRegister::A) | cpu.imm_byte(); + cpu.set_register(CpuRegister::A, acc); + cpu.update_flags(acc == 0, false, false, false); Cycle::new(8) } - _ => unreachable!("There is no \"OR {:?}\" instruction", target), }, Instruction::CP(target) => match target { - MATHTarget::Register(reg) => { - // CP r[z] | Same behaviour as SUB, except the result is not stored. - use InstrRegister::*; - + AluSource::Register(reg) => { + // CP r8 | Compare accumulator to 8-bit register. Do not store result + use Register::*; let mut flags: Flags = *cpu.flags(); - let a_value = cpu.register(Register::A); + + let left = cpu.register(CpuRegister::A); let cycles = match reg { B | C | D | E | H | L | A => { - let value = cpu.register(reg.to_register()); - let _ = Self::sub(a_value, value, &mut flags); + let _ = Self::sub(left, cpu.register(reg.cpu_register()), &mut flags); Cycle::new(4) } IndirectHL => { - let value = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); - let _ = Self::sub(a_value, value, &mut flags); + let right = cpu.read_byte(cpu.register_pair(RegisterPair::HL)); + let _ = Self::sub(left, right, &mut flags); Cycle::new(8) } }; - cpu.set_flags(flags); cycles } - MATHTarget::ImmediateByte(n) => { - // CP n | Same behaviour as SUB, except the result is not stored, + AluSource::ImmediateByte => { + // CP u8 | Compare accumulator to u8. Do not store result let mut flags: Flags = *cpu.flags(); - let _ = Self::sub(cpu.register(Register::A), n, &mut flags); + let left = cpu.register(CpuRegister::A); + let _ = Self::sub(left, cpu.imm_byte(), &mut flags); cpu.set_flags(flags); Cycle::new(8) } - _ => unreachable!("There is no \"CP {:?}\" instruction", target), }, + Instruction::LDHL => { + // LD HL, SP + i8 | Store stack pointer + i8 in HL + let mut flags: Flags = *cpu.flags(); + + let left = cpu.register_pair(RegisterPair::SP); + let sum = Self::add_u16_i8(left, cpu.imm_byte() as i8, &mut flags); + cpu.set_register_pair(RegisterPair::HL, sum); + cpu.set_flags(flags); + Cycle::new(12) + } Instruction::RET(cond) => { - // RET cc[y] | Essentially a POP PC, Return from Subroutine - // RET | Essentially a POP PC, Return from Subroutine + // RET cond | Return from subroutine if condition is true + // RET | Return from subroutine let flags: &Flags = cpu.flags(); match cond { @@ -933,54 +864,43 @@ impl Instruction { } } } - Instruction::LDHL(d) => { - // LDHL SP + d | Add SP + d to register HL - // LD HL, SP + d | Add SP + d to register HL - let mut flags: Flags = *cpu.flags(); - let sum = Self::add_u16_i8(cpu.register_pair(RegisterPair::SP), d, &mut flags); - - cpu.set_register_pair(RegisterPair::HL, sum); - cpu.set_flags(flags); - Cycle::new(12) - } Instruction::POP(pair) => { - // POP rp2[p] | Pop from stack into register pair rp2[] - // Flags are set when we call cpu.set_register_pair(RegisterPair::AF, value); - use RegisterPair::*; + // POP r16 | Store word popped from the stack in r16 + use Group3RegisterPair::*; match pair { BC | DE | HL | AF => { - let value = Self::pop(cpu); - cpu.set_register_pair(pair, value); + let right = Self::pop(cpu); + cpu.set_register_pair(pair.as_register_pair(), right); } - _ => unreachable!("There is no \"POP {:?}\" instruction", pair), } Cycle::new(12) } Instruction::RETI => { - // Same as RET, after which interrupts are enabled. + // RETI | Return from subroutine, then enable interrupts let addr = Self::pop(cpu); cpu.set_register_pair(RegisterPair::PC, addr); cpu.set_ime(ImeState::Enabled); Cycle::new(16) } - Instruction::JP(cond, target) => match target { - JPTarget::RegisterPair(RegisterPair::HL) => { - // JP HL | Load register pair HL into program counter - let addr = cpu.register_pair(RegisterPair::HL); - - cpu.set_register_pair(RegisterPair::PC, addr); + Instruction::JP(cond, location) => match location { + JumpLocation::HL => { + // JP HL | Store HL in program counter + let right = cpu.register_pair(RegisterPair::HL); + cpu.set_register_pair(RegisterPair::PC, right); Cycle::new(4) } - JPTarget::ImmediateWord(nn) => { - // JP cc[y], nn | Store Immediate Word in the Program Counter if cond is met - // JP nn | Store Immediate Word in the Program Counter - let flags: &Flags = cpu.flags(); + JumpLocation::ImmediateWord => { + // JP cond u16 | Store u16 in program counter if condition is true + // JP u16 | Store u16 in program counter + let flags: Flags = *cpu.flags(); + + let addr = cpu.imm_word(); match cond { JumpCondition::NotZero => { if !flags.z() { - cpu.set_register_pair(RegisterPair::PC, nn); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(16) } else { Cycle::new(12) @@ -988,7 +908,7 @@ impl Instruction { } JumpCondition::Zero => { if flags.z() { - cpu.set_register_pair(RegisterPair::PC, nn); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(16) } else { Cycle::new(12) @@ -996,7 +916,7 @@ impl Instruction { } JumpCondition::NotCarry => { if !flags.c() { - cpu.set_register_pair(RegisterPair::PC, nn); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(16) } else { Cycle::new(12) @@ -1004,42 +924,42 @@ impl Instruction { } JumpCondition::Carry => { if flags.c() { - cpu.set_register_pair(RegisterPair::PC, nn); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(16) } else { Cycle::new(12) } } JumpCondition::Always => { - cpu.set_register_pair(RegisterPair::PC, nn); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(16) } } } - _ => unreachable!("There is no \"JP {:?}\" instruction", target), }, Instruction::DI => { - // Disable IME + // DI | Disable IME cpu.set_ime(ImeState::Disabled); Cycle::new(4) } Instruction::EI => { - // Enable IME (After the next instruction) - // FIXME: IME is set after the next instruction, this currently is not represented in this emulator. + // EI | Enable IME after the next instruction cpu.set_ime(ImeState::Pending); Cycle::new(4) } - Instruction::CALL(cond, nn) => { - // CALL cc[y], nn | Store pc on the stack, then store nn in the program counter if cond is met - // CALL nn | Store nn on the stack, then store nn in the program counter - let flags: &Flags = cpu.flags(); - let pc = cpu.register_pair(RegisterPair::PC); + Instruction::CALL(cond) => { + // CALL cond u16 | Push PC on the stack and store u16 in program counter if condition is true + // CALL u16 | Push PC on the stack then store u16 in program counter + let flags: Flags = *cpu.flags(); + + let addr = cpu.imm_word(); + let return_addr = cpu.register_pair(RegisterPair::PC); match cond { JumpCondition::NotZero => { if !flags.z() { - Self::push(cpu, pc); - cpu.set_register_pair(RegisterPair::PC, nn); + Self::push(cpu, return_addr); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) } else { Cycle::new(12) @@ -1047,8 +967,8 @@ impl Instruction { } JumpCondition::Zero => { if flags.z() { - Self::push(cpu, pc); - cpu.set_register_pair(RegisterPair::PC, nn); + Self::push(cpu, return_addr); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) } else { Cycle::new(12) @@ -1056,8 +976,8 @@ impl Instruction { } JumpCondition::NotCarry => { if !flags.c() { - Self::push(cpu, pc); - cpu.set_register_pair(RegisterPair::PC, nn); + Self::push(cpu, return_addr); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) } else { Cycle::new(12) @@ -1065,370 +985,280 @@ impl Instruction { } JumpCondition::Carry => { if flags.c() { - Self::push(cpu, pc); - cpu.set_register_pair(RegisterPair::PC, nn); + Self::push(cpu, return_addr); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) } else { Cycle::new(12) } } JumpCondition::Always => { - Self::push(cpu, pc); - cpu.set_register_pair(RegisterPair::PC, nn); + Self::push(cpu, return_addr); + cpu.set_register_pair(RegisterPair::PC, addr); Cycle::new(24) } } } Instruction::PUSH(pair) => { - // PUSH rp2[p] | Push register pair onto the stack - use RegisterPair::*; + // PUSH r16 | Push r16 onto the stack + use Group3RegisterPair::*; match pair { BC | DE | HL | AF => { - let value = cpu.register_pair(pair); - Self::push(cpu, value); + let word = cpu.register_pair(pair.as_register_pair()); + Self::push(cpu, word); } - _ => unreachable!("There is no \"PUSH {:?}\" instruction", pair), } Cycle::new(16) } - Instruction::RST(n) => { - // RST n | Push current address onto the stack, jump to 0x0000 + n - - // The same behaviour will occur when handling an interrupt so this code - // is relegated to a method - Self::reset(cpu, n) + Instruction::RST(vector) => { + // RST vector | Push current address onto the stack, jump to 0x0000 + n + Self::reset(cpu, vector) } Instruction::RLC(reg) => { - // RLC r[z] | Rotate register r[z] left - use InstrRegister::*; + // RLC r8 | Rotate r8 left + use Register::*; - let mut flags: Flags = *cpu.flags(); - - let (cycles, msb, rotated) = match reg { + let (cycles, most_sgfnt, rotated) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - - let value = cpu.register(register); - let rotated = value.rotate_left(1); - - cpu.set_register(register, rotated); - (Cycle::new(8), value >> 7, rotated) + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let rotated = byte.rotate_left(1); + cpu.set_register(reg, rotated); + (Cycle::new(8), byte >> 7, rotated) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - - let value = cpu.read_byte(addr); - let rotated = value.rotate_left(1); - + let byte = cpu.read_byte(addr); + let rotated = byte.rotate_left(1); cpu.write_byte(addr, rotated); - (Cycle::new(16), value >> 7, rotated) + (Cycle::new(16), byte >> 7, rotated) } }; - - flags.update(rotated == 0, false, false, msb == 0x01); - cpu.set_flags(flags); + cpu.update_flags(rotated == 0, false, false, most_sgfnt == 0x01); cycles } Instruction::RRC(reg) => { - // RRC r[z] | Rotate Register r[z] right - use InstrRegister::*; + // RRC r8 | Rotate r8 right + use Register::*; - let mut flags: Flags = *cpu.flags(); - - let (cycles, lsb, rotated) = match reg { + let (cycles, least_sgfnt, rotated) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - - let value = cpu.register(register); - let rotated = value.rotate_right(1); - - cpu.set_register(register, rotated); - (Cycle::new(8), value & 0x01, rotated) + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let rotated = byte.rotate_right(1); + cpu.set_register(reg, rotated); + (Cycle::new(8), byte & 0x01, rotated) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - - let value = cpu.read_byte(addr); - let rotated = value.rotate_right(1); - + let byte = cpu.read_byte(addr); + let rotated = byte.rotate_right(1); cpu.write_byte(addr, rotated); - (Cycle::new(16), value & 0x01, rotated) + (Cycle::new(16), byte & 0x01, rotated) } }; - - flags.update(rotated == 0, false, false, lsb == 0x01); - cpu.set_flags(flags); + cpu.update_flags(rotated == 0, false, false, least_sgfnt == 0x01); cycles } Instruction::RL(reg) => { - // RL r[z] | Rotate register r[z] left through carry - use InstrRegister::*; + // RL r8 | Rotate r8 left through carry + use Register::*; - let mut flags: Flags = *cpu.flags(); + let flags: Flags = *cpu.flags(); let (cycles, rotated, carry) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let (rotated, carry) = Self::rl_thru_carry(value, flags.c()); - - cpu.set_register(register, rotated); + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); + cpu.set_register(reg, rotated); (Cycle::new(8), rotated, carry) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let (rotated, carry) = Self::rl_thru_carry(value, flags.c()); - + let byte = cpu.read_byte(addr); + let (rotated, carry) = Self::rl_thru_carry(byte, flags.c()); cpu.write_byte(addr, rotated); (Cycle::new(16), rotated, carry) } }; - - flags.update(rotated == 0, false, false, carry); - cpu.set_flags(flags); - + cpu.update_flags(rotated == 0, false, false, carry); cycles } Instruction::RR(reg) => { - // RR r[z] | Rotate register r[z] right through carry - use InstrRegister::*; + // RR r8 | Rotate register r8 right through carry + use Register::*; - let mut flags: Flags = *cpu.flags(); + let flags: Flags = *cpu.flags(); let (cycles, rotated, carry) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let (rotated, carry) = Self::rr_thru_carry(value, flags.c()); - - cpu.set_register(register, rotated); + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); + cpu.set_register(reg, rotated); (Cycle::new(8), rotated, carry) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let (rotated, carry) = Self::rr_thru_carry(value, flags.c()); - + let byte = cpu.read_byte(addr); + let (rotated, carry) = Self::rr_thru_carry(byte, flags.c()); cpu.write_byte(addr, rotated); (Cycle::new(16), rotated, carry) } }; - - flags.update(rotated == 0, false, false, carry); - cpu.set_flags(flags); - + cpu.update_flags(rotated == 0, false, false, carry); cycles } Instruction::SLA(reg) => { - // SLA r[z] | Shift left arithmetic register r[z] - use InstrRegister::*; + // SLA r8 | Shift left arithmetic r8 + use Register::*; - let mut flags: Flags = *cpu.flags(); - - let (cycles, msb, shifted) = match reg { + let (cycles, most_sgfnt, shifted) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let shifted = value << 1; - - cpu.set_register(register, shifted); - (Cycle::new(8), (value >> 7) & 0x01, shifted) + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let shifted = byte << 1; + cpu.set_register(reg, shifted); + (Cycle::new(8), (byte >> 7) & 0x01, shifted) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let shifted = value << 1; - + let byte = cpu.read_byte(addr); + let shifted = byte << 1; cpu.write_byte(addr, shifted); - (Cycle::new(16), (value >> 7) & 0x01, shifted) + (Cycle::new(16), (byte >> 7) & 0x01, shifted) } }; - - flags.update(shifted == 0, false, false, msb == 0x01); - cpu.set_flags(flags); - + cpu.update_flags(shifted == 0, false, false, most_sgfnt == 0x01); cycles } Instruction::SRA(reg) => { - // SRA r[z] | Shift right arithmetic register r[z] - use InstrRegister::*; + // SRA r8 | Shift right arithmetic r8 + use Register::*; - let mut flags: Flags = *cpu.flags(); - - let (cycles, lsb, shifted) = match reg { + let (cycles, least_sgfnt, shifted) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let msb = (value >> 7) & 0x01; - let shifted = msb << 7 | value >> 1; - - cpu.set_register(register, shifted); - (Cycle::new(8), value & 0x01, shifted) + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; + cpu.set_register(reg, shifted); + (Cycle::new(8), byte & 0x01, shifted) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let msb = (value >> 7) & 0x01; - let shifted = msb << 7 | value >> 1; - + let byte = cpu.read_byte(addr); + let shifted = ((byte >> 7) & 0x01) << 7 | byte >> 1; cpu.write_byte(addr, shifted); - (Cycle::new(16), value & 0x01, shifted) + (Cycle::new(16), byte & 0x01, shifted) } }; - - flags.update(shifted == 0, false, false, lsb == 0x01); - cpu.set_flags(flags); - + cpu.update_flags(shifted == 0, false, false, least_sgfnt == 0x01); cycles } Instruction::SWAP(reg) => { - // SWAP r[z] | Swap the 4 highest and lowest bits in a byte - use InstrRegister::*; - - let mut flags: Flags = *cpu.flags(); + // SWAP r[z] | Swap the two nybbles in a byte + use Register::*; let (cycles, swapped) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let swapped = Self::swap_bits(value); - - cpu.set_register(register, swapped); + let reg = reg.cpu_register(); + let swapped = Self::swap_bits(cpu.register(reg)); + cpu.set_register(reg, swapped); (Cycle::new(8), swapped) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let swapped = Self::swap_bits(value); - + let swapped = Self::swap_bits(cpu.read_byte(addr)); cpu.write_byte(addr, swapped); (Cycle::new(16), swapped) } }; - - flags.update(swapped == 0, false, false, false); - cpu.set_flags(flags); - + cpu.update_flags(swapped == 0, false, false, false); cycles } Instruction::SRL(reg) => { - // SRL r[z] | Shift right logic register r[z] - use InstrRegister::*; + // SRL r[z] | Shift right logic r8 + use Register::*; - let mut flags: Flags = *cpu.flags(); - - let (cycles, lsb, shift_reg) = match reg { + let (cycles, least_sgfnt, shift_reg) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let shifted = value >> 1; - - cpu.set_register(register, shifted); - (Cycle::new(8), value & 0x01, shifted) + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + let shifted = byte >> 1; + cpu.set_register(reg, shifted); + (Cycle::new(8), byte & 0x01, shifted) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let shifted = value >> 1; - + let byte = cpu.read_byte(addr); + let shifted = byte >> 1; cpu.write_byte(addr, shifted); - (Cycle::new(16), value & 0x01, shifted) + (Cycle::new(16), byte & 0x01, shifted) } }; - - flags.update(shift_reg == 0, false, false, lsb == 0x01); - cpu.set_flags(flags); - + cpu.update_flags(shift_reg == 0, false, false, least_sgfnt == 0x01); cycles } - Instruction::BIT(y, reg) => { - // BIT y, r[z] | Test y is in register r[z] - use InstrRegister::*; - + Instruction::BIT(bit, reg) => { + // BIT u8, r8 | Test bit u8 in r8 + use Register::*; let mut flags: Flags = *cpu.flags(); - let (cycles, is_bit_set) = match reg { + let (cycles, is_set) = match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - let is_bit_set = ((value >> y) & 0x01) == 0x01; - (Cycle::new(8), is_bit_set) + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + (Cycle::new(8), ((byte >> bit) & 0x01) == 0x01) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - let is_bit_set = ((value >> y) & 0x01) == 0x01; - (Cycle::new(12), is_bit_set) + let byte = cpu.read_byte(addr); + (Cycle::new(12), ((byte >> bit) & 0x01) == 0x01) } }; - - flags.update(!is_bit_set, false, true, flags.c()); + flags.set_z(!is_set); + flags.set_n(false); + flags.set_h(true); cpu.set_flags(flags); - cycles } - Instruction::RES(y, reg) => { - // RES y, r[z] | Reset bit y to zero - // - // 00000001 << 3 = 00001000 - // ~00001000 = 11110111 - // value & 11110111 means that only a specific bit will be reset - use InstrRegister::*; + Instruction::RES(bit, reg) => { + // RES u8, r8 | Reset bit u8 in r8 + use Register::*; match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - cpu.set_register(register, value & !(1u8 << y)); + let register = reg.cpu_register(); + let byte = cpu.register(register); + cpu.set_register(register, byte & !(1 << bit)); Cycle::new(8) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - cpu.write_byte(addr, value & !(1u8 << y)); + let byte = cpu.read_byte(addr); + cpu.write_byte(addr, byte & !(1 << bit)); Cycle::new(16) } } } - Instruction::SET(y, reg) => { - // BIT y, r[z] | Set bit y to one - // - // 00000001 << 3 = 00001000 - // value | 00001000 means that only a specific bit will be set - use InstrRegister::*; + Instruction::SET(bit, reg) => { + // SET u8, r8 | Set bit u8 + use Register::*; match reg { B | C | D | E | H | L | A => { - let register = reg.to_register(); - let value = cpu.register(register); - - cpu.set_register(register, value | (1u8 << y)); + let reg = reg.cpu_register(); + let byte = cpu.register(reg); + cpu.set_register(reg, byte | (1u8 << bit)); Cycle::new(8) } IndirectHL => { let addr = cpu.register_pair(RegisterPair::HL); - let value = cpu.read_byte(addr); - - cpu.write_byte(addr, value | (1u8 << y)); + let byte = cpu.read_byte(addr); + cpu.write_byte(addr, byte | (1u8 << bit)); Cycle::new(16) } } @@ -1465,12 +1295,12 @@ impl Instruction { (high as u16) << 8 | low as u16 } - fn dec_register(byte: u8, flags: &mut Flags) -> u8 { - Self::sub_no_carry(byte, 1, flags) + fn dec(left: u8, flags: &mut Flags) -> u8 { + Self::sub_no_carry(left, 1, flags) } - fn inc_register(byte: u8, flags: &mut Flags) -> u8 { - Self::add_no_carry(byte, 1, flags) + fn inc(left: u8, flags: &mut Flags) -> u8 { + Self::add_no_carry(left, 1, flags) } fn sub_no_carry(left: u8, right: u8, flags: &mut Flags) -> u8 { @@ -1618,671 +1448,758 @@ impl Instruction { } impl Instruction { - pub(crate) fn from_byte(cpu: &mut Cpu, byte: u8) -> Self { - if byte == 0xCB { - Self::from_prefixed_byte(cpu) + pub(crate) fn decode(byte: u8, prefixed: bool) -> Self { + if prefixed { + Self::prefixed(byte) } else { - Self::from_unprefixed_byte(cpu, byte) + Self::unprefixed(byte) } } - fn from_unprefixed_byte(cpu: &mut Cpu, opcode: u8) -> Self { - // https://gb-archive.github.io/salvage/decoding_gbz80_opcodes/Decoding%20Gamboy%20Z80%20Opcodes.html - #![allow(clippy::many_single_char_names)] - - let x = (opcode >> 6) & 0x03; - let y = (opcode >> 3) & 0x07; - let z = opcode & 0x07; - let p = y >> 1; - let q = y & 0x01; - - 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(cpu.read_imm_word(pc)), - LDTarget::RegisterPair(RegisterPair::SP), - ), - (0, 0, _, 2, _) => Self::STOP, // STOP - (0, 0, _, 3, _) => Self::JR(JumpCondition::Always, cpu.read_imm_byte(pc) as i8), // JR d - (0, 0, _, 4..=7, _) => Self::JR(Table::cc(y - 4), cpu.read_imm_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(cpu.read_imm_word(pc)), - ), - (0, 1, 1, _, _) => Self::ADD( - // ADD HL, rp[p] - MATHTarget::RegisterPair(RegisterPair::HL), - MATHTarget::RegisterPair(Table::rp(p)), - ), - (0, 2, 0, _, 0) => Self::LD( - // LD (BC), A - LDTarget::IndirectRegister(InstrRegisterPair::BC), - LDTarget::Register(InstrRegister::A), - ), - (0, 2, 0, _, 1) => Self::LD( - // LD (DE), A - LDTarget::IndirectRegister(InstrRegisterPair::DE), - LDTarget::Register(InstrRegister::A), - ), - (0, 2, 1, _, 0) => Self::LD( - // LD A, (BC) - LDTarget::Register(InstrRegister::A), - LDTarget::IndirectRegister(InstrRegisterPair::BC), - ), - (0, 2, 1, _, 1) => Self::LD( - // LD A, (DE) - LDTarget::Register(InstrRegister::A), - LDTarget::IndirectRegister(InstrRegisterPair::DE), - ), - (0, 2, 0, _, 2) => Self::LD( - // LD (HL+), A - LDTarget::IndirectRegister(InstrRegisterPair::IncrementHL), - LDTarget::Register(InstrRegister::A), - ), - (0, 2, 0, _, 3) => Self::LD( - // LD (HL-), A - LDTarget::IndirectRegister(InstrRegisterPair::DecrementHL), - LDTarget::Register(InstrRegister::A), - ), - (0, 2, 1, _, 2) => Self::LD( - // LD A, (HL+) - LDTarget::Register(InstrRegister::A), - LDTarget::IndirectRegister(InstrRegisterPair::IncrementHL), - ), - (0, 2, 1, _, 3) => Self::LD( - // LD A, (HL-) - LDTarget::Register(InstrRegister::A), - LDTarget::IndirectRegister(InstrRegisterPair::DecrementHL), - ), - (0, 3, 0, _, _) => Self::INC( - // INC rp[p] - Registers::Word(Table::rp(p)), - ), - (0, 3, 1, _, _) => Self::DEC( - // DEC rp[p] - Registers::Word(Table::rp(p)), - ), - (0, 4, _, _, _) => Self::INC( - // INC r[y] - Registers::Byte(Table::r(y)), - ), - (0, 5, _, _, _) => Self::DEC( - // DEC r[y] - Registers::Byte(Table::r(y)), - ), - (0, 6, _, _, _) => Self::LD( - // LD r[y], n - LDTarget::Register(Table::r(y)), - LDTarget::ImmediateByte(cpu.read_imm_byte(pc)), - ), - (0, 7, _, 0, _) => Self::RLCA, - (0, 7, _, 1, _) => Self::RRCA, - (0, 7, _, 2, _) => Self::RLA, - (0, 7, _, 3, _) => Self::RRA, - (0, 7, _, 4, _) => Self::DAA, - (0, 7, _, 5, _) => Self::CPL, - (0, 7, _, 6, _) => Self::SCF, - (0, 7, _, 7, _) => Self::CCF, - (1, 6, _, 6, _) => Self::HALT, - (1, _, _, _, _) => Self::LD( - // LD r[y], r[z] - LDTarget::Register(Table::r(y)), - LDTarget::Register(Table::r(z)), - ), - (2, _, _, _, _) => Table::x2_alu(y, z), // alu[y] r[z] - (3, 0, _, 0..=3, _) => Self::RET(Table::cc(y)), // RET cc[y] - (3, 0, _, 4, _) => Self::LD( - // LD (0xFF00 + n), A - LDTarget::ByteAtAddressWithOffset(cpu.read_imm_byte(pc)), - LDTarget::Register(InstrRegister::A), - ), - (3, 0, _, 5, _) => Self::ADD( - // ADD SP, d - MATHTarget::RegisterPair(RegisterPair::SP), - MATHTarget::ImmediateByte(cpu.read_imm_byte(pc)), - ), - (3, 0, _, 6, _) => Self::LD( - // LD A, (0xFF00 + n) - LDTarget::Register(InstrRegister::A), - LDTarget::ByteAtAddressWithOffset(cpu.read_imm_byte(pc)), - ), - (3, 0, _, 7, _) => Self::LDHL(cpu.read_imm_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 - JumpCondition::Always, - JPTarget::RegisterPair(RegisterPair::HL), - ), - (3, 1, 1, _, 3) => Self::LD( - // LD SP, HL - LDTarget::RegisterPair(RegisterPair::SP), - LDTarget::RegisterPair(RegisterPair::HL), - ), - (3, 2, _, 0..=3, _) => Self::JP( - // JP cc[y], nn - Table::cc(y), - JPTarget::ImmediateWord(cpu.read_imm_word(pc)), - ), - (3, 2, _, 4, _) => Self::LD( - // LD (0xFF00 + C) ,A - LDTarget::IndirectC, - LDTarget::Register(InstrRegister::A), - ), - (3, 2, _, 5, _) => Self::LD( - // LD (nn), A - LDTarget::ByteAtAddress(cpu.read_imm_word(pc)), - LDTarget::Register(InstrRegister::A), - ), - (3, 2, _, 6, _) => Self::LD( - // LD A, (0xFF00 + C) - LDTarget::Register(InstrRegister::A), - LDTarget::IndirectC, - ), - (3, 2, _, 7, _) => Self::LD( - // LD A, (nn) - LDTarget::Register(InstrRegister::A), - LDTarget::ByteAtAddress(cpu.read_imm_word(pc)), - ), - (3, 3, _, 0, _) => Self::JP( - // JP nn - JumpCondition::Always, - JPTarget::ImmediateWord(cpu.read_imm_word(pc)), - ), - (3, 3, _, 1, _) => unreachable!("0xCB is handled by Instruction::from_prefixed_byte"), - // (3, 3, _, 2, _) => unreachable!("\"removed\" in documentation"), - // (3, 3, _, 3, _) => unreachable!("\"removed\" in documentation"), - // (3, 3, _, 4, _) => unreachable!("\"removed\" in documentation"), - // (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), cpu.read_imm_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, cpu.read_imm_word(pc)), // CALL nn - // (3, 5, 1, _, 1..=3) => unreachable!("\"removed\" in documentation"), - (3, 6, _, _, _) => Table::x3_alu(y, cpu.read_imm_byte(pc)), - (3, 7, _, _, _) => Self::RST(y * 8), // RST n - _ => unreachable!( - "Unknown Opcode: {:#04X}\n x: {}, z: {}, q: {}, y: {}, p: {}", - opcode, x, z, q, y, p - ), - } - } - - fn from_prefixed_byte(cpu: &mut Cpu) -> Self { - #![allow(clippy::many_single_char_names)] - - let pc = cpu.register_pair(RegisterPair::PC); - let opcode = cpu.read_imm_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; - let y = (opcode >> 3) & 0b00000111; - let z = opcode & 0b00000111; - let p = y >> 1; - let q = y & 0b00000001; - - match x { - 0 => Table::rot(y, z), - 1 => Self::BIT(y, Table::r(z)), // BIT y, r[z] - 2 => Self::RES(y, Table::r(z)), // RES y, r[z] - 3 => Self::SET(y, Table::r(z)), // SET y, r[z] - _ => unreachable!( - "Unknown Prefixed Opcode: 0xCB {:#04X}\n x: {}, z: {}, q: {}, y: {}, p: {}", - opcode, x, z, q, y, p - ), - } - } -} - -impl From for InstrRegisterPair { - fn from(pair: RegisterPair) -> Self { - match pair { - RegisterPair::AF => Self::AF, - RegisterPair::BC => Self::BC, - RegisterPair::DE => Self::DE, - RegisterPair::HL => Self::HL, - RegisterPair::SP => Self::SP, - RegisterPair::PC => Self::PC, - } - } -} - -impl TryFrom for RegisterPair { - type Error = &'static str; // FIXME: Proper error type goes here. - - fn try_from(pair: InstrRegisterPair) -> Result { - match pair { - InstrRegisterPair::AF => Ok(Self::AF), - InstrRegisterPair::BC => Ok(Self::BC), - InstrRegisterPair::DE => Ok(Self::DE), - InstrRegisterPair::HL => Ok(Self::HL), - InstrRegisterPair::SP => Ok(Self::SP), - InstrRegisterPair::PC => Ok(Self::PC), - InstrRegisterPair::IncrementHL => { - Err("Can not convert InstrRegisterPair::IncrementHL to RegisterPair") - } - InstrRegisterPair::DecrementHL => { - Err("Can not convert InstrRegisterPair::DecrementHL to RegisterPair") - } - } - } -} - -impl TryFrom for InstrRegister { - type Error = &'static str; // FIXME: Proper error type goes here - - fn try_from(register: Register) -> Result { - match register { - Register::A => Ok(Self::A), - Register::B => Ok(Self::B), - Register::C => Ok(Self::C), - Register::D => Ok(Self::D), - Register::E => Ok(Self::E), - Register::H => Ok(Self::H), - Register::L => Ok(Self::L), - } - } -} - -impl TryFrom for Register { - type Error = String; // FIXME: Proper error type goes here. - - fn try_from(register: InstrRegister) -> Result { - match register { - InstrRegister::A => Ok(Self::A), - InstrRegister::B => Ok(Self::B), - InstrRegister::C => Ok(Self::C), - InstrRegister::D => Ok(Self::D), - InstrRegister::E => Ok(Self::E), - InstrRegister::H => Ok(Self::H), - InstrRegister::L => Ok(Self::L), - InstrRegister::IndirectHL => { - Err("Can not convert InstrRegister::IndirectHL to Register".to_string()) - } - } - } -} - -impl Table { - fn r(index: u8) -> InstrRegister { - match index { - 0 => InstrRegister::B, - 1 => InstrRegister::C, - 2 => InstrRegister::D, - 3 => InstrRegister::E, - 4 => InstrRegister::H, - 5 => InstrRegister::L, - 6 => InstrRegister::IndirectHL, - 7 => InstrRegister::A, - _ => unreachable!("Index {} is out of bounds in r[]", index), - } - } - - fn rp2(index: u8) -> RegisterPair { - match index { - 0 => RegisterPair::BC, - 1 => RegisterPair::DE, - 2 => RegisterPair::HL, - 3 => RegisterPair::AF, - _ => unreachable!("Index {} out of bounds in rp2[]", index), - } - } - - fn rp(index: u8) -> RegisterPair { - match index { - 0 => RegisterPair::BC, - 1 => RegisterPair::DE, - 2 => RegisterPair::HL, - 3 => RegisterPair::SP, - _ => unreachable!("Index {} out of bounds in rp[]", index), - } - } - - fn cc(index: u8) -> JumpCondition { - match index { - 0 => JumpCondition::NotZero, - 1 => JumpCondition::Zero, - 2 => JumpCondition::NotCarry, - 3 => JumpCondition::Carry, - _ => unreachable!("Index {} out of bounds in cc[]", index), - } - } - - fn x2_alu(index: u8, r_index: u8) -> Instruction { - match index { - 0 => Instruction::ADD( - // ADD A, r[z] - MATHTarget::Register(InstrRegister::A), - MATHTarget::Register(Self::r(r_index)), - ), - 1 => Instruction::ADC(MATHTarget::Register(Self::r(r_index))), // ADC A, r[z] - 2 => Instruction::SUB(MATHTarget::Register(Self::r(r_index))), // SUB r[z] - 3 => Instruction::SBC(MATHTarget::Register(Self::r(r_index))), // SBC A, r[z] - 4 => Instruction::AND(MATHTarget::Register(Self::r(r_index))), // AND r[z] - 5 => Instruction::XOR(MATHTarget::Register(Self::r(r_index))), // XOR r[z] - 6 => Instruction::OR(MATHTarget::Register(Self::r(r_index))), // OR r[z] - 7 => Instruction::CP(MATHTarget::Register(Self::r(r_index))), // CP r[z] - _ => unreachable!("Index {} is out of bounds in alu[]"), - } - } - - fn x3_alu(index: u8, n: u8) -> Instruction { - match index { - 0 => Instruction::ADD( - // ADD A, n - MATHTarget::Register(InstrRegister::A), - MATHTarget::ImmediateByte(n), - ), - 1 => Instruction::ADC(MATHTarget::ImmediateByte(n)), // ADC A, n - 2 => Instruction::SUB(MATHTarget::ImmediateByte(n)), // SUB n - 3 => Instruction::SBC(MATHTarget::ImmediateByte(n)), // SBC A, n - 4 => Instruction::AND(MATHTarget::ImmediateByte(n)), // AND n - 5 => Instruction::XOR(MATHTarget::ImmediateByte(n)), // XOR n - 6 => Instruction::OR(MATHTarget::ImmediateByte(n)), // OR n - 7 => Instruction::CP(MATHTarget::ImmediateByte(n)), // CP n - _ => unreachable!("Index {} is out of bounds in alu[]"), - } - } - - fn rot(index: u8, r_index: u8) -> Instruction { - match index { - 0 => Instruction::RLC(Self::r(r_index)), // RLC r[z] - 1 => Instruction::RRC(Self::r(r_index)), // RRC r[z] - 2 => Instruction::RL(Self::r(r_index)), // RL r[z] - 3 => Instruction::RR(Self::r(r_index)), // RR r[z] - 4 => Instruction::SLA(Self::r(r_index)), // SLA r[z] - 5 => Instruction::SRA(Self::r(r_index)), // SRA r[z] - 6 => Instruction::SWAP(Self::r(r_index)), // SWAP r[z] - 7 => Instruction::SRL(Self::r(r_index)), // SRL r[z] - _ => unreachable!("Index {} is out of bounds in rot[]"), - } - } -} - -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("(0xFF00 + C)"), - 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, "(0xFF00 + {:#04X})", byte) - } - } - } -} - -impl std::fmt::Debug for MATHTarget { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - MATHTarget::Register(reg) => write!(f, "{:?}", reg), - MATHTarget::RegisterPair(pair) => write!(f, "{:?}", pair), - MATHTarget::ImmediateByte(byte) => write!(f, "{:#04X}", byte), - } - } -} - -impl std::fmt::Debug for InstrRegisterPair { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - InstrRegisterPair::AF => f.write_str("AF"), - InstrRegisterPair::BC => f.write_str("BC"), - InstrRegisterPair::DE => f.write_str("DE"), - InstrRegisterPair::HL => f.write_str("HL"), - InstrRegisterPair::SP => f.write_str("SP"), - InstrRegisterPair::PC => f.write_str("PC"), - InstrRegisterPair::IncrementHL => f.write_str("HL+"), - InstrRegisterPair::DecrementHL => f.write_str("HL-"), - } - } -} - -impl std::fmt::Debug for Registers { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match *self { - Registers::Byte(reg) => write!(f, "{:?}", reg), - Registers::Word(pair) => write!(f, "{:?}", pair), - } - } -} - -impl std::fmt::Debug for Instruction { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + #[inline] + fn unprefixed(byte: u8) -> Self { use Instruction::*; - match *self { - NOP => f.write_str("NOP"), - LD(left, right) => write!(f, "LD {:?}, {:?}", left, right), - STOP => f.write_str("STOP"), - JR(cond, dist) => write!(f, "JR {:?}, {:?}", cond, dist), - ADD(left, right) => write!(f, "ADD {:?}, {:?}", left, right), - INC(register) => write!(f, "INC {:?}", register), - DEC(register) => write!(f, "DEC {:?}", register), - RLCA => f.write_str("RLCA"), - RRCA => f.write_str("RRCA"), - RLA => f.write_str("RLA"), - RRA => f.write_str("RRA"), - DAA => f.write_str("DAA"), - CPL => f.write_str("CPL"), - SCF => f.write_str("SCF"), - CCF => f.write_str("CCF"), - HALT => f.write_str("HALT"), - ADC(target) => write!(f, "ADC {:?}", target), - SUB(target) => write!(f, "SUB {:?}", target), - SBC(target) => write!(f, "SBC {:?}", target), - AND(target) => write!(f, "AND {:?}", target), - XOR(target) => write!(f, "XOR {:?}", target), - OR(target) => write!(f, "OR {:?}", target), - CP(target) => write!(f, "CP {:?}", target), - RET(cond) => write!(f, "RET {:?}", cond), - LDHL(value) => write!(f, "LDHL {:?}", value), - POP(pair) => write!(f, "POP {:?}", pair), - RETI => f.write_str("RETI"), - JP(cond, target) => write!(f, "JP {:?}, {:?}", cond, target), - DI => f.write_str("DI"), - EI => f.write_str("EI"), - CALL(cond, addr) => write!(f, "CALL {:?}, {:?}", cond, addr), - PUSH(pair) => write!(f, "PUSH {:?}", pair), - RST(vector) => write!(f, "RST {:?}", vector), - RLC(register) => write!(f, "RLC {:?}", register), - RRC(register) => write!(f, "RRC {:?}", register), - RL(register) => write!(f, "RL {:?}", register), - RR(register) => write!(f, "RR {:?}", register), - SLA(register) => write!(f, "SLA {:?}", register), - SRA(register) => write!(f, "SRA {:?}", register), - SWAP(register) => write!(f, "SWAP {:?}", register), - SRL(register) => write!(f, "SRL {:?}", register), - BIT(bit, register) => write!(f, "BIT {:?}, {:?}", bit, register), - RES(bit, register) => write!(f, "RES {:?}, {:?}", bit, register), - SET(bit, register) => write!(f, "SET {:?}, {:?}", bit, register), + match byte { + // NOP + 0o000 => NOP, + // LD (u16), SP + 0o010 => LD(LDTarget::IndirectImmediateWord, LDSource::SP), + // STOP + 0o020 => STOP, + // JR i8 + 0o030 => JR(JumpCondition::Always), + // JR cond i8 + 0o040 | 0o050 | 0o060 | 0o070 => JR(jump_cond((byte >> 3) & 0x03)), + // LD r16, u16 + 0o001 | 0o021 | 0o041 | 0o061 => LD( + LDTarget::Group1(group1((byte >> 4) & 0x03)), + LDSource::ImmediateWord, + ), + // ADD HL, r16 + 0o011 | 0o031 | 0o051 | 0o071 => { + ADD(AddTarget::HL, AddSource::Group1(group1((byte >> 4) & 0x03))) + } + // LD (r16), A + 0o002 | 0o022 | 0o042 | 0o062 => LD( + LDTarget::IndirectGroup2(group2((byte >> 4) & 0x03)), + LDSource::A, + ), + // LD A, (r16) + 0o012 | 0o032 | 0o052 | 0o072 => LD( + LDTarget::A, + LDSource::IndirectGroup2(group2((byte >> 4) & 0x03)), + ), + // INC r16 + 0o003 | 0o023 | 0o043 | 0o063 => INC(AllRegisters::Group1(group1((byte >> 4) & 0x03))), + // DEC r16 + 0o013 | 0o033 | 0o053 | 0o073 => DEC(AllRegisters::Group1(group1((byte >> 4) & 0x03))), + // INC r8 + 0o004 | 0o014 | 0o024 | 0o034 | 0o044 | 0o054 | 0o064 | 0o074 => { + INC(AllRegisters::Register(register((byte >> 3) & 0x07))) + } + // DEC r8 + 0o005 | 0o015 | 0o025 | 0o035 | 0o045 | 0o055 | 0o065 | 0o075 => { + DEC(AllRegisters::Register(register((byte >> 3) & 0x07))) + } + // LD r8, u8 + 0o006 | 0o016 | 0o026 | 0o036 | 0o046 | 0o056 | 0o066 | 0o076 => LD( + LDTarget::Register(register((byte >> 3) & 0x07)), + LDSource::ImmediateByte, + ), + // RLCA, RRCA, RLA, RRA, DAA, CPL, SCF, and CCF + 0o007 | 0o017 | 0o027 | 0o037 | 0o047 | 0o057 | 0o067 | 0o077 => { + flag_instr((byte >> 3) & 0x07) + } + // HALT + 0o166 => HALT, + // LD r8, r8 + 0o100..=0o177 => LD( + LDTarget::Register(register((byte >> 3) & 0x07)), + LDSource::Register(register(byte & 0x07)), + ), + // ADD, ADC, SUB, SBC, AND, XOR, OR, and CP + 0o200..=0o277 => alu_reg_instr((byte >> 3) & 0x07, byte & 0x07), + // RET cond + 0o300 | 0o310 | 0o320 | 0o330 => RET(jump_cond((byte >> 3) & 0x03)), + // LD (0xFF00 + u8), A + 0o340 => LD(LDTarget::IoWithImmediateOffset, LDSource::A), + // ADD SP, i8 + 0o350 => ADD(AddTarget::SP, AddSource::ImmediateSignedByte), + // LD A, (0xFF00 + u8) + 0o360 => LD(LDTarget::A, LDSource::IoWithImmediateOffset), + // LD HL, SP + i8 + 0o370 => LDHL, + // POP r16 + 0o301 | 0o321 | 0o341 | 0o361 => POP(group3((byte >> 4) & 0x03)), + // RET + 0o311 => RET(JumpCondition::Always), + // RETI + 0o331 => RETI, + // JP HL + 0o351 => JP(JumpCondition::Always, JumpLocation::HL), + // LD SP, HL + 0o371 => LD(LDTarget::SP, LDSource::HL), + // JP cond u16 + 0o302 | 0o312 | 0o322 | 0o332 => { + JP(jump_cond((byte >> 3) & 0x03), JumpLocation::ImmediateWord) + } + // LD (0xFF00 + C), A + 0o342 => LD(LDTarget::IoWithC, LDSource::A), + // LD (u16), A + 0o352 => LD(LDTarget::IndirectImmediateWord, LDSource::A), + // LD A, (0xFF00 + C) + 0o362 => LD(LDTarget::A, LDSource::IoWithC), + // LD A, (u16) + 0o372 => LD(LDTarget::A, LDSource::IndirectImmediateWord), + // JP u16 + 0o303 => JP(JumpCondition::Always, JumpLocation::ImmediateWord), + // 0xCB Prefix + 0o313 => unreachable!("{:#04X} should be handled by the prefixed decoder", byte), + // DI + 0o363 => DI, + // EI + 0o373 => EI, + // CALL cond u16 + 0o304 | 0o314 | 0o324 | 0o334 => CALL(jump_cond((byte >> 3) & 0x03)), + // PUSH r16 + 0o305 | 0o325 | 0o345 | 0o365 => PUSH(group3((byte >> 4) & 0x03)), + 0o315 => CALL(JumpCondition::Always), + 0o306 | 0o316 | 0o326 | 0o336 | 0o346 | 0o356 | 0o366 | 0o376 => { + alu_imm_instr((byte >> 3) & 0x07) + } + 0o307 | 0o317 | 0o327 | 0o337 | 0o347 | 0o357 | 0o367 | 0o377 => RST(byte & 0b00111000), + _ => panic!("{:#04X} is an illegal opcode", byte), + } + } + + #[inline] + fn prefixed(byte: u8) -> Self { + use Instruction::*; + + match byte { + // RLC, RRC, RL, RR, SLA, SRA, SWAP and SRL + 0o000..=0o077 => prefix_alu((byte >> 3) & 0x07, byte & 0x07), + // BIT bit, r8 + 0o100..=0o177 => BIT((byte >> 3) & 0x07, register(byte & 0x07)), + // RES bit, r8 + 0o200..=0o277 => RES((byte >> 3) & 0x07, register(byte & 0x07)), + // SET bit, r8 + 0o300..=0o377 => SET((byte >> 3) & 0x07, register(byte & 0x07)), } } } -impl std::fmt::Debug for JumpCondition { +mod jump { + + #[derive(Clone, Copy)] + pub(crate) enum JumpCondition { + Always, + NotZero, + Zero, + NotCarry, + Carry, + } + + impl std::fmt::Debug for JumpCondition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use JumpCondition::*; + + match self { + Always => f.write_str(""), + NotZero => f.write_str("NZ"), + Zero => f.write_str("Z"), + NotCarry => f.write_str("NC"), + Carry => f.write_str("C"), + } + } + } + + #[derive(Clone, Copy)] + pub(crate) enum JumpLocation { + HL, + ImmediateWord, + } + + impl std::fmt::Debug for JumpLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use JumpLocation::*; + + match *self { + HL => f.write_str("HL"), + ImmediateWord => f.write_str("u16"), + } + } + } +} + +#[derive(Clone, Copy)] +pub(crate) enum AllRegisters { + Group1(Group1RegisterPair), + Register(Register), +} + +impl std::fmt::Debug for AllRegisters { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use AllRegisters::*; + + match self { + Group1(rp) => write!(f, "{:?}", rp), + Register(r) => write!(f, "{:?}", r), + } + } +} + +mod alu { + use super::table::Register; + + #[derive(Clone, Copy)] + pub(crate) enum Source { + Register(Register), + ImmediateByte, + } + + impl std::fmt::Debug for Source { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Source::*; + match self { + Register(r) => write!(f, "{:?}", r), + ImmediateByte => f.write_str("u8"), + } + } + } +} + +mod add { + use super::table::{Group1RegisterPair, Register}; + + #[derive(Clone, Copy)] + pub(crate) enum Target { + HL, + A, + SP, + } + + impl std::fmt::Debug for Target { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Target::*; + + match self { + HL => f.write_str("HL"), + A => f.write_str("A"), + SP => f.write_str("SP"), + } + } + } + + #[derive(Clone, Copy)] + pub(crate) enum Source { + Group1(Group1RegisterPair), + Register(Register), + ImmediateSignedByte, + ImmediateByte, + } + + impl std::fmt::Debug for Source { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Source::*; + + match self { + Group1(rp) => write!(f, "{:?}", rp), + Register(r) => write!(f, "{:?}", r), + ImmediateSignedByte => f.write_str("i8"), + ImmediateByte => f.write_str("u8"), + } + } + } +} + +mod load { + use super::table::{Group1RegisterPair, Group2RegisterPair, Register}; + + #[derive(Clone, Copy)] + pub(crate) enum Target { + IndirectImmediateWord, // (u16) + Group1(Group1RegisterPair), + IndirectGroup2(Group2RegisterPair), + A, + Register(Register), + IoWithImmediateOffset, // 0xFF00 + offset + SP, + IoWithC, // 0xFF00 + C + } + + impl std::fmt::Debug for Target { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Target::*; + + match self { + IndirectImmediateWord => f.write_str("(u16)"), + Group1(rp) => write!(f, "{:?}", rp), + IndirectGroup2(rp) => write!(f, "({:?})", rp), + A => f.write_str("A"), + Register(r) => write!(f, "{:?}", r), + IoWithImmediateOffset => f.write_str("(0xFF00 + u8)"), + SP => f.write_str("SP"), + IoWithC => f.write_str("(0xFF00 + C)"), + } + } + } + + #[derive(Clone, Copy)] + pub(crate) enum Source { + SP, + ImmediateWord, // u16 + ImmediateByte, // u8 + A, + IndirectGroup2(Group2RegisterPair), + Register(Register), + IoWithImmediateOffset, // 0xFF00 + offset + HL, + IoWithC, + IndirectImmediateWord, // (u16) + } + + impl std::fmt::Debug for Source { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Source::*; + + match self { + SP => f.write_str("SP"), + ImmediateWord => f.write_str("u16"), + ImmediateByte => f.write_str("u8"), + A => f.write_str("A"), + IndirectGroup2(rp) => write!(f, "({:?})", rp), + Register(r) => write!(f, "{:?}", r), + IoWithImmediateOffset => f.write_str("(0xFF00 + u8)"), + HL => f.write_str("HL"), + IoWithC => f.write_str("(0xFF00 + C)"), + IndirectImmediateWord => f.write_str("(u16)"), + } + } + } +} + +mod table { + use super::add::{Source as AddSource, Target as AddTarget}; + use super::alu::Source as AluSource; + use super::{Instruction, JumpCondition}; + use crate::cpu::{Register as CpuRegister, RegisterPair}; + + #[derive(Clone, Copy)] + pub(crate) enum Group1RegisterPair { + BC, + DE, + HL, + SP, + } + + impl std::fmt::Debug for Group1RegisterPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Group1RegisterPair::*; + + match self { + BC => f.write_str("BC"), + DE => f.write_str("DE"), + HL => f.write_str("HL"), + SP => f.write_str("SP"), + } + } + } + + impl Group1RegisterPair { + pub fn as_register_pair(&self) -> RegisterPair { + use Group1RegisterPair::*; + + match self { + BC => RegisterPair::BC, + DE => RegisterPair::DE, + HL => RegisterPair::HL, + SP => RegisterPair::SP, + } + } + } + + #[derive(Clone, Copy)] + pub(crate) enum Group2RegisterPair { + BC, + DE, + IncrementHL, + DecrementHL, + } + + impl std::fmt::Debug for Group2RegisterPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Group2RegisterPair::*; + + match self { + BC => f.write_str("BC"), + DE => f.write_str("DE"), + IncrementHL => f.write_str("HL+"), + DecrementHL => f.write_str("HL-"), + } + } + } + + impl Group2RegisterPair { + pub fn as_register_pair(&self) -> RegisterPair { + use Group2RegisterPair::*; + + match self { + BC => RegisterPair::BC, + DE => RegisterPair::DE, + IncrementHL => RegisterPair::HL, + DecrementHL => RegisterPair::HL, + } + } + } + + #[derive(Clone, Copy)] + pub(crate) enum Group3RegisterPair { + BC, + DE, + HL, + AF, + } + + impl std::fmt::Debug for Group3RegisterPair { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Group3RegisterPair::*; + + match self { + BC => f.write_str("BC"), + DE => f.write_str("DE"), + HL => f.write_str("HL"), + AF => f.write_str("AF"), + } + } + } + + impl Group3RegisterPair { + pub fn as_register_pair(&self) -> RegisterPair { + use Group3RegisterPair::*; + + match self { + BC => RegisterPair::BC, + DE => RegisterPair::DE, + HL => RegisterPair::HL, + AF => RegisterPair::AF, + } + } + } + + #[derive(Clone, Copy)] + pub(crate) enum Register { + B, + C, + D, + E, + H, + L, + IndirectHL, + A, + } + + impl std::fmt::Debug for Register { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use Register::*; + + match self { + B => f.write_str("B"), + C => f.write_str("C"), + D => f.write_str("D"), + E => f.write_str("E"), + H => f.write_str("H"), + L => f.write_str("L"), + IndirectHL => f.write_str("(HL)"), + A => f.write_str("A"), + } + } + } + + impl Register { + pub fn cpu_register(&self) -> CpuRegister { + use Register::*; + + match self { + B => CpuRegister::B, + C => CpuRegister::C, + D => CpuRegister::D, + E => CpuRegister::E, + H => CpuRegister::H, + L => CpuRegister::L, + A => CpuRegister::A, + IndirectHL => panic!("Register::HL doesn't map onto CpuRegister"), + } + } + } + + #[inline] + pub(crate) fn group1(code: u8) -> Group1RegisterPair { + use Group1RegisterPair::*; + + match code { + 0b00 => BC, + 0b01 => DE, + 0b10 => HL, + 0b11 => SP, + _ => unreachable!("{:#04X} is not a valid Group 1 Register Pair", code), + } + } + + #[inline] + pub(crate) fn group2(code: u8) -> Group2RegisterPair { + use Group2RegisterPair::*; + + match code { + 0b00 => BC, + 0b01 => DE, + 0b10 => IncrementHL, + 0b11 => DecrementHL, + _ => unreachable!("{:#04X} is not a valid Group 2 Register Pair", code), + } + } + + #[inline] + pub(crate) fn group3(code: u8) -> Group3RegisterPair { + use Group3RegisterPair::*; + + match code { + 0b00 => BC, + 0b01 => DE, + 0b10 => HL, + 0b11 => AF, + _ => unreachable!("{:#04X} is not a valid Group 3 Register Pair", code), + } + } + + #[inline] + pub(crate) fn register(code: u8) -> Register { + use Register::*; + + match code { + 0b000 => B, + 0b001 => C, + 0b010 => D, + 0b011 => E, + 0b100 => H, + 0b101 => L, + 0b110 => IndirectHL, + 0b111 => A, + _ => unreachable!("{:#04X} is not a valid Register"), + } + } + + #[inline] + pub(crate) fn jump_cond(code: u8) -> JumpCondition { use JumpCondition::*; - match *self { - NotZero => f.write_str("NZ"), - Zero => f.write_str("Z"), - NotCarry => f.write_str("NC"), - Carry => f.write_str("C"), - Always => f.write_str(""), + match code { + 0b00 => NotZero, + 0b01 => Zero, + 0b10 => NotCarry, + 0b11 => Carry, + _ => unreachable!("{:#04X} is not a valid JumpCondition", code), + } + } + + #[inline] + pub(crate) fn flag_instr(code: u8) -> Instruction { + use Instruction::*; + + match code { + 0b000 => RLCA, + 0b001 => RRCA, + 0b010 => RLA, + 0b011 => RRA, + 0b100 => DAA, + 0b101 => CPL, + 0b110 => SCF, + 0b111 => CCF, + _ => unreachable!("{:#04X} is not a valid flag opcode code", code), + } + } + + #[inline] + pub(crate) fn alu_reg_instr(alu_code: u8, reg_code: u8) -> Instruction { + use Instruction::*; + + match alu_code { + 0b000 => ADD(AddTarget::A, AddSource::Register(register(reg_code))), + 0b001 => ADC(AluSource::Register(register(reg_code))), + 0b010 => SUB(AluSource::Register(register(reg_code))), + 0b011 => SBC(AluSource::Register(register(reg_code))), + 0b100 => AND(AluSource::Register(register(reg_code))), + 0b101 => XOR(AluSource::Register(register(reg_code))), + 0b110 => OR(AluSource::Register(register(reg_code))), + 0b111 => CP(AluSource::Register(register(reg_code))), + _ => unreachable!("{:#04X} is not a valid alu reg instruction code", alu_code), + } + } + + #[inline] + pub(crate) fn alu_imm_instr(code: u8) -> Instruction { + use Instruction::*; + + match code { + 0b000 => ADD(AddTarget::A, AddSource::ImmediateByte), + 0b001 => ADC(AluSource::ImmediateByte), + 0b010 => SUB(AluSource::ImmediateByte), + 0b011 => SBC(AluSource::ImmediateByte), + 0b100 => AND(AluSource::ImmediateByte), + 0b101 => XOR(AluSource::ImmediateByte), + 0b110 => OR(AluSource::ImmediateByte), + 0b111 => CP(AluSource::ImmediateByte), + _ => unreachable!("{:#04X} is not a valid alu imm instruction code", code), + } + } + + #[inline] + pub(crate) fn prefix_alu(alu_code: u8, reg_code: u8) -> Instruction { + use Instruction::*; + + match alu_code { + 0b000 => RLC(register(reg_code)), + 0b001 => RRC(register(reg_code)), + 0b010 => RL(register(reg_code)), + 0b011 => RR(register(reg_code)), + 0b100 => SLA(register(reg_code)), + 0b101 => SRA(register(reg_code)), + 0b110 => SWAP(register(reg_code)), + 0b111 => SRL(register(reg_code)), + _ => unreachable!("{:#04X} is not a valid pfx alu instruction code", alu_code), } } } -impl std::fmt::Debug for InstrRegister { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - use InstrRegister::*; +pub(crate) mod cycle { - match *self { - A => f.write_str("A"), - B => f.write_str("B"), - C => f.write_str("C"), - D => f.write_str("D"), - E => f.write_str("E"), - H => f.write_str("H"), - L => f.write_str("L"), - IndirectHL => f.write_str("(HL)"), + #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] + #[repr(transparent)] + pub struct Cycle(u32); + + impl Cycle { + pub const fn new(num: u32) -> Self { + Self(num) } } -} -impl Cycle { - pub const fn new(num: u32) -> Self { - Self(num) + impl std::ops::Add for Cycle { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } } -} -impl std::ops::Add for Cycle { - type Output = Self; + impl std::ops::Add for Cycle { + type Output = Self; - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) + fn add(self, rhs: u32) -> Self::Output { + Self(self.0 + rhs) + } } -} -impl std::ops::Add for Cycle { - type Output = Self; - - fn add(self, rhs: u32) -> Self::Output { - Self(self.0 + rhs) + impl std::ops::AddAssign for Cycle { + fn add_assign(&mut self, rhs: Self) { + *self = Self(self.0 + rhs.0); + } } -} -impl std::ops::AddAssign for Cycle { - fn add_assign(&mut self, rhs: Self) { - *self = Self(self.0 + rhs.0); + impl std::ops::AddAssign for Cycle { + fn add_assign(&mut self, rhs: u32) { + *self = Self(self.0 + rhs); + } } -} -impl std::ops::AddAssign for Cycle { - fn add_assign(&mut self, rhs: u32) { - *self = Self(self.0 + rhs); + impl std::ops::Rem for Cycle { + type Output = Self; + + fn rem(self, rhs: Self) -> Self::Output { + Self(self.0 % rhs.0) + } } -} -impl std::ops::Rem for Cycle { - type Output = Self; + impl std::ops::Rem for Cycle { + type Output = Self; - fn rem(self, rhs: Self) -> Self::Output { - Self(self.0 % rhs.0) + fn rem(self, rhs: u32) -> Self::Output { + Self(self.0 % rhs) + } } -} -impl std::ops::Rem for Cycle { - type Output = Self; - - fn rem(self, rhs: u32) -> Self::Output { - Self(self.0 % rhs) + impl std::ops::RemAssign for Cycle { + fn rem_assign(&mut self, rhs: Self) { + *self = Self(self.0 % rhs.0); + } } -} -impl std::ops::RemAssign for Cycle { - fn rem_assign(&mut self, rhs: Self) { - *self = Self(self.0 % rhs.0); + impl std::ops::RemAssign for Cycle { + fn rem_assign(&mut self, rhs: u32) { + *self = Self(self.0 % rhs); + } } -} -impl std::ops::RemAssign for Cycle { - fn rem_assign(&mut self, rhs: u32) { - *self = Self(self.0 % rhs); + impl std::ops::Sub for Cycle { + type Output = Cycle; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } } -} -impl std::ops::Sub for Cycle { - type Output = Cycle; + impl std::ops::Sub for Cycle { + type Output = Cycle; - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) + fn sub(self, rhs: u32) -> Self::Output { + Self(self.0 - rhs) + } } -} -impl std::ops::Sub for Cycle { - type Output = Cycle; - - fn sub(self, rhs: u32) -> Self::Output { - Self(self.0 - rhs) + impl std::ops::SubAssign for Cycle { + fn sub_assign(&mut self, rhs: Self) { + *self = Self(self.0 - rhs.0); + } } -} -impl std::ops::SubAssign for Cycle { - fn sub_assign(&mut self, rhs: Self) { - *self = Self(self.0 - rhs.0); + impl std::ops::SubAssign for Cycle { + fn sub_assign(&mut self, rhs: u32) { + *self = Self(self.0 - rhs); + } } -} -impl std::ops::SubAssign for Cycle { - fn sub_assign(&mut self, rhs: u32) { - *self = Self(self.0 - rhs); + impl PartialEq for Cycle { + fn eq(&self, other: &u32) -> bool { + self.0 == *other + } } -} -impl PartialEq for Cycle { - fn eq(&self, other: &u32) -> bool { - self.0 == *other + impl std::ops::Div for Cycle { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + Self::new(self.0 / rhs.0) + } } -} -impl std::ops::Div for Cycle { - type Output = Self; + impl std::ops::Div for Cycle { + type Output = Self; - fn div(self, rhs: Self) -> Self::Output { - Self::new(self.0 / rhs.0) + fn div(self, rhs: u32) -> Self::Output { + Self::new(self.0 / rhs) + } } -} -impl std::ops::Div for Cycle { - type Output = Self; - - fn div(self, rhs: u32) -> Self::Output { - Self::new(self.0 / rhs) + impl From for Cycle { + fn from(num: u32) -> Self { + Self(num) + } } -} -impl From for Cycle { - fn from(num: u32) -> Self { - Self(num) - } -} - -impl From for u32 { - fn from(cycles: Cycle) -> Self { - cycles.0 - } -} - -impl InstrRegisterPair { - fn to_register_pair(self) -> RegisterPair { - RegisterPair::try_from(self).expect("InstrRegisterPair is a valid RegisterPair") - } -} - -impl InstrRegister { - fn to_register(self) -> Register { - Register::try_from(self).expect("InstrRegister is a valid Register") + impl From for u32 { + fn from(cycles: Cycle) -> Self { + cycles.0 + } } } diff --git a/src/lib.rs b/src/lib.rs index 6ba0645..0f7e491 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ pub use apu::gen::AudioSPSC; -pub use instruction::Cycle; +pub use instruction::cycle::Cycle; pub const GB_WIDTH: usize = 160; pub const GB_HEIGHT: usize = 144; diff --git a/src/ppu.rs b/src/ppu.rs index aa04567..eab435f 100644 --- a/src/ppu.rs +++ b/src/ppu.rs @@ -1,5 +1,5 @@ use crate::bus::BusIo; -use crate::instruction::Cycle; +use crate::instruction::cycle::Cycle; use crate::GB_HEIGHT; use crate::GB_WIDTH; use dma::DirectMemoryAccess; diff --git a/src/ppu/dma.rs b/src/ppu/dma.rs index 129d9ca..fe47f78 100644 --- a/src/ppu/dma.rs +++ b/src/ppu/dma.rs @@ -1,4 +1,4 @@ -use crate::instruction::Cycle; +use crate::instruction::cycle::Cycle; #[derive(Debug, Default)] pub(crate) struct DirectMemoryAccess {