ferris-boi/tests/cpu/arith8.rs

388 lines
17 KiB
Rust

use ferris_boi::{
cpu::{instructions::RunnableInstruction, Processor},
register,
};
use test_case::test_case;
fn set_opposite_of_expected_flags(
processor: &mut Processor,
(zero_flag, sub_flag, half_carry_flag, carry_flag): (u8, u8, u8, u8),
) {
let invert_flag = |value| {
if value == 0 {
1
} else if value == 1 {
0
} else {
panic!("invalid flag value of {value} ")
}
};
processor
.registers
.set_flag_bit(register::Flag::Zero, invert_flag(zero_flag));
processor
.registers
.set_flag_bit(register::Flag::HalfCarry, invert_flag(half_carry_flag));
processor
.registers
.set_flag_bit(register::Flag::Carry, invert_flag(carry_flag));
processor
.registers
.set_flag_bit(register::Flag::Subtract, invert_flag(sub_flag));
}
macro_rules! assert_flags_eq {
($processor: expr, $(($flag: path, $value: expr)),+ $(,)?) => {
$(
assert_eq!(
$value,
$processor.registers.get_flag_bit($flag),
"{:?} flag had unexpected value",
$flag
);
)+
};
}
#[test]
fn test_add_a_to_itself() {
let mut processor = Processor::default();
processor.registers.a = 10;
let data = [0x87, 0x02];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run(&ins);
assert_eq!(20, processor.registers.a);
}
#[test_case(0x80, register::SingleEightBit::B)]
#[test_case(0x81, register::SingleEightBit::C)]
#[test_case(0x82, register::SingleEightBit::D)]
#[test_case(0x83, register::SingleEightBit::E)]
#[test_case(0x84, register::SingleEightBit::H)]
#[test_case(0x85, register::SingleEightBit::L)]
fn test_add_to_a_value(opcode: u8, src: register::SingleEightBit) {
let mut processor = Processor::default();
processor.registers.a = 10;
processor.registers.set_single_8bit_register(src, 20);
let data = [opcode, 0x02];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run(&ins);
assert_eq!(30, processor.registers.a);
}
#[test_case(0x80, register::SingleEightBit::B, 0x00, 0x00, 1, 0, 0; "zero flag for zero value")]
#[test_case(0x80, register::SingleEightBit::B, 0x00, 0x01, 0, 0, 0; "no zero flag for non-zero value")]
#[test_case(0x81, register::SingleEightBit::C, 0x0F, 0x01, 0, 1, 0; "half carry flag")]
#[test_case(0x81, register::SingleEightBit::C, 0x80, 0x80, 0, 0, 1; "full carry flag")]
#[test_case(0x81, register::SingleEightBit::C, 0xFF, 0x01, 0, 1, 1; "both full and half carry flag")]
// 0000 1111
fn test_add_register_to_a_flags(
opcode: u8,
src: register::SingleEightBit,
a_value: u8,
src_value: u8,
zero_flag: u8,
half_carry_flag: u8,
carry_flag: u8,
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
// Set all the register to the opposite we expect to ensure they all get set
set_opposite_of_expected_flags(&mut processor, (zero_flag, 0, half_carry_flag, carry_flag));
processor.registers.set_single_8bit_register(src, src_value);
let data = [opcode, 0x02];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run(&ins);
assert_flags_eq!(
processor,
(register::Flag::Zero, zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, half_carry_flag),
(register::Flag::Carry, carry_flag),
);
}
#[test]
fn test_add_hl_addr_to_a_value() {
let mut processor = Processor::default();
processor
.memory
.set(0xFF00, 0x34)
.expect("expected to be able to set 0xFF00");
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor.registers.a = 0x12;
let data = [0x86, 0x02];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run(&ins);
assert_eq!(0x46, processor.registers.a);
}
#[test_case(0x00, 0x00, 1, 0, 0; "zero flag for zero value")]
#[test_case(0x00, 0x01, 0, 0, 0; "no zero flag for non-zero value")]
#[test_case(0x0F, 0x01, 0, 1, 0; "half carry flag")]
#[test_case(0x80, 0x80, 0, 0, 1; "full carry flag")]
#[test_case(0xFF, 0x01, 0, 1, 1; "both full and half carry flag")]
fn test_add_hl_addr_to_a_flags(
a_value: u8,
hl_addr_value: u8,
zero_flag: u8,
half_carry_flag: u8,
carry_flag: u8,
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, hl_addr_value)
.expect("expected to set address 0xFF00 but could not");
// Set all the register to the opposite we expect to ensure they all get set
set_opposite_of_expected_flags(&mut processor, (zero_flag, 0, half_carry_flag, carry_flag));
let data = [0x86, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&ins);
assert_flags_eq!(
processor,
(register::Flag::Zero, zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, half_carry_flag),
(register::Flag::Carry, carry_flag),
);
}
#[test]
fn test_add_immediate_to_a_value() {
let mut processor = Processor::default();
processor.registers.a = 50;
let data = [0xC6, 0x13, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&ins);
assert_eq!(69, processor.registers.a);
}
#[test_case(0x00, 0x00, 1, 0, 0; "zero flag for zero value")]
#[test_case(0x00, 0x01, 0, 0, 0; "no zero flag for non-zero value")]
#[test_case(0x0F, 0x01, 0, 1, 0; "half carry flag")]
#[test_case(0x80, 0x80, 0, 0, 1; "full carry flag")]
#[test_case(0xFF, 0x01, 0, 1, 1; "both full and half carry flag")]
fn test_add_immediate_to_a_flags(
a_value: u8,
n: u8,
zero_flag: u8,
half_carry_flag: u8,
carry_flag: u8,
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
// Set all the register to the opposite we expect to ensure they all get set
set_opposite_of_expected_flags(&mut processor, (zero_flag, 0, half_carry_flag, carry_flag));
let data = [0xC6, n, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&ins);
assert_flags_eq!(
processor,
(register::Flag::Zero, zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, half_carry_flag),
(register::Flag::Carry, carry_flag),
);
}
#[test_case(0x88, 0x12, 0, register::SingleEightBit::B, 0x5, 0x17; "no carry to register b")]
#[test_case(0x88, 0x12, 1, register::SingleEightBit::B, 0x5, 0x18; "carry to register b")]
#[test_case(0x89, 0x12, 0, register::SingleEightBit::C, 0x5, 0x17; "no carry to register c")]
#[test_case(0x89, 0x12, 1, register::SingleEightBit::C, 0x5, 0x18; "carry to register c")]
#[test_case(0x8A, 0x12, 0, register::SingleEightBit::D, 0x5, 0x17; "no carry to register d")]
#[test_case(0x8A, 0x12, 1, register::SingleEightBit::D, 0x5, 0x18; "carry to register d")]
#[test_case(0x8B, 0x12, 0, register::SingleEightBit::E, 0x5, 0x17; "no carry to register e")]
#[test_case(0x8B, 0x12, 1, register::SingleEightBit::E, 0x5, 0x18; "carry to register e")]
#[test_case(0x8C, 0x12, 0, register::SingleEightBit::H, 0x5, 0x17; "no carry to register h")]
#[test_case(0x8C, 0x12, 1, register::SingleEightBit::H, 0x5, 0x18; "carry to register h")]
#[test_case(0x8D, 0x12, 0, register::SingleEightBit::L, 0x5, 0x17; "no carry to register l")]
#[test_case(0x8D, 0x12, 1, register::SingleEightBit::L, 0x5, 0x18; "carry to register l")]
fn test_add_with_carry_to_a_value(
opcode: u8,
initial_value: u8,
carry_bit: u8,
operand_register: register::SingleEightBit,
operand: u8,
expected_value: u8,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [opcode, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&ins);
assert_eq!(expected_value, processor.registers.a);
}
// these parameters are like impossible to read but I can't think of a nicer way to do this right now
// B register
#[test_case(0x88, 0x12, 0, register::SingleEightBit::B, 0x5, 0, 0, 0; "no carry bit set, results in no flags, register b")]
#[test_case(0x88, 0x12, 1, register::SingleEightBit::B, 0x5, 0, 0, 0; "carry bit set, results in no flags, register b")]
#[test_case(0x88, 0x00, 0, register::SingleEightBit::B, 0x0, 1, 0, 0; "no carry bit set, zero operands result in zero flag set, register b")]
#[test_case(0x88, 0x00, 1, register::SingleEightBit::B, 0x0, 0, 0, 0; "carry bit set, zero operands result in no zero flag set, register b")]
#[test_case(0x88, 0x0F, 0, register::SingleEightBit::B, 0x1, 0, 1, 0; "no carry bit, half carry, register b")]
#[test_case(0x88, 0x0F, 1, register::SingleEightBit::B, 0x1, 0, 1, 0; "carry bit, half carry, register b")]
#[test_case(0x88, 0x0F, 0, register::SingleEightBit::B, 0x0F, 0, 1, 0; "no carry bit, full carry, register b")]
#[test_case(0x88, 0x0F, 1, register::SingleEightBit::B, 0x0F, 0, 1, 0; "carry bit, full carry, register b")]
#[test_case(0x88, 0xFF, 1, register::SingleEightBit::B, 0xFF, 0, 1, 1; "carry bit, both carries, register b")]
// C register
#[test_case(0x89, 0x12, 0, register::SingleEightBit::C, 0x5, 0, 0, 0; "no carry bit set, results in no flags, register c")]
#[test_case(0x89, 0x12, 1, register::SingleEightBit::C, 0x5, 0, 0, 0; "carry bit set, results in no flags, register c")]
#[test_case(0x89, 0x00, 0, register::SingleEightBit::C, 0x0, 1, 0, 0; "no carry bit set, zero operands result in zero flag set, register c")]
#[test_case(0x89, 0x00, 1, register::SingleEightBit::C, 0x0, 0, 0, 0; "carry bit set, zero operands result in no zero flag set, register c")]
#[test_case(0x89, 0x0F, 0, register::SingleEightBit::C, 0x1, 0, 1, 0; "no carry bit, half carry, register c")]
#[test_case(0x89, 0x0F, 1, register::SingleEightBit::C, 0x1, 0, 1, 0; "carry bit, half carry, register c")]
#[test_case(0x89, 0x0F, 0, register::SingleEightBit::C, 0x0F, 0, 1, 0; "no carry bit, full carry, register c")]
#[test_case(0x89, 0x0F, 1, register::SingleEightBit::C, 0x0F, 0, 1, 0; "carry bit, full carry, register c")]
#[test_case(0x89, 0xFF, 1, register::SingleEightBit::C, 0xFF, 0, 1, 1; "carry bit, both carries, register c")]
// D register
#[test_case(0x8A, 0x12, 0, register::SingleEightBit::D, 0x5, 0, 0, 0; "no carry bit set, results in no flags, register d")]
#[test_case(0x8A, 0x12, 1, register::SingleEightBit::D, 0x5, 0, 0, 0; "carry bit set, results in no flags, register d")]
#[test_case(0x8A, 0x00, 0, register::SingleEightBit::D, 0x0, 1, 0, 0; "no carry bit set, zero operands result in zero flag set, register d")]
#[test_case(0x8A, 0x00, 1, register::SingleEightBit::D, 0x0, 0, 0, 0; "carry bit set, zero operands result in no zero flag set, register d")]
#[test_case(0x8A, 0x0F, 0, register::SingleEightBit::D, 0x1, 0, 1, 0; "no carry bit, half carry, register d")]
#[test_case(0x8A, 0x0F, 1, register::SingleEightBit::D, 0x1, 0, 1, 0; "carry bit, half carry, register d")]
#[test_case(0x8A, 0x0F, 0, register::SingleEightBit::D, 0x0F, 0, 1, 0; "no carry bit, full carry, register d")]
#[test_case(0x8A, 0x0F, 1, register::SingleEightBit::D, 0x0F, 0, 1, 0; "carry bit, full carry, register d")]
#[test_case(0x8A, 0xFF, 1, register::SingleEightBit::D, 0xFF, 0, 1, 1; "carry bit, both carries, register d")]
// E register
#[test_case(0x8B, 0x12, 0, register::SingleEightBit::E, 0x5, 0, 0, 0; "no carry bit set, results in no flags, register e")]
#[test_case(0x8B, 0x12, 1, register::SingleEightBit::E, 0x5, 0, 0, 0; "carry bit set, results in no flags, register e")]
#[test_case(0x8B, 0x00, 0, register::SingleEightBit::E, 0x0, 1, 0, 0; "no carry bit set, zero operands result in zero flag set, register e")]
#[test_case(0x8B, 0x00, 1, register::SingleEightBit::E, 0x0, 0, 0, 0; "carry bit set, zero operands result in no zero flag set, register e")]
#[test_case(0x8B, 0x0F, 0, register::SingleEightBit::E, 0x1, 0, 1, 0; "no carry bit, half carry, register e")]
#[test_case(0x8B, 0x0F, 1, register::SingleEightBit::E, 0x1, 0, 1, 0; "carry bit, half carry, register e")]
#[test_case(0x8B, 0x0F, 0, register::SingleEightBit::E, 0x0F, 0, 1, 0; "no carry bit, full carry, register e")]
#[test_case(0x8B, 0x0F, 1, register::SingleEightBit::E, 0x0F, 0, 1, 0; "carry bit, full carry, register e")]
#[test_case(0x8B, 0xFF, 1, register::SingleEightBit::E, 0xFF, 0, 1, 1; "carry bit, both carries, register e")]
// H register
#[test_case(0x8C, 0x12, 0, register::SingleEightBit::H, 0x5, 0, 0, 0; "no carry bit set, results in no flags, register h")]
#[test_case(0x8C, 0x12, 1, register::SingleEightBit::H, 0x5, 0, 0, 0; "carry bit set, results in no flags, register h")]
#[test_case(0x8C, 0x00, 0, register::SingleEightBit::H, 0x0, 1, 0, 0; "no carry bit set, zero operands result in zero flag set, register h")]
#[test_case(0x8C, 0x00, 1, register::SingleEightBit::H, 0x0, 0, 0, 0; "carry bit set, zero operands result in no zero flag set, register h")]
#[test_case(0x8C, 0x0F, 0, register::SingleEightBit::H, 0x1, 0, 1, 0; "no carry bit, half carry, register h")]
#[test_case(0x8C, 0x0F, 1, register::SingleEightBit::H, 0x1, 0, 1, 0; "carry bit, half carry, register h")]
#[test_case(0x8C, 0x0F, 0, register::SingleEightBit::H, 0x0F, 0, 1, 0; "no carry bit, full carry, register h")]
#[test_case(0x8C, 0x0F, 1, register::SingleEightBit::H, 0x0F, 0, 1, 0; "carry bit, full carry, register h")]
#[test_case(0x8C, 0xFF, 1, register::SingleEightBit::H, 0xFF, 0, 1, 1; "carry bit, both carries, register h")]
// L register
#[test_case(0x8D, 0x12, 0, register::SingleEightBit::L, 0x5, 0, 0, 0; "no carry bit set, results in no flags, register l")]
#[test_case(0x8D, 0x12, 1, register::SingleEightBit::L, 0x5, 0, 0, 0; "carry bit set, results in no flags, register l")]
#[test_case(0x8D, 0x00, 0, register::SingleEightBit::L, 0x0, 1, 0, 0; "no carry bit set, zero operands result in zero flag set, register l")]
#[test_case(0x8D, 0x00, 1, register::SingleEightBit::L, 0x0, 0, 0, 0; "carry bit set, zero operands result in no zero flag set, register l")]
#[test_case(0x8D, 0x0F, 0, register::SingleEightBit::L, 0x1, 0, 1, 0; "no carry bit, half carry, register l")]
#[test_case(0x8D, 0x0F, 1, register::SingleEightBit::L, 0x1, 0, 1, 0; "carry bit, half carry, register l")]
#[test_case(0x8D, 0x0F, 0, register::SingleEightBit::L, 0x0F, 0, 1, 0; "no carry bit, full carry, register l")]
#[test_case(0x8D, 0x0F, 1, register::SingleEightBit::L, 0x0F, 0, 1, 0; "carry bit, full carry, register l")]
#[test_case(0x8D, 0xFF, 1, register::SingleEightBit::L, 0xFF, 0, 1, 1; "carry bit, both carries, register l")]
fn test_add_with_carry_to_a_flags(
opcode: u8,
initial_value: u8,
carry_bit: u8,
operand_register: register::SingleEightBit,
operand: u8,
expected_zero_flag: u8,
expected_half_carry_flag: u8,
expected_carry_flag: u8,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
// Set all the register to the opposite we expect to ensure they all get set
set_opposite_of_expected_flags(
&mut processor,
(
expected_zero_flag,
0,
expected_half_carry_flag,
expected_carry_flag,
),
);
// ...except for the carry bit, which we must set for the test
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [opcode, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&ins);
assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_half_carry_flag),
(register::Flag::Carry, expected_carry_flag),
);
}