Add support for combined registers

jsmoo
Nick Krichevsky 2022-04-01 19:47:41 -04:00
parent 607e4ab2df
commit ac6097794d
1 changed files with 120 additions and 1 deletions

View File

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