diff --git a/src/register.rs b/src/register.rs index 7be8ecb..259276b 100644 --- a/src/register.rs +++ b/src/register.rs @@ -3,7 +3,7 @@ const INITIAL_PROGRAM_COUNTER_VALUE: u16 = 0x100; -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub enum Flag { Zero, Subtract, @@ -11,6 +11,16 @@ pub enum Flag { Carry, } +// Combined represents a pair of two registers taht can be acted on as a single +// 16 bit register. +#[derive(Debug, Clone, Copy)] +pub enum Combined { + AF, + BC, + DE, + HL, +} + /// Registers holds all of the registers for the Gameboy #[derive(Debug, Clone)] pub struct Registers { @@ -90,6 +100,45 @@ impl Registers { // ... and set it to our new value self.flags |= bit << pos; } + + /// Get the value of a pair of registers + pub fn get_combined_register(&self, registers: Combined) -> u16 { + let (left_register, right_register) = self.get_register_values_for_combined(registers); + let mut res = 0_u16; + res |= u16::from(left_register) << 8; + res |= u16::from(right_register); + + res + } + + /// Set the value of a pair of registers + pub fn set_combined_register(&mut self, registers: Combined, value: u16) { + let (left_register, right_register) = self.get_register_mut_refs_for_combined(registers); + let [left_value, right_value] = value.to_be_bytes(); + + *left_register = left_value; + *right_register = right_value; + } + + // TODO: These two could be simplified with macros + + fn get_register_values_for_combined(&self, registers: Combined) -> (u8, u8) { + match registers { + Combined::AF => (self.a, self.f), + Combined::BC => (self.b, self.c), + Combined::DE => (self.d, self.e), + Combined::HL => (self.h, self.l), + } + } + + fn get_register_mut_refs_for_combined(&mut self, registers: Combined) -> (&mut u8, &mut u8) { + match registers { + Combined::AF => (&mut self.a, &mut self.f), + Combined::BC => (&mut self.b, &mut self.c), + Combined::DE => (&mut self.d, &mut self.e), + Combined::HL => (&mut self.h, &mut self.l), + } + } } #[cfg(test)] @@ -109,4 +158,74 @@ mod tests { registers.set_flag_bit(flag, 0); assert_eq!(0, registers.get_flag_bit(flag)); } + + #[test_case(Combined::AF)] + #[test_case(Combined::BC)] + #[test_case(Combined::DE)] + #[test_case(Combined::HL)] + fn test_combined_registers(registers: Combined) { + let mut register = Registers::new(); + register.set_combined_register(registers, 0xF0_0F); + + let stored_value = register.get_combined_register(registers); + assert_eq!( + 0xF0_0F, stored_value, + "got {stored_value:0x}, expected 0xF0_0F" + ); + } + + // I don't think there's a nice way to parametrize these tests without + // it being more work than its worth + + #[test] + fn test_get_combined_registers_af() { + let mut registers = Registers::new(); + registers.a = 0b11_10_00_01; + registers.f = 0b10_00_01_11; + + let combined_value = registers.get_combined_register(Combined::AF); + assert_eq!( + 0b11_10_00_01_10_00_01_11, combined_value, + "got {combined_value:0b}, expected 1110000110000111" + ); + } + + #[test] + fn test_get_combined_registers_bc() { + let mut registers = Registers::new(); + registers.b = 0b00_11_00_11; + registers.c = 0b11_00_11_00; + + let combined_value = registers.get_combined_register(Combined::BC); + assert_eq!( + 0b00_11_00_11_11_00_11_00, combined_value, + "got {combined_value:0b}, expected 0011001111001100" + ); + } + + #[test] + fn test_get_combined_registers_de() { + let mut registers = Registers::new(); + registers.d = 0b11_10_00_01; + registers.e = 0b10_00_01_11; + + let combined_value = registers.get_combined_register(Combined::DE); + assert_eq!( + 0b11_10_00_01_10_00_01_11, combined_value, + "got {combined_value:0b}, expected 1110000110000111" + ); + } + + #[test] + fn test_get_combined_registers_hl() { + let mut registers = Registers::new(); + registers.h = 0b00_11_00_11; + registers.l = 0b11_00_11_00; + + let combined_value = registers.get_combined_register(Combined::HL); + assert_eq!( + 0b00_11_00_11_11_00_11_00, combined_value, + "got {combined_value:0b}, expected 0011001111001100" + ); + } }