From 31af5a11a3480d3ac8a65d709e281d4be2833d5f Mon Sep 17 00:00:00 2001 From: Cedrik Hoffmann Date: Sat, 18 May 2024 18:21:52 +0200 Subject: [PATCH] cpu: add more instructions --- gameboy-lib/src/cpu/command/misc_commands.rs | 18 ++ gameboy-lib/src/cpu/command/mod.rs | 3 + .../src/cpu/command/rotate_commands.rs | 229 ++++++++++++++++++ gameboy-lib/src/cpu/mod.rs | 203 ++++++++++++++++ 4 files changed, 453 insertions(+) create mode 100644 gameboy-lib/src/cpu/command/rotate_commands.rs diff --git a/gameboy-lib/src/cpu/command/misc_commands.rs b/gameboy-lib/src/cpu/command/misc_commands.rs index 405818e..9d854fb 100644 --- a/gameboy-lib/src/cpu/command/misc_commands.rs +++ b/gameboy-lib/src/cpu/command/misc_commands.rs @@ -37,6 +37,22 @@ impl<'a> MiscCommand<'a> { return pc; } + + fn ccf(&mut self) -> u16 { + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = !self.cpu.registers.f.carry; + + self.cpu.pc.wrapping_add(1) + } + + fn scf(&mut self) -> u16 { + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = true; + + self.cpu.pc.wrapping_add(1) + } } impl Command for MiscCommand<'_> { @@ -44,6 +60,8 @@ impl Command for MiscCommand<'_> { match &self.instruction { MiscInstruction::Nop => self.nop(), MiscInstruction::Swap(from) => self.swap(from), + MiscInstruction::CCF => self.ccf(), + MiscInstruction::SCF => self.scf(), _ => unimplemented!(), } } diff --git a/gameboy-lib/src/cpu/command/mod.rs b/gameboy-lib/src/cpu/command/mod.rs index 1495cd1..a4a91a2 100644 --- a/gameboy-lib/src/cpu/command/mod.rs +++ b/gameboy-lib/src/cpu/command/mod.rs @@ -5,6 +5,7 @@ use super::{instructions::Instruction, Cpu}; pub mod alu_commands; pub mod load_commands; pub mod misc_commands; +pub mod rotate_commands; pub trait Command { fn execute(&mut self) -> u16; @@ -32,6 +33,7 @@ impl<'a> CommandFactory<'a> { Instruction::Load(load_command) => Box::new(LoadCommand::new(load_command, self.cpu)), Instruction::Arithmetic(alu_command) => Box::new(ArithmeticCommand::new(alu_command, self.cpu)), Instruction::Misc(misc_command) => Box::new(misc_commands::MiscCommand::new(&misc_command, self.cpu)), + Instruction::Rotate(rotate_command) => Box::new(rotate_commands::RotateCommand::new(rotate_command, self.cpu)), _ => unimplemented!(), } } @@ -39,6 +41,7 @@ impl<'a> CommandFactory<'a> { fn prefix_command(&'a mut self, instruction: &'a Instruction) -> Box { match instruction { Instruction::Misc(misc_command) => Box::new(misc_commands::MiscCommand::new(&misc_command, self.cpu)), + Instruction::Rotate(rotate_command) => Box::new(rotate_commands::RotateCommand::new(rotate_command, self.cpu)), _ => unimplemented!(), } } diff --git a/gameboy-lib/src/cpu/command/rotate_commands.rs b/gameboy-lib/src/cpu/command/rotate_commands.rs new file mode 100644 index 0000000..e7ffcf6 --- /dev/null +++ b/gameboy-lib/src/cpu/command/rotate_commands.rs @@ -0,0 +1,229 @@ +use crate::cpu::{instructions::RotateInstruction, registers::Register, Cpu}; + +use super::Command; + + +pub struct RotateCommand<'a> { + instruction: &'a RotateInstruction, + cpu: &'a mut Cpu, +} + +impl<'a> RotateCommand<'a> { + pub fn new(instruction: &'a RotateInstruction, cpu: &'a mut Cpu) -> RotateCommand<'a> { + RotateCommand { instruction, cpu } + } + + fn rlca(&mut self) -> u16 { + let value = self.cpu.registers.a; + let carry = value >> 7; + let result = (value << 1) | carry; + + self.cpu.registers.a = result; + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + self.cpu.pc.wrapping_add(1) + } + + fn rla(&mut self) -> u16 { + let value = self.cpu.registers.a; + let carry = value >> 7; + let result = (value << 1) | self.cpu.registers.f.carry as u8; + + self.cpu.registers.a = result; + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + self.cpu.pc.wrapping_add(1) + } + + fn rrca(&mut self) -> u16 { + let value = self.cpu.registers.a; + let carry = value & 1; + let result = (value >> 1) | (carry << 7); + + self.cpu.registers.a = result; + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + self.cpu.pc.wrapping_add(1) + } + + fn rra(&mut self) -> u16 { + let value = self.cpu.registers.a; + let carry = value & 1; + let result = (value >> 1) | ((self.cpu.registers.f.carry as u8) << 7); + + self.cpu.registers.a = result; + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + self.cpu.pc.wrapping_add(1) + } + + fn rlc(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value >> 7; + let result = (value << 1) | carry; + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } + + fn rl(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value >> 7; + let result = (value << 1) | self.cpu.registers.f.carry as u8; + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } + + fn rrc(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value & 1; + let result = (value >> 1) | (carry << 7); + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } + + fn rr(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value & 1; + let result = (value >> 1) | ((self.cpu.registers.f.carry as u8) << 7); + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } + + fn sla(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value >> 7; + let result = value << 1; + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } + + fn sra(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value & 1; + let result = (value >> 1) | (value & 0x80); + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } + + fn srl(&mut self, register: &Register) -> u16 { + let (value, pc) = self.cpu.extract_operand(register); + let carry = value & 1; + let result = value >> 1; + + if let Register::HL = register { + self.cpu.memory + .write(self.cpu.registers.get_16(&Register::HL), result); + } else { + self.cpu.registers.set(register, result); + } + + self.cpu.registers.f.zero = result == 0; + self.cpu.registers.f.subtract = false; + self.cpu.registers.f.half_carry = false; + self.cpu.registers.f.carry = carry == 1; + + return pc; + } +} + +impl Command for RotateCommand<'_> { + fn execute(&mut self) -> u16 { + match &self.instruction { + RotateInstruction::RLCA => self.rlca(), + RotateInstruction::RLA => self.rla(), + RotateInstruction::RRCA => self.rrca(), + RotateInstruction::RRA => self.rra(), + RotateInstruction::RLC(register) => self.rlc(register), + RotateInstruction::RL(register) => self.rl(register), + RotateInstruction::RRC(register) => self.rrc(register), + RotateInstruction::RR(register) => self.rr(register), + RotateInstruction::SLA(register) => self.sla(register), + RotateInstruction::SRA(register) => self.sra(register), + RotateInstruction::SRL(register) => self.srl(register), + } + } +} diff --git a/gameboy-lib/src/cpu/mod.rs b/gameboy-lib/src/cpu/mod.rs index 74e8621..e5fed3c 100644 --- a/gameboy-lib/src/cpu/mod.rs +++ b/gameboy-lib/src/cpu/mod.rs @@ -1553,4 +1553,207 @@ mod tests { assert_eq!(cpu.registers.f.half_carry, false); assert_eq!(cpu.registers.f.carry, false); } + + #[test] + fn execute_ccf() { + let mut cpu = Cpu::new(); + + cpu.boot(vec![0x3F, 0x3F]); + + cpu.registers.f.subtract = true; + cpu.registers.f.half_carry = true; + cpu.registers.f.carry = true; + cpu.step(); + assert_eq!(cpu.registers.f.subtract, false); + assert_eq!(cpu.registers.f.half_carry, false); + assert_eq!(cpu.registers.f.carry, false); + + cpu.registers.f.subtract = false; + cpu.registers.f.half_carry = true; + cpu.registers.f.carry = false; + cpu.step(); + assert_eq!(cpu.registers.f.subtract, false); + assert_eq!(cpu.registers.f.half_carry, false); + assert_eq!(cpu.registers.f.carry, true); + } + + #[test] + fn execute_scf() { + let mut cpu = Cpu::new(); + + cpu.boot(vec![0x37, 0x37]); + + cpu.registers.f.subtract = true; + cpu.registers.f.half_carry = true; + cpu.registers.f.carry = true; + cpu.step(); + assert_eq!(cpu.registers.f.subtract, false); + assert_eq!(cpu.registers.f.half_carry, false); + assert_eq!(cpu.registers.f.carry, true); + + cpu.registers.f.subtract = false; + cpu.registers.f.half_carry = true; + cpu.registers.f.carry = false; + cpu.step(); + assert_eq!(cpu.registers.f.subtract, false); + assert_eq!(cpu.registers.f.half_carry, false); + assert_eq!(cpu.registers.f.carry, true); + } + + #[test] + fn execute_rlca() { + let mut cpu = Cpu::new(); + cpu.boot(vec![0x07, 0x07]); + + cpu.registers.f.zero = true; + cpu.registers.f.subtract = true; + cpu.registers.f.half_carry = true; + cpu.registers.f.carry = true; + cpu.registers.set(&Register::A, 0b10000000); + cpu.step(); + + assert_eq!(cpu.registers.get(&Register::A), 0b00000001); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.subtract, false); + assert_eq!(cpu.registers.f.half_carry, false); + assert_eq!(cpu.registers.f.carry, true); + + cpu.registers.set(&Register::A, 0b00000001); + cpu.registers.f.zero = true; + cpu.registers.f.subtract = true; + cpu.registers.f.half_carry = true; + cpu.registers.f.carry = true; + cpu.step(); + + assert_eq!(cpu.registers.get(&Register::A), 0b00000010); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.subtract, false); + assert_eq!(cpu.registers.f.half_carry, false); + assert_eq!(cpu.registers.f.carry, false); + } + + #[test] + fn execute_rla() { + let mut cpu = Cpu::new(); + + cpu.boot(vec![0x17, 0x17]); + + cpu.registers.set(&Register::A, 0b10000000); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b00000000); + assert_eq!(cpu.registers.f.zero, true); + assert_eq!(cpu.registers.f.carry, true); + + cpu.registers.set(&Register::A, 0b00000001); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b00000011); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, false); + } + + #[test] + fn execute_rrca() { + let mut cpu = Cpu::new(); + + cpu.boot(vec![0x0F, 0x0F]); + + cpu.registers.set(&Register::A, 0b00000001); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b10000000); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, true); + + cpu.registers.set(&Register::A, 0b10000000); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b01000000); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, false); + } + + #[test] + fn execute_rra() { + let mut cpu = Cpu::new(); + + cpu.boot(vec![0x1F, 0x1F]); + + assert_eq!(cpu.registers.f.carry, false); + cpu.registers.set(&Register::A, 0b00000001); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b00000000); + assert_eq!(cpu.registers.f.zero, true); + assert_eq!(cpu.registers.f.carry, true); + + assert_eq!(cpu.registers.f.carry, true); + cpu.registers.set(&Register::A, 0b00000010); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b10000001); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, false); + } + + #[test] + fn execute_rlc() { + let mut cpu = Cpu::new(); + cpu.boot(vec![ + 0xCB, 0x00, 0xCB, 0x01, 0xCB, 0x02, 0xCB, 0x03, 0xCB, 0x04, 0xCB, 0x05, 0xCB, 0x06, + 0xCB, 0x07, + ]); + + // RLC B + cpu.registers.set(&Register::B, 0b10000000); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::B), 0b00000001); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, true); + + // RLC C + cpu.registers.set(&Register::C, 0b00000001); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::C), 0b00000010); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, false); + + // RLC D + cpu.registers.set(&Register::D, 0b00000000); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::D), 0b00000000); + assert_eq!(cpu.registers.f.zero, true); + assert_eq!(cpu.registers.f.carry, false); + + // RLC E + cpu.registers.set(&Register::E, 0b00000001); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::E), 0b00000010); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, false); + + // RLC H + cpu.registers.set(&Register::H, 0b10000000); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::H), 0b00000001); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, true); + + // RLC L + cpu.registers.set(&Register::L, 0b00000001); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::L), 0b00000010); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, false); + + // RLC (HL) + cpu.memory.write(0x00FF, 0b10000000); + cpu.registers.set_16(&Register::HL, 0x00FF); + cpu.step(); + assert_eq!(cpu.memory.read(0x00FF), 0b00000001); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, true); + + // RLC A + cpu.registers.set(&Register::A, 0b10000000); + cpu.step(); + assert_eq!(cpu.registers.get(&Register::A), 0b00000001); + assert_eq!(cpu.registers.f.zero, false); + assert_eq!(cpu.registers.f.carry, true); + } }