ferris-boi/tests/cpu/misc.rs

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());
}