239 lines
7.8 KiB
Rust
239 lines
7.8 KiB
Rust
use crate::testutil::{self, assert_flags_eq};
|
|
use ferris_boi::{
|
|
cpu::{instructions::Instruction, Processor},
|
|
register,
|
|
};
|
|
use test_case::test_case;
|
|
|
|
struct DAAInputFlags {
|
|
subtract: bool,
|
|
half_carry: bool,
|
|
full_carry: bool,
|
|
}
|
|
|
|
struct DAAOutputFlags {
|
|
zero: bool,
|
|
full_carry: bool,
|
|
}
|
|
|
|
#[test_case(1)]
|
|
#[test_case(0)]
|
|
fn test_set_carry_flag_always_sets_to_1(starting_value: u8) {
|
|
let mut processor = Processor::default();
|
|
processor
|
|
.registers
|
|
.set_flag_bit(register::Flag::Carry, starting_value);
|
|
|
|
let data = [0x37, 0x03];
|
|
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
|
|
|
|
assert_eq!(extra_data, &[0x03]);
|
|
processor.run_instruction(ins);
|
|
|
|
let new_carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
|
assert_eq!(1, new_carry_bit);
|
|
}
|
|
|
|
#[test_case(1, 0)]
|
|
#[test_case(0, 1)]
|
|
fn test_complement_carry_bit(starting_value: u8, expected_value: u8) {
|
|
let mut processor = Processor::default();
|
|
processor
|
|
.registers
|
|
.set_flag_bit(register::Flag::Carry, starting_value);
|
|
|
|
let data = [0x3F, 0x03];
|
|
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
|
|
|
|
assert_eq!(extra_data, &[0x03]);
|
|
processor.run_instruction(ins);
|
|
|
|
let new_carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
|
assert_eq!(expected_value, new_carry_bit);
|
|
}
|
|
|
|
#[test_case(0x37)]
|
|
#[test_case(0x3F)]
|
|
fn test_all_carry_bit_instructions_adjust_flags(opcode: u8) {
|
|
let mut processor = Processor::default();
|
|
testutil::set_opposite_of_expected_flags(&mut processor, (0, 0, 0, 1));
|
|
|
|
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,
|
|
// Always zero
|
|
(register::Flag::HalfCarry, 0),
|
|
(register::Flag::Carry, 1),
|
|
(register::Flag::Subtract, 0),
|
|
// Value is preserved
|
|
(register::Flag::Zero, 1),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_complement_a_register_value() {
|
|
let mut processor = Processor::default();
|
|
processor.registers.a = 0xF0;
|
|
|
|
let data = [0x2F, 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!(0x0F, processor.registers.a);
|
|
}
|
|
|
|
#[test]
|
|
fn test_complement_a_register_flags() {
|
|
let mut processor = Processor::default();
|
|
processor.registers.a = 0xF0;
|
|
testutil::set_opposite_of_expected_flags(&mut processor, (0, 1, 1, 0));
|
|
|
|
let data = [0x2F, 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,
|
|
// Always 1
|
|
(register::Flag::HalfCarry, 1),
|
|
(register::Flag::Subtract, 1),
|
|
// Value is preserved
|
|
(register::Flag::Carry, 1),
|
|
(register::Flag::Zero, 1),
|
|
);
|
|
}
|
|
|
|
#[test_case(0x22, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x22, DAAOutputFlags{zero: false, full_carry: false}; "both digits less than 9 should not adjust A register")]
|
|
#[test_case(0x0A, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x10, DAAOutputFlags{zero: false, full_carry: false}; "adjust result by 0x06 if lower nibble is greater than 9")]
|
|
#[test_case(0xA5, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x05, DAAOutputFlags{zero: false, full_carry: true}; "adjust result by 0x60 if greater than 99")]
|
|
#[test_case(0x20, DAAInputFlags{subtract: false, half_carry: true, full_carry: false}, 0x26, DAAOutputFlags{zero: false, full_carry: false}; "adjust result by 0x06 if half carry was performed")]
|
|
#[test_case(0x33, DAAInputFlags{subtract: false, half_carry: false, full_carry: true}, 0x93, DAAOutputFlags{zero: false, full_carry: true}; "adjust result by 0x60 if full carry was performed")]
|
|
#[test_case(0x26, DAAInputFlags{subtract: true, half_carry: true, full_carry: false}, 0x20, DAAOutputFlags{zero: false, full_carry: false}; "adjust result by -0x06 if half carry and a subtract were performed")]
|
|
#[test_case(0x93, DAAInputFlags{subtract: true, half_carry: false, full_carry: true}, 0x33, DAAOutputFlags{zero: false, full_carry: true}; "adjust result by -0x60 if full carry and a subtract were performed")]
|
|
#[test_case(0x00, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x00, DAAOutputFlags{zero: true, full_carry: false}; "zero flag is true if result is zero")]
|
|
#[test_case(0x9C, DAAInputFlags{subtract: false, half_carry: true, full_carry: false}, 0x02, DAAOutputFlags{zero: false, full_carry: true}; "just because the upper nibble is 9 does not mean that 0x06 is added")]
|
|
fn test_daa(
|
|
a_value: u8,
|
|
flags: DAAInputFlags,
|
|
expected_a_value: u8,
|
|
expected_flags: DAAOutputFlags,
|
|
) {
|
|
let mut processor = Processor::default();
|
|
processor.registers.a = a_value;
|
|
processor
|
|
.registers
|
|
.set_flag_bit(register::Flag::Carry, flags.full_carry.into());
|
|
processor
|
|
.registers
|
|
.set_flag_bit(register::Flag::HalfCarry, flags.half_carry.into());
|
|
processor
|
|
.registers
|
|
.set_flag_bit(register::Flag::Subtract, flags.subtract.into());
|
|
|
|
processor
|
|
.registers
|
|
// Set the opposite of expected so we know we're setting it right
|
|
.set_flag_bit(register::Flag::Zero, (!expected_flags.zero).into());
|
|
|
|
let data = [0x27, 0x06];
|
|
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
|
|
|
|
assert_eq!(extra_data, &[0x06]);
|
|
processor.run_instruction(ins);
|
|
|
|
assert_eq!(expected_a_value, processor.registers.a);
|
|
assert_flags_eq!(
|
|
processor,
|
|
(register::Flag::Carry, u8::from(expected_flags.full_carry)),
|
|
(register::Flag::Zero, u8::from(expected_flags.zero))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_nop_executes_successfully() {
|
|
let mut processor = Processor::default();
|
|
let data = [0x00, 0x06];
|
|
|
|
let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction");
|
|
|
|
assert_eq!(extra_data, &[0x06]);
|
|
processor.run_instruction(ins);
|
|
|
|
// uhhh it does nothing
|
|
assert_eq!(processor.num_cycles, 4);
|
|
}
|
|
|
|
#[test]
|
|
fn test_enable_interrupts_enables_interrupts_on_the_following_instruction() {
|
|
let mut processor = Processor::default();
|
|
|
|
[
|
|
// Enable interrupts
|
|
0xFB, // Nop
|
|
0x00,
|
|
]
|
|
.iter()
|
|
.copied()
|
|
.enumerate()
|
|
.for_each(|(idx, opcode)| {
|
|
processor
|
|
.memory
|
|
.set(
|
|
usize::from(processor.registers.program_counter) + idx,
|
|
opcode,
|
|
)
|
|
.expect("could not program data");
|
|
});
|
|
|
|
assert!(!processor.interrupts_enabled());
|
|
|
|
processor.run_next_instruction();
|
|
assert!(!processor.interrupts_enabled());
|
|
|
|
processor.run_next_instruction();
|
|
assert!(processor.interrupts_enabled());
|
|
}
|
|
|
|
#[test]
|
|
fn test_disable_interrupts_disables_interrupts_once_enabled() {
|
|
let mut processor = Processor::default();
|
|
|
|
[
|
|
// Enable interrupts
|
|
0xFB, // Nop
|
|
0x00, 0xF3,
|
|
]
|
|
.iter()
|
|
.copied()
|
|
.enumerate()
|
|
.for_each(|(idx, opcode)| {
|
|
processor
|
|
.memory
|
|
.set(
|
|
usize::from(processor.registers.program_counter) + idx,
|
|
opcode,
|
|
)
|
|
.expect("could not program data");
|
|
});
|
|
|
|
assert!(!processor.interrupts_enabled());
|
|
|
|
processor.run_next_instruction();
|
|
assert!(!processor.interrupts_enabled());
|
|
|
|
processor.run_next_instruction();
|
|
assert!(processor.interrupts_enabled());
|
|
|
|
processor.run_next_instruction();
|
|
assert!(!processor.interrupts_enabled());
|
|
}
|