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