ferris-boi/tests/cpu/arith8.rs
Nick Krichevsky b5117e7cf9 Remove RunnableInstruction, encode cycles as a result of runtime
This is necessary for certain instructions, since they can take a different number of cycles
2023-11-20 14:25:07 -05:00

2244 lines
76 KiB
Rust

use ferris_boi::{
cpu::{instructions::Instruction, Processor},
register,
};
use crate::testutil;
use test_case::{test_case, test_matrix};
struct AdditionOperationFlags {
zero: u8,
half_carry: u8,
carry: u8,
}
struct IncrementOperationFlags {
zero: u8,
subtract: u8,
half_carry: u8,
}
#[test]
fn test_add_a_to_itself() {
let mut processor = Processor::default();
processor.registers.a = 10;
let data = [0x87, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(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) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(30, processor.registers.a);
}
#[test_matrix(
[
(0x00, 0x00, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}),
(0x00, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}),
(0x0F, 0x01, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x80, 0x80, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 1}),
(0xFF, 0x01, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1})
],
[
(0x80, register::SingleEightBit::B),
(0x81, register::SingleEightBit::C),
(0x82, register::SingleEightBit::D),
(0x83, register::SingleEightBit::E),
(0x84, register::SingleEightBit::H),
(0x85, register::SingleEightBit::L),
]
)]
fn test_add_register_to_a_flags(
(a_value, src_value, expected_flags): (u8, u8, AdditionOperationFlags),
(opcode, src): (u8, register::SingleEightBit),
) {
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
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor.registers.set_single_8bit_register(src, src_value);
let data = [opcode, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[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) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(0x46, processor.registers.a);
}
#[test_case(0x00, 0x00, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "zero flag for zero value")]
#[test_case(0x00, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no zero flag for non-zero value")]
#[test_case(0x0F, 0x01, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")]
#[test_case(0x80, 0x80, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 1}; "full carry flag")]
#[test_case(0xFF, 0x01, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")]
fn test_add_hl_addr_to_a_flags(
a_value: u8,
hl_addr_value: u8,
expected_flags: AdditionOperationFlags,
) {
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
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
let data = [0x86, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[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) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(69, processor.registers.a);
}
#[test_case(0x00, 0x00, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "zero flag for zero value")]
#[test_case(0x00, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no zero flag for non-zero value")]
#[test_case(0x0F, 0x01, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")]
#[test_case(0x80, 0x80, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 1}; "full carry flag")]
#[test_case(0xFF, 0x01, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")]
fn test_add_immediate_to_a_flags(a_value: u8, n: u8, expected_flags: AdditionOperationFlags) {
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
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
let data = [0xC6, n, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_matrix(
[
(0x88, register::SingleEightBit::B),
(0x89, register::SingleEightBit::C),
(0x8A, register::SingleEightBit::D),
(0x8B, register::SingleEightBit::E),
(0x8C, register::SingleEightBit::H),
(0x8D, register::SingleEightBit::L),
],
[
(0x12, 0x05, 0, 0x17),
(0x12, 0x05, 1, 0x18)
]
)]
fn test_add_register_with_carry_to_a_value(
(opcode, operand_register): (u8, register::SingleEightBit),
(initial_value, operand, carry_bit, expected_value): (u8, u8, u8, 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) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(expected_value, processor.registers.a);
}
#[test_matrix(
[
(0x88, register::SingleEightBit::B),
(0x89, register::SingleEightBit::C),
(0x8A, register::SingleEightBit::D),
(0x8B, register::SingleEightBit::E),
(0x8C, register::SingleEightBit::H),
(0x8D, register::SingleEightBit::L),
],
[
(0x12, 0x05, 0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}),
(0x12, 0x05, 1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}),
(0x00, 0x00, 0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}),
(0x00, 0x00, 1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}),
(0x0F, 0x01, 0, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x0F, 0x01, 1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x0F, 0x0F, 0, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x0F, 0x0F, 1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x80, 0x82, 0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}),
(0x80, 0x82, 1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}),
(0x0F, 0xFF, 0, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}),
(0x7F, 0x80, 1, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}),
]
) ]
fn test_add_register_with_carry_to_a_flags(
(opcode, operand_register): (u8, register::SingleEightBit),
(initial_value, operand, carry_bit, expected_flags): (u8, u8, u8, AdditionOperationFlags),
) {
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
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
// ...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) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0xF0, 0, 0xE0; "no carry bit")]
#[test_case(0xF0, 1, 0xE1; "carry bit")]
fn test_add_a_to_itself_with_carry_value(initial_value: u8, carry_bit: u8, expected_value: u8) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0x8F, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(processor.registers.a, expected_value);
}
#[test_case(0xF0, 0, AdditionOperationFlags{zero: 0, carry: 1, half_carry: 0}; "carry bit")]
#[test_case(0x0F, 0, AdditionOperationFlags{zero: 0, carry: 0, half_carry: 1}; "half carry bit")]
#[test_case(0x0E, 1, AdditionOperationFlags{zero: 0, carry: 0, half_carry: 1}; "half carry bit with input carry bit")]
fn test_add_a_to_itself_with_carry_flags(
initial_value: u8,
carry_bit: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0x8F, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0x01, 0x00, 0x02, 0x03; "no carry")]
#[test_case(0x01, 0x01, 0x02, 0x04; "carry")]
fn test_add_immediate_with_carry_to_a_value(
initial_value: u8,
carry_bit: u8,
operand: u8,
expected_value: u8,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0xCE, operand, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(processor.registers.a, expected_value);
}
#[test_case(0x01, 0, 0x02, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry")]
#[test_case(0xFF, 1, 0x00, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")]
#[test_case(0x0F, 1, 0x80, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")]
#[test_case(0xF0, 1, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "full carry flag")]
fn test_add_immediate_with_carry_to_a_flags(
initial_value: u8,
carry_bit: u8,
operand: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0xCE, operand, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0, 0xFE; "no carry bit")]
#[test_case(1, 0xFF; "carry bit")]
fn test_add_hl_addr_to_a_with_carry_value(carry_flag: u8, expected: u8) {
let mut processor = Processor::default();
processor
.memory
.set(0xFFFE, 0xF0)
.expect("expected to be able to set 0xFF00");
processor
.registers
.set_combined_register(register::Combined::HL, 0xFFFE);
processor.registers.a = 0x0E;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_flag);
let data = [0x8E, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(expected, processor.registers.a);
}
#[test_case(0x01, 0, 0x02, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry")]
#[test_case(0xFF, 1, 0x00, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")]
#[test_case(0x0F, 1, 0x80, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")]
#[test_case(0xF0, 1, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "full carry flag")]
fn test_add_hl_addr_to_a_with_carry_flags(
initial_value: u8,
carry_bit: u8,
operand: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
processor
.memory
.set(0xFFFE, operand)
.expect("expected to be able to set 0xFF00");
processor
.registers
.set_combined_register(register::Combined::HL, 0xFFFE);
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0x8E, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_matrix(
[
(0x90, register::SingleEightBit::B),
(0x91, register::SingleEightBit::C),
(0x92, register::SingleEightBit::D),
(0x93, register::SingleEightBit::E),
(0x94, register::SingleEightBit::H),
(0x95, register::SingleEightBit::L),
],
[
(0xFF, 0x0F, 0xF0),
(0x00, 0x03, 0xFD),
]
)]
fn test_sub_register_from_a_value(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, expected): (u8, u8, u8),
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(processor.registers.a, expected);
}
#[test_matrix(
[
(0x90, register::SingleEightBit::B),
(0x91, register::SingleEightBit::C),
(0x92, register::SingleEightBit::D),
(0x93, register::SingleEightBit::E),
(0x94, register::SingleEightBit::H),
(0x95, register::SingleEightBit::L),
],
[
(0xFF, 0x0F, AdditionOperationFlags{ zero: 0, half_carry: 0, carry: 0 }),
(0xF0, 0x0F, AdditionOperationFlags{ zero: 0, half_carry: 1, carry: 0 }),
(0x0F, 0xF0, AdditionOperationFlags{ zero: 0, half_carry: 0, carry: 1 }),
(0x01, 0x01, AdditionOperationFlags{ zero: 1, half_carry: 0, carry: 0 })
]
)]
fn test_sub_register_from_a_flags(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, expected_flags): (u8, u8, AdditionOperationFlags),
) {
let mut processor = Processor::default();
processor.registers.a = a_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
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
1,
expected_flags.half_carry,
expected_flags.carry,
),
);
let data = [opcode, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test]
fn test_sub_a_from_itself() {
let mut processor = Processor::default();
processor.registers.a = 0xFF;
let data = [0x97, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (0, 0, 1, 1));
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 1),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test_matrix(
[
(0x98, register::SingleEightBit::B),
(0x99, register::SingleEightBit::C),
(0x9A, register::SingleEightBit::D),
(0x9B, register::SingleEightBit::E),
(0x9C, register::SingleEightBit::H),
(0x9D, register::SingleEightBit::L),
],
[
(0xFF, 0x0F, 0, 0xF0),
(0xFF, 0x0F, 1, 0xEF)
]
)]
fn test_sub_register_with_carry_from_a_value(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, carry_bit, expected): (u8, u8, u8, u8),
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [opcode, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(processor.registers.a, expected);
}
#[test_matrix(
[
(0x98, register::SingleEightBit::B),
(0x99, register::SingleEightBit::C),
(0x9A, register::SingleEightBit::D),
(0x9B, register::SingleEightBit::E),
(0x9C, register::SingleEightBit::H),
(0x9D, register::SingleEightBit::L),
],
[
(0xFE, 0x01, 0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}),
(0xFE, 0x01, 1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}),
(0x00, 0x00, 0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}),
(0x01, 0x00, 1, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}),
(0x02, 0x02, 0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}),
(0xF0, 0x0F, 0, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x0F, 0xF0, 0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}),
(0xF0, 0x0E, 1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}),
(0x0F, 0xEE, 1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}),
]
)]
fn test_sub_register_from_carry_from_a_flags(
(opcode, operand_register): (u8, register::SingleEightBit),
(initial_value, operand, carry_bit, expected_flags): (u8, u8, u8, AdditionOperationFlags),
) {
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
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
// ...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) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test]
fn test_subtract_immediate_from_a_value() {
let mut processor = Processor::default();
processor.registers.a = 0xFF;
let data = [0xD6, 0x0F, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(processor.registers.a, 0xF0);
}
#[test_case(0xFF, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")]
#[test_case(0xF0, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")]
#[test_case(0x0F, 0xF0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")]
#[test_case(0x01, 0x01, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")]
fn test_sub_immediate_from_a_flags(
initial_value: u8,
operand: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
let data = [0xD6, operand, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0xFF, 0x0F, 0, 0xF0; "no carry")]
#[test_case(0xFF, 0x0F, 1, 0xEF; "carry")]
fn test_sub_immediate_with_carry_from_a_value(
a_value: u8,
operand: u8,
carry_bit: u8,
expected: u8,
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0xDE, operand, 0x02];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x02]);
processor.run_instruction(ins);
assert_eq!(processor.registers.a, expected);
}
#[test_case(0xFE, 0, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags")]
#[test_case(0xFE, 1, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags")]
#[test_case(0x00, 0, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag")]
#[test_case(0x01, 1, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit")]
#[test_case(0x02, 0, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no")]
#[test_case(0xF0, 0, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit")]
#[test_case(0x0F, 0, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit")]
#[test_case(0xF0, 1, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit")]
#[test_case(0x0F, 1, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit")]
fn test_sub_immediate_from_carry_from_a_flags(
initial_value: u8,
carry_bit: u8,
operand: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
// ...except for the carry bit, which we must set for the test
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0xDE, operand, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0, 0; "subtract from itself")]
#[test_case(1, 0xFF; "subtract from itself with carry bit")]
fn test_sub_a_register_from_itself_with_carry_value(carry_bit: u8, expected: u8) {
let mut processor = Processor::default();
processor.registers.a = 0xEE;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0x9F, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(expected, processor.registers.a);
}
#[test_case(0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtract from itself")]
#[test_case(1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "subtract from itself with carry bit")]
fn test_sub_a_register_from_itself_with_carry_flags(
carry_bit: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor.registers.a = 0xEE;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0x9F, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test]
fn test_sub_hl_addr_from_a_value() {
let mut processor = Processor::default();
processor.registers.a = 0xBA;
processor
.memory
.set(0xABCD, 0xA9)
.expect("failed to set memory value");
processor
.registers
.set_combined_register(register::Combined::HL, 0xABCD);
let data = [0x96, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(0x11, processor.registers.a);
}
#[test_case(0xFF, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")]
#[test_case(0xF0, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")]
#[test_case(0x0F, 0xF0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")]
#[test_case(0x01, 0x01, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")]
fn test_sub_hl_addr_from_a_flags(
a_value: u8,
operand_value: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor.registers.a = a_value;
processor
.memory
.set(0xABCD, operand_value)
.expect("failed to set memory value");
processor
.registers
.set_combined_register(register::Combined::HL, 0xABCD);
let data = [0x96, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0xFE, 0xED, 0, 0x11; "no carry bit")]
#[test_case(0xFE, 0xED, 1, 0x10; "carry bit")]
fn test_sub_hl_from_a_flags(a_value: u8, operand_value: u8, carry_bit: u8, expected_value: u8) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
processor
.memory
.set(0xABCD, operand_value)
.expect("failed to set memory value");
processor
.registers
.set_combined_register(register::Combined::HL, 0xABCD);
let data = [0x9E, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(expected_value, processor.registers.a);
}
#[test_case(0xFF, 0x0F, 0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")]
#[test_case(0xFF, 0x0E, 1, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags with carry bit")]
#[test_case(0xF0, 0x0F, 0, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")]
#[test_case(0xF0, 0x0E, 1, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry with carry bit")]
#[test_case(0x0F, 0xF0, 0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")]
#[test_case(0x0F, 0xEF, 1, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 1 }; "full carry with carry bit")]
#[test_case(0x01, 0x01, 0, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")]
#[test_case(0x01, 0x00, 1, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero with carry bit")]
fn test_sub_hl_addr_from_a_with_carry_flags(
a_value: u8,
operand_value: u8,
carry_bit: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
0,
expected_flags.half_carry,
expected_flags.carry,
),
);
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
processor.registers.a = a_value;
processor
.memory
.set(0xABCD, operand_value)
.expect("failed to set memory value");
processor
.registers
.set_combined_register(register::Combined::HL, 0xABCD);
let data = [0x9E, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0xA0, 0xFB, register::SingleEightBit::B, 0x0F, 0x0B; "AND with B")]
#[test_case(0xA1, 0xFC, register::SingleEightBit::C, 0x0F, 0x0C; "AND with C")]
#[test_case(0xA2, 0xFD, register::SingleEightBit::D, 0x0F, 0x0D; "AND with D")]
#[test_case(0xA3, 0xFE, register::SingleEightBit::E, 0x0F, 0x0E; "AND with E")]
#[test_case(0xA4, 0xF0, register::SingleEightBit::H, 0x0F, 0x00; "AND with H")]
#[test_case(0xA5, 0xF1, register::SingleEightBit::L, 0x0F, 0x01; "AND with L")]
fn test_and_single_register_with_a_value(
opcode: u8,
a_value: u8,
operand_register: register::SingleEightBit,
operand: u8,
expected_value: u8,
) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(expected_value, processor.registers.a);
}
#[test_matrix(
[
(0xA0, register::SingleEightBit::B),
(0xA1, register::SingleEightBit::C),
(0xA2, register::SingleEightBit::D),
(0xA3, register::SingleEightBit::E),
],
[
(0xFA, 0x0F, 0),
(0xFA, 0x00, 1),
]
)]
fn test_and_single_register_with_a_flags(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, expected_zero_flag): (u8, u8, u8),
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 1, 0));
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_and_a_with_itself_value() {
let mut processor = Processor::default();
processor.registers.a = 0xAB;
let data = [0xA7, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(0xAB, processor.registers.a);
}
#[test]
fn test_and_a_with_itself_flags() {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (0, 0, 1, 0));
processor.registers.a = 0xAB;
let data = [0xA7, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 0),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_and_hl_value_with_a_value() {
let mut processor = Processor::default();
processor.registers.a = 0xAB;
processor
.registers
.set_combined_register(register::Combined::HL, 0x00FF);
processor
.memory
.set(0x00FF, 0xF0)
.expect("failed to set value");
let data = [0xA6, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(0xA0, processor.registers.a);
}
#[test_case(0xFA, 0x0F, 0; "not zero")]
#[test_case(0xFA, 0x00, 1; "zero")]
fn test_and_hl_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
processor.registers.a = a_value;
processor
.registers
.set_combined_register(register::Combined::HL, 0x00FF);
processor
.memory
.set(0x00FF, operand)
.expect("failed to set value");
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 1, 0));
let data = [0xA6, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_and_immediate_with_a_value() {
let mut processor = Processor::default();
processor.registers.a = 0xAB;
let data = [0xE6, 0x0F, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(0x0B, processor.registers.a);
}
#[test_case(0xFA, 0x0F, 0; "not zero")]
#[test_case(0xFA, 0x00, 1; "zero")]
fn test_and_immediate_with_a_flags(a_value: u8, operand: u8, expected_zero_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
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 1, 0));
let data = [0xE6, operand, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 0),
);
}
#[test_case(0xA8, 0xFF, register::SingleEightBit::B, 0xCC, 0x33; "register B")]
#[test_case(0xA9, 0xFF, register::SingleEightBit::C, 0x33, 0xCC; "register C")]
#[test_case(0xAA, 0xFF, register::SingleEightBit::D, 0xCC, 0x33; "register D")]
#[test_case(0xAB, 0xFF, register::SingleEightBit::E, 0x33, 0xCC; "register E")]
#[test_case(0xAC, 0xFF, register::SingleEightBit::H, 0xCC, 0x33; "register H")]
#[test_case(0xAD, 0xFF, register::SingleEightBit::L, 0x33, 0xCC; "register L")]
fn test_xor_register_with_a_value(
opcode: u8,
a_value: u8,
operand_register: register::SingleEightBit,
operand: u8,
expected_value: u8,
) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(expected_value, processor.registers.a);
}
#[test_matrix(
[
(0xA8, register::SingleEightBit::B),
(0xA9, register::SingleEightBit::C),
(0xAA, register::SingleEightBit::D),
(0xAB, register::SingleEightBit::E),
(0xAC, register::SingleEightBit::H),
(0xAD, register::SingleEightBit::L),
],
[
(0xFF, 0xCC, 0),
(0xCC, 0xCC, 1),
]
)]
fn test_xor_register_with_a_flags(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, expected_zero_flag): (u8, u8, u8),
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_xor_register_a_with_itself_value() {
let mut processor = Processor::default();
processor.registers.a = 0x45;
let data = [0xAF, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(0, processor.registers.a);
}
#[test]
fn test_xor_register_a_with_itself_flags() {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (1, 0, 0, 0));
processor.registers.a = 0x45;
let data = [0xAF, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 1),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_xor_hl_value_with_a_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xEE);
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, 0xBB)
.expect("failed to set memory value");
let data = [0xAE, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(0x55, processor.registers.a);
}
#[test_case(0xCC, 0x33, 0; "not zero")]
#[test_case(0xCC, 0xCC, 1; "zero")]
fn test_xor_hl_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, operand)
.expect("failed to set memory value");
let data = [0xAE, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_xor_immediate_value_with_a_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0x22);
let data = [0xEE, 0xEE, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(0xCC, processor.registers.a);
}
#[test_case(0xCC, 0x33, 0; "not zero")]
#[test_case(0xCC, 0xCC, 1; "zero")]
fn test_xor_immediate_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
let data = [0xEE, operand, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test_matrix(
[
(0xB0, register::SingleEightBit:: B),
(0xB1, register::SingleEightBit:: C),
(0xB2, register::SingleEightBit:: D),
(0xB3, register::SingleEightBit:: E),
(0xB4, register::SingleEightBit:: H),
(0xB5, register::SingleEightBit:: L),
],
[
(0xCC, 0x11, 0xDD),
(0x00, 0x1F, 0x1F)
]
)]
fn test_or_register_with_a_value(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, expected_value): (u8, u8, u8),
) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(expected_value, processor.registers.a);
}
#[test_matrix(
[
(0xB0, register::SingleEightBit::B),
(0xB1, register::SingleEightBit::C),
(0xB2, register::SingleEightBit::D),
(0xB3, register::SingleEightBit::E),
(0xB4, register::SingleEightBit::H),
(0xB5, register::SingleEightBit::L),
],
[
(0xCC, 0x22, 0),
(0x00, 0x00, 1),
]
)]
fn test_or_register_with_a_flags(
(opcode, operand_register): (u8, register::SingleEightBit),
(a_value, operand, expected_zero_flag): (u8, u8, u8),
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor.registers.a = a_value;
processor
.registers
.set_single_8bit_register(operand_register, operand);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_or_register_a_with_itself_value() {
let mut processor = Processor::default();
processor.registers.a = 0x45;
let data = [0xB7, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
assert_eq!(0x45, processor.registers.a);
}
#[test_case(0xCC, 0)]
#[test_case(0x00, 1)]
fn test_or_register_a_with_itself_flags(operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor.registers.a = operand;
let data = [0xB7, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_or_hl_value_with_a_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0x11);
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, 0xAA)
.expect("failed to set memory value");
let data = [0xB6, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(0xBB, processor.registers.a);
}
#[test_case(0xBB, 0x11, 0)]
#[test_case(0x00, 0x00, 1)]
fn test_or_hl_value_with_a_flags(a_value: u8, hl_value: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, hl_value)
.expect("failed to set memory value");
let data = [0xB7, 0x01];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test]
fn test_or_immediate_value_with_a_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0x33);
let data = [0xF6, 0xAA, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(0xBB, processor.registers.a);
}
#[test_case(0xFF, 0x00, 0; "not zero")]
#[test_case(0x00, 0x00, 1; "zero")]
fn test_or_immediate_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) {
let mut processor = Processor::default();
testutil::set_opposite_of_expected_flags(&mut processor, (expected_zero_flag, 0, 0, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
let data = [0xF6, operand, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_zero_flag),
(register::Flag::Subtract, 0),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test_case(0xB8, register::SingleEightBit::B)]
#[test_case(0xB9, register::SingleEightBit::C)]
#[test_case(0xBA, register::SingleEightBit::D)]
#[test_case(0xBB, register::SingleEightBit::E)]
#[test_case(0xBC, register::SingleEightBit::H)]
#[test_case(0xBD, register::SingleEightBit::L)]
fn test_compare_register_instructions_do_not_modify_register_values(
opcode: u8,
register: register::SingleEightBit,
) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFF);
processor.registers.set_single_8bit_register(register, 0x22);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
0xFF,
processor
.registers
.get_single_8bit_register(register::SingleEightBit::A)
);
assert_eq!(0x22, processor.registers.get_single_8bit_register(register));
}
#[test]
fn test_compare_register_with_itself_does_not_modify_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFF);
let data = [0xBF, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
0xFF,
processor
.registers
.get_single_8bit_register(register::SingleEightBit::A)
);
}
#[test_case(0xB8, register::SingleEightBit::B)]
#[test_case(0xB9, register::SingleEightBit::C)]
#[test_case(0xBA, register::SingleEightBit::D)]
#[test_case(0xBB, register::SingleEightBit::E)]
#[test_case(0xBC, register::SingleEightBit::H)]
#[test_case(0xBD, register::SingleEightBit::L)]
#[test_case(0xBF, register::SingleEightBit::A)]
fn test_comparing_equal_values_between_registers_sets_zero_flag(
opcode: u8,
register: register::SingleEightBit,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (1, 1, 0, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFF);
processor.registers.set_single_8bit_register(register, 0xFF);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
0xFF,
processor
.registers
.get_single_8bit_register(register::SingleEightBit::A)
);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 1),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test_case(0xB8, register::SingleEightBit::B)]
#[test_case(0xB9, register::SingleEightBit::C)]
#[test_case(0xBA, register::SingleEightBit::D)]
#[test_case(0xBB, register::SingleEightBit::E)]
#[test_case(0xBC, register::SingleEightBit::H)]
#[test_case(0xBD, register::SingleEightBit::L)]
fn test_comparing_extremely_less_than_values_between_registers_should_set_no_flags(
opcode: u8,
register: register::SingleEightBit,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (0, 1, 0, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFF);
processor.registers.set_single_8bit_register(register, 0x01);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 0),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 0),
);
}
#[test_case(0xB8, register::SingleEightBit::B)]
#[test_case(0xB9, register::SingleEightBit::C)]
#[test_case(0xBA, register::SingleEightBit::D)]
#[test_case(0xBB, register::SingleEightBit::E)]
#[test_case(0xBC, register::SingleEightBit::H)]
#[test_case(0xBD, register::SingleEightBit::L)]
fn test_comparing_extremely_somewhat_less_than_values_between_registers_should_set_half_carry_flag(
opcode: u8,
register: register::SingleEightBit,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (0, 1, 1, 0));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFE);
processor.registers.set_single_8bit_register(register, 0x1F);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 0),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 0),
);
}
#[test_case(0xB8, register::SingleEightBit::B)]
#[test_case(0xB9, register::SingleEightBit::C)]
#[test_case(0xBA, register::SingleEightBit::D)]
#[test_case(0xBB, register::SingleEightBit::E)]
#[test_case(0xBC, register::SingleEightBit::H)]
#[test_case(0xBD, register::SingleEightBit::L)]
fn test_comparing_greater_than_values_between_registers_should_set_carry_flag(
opcode: u8,
register: register::SingleEightBit,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (0, 1, 0, 1));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0x1F);
processor.registers.set_single_8bit_register(register, 0xFF);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 0),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, 0),
(register::Flag::Carry, 1),
);
}
#[test_case(0xB8, register::SingleEightBit::B)]
#[test_case(0xB9, register::SingleEightBit::C)]
#[test_case(0xBA, register::SingleEightBit::D)]
#[test_case(0xBB, register::SingleEightBit::E)]
#[test_case(0xBC, register::SingleEightBit::H)]
#[test_case(0xBD, register::SingleEightBit::L)]
fn test_comparing_lower_nibble_greater_than_values_between_registers_should_set_carry_and_half_carry_flag(
opcode: u8,
register: register::SingleEightBit,
) {
let mut processor = Processor::default();
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(&mut processor, (0, 1, 1, 1));
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0x1E);
processor.registers.set_single_8bit_register(register, 0x2F);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, 0),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, 1),
(register::Flag::Carry, 1),
);
}
#[test]
fn test_comparing_hl_value_to_a_does_not_change_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFF);
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, 0x11)
.expect("failde to set value");
let data = [0xBE, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
0xFF,
processor
.registers
.get_single_8bit_register(register::SingleEightBit::A)
);
}
#[test_case(0xFF, 0xFF, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "equal values sets zero flag")]
#[test_case(0xFF, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "very less value sets no flags")]
#[test_case(0xFC, 0x1F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "somewhat less value sets half carry")]
#[test_case(0x1F, 0xFC, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "greater value sets carry")]
#[test_case(0x1C, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "greater lower nibble value sets half carry")]
fn test_comparing_hl_value_to_a(a_value: u8, operand: u8, expected_flags: AdditionOperationFlags) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF00);
processor
.memory
.set(0xFF00, operand)
.expect("failed to set value");
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
1,
expected_flags.half_carry,
expected_flags.carry,
),
);
let data = [0xBE, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
#[test]
fn test_comparing_immediate_value_to_a_does_not_change_value() {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, 0xFF);
let data = [0xFE, 0xBB, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
0xFF,
processor
.registers
.get_single_8bit_register(register::SingleEightBit::A)
);
}
#[test_case(0xFF, 0xFF, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "equal values sets zero flag")]
#[test_case(0xFF, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "very less value sets no flags")]
#[test_case(0xFC, 0x1F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "somewhat less value sets half carry")]
#[test_case(0x1F, 0xFC, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "greater value sets carry")]
#[test_case(0x1C, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "greater lower nibble value sets half carry")]
fn test_comparing_immediate_value_to_a(
a_value: u8,
operand: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register::SingleEightBit::A, a_value);
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
1,
expected_flags.half_carry,
expected_flags.carry,
),
);
let data = [0xFE, operand, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, 1),
(register::Flag::HalfCarry, expected_flags.half_carry),
(register::Flag::Carry, expected_flags.carry),
);
}
// increment
#[test_matrix(
[
(0x04, register::SingleEightBit:: B),
(0x0C, register::SingleEightBit:: C),
(0x14, register::SingleEightBit:: D),
(0x1C, register::SingleEightBit:: E),
(0x24, register::SingleEightBit:: H),
(0x2C, register::SingleEightBit:: L),
(0x3C, register::SingleEightBit:: A),
],
[
(0x05, 0x06),
(0xFF, 0x00),
]
)]
// decrement
#[test_matrix(
[
(0x05, register::SingleEightBit:: B),
(0x0D, register::SingleEightBit:: C),
(0x15, register::SingleEightBit:: D),
(0x1D, register::SingleEightBit:: E),
(0x25, register::SingleEightBit:: H),
(0x2D, register::SingleEightBit:: L),
(0x3D, register::SingleEightBit:: A),
],
[
(0x05, 0x04),
(0x00, 0xFF),
]
)]
fn test_increment_decrement_single_register_value(
(opcode, register): (u8, register::SingleEightBit),
(initial_value, expected_value): (u8, u8),
) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register, initial_value);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
expected_value,
processor.registers.get_single_8bit_register(register)
);
}
#[test_matrix(
[
(0x04, register::SingleEightBit::B),
(0x0C, register::SingleEightBit::C),
(0x14, register::SingleEightBit::D),
(0x1C, register::SingleEightBit::E),
(0x24, register::SingleEightBit::H),
(0x2C, register::SingleEightBit::L),
(0x3C, register::SingleEightBit::A),
],
[
(0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0}),
(0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0}),
(0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0}),
]
)]
#[test_matrix(
[
(0x05, register::SingleEightBit::B),
(0x0D, register::SingleEightBit::C),
(0x15, register::SingleEightBit::D),
(0x1D, register::SingleEightBit::E),
(0x25, register::SingleEightBit::H),
(0x2D, register::SingleEightBit::L),
(0x3D, register::SingleEightBit::A),
],
[
(0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}),
(0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}),
(0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}),
(0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}),
]
)]
fn test_increment_decrement_single_register_flags(
(opcode, register): (u8, register::SingleEightBit),
(initial_value, expected_flags): (u8, IncrementOperationFlags),
) {
let mut processor = Processor::default();
processor
.registers
.set_single_8bit_register(register, initial_value);
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
expected_flags.subtract,
expected_flags.half_carry,
// Carry flag should remain unchanged - quick lazy hack to just set this to 1.
0,
),
);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, expected_flags.subtract),
(register::Flag::HalfCarry, expected_flags.half_carry),
// Carry flag should remain unchanged
(register::Flag::Carry, 1),
);
}
#[test_case(0x34, 0x05, 0x06; "add one")]
#[test_case(0x34, 0xFF, 0x00; "add one with wrapping")]
#[test_case(0x35, 0x05, 0x04; "sub one")]
#[test_case(0x35, 0x000, 0xFF; "sub one with wrapping")]
fn test_increment_decrement_hl_value(opcode: u8, initial_value: u8, expected_value: u8) {
let mut processor = Processor::default();
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF23);
processor
.memory
.set(0xFF23, initial_value)
.expect("failed to set memory value");
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(
expected_value,
processor
.memory
.get(0xFF23)
.expect("failed to get memory value")
);
}
#[test_case(0x34, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 0}; "increment")]
#[test_case(0x34, 0xFF, IncrementOperationFlags { zero: 1, half_carry: 1, subtract: 0}; "increment with wrapping")]
#[test_case(0x34, 0x0F, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 0}; "increment with half carry")]
#[test_case(0x35, 0x05, IncrementOperationFlags { zero: 0, half_carry: 0, subtract: 1}; "decrement")]
#[test_case(0x35, 0x01, IncrementOperationFlags { zero: 1, half_carry: 0, subtract: 1}; "decrement to zero")]
#[test_case(0x35, 0x00, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with wrapping")]
#[test_case(0x35, 0x10, IncrementOperationFlags { zero: 0, half_carry: 1, subtract: 1}; "decrement with half carry")]
fn test_increment_decrement_hl_flags(
opcode: u8,
initial_value: u8,
expected_flags: IncrementOperationFlags,
) {
let mut processor = Processor::default();
processor
.registers
.set_combined_register(register::Combined::HL, 0xFF23);
processor
.memory
.set(0xFF23, initial_value)
.expect("failed to set memory value");
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(
expected_flags.zero,
expected_flags.subtract,
expected_flags.half_carry,
// Carry flag should remain unchanged - quick lazy hack to just set this to 1.
0,
),
);
let data = [opcode, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::Zero, expected_flags.zero),
(register::Flag::Subtract, expected_flags.subtract),
(register::Flag::HalfCarry, expected_flags.half_carry),
// Carry flag should remain unchanged
(register::Flag::Carry, 1),
);
}
#[test_case(0xFF00, 0x22, 0xFF22)]
#[test_case(0xFF00, -0x22, 0xFEDE)]
#[test_case(0x0000, -0x22, 0xFFDE)]
#[test_case(0xFFFF, 0x22, 0x0021)]
fn test_adjust_stack_pointer_value(initial_sp: u16, adjustment: i8, expected_sp: u16) {
let mut processor = Processor::default();
processor
.registers
.set_single_16bit_register(register::SingleSixteenBit::StackPointer, initial_sp);
let twos_comp_adjustment = adjustment.to_be_bytes()[0];
let data = [0xE8, twos_comp_adjustment, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
assert_eq!(expected_sp, processor.registers.stack_pointer);
}
#[test_case(0xFF00, 0x22, false, false)]
#[test_case(0x04F0, -0x22, false, true; "subtraction full carry")]
#[test_case(0x0022, -0x22, true, true; "even as zero, zero flags are unset")]
fn test_adjust_stack_pointer(initial_sp: u16, adjustment: i8, half_carry: bool, carry: bool) {
let mut processor = Processor::default();
processor
.registers
.set_single_16bit_register(register::SingleSixteenBit::StackPointer, initial_sp);
// Set all the register to the opposite we expect to ensure they all get set
testutil::set_opposite_of_expected_flags(
&mut processor,
(0, 0, half_carry.into(), carry.into()),
);
let twos_comp_adjustment = adjustment.to_be_bytes()[0];
let data = [0xE8, twos_comp_adjustment, 0x03];
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x03]);
processor.run_instruction(ins);
testutil::assert_flags_eq!(
processor,
(register::Flag::HalfCarry, u8::from(half_carry)),
(register::Flag::Carry, u8::from(carry)),
// Both are always zero
(register::Flag::Subtract, 0),
(register::Flag::Zero, 0),
);
}