459 lines
16 KiB
Rust
459 lines
16 KiB
Rust
use ferris_boi::{
|
|
cpu::{instructions::Instruction, Processor},
|
|
register::{self, Flag, SingleEightBit},
|
|
};
|
|
use test_case::{test_case, test_matrix};
|
|
|
|
use crate::testutil;
|
|
|
|
#[test_case(0xC0, register::SingleEightBit::B, 0b00000001)]
|
|
#[test_case(0xC8, register::SingleEightBit::B, 0b00000010)]
|
|
#[test_case(0xD0, register::SingleEightBit::B, 0b00000100)]
|
|
#[test_case(0xD8, register::SingleEightBit::B, 0b00001000)]
|
|
#[test_case(0xE0, register::SingleEightBit::B, 0b00010000)]
|
|
#[test_case(0xE8, register::SingleEightBit::B, 0b00100000)]
|
|
#[test_case(0xF0, register::SingleEightBit::B, 0b01000000)]
|
|
#[test_case(0xF8, register::SingleEightBit::B, 0b10000000)]
|
|
// Register C
|
|
#[test_case(0xC1, register::SingleEightBit::C, 0b00000001)]
|
|
#[test_case(0xC9, register::SingleEightBit::C, 0b00000010)]
|
|
#[test_case(0xD1, register::SingleEightBit::C, 0b00000100)]
|
|
#[test_case(0xD9, register::SingleEightBit::C, 0b00001000)]
|
|
#[test_case(0xE1, register::SingleEightBit::C, 0b00010000)]
|
|
#[test_case(0xE9, register::SingleEightBit::C, 0b00100000)]
|
|
#[test_case(0xF1, register::SingleEightBit::C, 0b01000000)]
|
|
#[test_case(0xF9, register::SingleEightBit::C, 0b10000000)]
|
|
// Register D
|
|
#[test_case(0xC2, register::SingleEightBit::D, 0b00000001)]
|
|
#[test_case(0xCA, register::SingleEightBit::D, 0b00000010)]
|
|
#[test_case(0xD2, register::SingleEightBit::D, 0b00000100)]
|
|
#[test_case(0xDA, register::SingleEightBit::D, 0b00001000)]
|
|
#[test_case(0xE2, register::SingleEightBit::D, 0b00010000)]
|
|
#[test_case(0xEA, register::SingleEightBit::D, 0b00100000)]
|
|
#[test_case(0xF2, register::SingleEightBit::D, 0b01000000)]
|
|
#[test_case(0xFA, register::SingleEightBit::D, 0b10000000)]
|
|
// Register E
|
|
#[test_case(0xC3, register::SingleEightBit::E, 0b00000001)]
|
|
#[test_case(0xCB, register::SingleEightBit::E, 0b00000010)]
|
|
#[test_case(0xD3, register::SingleEightBit::E, 0b00000100)]
|
|
#[test_case(0xDB, register::SingleEightBit::E, 0b00001000)]
|
|
#[test_case(0xE3, register::SingleEightBit::E, 0b00010000)]
|
|
#[test_case(0xEB, register::SingleEightBit::E, 0b00100000)]
|
|
#[test_case(0xF3, register::SingleEightBit::E, 0b01000000)]
|
|
#[test_case(0xFB, register::SingleEightBit::E, 0b10000000)]
|
|
// Register H
|
|
#[test_case(0xC4, register::SingleEightBit::H, 0b00000001)]
|
|
#[test_case(0xCC, register::SingleEightBit::H, 0b00000010)]
|
|
#[test_case(0xD4, register::SingleEightBit::H, 0b00000100)]
|
|
#[test_case(0xDC, register::SingleEightBit::H, 0b00001000)]
|
|
#[test_case(0xE4, register::SingleEightBit::H, 0b00010000)]
|
|
#[test_case(0xEC, register::SingleEightBit::H, 0b00100000)]
|
|
#[test_case(0xF4, register::SingleEightBit::H, 0b01000000)]
|
|
#[test_case(0xFC, register::SingleEightBit::H, 0b10000000)]
|
|
// Register L
|
|
#[test_case(0xC5, register::SingleEightBit::L, 0b00000001)]
|
|
#[test_case(0xCD, register::SingleEightBit::L, 0b00000010)]
|
|
#[test_case(0xD5, register::SingleEightBit::L, 0b00000100)]
|
|
#[test_case(0xDD, register::SingleEightBit::L, 0b00001000)]
|
|
#[test_case(0xE5, register::SingleEightBit::L, 0b00010000)]
|
|
#[test_case(0xED, register::SingleEightBit::L, 0b00100000)]
|
|
#[test_case(0xF5, register::SingleEightBit::L, 0b01000000)]
|
|
#[test_case(0xFD, register::SingleEightBit::L, 0b10000000)]
|
|
// Register A
|
|
#[test_case(0xC7, register::SingleEightBit::A, 0b00000001)]
|
|
#[test_case(0xCF, register::SingleEightBit::A, 0b00000010)]
|
|
#[test_case(0xD7, register::SingleEightBit::A, 0b00000100)]
|
|
#[test_case(0xDF, register::SingleEightBit::A, 0b00001000)]
|
|
#[test_case(0xE7, register::SingleEightBit::A, 0b00010000)]
|
|
#[test_case(0xEF, register::SingleEightBit::A, 0b00100000)]
|
|
#[test_case(0xF7, register::SingleEightBit::A, 0b01000000)]
|
|
#[test_case(0xFF, register::SingleEightBit::A, 0b10000000)]
|
|
fn test_set_register_bit(opcode_variant: u8, register: register::SingleEightBit, expected: u8) {
|
|
let mut processor = Processor::default();
|
|
|
|
processor.registers.set_single_8bit_register(register, 0);
|
|
let data = [0xCB, opcode_variant, 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.get_single_8bit_register(register)
|
|
);
|
|
}
|
|
|
|
#[test_case(0xC6, 0b00000001)]
|
|
#[test_case(0xCE, 0b00000010)]
|
|
#[test_case(0xD6, 0b00000100)]
|
|
#[test_case(0xDE, 0b00001000)]
|
|
#[test_case(0xE6, 0b00010000)]
|
|
#[test_case(0xEE, 0b00100000)]
|
|
#[test_case(0xF6, 0b01000000)]
|
|
#[test_case(0xFE, 0b10000000)]
|
|
fn test_set_hl_value_bit(opcode_variant: u8, expected: u8) {
|
|
let mut processor = Processor::default();
|
|
|
|
processor
|
|
.registers
|
|
.set_combined_register(register::Combined::HL, 0xFF00);
|
|
|
|
processor
|
|
.memory
|
|
.set(0xFF00, 0)
|
|
.expect("failed to set memory value");
|
|
|
|
let data = [0xCB, opcode_variant, 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
|
|
.memory
|
|
.get(0xFF00)
|
|
.expect("failed to get memory value")
|
|
);
|
|
}
|
|
|
|
// Register B
|
|
#[test_case(0x80, register::SingleEightBit::B, 0b11111110)]
|
|
#[test_case(0x88, register::SingleEightBit::B, 0b11111101)]
|
|
#[test_case(0x90, register::SingleEightBit::B, 0b11111011)]
|
|
#[test_case(0x98, register::SingleEightBit::B, 0b11110111)]
|
|
#[test_case(0xA0, register::SingleEightBit::B, 0b11101111)]
|
|
#[test_case(0xA8, register::SingleEightBit::B, 0b11011111)]
|
|
#[test_case(0xB0, register::SingleEightBit::B, 0b10111111)]
|
|
#[test_case(0xB8, register::SingleEightBit::B, 0b01111111)]
|
|
// Register C
|
|
#[test_case(0x81, register::SingleEightBit::C, 0b11111110)]
|
|
#[test_case(0x89, register::SingleEightBit::C, 0b11111101)]
|
|
#[test_case(0x91, register::SingleEightBit::C, 0b11111011)]
|
|
#[test_case(0x99, register::SingleEightBit::C, 0b11110111)]
|
|
#[test_case(0xA1, register::SingleEightBit::C, 0b11101111)]
|
|
#[test_case(0xA9, register::SingleEightBit::C, 0b11011111)]
|
|
#[test_case(0xB1, register::SingleEightBit::C, 0b10111111)]
|
|
#[test_case(0xB9, register::SingleEightBit::C, 0b01111111)]
|
|
// Register D
|
|
#[test_case(0x82, register::SingleEightBit::D, 0b11111110)]
|
|
#[test_case(0x8A, register::SingleEightBit::D, 0b11111101)]
|
|
#[test_case(0x92, register::SingleEightBit::D, 0b11111011)]
|
|
#[test_case(0x9A, register::SingleEightBit::D, 0b11110111)]
|
|
#[test_case(0xA2, register::SingleEightBit::D, 0b11101111)]
|
|
#[test_case(0xAA, register::SingleEightBit::D, 0b11011111)]
|
|
#[test_case(0xB2, register::SingleEightBit::D, 0b10111111)]
|
|
#[test_case(0xBA, register::SingleEightBit::D, 0b01111111)]
|
|
// Register E
|
|
#[test_case(0x83, register::SingleEightBit::E, 0b11111110)]
|
|
#[test_case(0x8B, register::SingleEightBit::E, 0b11111101)]
|
|
#[test_case(0x93, register::SingleEightBit::E, 0b11111011)]
|
|
#[test_case(0x9B, register::SingleEightBit::E, 0b11110111)]
|
|
#[test_case(0xA3, register::SingleEightBit::E, 0b11101111)]
|
|
#[test_case(0xAB, register::SingleEightBit::E, 0b11011111)]
|
|
#[test_case(0xB3, register::SingleEightBit::E, 0b10111111)]
|
|
#[test_case(0xBB, register::SingleEightBit::E, 0b01111111)]
|
|
// Register H
|
|
#[test_case(0x84, register::SingleEightBit::H, 0b11111110)]
|
|
#[test_case(0x8C, register::SingleEightBit::H, 0b11111101)]
|
|
#[test_case(0x94, register::SingleEightBit::H, 0b11111011)]
|
|
#[test_case(0x9C, register::SingleEightBit::H, 0b11110111)]
|
|
#[test_case(0xA4, register::SingleEightBit::H, 0b11101111)]
|
|
#[test_case(0xAC, register::SingleEightBit::H, 0b11011111)]
|
|
#[test_case(0xB4, register::SingleEightBit::H, 0b10111111)]
|
|
#[test_case(0xBC, register::SingleEightBit::H, 0b01111111)]
|
|
// Register L
|
|
#[test_case(0x85, register::SingleEightBit::L, 0b11111110)]
|
|
#[test_case(0x8D, register::SingleEightBit::L, 0b11111101)]
|
|
#[test_case(0x95, register::SingleEightBit::L, 0b11111011)]
|
|
#[test_case(0x9D, register::SingleEightBit::L, 0b11110111)]
|
|
#[test_case(0xA5, register::SingleEightBit::L, 0b11101111)]
|
|
#[test_case(0xAD, register::SingleEightBit::L, 0b11011111)]
|
|
#[test_case(0xB5, register::SingleEightBit::L, 0b10111111)]
|
|
#[test_case(0xBD, register::SingleEightBit::L, 0b01111111)]
|
|
// Register A
|
|
#[test_case(0x87, register::SingleEightBit::A, 0b11111110)]
|
|
#[test_case(0x8F, register::SingleEightBit::A, 0b11111101)]
|
|
#[test_case(0x97, register::SingleEightBit::A, 0b11111011)]
|
|
#[test_case(0x9F, register::SingleEightBit::A, 0b11110111)]
|
|
#[test_case(0xA7, register::SingleEightBit::A, 0b11101111)]
|
|
#[test_case(0xAF, register::SingleEightBit::A, 0b11011111)]
|
|
#[test_case(0xB7, register::SingleEightBit::A, 0b10111111)]
|
|
#[test_case(0xBF, register::SingleEightBit::A, 0b01111111)]
|
|
fn test_clear_register_bit(opcode_variant: u8, register: SingleEightBit, expected: u8) {
|
|
let mut processor = Processor::default();
|
|
|
|
processor.registers.set_single_8bit_register(register, 0xFF);
|
|
let data = [0xCB, opcode_variant, 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.get_single_8bit_register(register)
|
|
);
|
|
}
|
|
|
|
#[test_case(0x86, 0b11111110)]
|
|
#[test_case(0x8E, 0b11111101)]
|
|
#[test_case(0x96, 0b11111011)]
|
|
#[test_case(0x9E, 0b11110111)]
|
|
#[test_case(0xA6, 0b11101111)]
|
|
#[test_case(0xAE, 0b11011111)]
|
|
#[test_case(0xB6, 0b10111111)]
|
|
#[test_case(0xBE, 0b01111111)]
|
|
fn test_clear_hl_value_bit(opcode_variant: u8, expected: u8) {
|
|
let mut processor = Processor::default();
|
|
|
|
processor
|
|
.registers
|
|
.set_combined_register(register::Combined::HL, 0xFF00);
|
|
|
|
processor
|
|
.memory
|
|
.set(0xFF00, 0xFF)
|
|
.expect("failed to set memory value");
|
|
|
|
let data = [0xCB, opcode_variant, 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
|
|
.memory
|
|
.get(0xFF00)
|
|
.expect("failed to get memory value")
|
|
);
|
|
}
|
|
|
|
#[test_matrix(
|
|
[0, 1],
|
|
[
|
|
(0x40, SingleEightBit::B, 0),
|
|
(0x41, SingleEightBit::C, 0),
|
|
(0x42, SingleEightBit::D, 0),
|
|
(0x43, SingleEightBit::E, 0),
|
|
(0x44, SingleEightBit::H, 0),
|
|
(0x45, SingleEightBit::L, 0),
|
|
(0x47, SingleEightBit::A, 0),
|
|
|
|
(0x48, SingleEightBit::B, 1),
|
|
(0x49, SingleEightBit::C, 1),
|
|
(0x4A, SingleEightBit::D, 1),
|
|
(0x4B, SingleEightBit::E, 1),
|
|
(0x4C, SingleEightBit::H, 1),
|
|
(0x4D, SingleEightBit::L, 1),
|
|
(0x4F, SingleEightBit::A, 1),
|
|
|
|
(0x50, SingleEightBit::B, 2),
|
|
(0x51, SingleEightBit::C, 2),
|
|
(0x52, SingleEightBit::D, 2),
|
|
(0x53, SingleEightBit::E, 2),
|
|
(0x54, SingleEightBit::H, 2),
|
|
(0x55, SingleEightBit::L, 2),
|
|
(0x57, SingleEightBit::A, 2),
|
|
|
|
(0x58, SingleEightBit::B, 3),
|
|
(0x59, SingleEightBit::C, 3),
|
|
(0x5A, SingleEightBit::D, 3),
|
|
(0x5B, SingleEightBit::E, 3),
|
|
(0x5C, SingleEightBit::H, 3),
|
|
(0x5D, SingleEightBit::L, 3),
|
|
(0x5F, SingleEightBit::A, 3),
|
|
|
|
(0x60, SingleEightBit::B, 4),
|
|
(0x61, SingleEightBit::C, 4),
|
|
(0x62, SingleEightBit::D, 4),
|
|
(0x63, SingleEightBit::E, 4),
|
|
(0x64, SingleEightBit::H, 4),
|
|
(0x65, SingleEightBit::L, 4),
|
|
(0x67, SingleEightBit::A, 4),
|
|
|
|
(0x68, SingleEightBit::B, 5),
|
|
(0x69, SingleEightBit::C, 5),
|
|
(0x6A, SingleEightBit::D, 5),
|
|
(0x6B, SingleEightBit::E, 5),
|
|
(0x6C, SingleEightBit::H, 5),
|
|
(0x6D, SingleEightBit::L, 5),
|
|
(0x6F, SingleEightBit::A, 5),
|
|
|
|
(0x70, SingleEightBit::B, 6),
|
|
(0x71, SingleEightBit::C, 6),
|
|
(0x72, SingleEightBit::D, 6),
|
|
(0x73, SingleEightBit::E, 6),
|
|
(0x74, SingleEightBit::H, 6),
|
|
(0x75, SingleEightBit::L, 6),
|
|
(0x77, SingleEightBit::A, 6),
|
|
|
|
(0x78, SingleEightBit::B, 7),
|
|
(0x79, SingleEightBit::C, 7),
|
|
(0x7A, SingleEightBit::D, 7),
|
|
(0x7B, SingleEightBit::E, 7),
|
|
(0x7C, SingleEightBit::H, 7),
|
|
(0x7D, SingleEightBit::L, 7),
|
|
(0x7F, SingleEightBit::A, 7),
|
|
]
|
|
)]
|
|
fn test_test_register_bit_sets_zero_flag_to_opposite_of_bit(
|
|
bit_value: u8,
|
|
(opcode_variant, register, bit): (u8, SingleEightBit, u8),
|
|
) {
|
|
let mut processor = Processor::default();
|
|
let expected_zero_flag = if bit_value == 0 { 1 } else { 0 };
|
|
// 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,
|
|
// value of carry does not matter
|
|
0,
|
|
),
|
|
);
|
|
|
|
processor
|
|
.registers
|
|
.set_single_8bit_register(register, bit_value << bit);
|
|
|
|
let data = [0xCB, opcode_variant, 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,
|
|
(Flag::Zero, expected_zero_flag),
|
|
(Flag::Subtract, 0),
|
|
(Flag::HalfCarry, 1)
|
|
);
|
|
}
|
|
|
|
#[test_matrix(
|
|
[0, 1],
|
|
[
|
|
(0x46, 0),
|
|
(0x4e, 1),
|
|
(0x56, 2),
|
|
(0x5e, 3),
|
|
(0x66, 4),
|
|
(0x6e, 5),
|
|
(0x76, 6),
|
|
(0x7e, 7),
|
|
]
|
|
)]
|
|
fn test_test_hl_value_bit_sets_zero_flag_to_opposite_of_bit(
|
|
bit_value: u8,
|
|
(opcode_variant, bit): (u8, u8),
|
|
) {
|
|
let mut processor = Processor::default();
|
|
let expected_zero_flag = if bit_value == 0 { 1 } else { 0 };
|
|
// 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,
|
|
// value of carry does not matter
|
|
0,
|
|
),
|
|
);
|
|
|
|
processor
|
|
.memory
|
|
.set(0xFF00, bit_value << bit)
|
|
.expect("failed to set memory value");
|
|
|
|
processor
|
|
.registers
|
|
.set_combined_register(register::Combined::HL, 0xFF00);
|
|
|
|
let data = [0xCB, opcode_variant, 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,
|
|
(Flag::Zero, expected_zero_flag),
|
|
(Flag::Subtract, 0),
|
|
(Flag::HalfCarry, 1)
|
|
);
|
|
}
|
|
|
|
#[test_case(0x30, SingleEightBit::B)]
|
|
#[test_case(0x31, SingleEightBit::C)]
|
|
#[test_case(0x32, SingleEightBit::D)]
|
|
#[test_case(0x33, SingleEightBit::E)]
|
|
#[test_case(0x34, SingleEightBit::H)]
|
|
#[test_case(0x35, SingleEightBit::L)]
|
|
#[test_case(0x37, SingleEightBit::A)]
|
|
fn test_swap_register_nibbles_swaps_register_value(opcode_variant: u8, register: SingleEightBit) {
|
|
let mut processor = Processor::default();
|
|
|
|
processor.registers.set_single_8bit_register(register, 0xA5);
|
|
|
|
let data = [0xCB, opcode_variant, 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!(0x5A, processor.registers.get_single_8bit_register(register));
|
|
}
|
|
|
|
#[test_matrix(
|
|
[(0x00, 1), (0xF0, 0)],
|
|
[
|
|
(0x30, SingleEightBit::B),
|
|
(0x31, SingleEightBit::C),
|
|
(0x32, SingleEightBit::D),
|
|
(0x33, SingleEightBit::E),
|
|
(0x34, SingleEightBit::H),
|
|
(0x35, SingleEightBit::L),
|
|
(0x37, SingleEightBit::A)
|
|
]
|
|
)]
|
|
fn test_swap_register_nibbles_sets_zero_flag_when_value_is_zero(
|
|
(value, expected_zero_flag): (u8, u8),
|
|
(opcode_variant, register): (u8, 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, (expected_zero_flag, 0, 0, 0));
|
|
|
|
processor
|
|
.registers
|
|
.set_single_8bit_register(register, value);
|
|
|
|
let data = [0xCB, opcode_variant, 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,
|
|
(Flag::Zero, expected_zero_flag),
|
|
(Flag::Subtract, 0),
|
|
(Flag::HalfCarry, 0),
|
|
(Flag::Carry, 0)
|
|
);
|
|
}
|