ferris-boi/tests/cpu/manip.rs

459 lines
16 KiB
Rust
Raw Permalink Normal View History

use ferris_boi::{
cpu::{instructions::Instruction, Processor},
2023-12-07 00:48:54 +00:00
register::{self, Flag, SingleEightBit},
};
2023-12-07 00:48:54 +00:00
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)
);
}
2023-11-27 13:45:29 +00:00
#[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")
);
}
2023-12-06 21:14:07 +00:00
// 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")
);
}
2023-12-07 00:48:54 +00:00
#[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)
);
}
2023-12-07 01:28:59 +00:00
#[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)
);
}