use ferris_boi::{ cpu::{instructions::RunnableInstruction, Processor}, register, }; use crate::testutil; use test_case::test_case; struct AdditionOperationFlags { zero: u8, half_carry: u8, carry: u8, } #[test] fn test_add_a_to_itself() { let mut processor = Processor::default(); processor.registers.a = 10; let data = [0x87, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(20, processor.registers.a); } #[test_case(0x80, register::SingleEightBit::B)] #[test_case(0x81, register::SingleEightBit::C)] #[test_case(0x82, register::SingleEightBit::D)] #[test_case(0x83, register::SingleEightBit::E)] #[test_case(0x84, register::SingleEightBit::H)] #[test_case(0x85, register::SingleEightBit::L)] fn test_add_to_a_value(opcode: u8, src: register::SingleEightBit) { let mut processor = Processor::default(); processor.registers.a = 10; processor.registers.set_single_8bit_register(src, 20); let data = [opcode, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(30, processor.registers.a); } #[test_case(0x80, register::SingleEightBit::B, 0x00, 0x00, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "zero flag for zero value")] #[test_case(0x80, register::SingleEightBit::B, 0x00, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no zero flag for non-zero value")] #[test_case(0x81, register::SingleEightBit::C, 0x0F, 0x01, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")] #[test_case(0x81, register::SingleEightBit::C, 0x80, 0x80, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 1}; "full carry flag")] #[test_case(0x81, register::SingleEightBit::C, 0xFF, 0x01, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")] // 0000 1111 fn test_add_register_to_a_flags( opcode: u8, src: register::SingleEightBit, a_value: u8, src_value: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = a_value; // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor.registers.set_single_8bit_register(src, src_value); let data = [opcode, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test] fn test_add_hl_addr_to_a_value() { let mut processor = Processor::default(); processor .memory .set(0xFF00, 0x34) .expect("expected to be able to set 0xFF00"); processor .registers .set_combined_register(register::Combined::HL, 0xFF00); processor.registers.a = 0x12; let data = [0x86, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(0x46, processor.registers.a); } #[test_case(0x00, 0x00, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "zero flag for zero value")] #[test_case(0x00, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no zero flag for non-zero value")] #[test_case(0x0F, 0x01, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")] #[test_case(0x80, 0x80, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 1}; "full carry flag")] #[test_case(0xFF, 0x01, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")] fn test_add_hl_addr_to_a_flags( a_value: u8, hl_addr_value: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_combined_register(register::Combined::HL, 0xFF00); processor .memory .set(0xFF00, hl_addr_value) .expect("expected to set address 0xFF00 but could not"); // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); let data = [0x86, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test] fn test_add_immediate_to_a_value() { let mut processor = Processor::default(); processor.registers.a = 50; let data = [0xC6, 0x13, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(69, processor.registers.a); } #[test_case(0x00, 0x00, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "zero flag for zero value")] #[test_case(0x00, 0x01, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no zero flag for non-zero value")] #[test_case(0x0F, 0x01, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")] #[test_case(0x80, 0x80, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 1}; "full carry flag")] #[test_case(0xFF, 0x01, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")] fn test_add_immediate_to_a_flags(a_value: u8, n: u8, expected_flags: AdditionOperationFlags) { let mut processor = Processor::default(); processor.registers.a = a_value; // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); let data = [0xC6, n, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0x88, 0x12, 0, register::SingleEightBit::B, 0x5, 0x17; "no carry to register b")] #[test_case(0x88, 0x12, 1, register::SingleEightBit::B, 0x5, 0x18; "carry to register b")] #[test_case(0x89, 0x12, 0, register::SingleEightBit::C, 0x5, 0x17; "no carry to register c")] #[test_case(0x89, 0x12, 1, register::SingleEightBit::C, 0x5, 0x18; "carry to register c")] #[test_case(0x8A, 0x12, 0, register::SingleEightBit::D, 0x5, 0x17; "no carry to register d")] #[test_case(0x8A, 0x12, 1, register::SingleEightBit::D, 0x5, 0x18; "carry to register d")] #[test_case(0x8B, 0x12, 0, register::SingleEightBit::E, 0x5, 0x17; "no carry to register e")] #[test_case(0x8B, 0x12, 1, register::SingleEightBit::E, 0x5, 0x18; "carry to register e")] #[test_case(0x8C, 0x12, 0, register::SingleEightBit::H, 0x5, 0x17; "no carry to register h")] #[test_case(0x8C, 0x12, 1, register::SingleEightBit::H, 0x5, 0x18; "carry to register h")] #[test_case(0x8D, 0x12, 0, register::SingleEightBit::L, 0x5, 0x17; "no carry to register l")] #[test_case(0x8D, 0x12, 1, register::SingleEightBit::L, 0x5, 0x18; "carry to register l")] fn test_add_register_with_carry_to_a_value( opcode: u8, initial_value: u8, carry_bit: u8, operand_register: register::SingleEightBit, operand: u8, expected_value: u8, ) { let mut processor = Processor::default(); processor.registers.a = initial_value; processor .registers .set_single_8bit_register(operand_register, operand); processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [opcode, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(expected_value, processor.registers.a); } // these parameters are like impossible to read but I can't think of a nicer way to do this right now // B register #[test_case(0x88, 0x12, 0, register::SingleEightBit::B, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register b")] #[test_case(0x88, 0x12, 1, register::SingleEightBit::B, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register b")] #[test_case(0x88, 0x00, 0, register::SingleEightBit::B, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "no carry bit set, zero operands result in zero flag set, register b")] #[test_case(0x88, 0x00, 1, register::SingleEightBit::B, 0x0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, zero operands result in no zero flag set, register b")] #[test_case(0x88, 0x0F, 0, register::SingleEightBit::B, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, half carry, register b")] #[test_case(0x88, 0x0F, 1, register::SingleEightBit::B, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, half carry, register b")] #[test_case(0x88, 0x0F, 0, register::SingleEightBit::B, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, full carry, register b")] #[test_case(0x88, 0x0F, 1, register::SingleEightBit::B, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, full carry, register b")] #[test_case(0x88, 0xFF, 1, register::SingleEightBit::B, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "carry bit, both carries, register b")] // C register #[test_case(0x89, 0x12, 0, register::SingleEightBit::C, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register c")] #[test_case(0x89, 0x12, 1, register::SingleEightBit::C, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register c")] #[test_case(0x89, 0x00, 0, register::SingleEightBit::C, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "no carry bit set, zero operands result in zero flag set, register c")] #[test_case(0x89, 0x00, 1, register::SingleEightBit::C, 0x0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, zero operands result in no zero flag set, register c")] #[test_case(0x89, 0x0F, 0, register::SingleEightBit::C, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, half carry, register c")] #[test_case(0x89, 0x0F, 1, register::SingleEightBit::C, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, half carry, register c")] #[test_case(0x89, 0x0F, 0, register::SingleEightBit::C, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, full carry, register c")] #[test_case(0x89, 0x0F, 1, register::SingleEightBit::C, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, full carry, register c")] #[test_case(0x89, 0xFF, 1, register::SingleEightBit::C, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "carry bit, both carries, register c")] // D register #[test_case(0x8A, 0x12, 0, register::SingleEightBit::D, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register d")] #[test_case(0x8A, 0x12, 1, register::SingleEightBit::D, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register d")] #[test_case(0x8A, 0x00, 0, register::SingleEightBit::D, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "no carry bit set, zero operands result in zero flag set, register d")] #[test_case(0x8A, 0x00, 1, register::SingleEightBit::D, 0x0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, zero operands result in no zero flag set, register d")] #[test_case(0x8A, 0x0F, 0, register::SingleEightBit::D, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, half carry, register d")] #[test_case(0x8A, 0x0F, 1, register::SingleEightBit::D, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, half carry, register d")] #[test_case(0x8A, 0x0F, 0, register::SingleEightBit::D, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, full carry, register d")] #[test_case(0x8A, 0x0F, 1, register::SingleEightBit::D, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, full carry, register d")] #[test_case(0x8A, 0xFF, 1, register::SingleEightBit::D, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "carry bit, both carries, register d")] // E register #[test_case(0x8B, 0x12, 0, register::SingleEightBit::E, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register e")] #[test_case(0x8B, 0x12, 1, register::SingleEightBit::E, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register e")] #[test_case(0x8B, 0x00, 0, register::SingleEightBit::E, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "no carry bit set, zero operands result in zero flag set, register e")] #[test_case(0x8B, 0x00, 1, register::SingleEightBit::E, 0x0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, zero operands result in no zero flag set, register e")] #[test_case(0x8B, 0x0F, 0, register::SingleEightBit::E, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, half carry, register e")] #[test_case(0x8B, 0x0F, 1, register::SingleEightBit::E, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, half carry, register e")] #[test_case(0x8B, 0x0F, 0, register::SingleEightBit::E, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, full carry, register e")] #[test_case(0x8B, 0x0F, 1, register::SingleEightBit::E, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, full carry, register e")] #[test_case(0x8B, 0xFF, 1, register::SingleEightBit::E, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "carry bit, both carries, register e")] // H register #[test_case(0x8C, 0x12, 0, register::SingleEightBit::H, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register h")] #[test_case(0x8C, 0x12, 1, register::SingleEightBit::H, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register h")] #[test_case(0x8C, 0x00, 0, register::SingleEightBit::H, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "no carry bit set, zero operands result in zero flag set, register h")] #[test_case(0x8C, 0x00, 1, register::SingleEightBit::H, 0x0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, zero operands result in no zero flag set, register h")] #[test_case(0x8C, 0x0F, 0, register::SingleEightBit::H, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, half carry, register h")] #[test_case(0x8C, 0x0F, 1, register::SingleEightBit::H, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, half carry, register h")] #[test_case(0x8C, 0x0F, 0, register::SingleEightBit::H, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, full carry, register h")] #[test_case(0x8C, 0x0F, 1, register::SingleEightBit::H, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, full carry, register h")] #[test_case(0x8C, 0xFF, 1, register::SingleEightBit::H, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "carry bit, both carries, register h")] // L register #[test_case(0x8D, 0x12, 0, register::SingleEightBit::L, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register l")] #[test_case(0x8D, 0x12, 1, register::SingleEightBit::L, 0x5, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register l")] #[test_case(0x8D, 0x00, 0, register::SingleEightBit::L, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "no carry bit set, zero operands result in zero flag set, register l")] #[test_case(0x8D, 0x00, 1, register::SingleEightBit::L, 0x0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, zero operands result in no zero flag set, register l")] #[test_case(0x8D, 0x0F, 0, register::SingleEightBit::L, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, half carry, register l")] #[test_case(0x8D, 0x0F, 1, register::SingleEightBit::L, 0x1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, half carry, register l")] #[test_case(0x8D, 0x0F, 0, register::SingleEightBit::L, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit, full carry, register l")] #[test_case(0x8D, 0x0F, 1, register::SingleEightBit::L, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit, full carry, register l")] #[test_case(0x8D, 0xFF, 1, register::SingleEightBit::L, 0xFF, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "carry bit, both carries, register l")] fn test_add_register_with_carry_to_a_flags( opcode: u8, initial_value: u8, carry_bit: u8, operand_register: register::SingleEightBit, operand: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = initial_value; processor .registers .set_single_8bit_register(operand_register, operand); // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); // ...except for the carry bit, which we must set for the test processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [opcode, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0xF0, 0, 0xE0; "no carry bit")] #[test_case(0xF0, 1, 0xE1; "carry bit")] fn test_add_a_to_itself_with_carry_value(initial_value: u8, carry_bit: u8, expected_value: u8) { let mut processor = Processor::default(); processor.registers.a = initial_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0x8F, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(processor.registers.a, expected_value); } #[test_case(0xF0, 0, AdditionOperationFlags{zero: 0, carry: 1, half_carry: 0}; "carry bit")] #[test_case(0x0F, 0, AdditionOperationFlags{zero: 0, carry: 0, half_carry: 1}; "half carry bit")] #[test_case(0x0E, 1, AdditionOperationFlags{zero: 0, carry: 0, half_carry: 1}; "half carry bit with input carry bit")] fn test_add_a_to_itself_with_carry_flags( initial_value: u8, carry_bit: u8, expected_flags: AdditionOperationFlags, ) { 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_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor.registers.a = initial_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0x8F, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0x01, 0x00, 0x02, 0x03; "no carry")] #[test_case(0x01, 0x01, 0x02, 0x04; "carry")] fn test_add_immediate_with_carry_to_a_value( initial_value: u8, carry_bit: u8, operand: u8, expected_value: u8, ) { let mut processor = Processor::default(); processor.registers.a = initial_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0xCE, operand, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(processor.registers.a, expected_value); } #[test_case(0x01, 0, 0x02, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry")] #[test_case(0xFF, 1, 0x00, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")] #[test_case(0x0F, 1, 0x80, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")] #[test_case(0xF0, 1, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "full carry flag")] fn test_add_immediate_with_carry_to_a_flags( initial_value: u8, carry_bit: u8, operand: u8, expected_flags: AdditionOperationFlags, ) { 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_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor.registers.a = initial_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0xCE, operand, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0, 0xFE; "no carry bit")] #[test_case(1, 0xFF; "carry bit")] fn test_add_hl_addr_to_a_with_carry_value(carry_flag: u8, expected: u8) { let mut processor = Processor::default(); processor .memory .set(0xFFFE, 0xF0) .expect("expected to be able to set 0xFF00"); processor .registers .set_combined_register(register::Combined::HL, 0xFFFE); processor.registers.a = 0x0E; processor .registers .set_flag_bit(register::Flag::Carry, carry_flag); let data = [0x8E, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(expected, processor.registers.a); } #[test_case(0x01, 0, 0x02, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry")] #[test_case(0xFF, 1, 0x00, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")] #[test_case(0x0F, 1, 0x80, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")] #[test_case(0xF0, 1, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "full carry flag")] fn test_add_hl_addr_to_a_with_carry_flags( initial_value: u8, carry_bit: u8, operand: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor .memory .set(0xFFFE, operand) .expect("expected to be able to set 0xFF00"); processor .registers .set_combined_register(register::Combined::HL, 0xFFFE); // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor.registers.a = initial_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0x8E, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0x90, 0xFF, 0x0F, register::SingleEightBit::B, 0xF0; "subtract B")] #[test_case(0x91, 0xFF, 0x0F, register::SingleEightBit::C, 0xF0; "subtract C")] #[test_case(0x92, 0xFF, 0x0F, register::SingleEightBit::D, 0xF0; "subtract D")] #[test_case(0x93, 0xFF, 0x0F, register::SingleEightBit::E, 0xF0; "subtract E")] #[test_case(0x94, 0xFF, 0x0F, register::SingleEightBit::H, 0xF0; "subtract H")] #[test_case(0x95, 0xFF, 0x0F, register::SingleEightBit::L, 0xF0; "subtract L")] fn test_sub_register_from_a_value( opcode: u8, a_value: u8, operand: u8, operand_register: register::SingleEightBit, expected: u8, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_single_8bit_register(operand_register, operand); let data = [opcode, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(processor.registers.a, expected); } #[test_case(0x90, 0xFF, 0x0F, register::SingleEightBit::B, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from B")] #[test_case(0x90, 0xF0, 0x0F, register::SingleEightBit::B, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from B")] #[test_case(0x90, 0x0F, 0xF0, register::SingleEightBit::B, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from B")] #[test_case(0x90, 0x01, 0x01, register::SingleEightBit::B, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from B")] #[test_case(0x91, 0xFF, 0x0F, register::SingleEightBit::C, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from C")] #[test_case(0x91, 0xF0, 0x0F, register::SingleEightBit::C, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from C")] #[test_case(0x91, 0x0F, 0xF0, register::SingleEightBit::C, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from C")] #[test_case(0x91, 0x01, 0x01, register::SingleEightBit::C, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from C")] #[test_case(0x92, 0xFF, 0x0F, register::SingleEightBit::D, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from D")] #[test_case(0x92, 0xF0, 0x0F, register::SingleEightBit::D, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from D")] #[test_case(0x92, 0x0F, 0xF0, register::SingleEightBit::D, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from D")] #[test_case(0x92, 0x01, 0x01, register::SingleEightBit::D, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from D")] #[test_case(0x93, 0xFF, 0x0F, register::SingleEightBit::E, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from E")] #[test_case(0x93, 0xF0, 0x0F, register::SingleEightBit::E, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from E")] #[test_case(0x93, 0x0F, 0xF0, register::SingleEightBit::E, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from E")] #[test_case(0x93, 0x01, 0x01, register::SingleEightBit::E, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from E")] #[test_case(0x94, 0xFF, 0x0F, register::SingleEightBit::H, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from H")] #[test_case(0x94, 0xF0, 0x0F, register::SingleEightBit::H, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from H")] #[test_case(0x94, 0x0F, 0xF0, register::SingleEightBit::H, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from H")] #[test_case(0x94, 0x01, 0x01, register::SingleEightBit::H, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from H")] #[test_case(0x95, 0xFF, 0x0F, register::SingleEightBit::L, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags from L")] #[test_case(0x95, 0xF0, 0x0F, register::SingleEightBit::L, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry from L")] #[test_case(0x95, 0x0F, 0xF0, register::SingleEightBit::L, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry from L")] #[test_case(0x95, 0x01, 0x01, register::SingleEightBit::L, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero from L")] fn test_sub_register_from_a_flags( opcode: u8, a_value: u8, operand: u8, operand_register: register::SingleEightBit, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_single_8bit_register(operand_register, operand); // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 1, expected_flags.half_carry, expected_flags.carry, ), ); let data = [opcode, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test] fn test_sub_a_from_itself() { let mut processor = Processor::default(); processor.registers.a = 0xFF; let data = [0x97, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags(&mut processor, (0, 0, 1, 1)); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, 1), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, 0), (register::Flag::Carry, 0), ); } #[test_case(0x98, 0xFF, 0x0F, 0, register::SingleEightBit::B, 0xF0; "subtract B with no carry")] #[test_case(0x98, 0xFF, 0x0F, 1, register::SingleEightBit::B, 0xEF; "subtract B with carry")] #[test_case(0x99, 0xFF, 0x0F, 0, register::SingleEightBit::C, 0xF0; "subtract C with no carry")] #[test_case(0x99, 0xFF, 0x0F, 1, register::SingleEightBit::C, 0xEF; "subtract C with carry")] #[test_case(0x9A, 0xFF, 0x0F, 0, register::SingleEightBit::D, 0xF0; "subtract D with no carry")] #[test_case(0x9A, 0xFF, 0x0F, 1, register::SingleEightBit::D, 0xEF; "subtract D with carry")] #[test_case(0x9B, 0xFF, 0x0F, 0, register::SingleEightBit::E, 0xF0; "subtract E with no carry")] #[test_case(0x9B, 0xFF, 0x0F, 1, register::SingleEightBit::E, 0xEF; "subtract E with carry")] #[test_case(0x9C, 0xFF, 0x0F, 0, register::SingleEightBit::H, 0xF0; "subtract H with no carry")] #[test_case(0x9C, 0xFF, 0x0F, 1, register::SingleEightBit::H, 0xEF; "subtract H with carry")] #[test_case(0x9D, 0xFF, 0x0F, 0, register::SingleEightBit::L, 0xF0; "subtract L with no carry")] #[test_case(0x9D, 0xFF, 0x0F, 1, register::SingleEightBit::L, 0xEF; "subtract L with carry")] fn test_sub_register_with_carry_from_a_value( opcode: u8, a_value: u8, operand: u8, carry_bit: u8, operand_register: register::SingleEightBit, expected: u8, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_single_8bit_register(operand_register, operand); processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [opcode, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(processor.registers.a, expected); } // Register B #[test_case(0x98, 0xFE, 0, register::SingleEightBit::B, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register b")] #[test_case(0x98, 0xFE, 1, register::SingleEightBit::B, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register b")] #[test_case(0x98, 0x00, 0, register::SingleEightBit::B, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag, register b")] #[test_case(0x98, 0x01, 1, register::SingleEightBit::B, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit, register b")] #[test_case(0x98, 0x02, 0, register::SingleEightBit::B, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no, register b")] #[test_case(0x98, 0xF0, 0, register::SingleEightBit::B, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit, register b")] #[test_case(0x98, 0x0F, 0, register::SingleEightBit::B, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit, register b")] #[test_case(0x98, 0xF0, 1, register::SingleEightBit::B, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit, register b")] #[test_case(0x98, 0x0F, 1, register::SingleEightBit::B, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit, register b")] // Register C #[test_case(0x99, 0xFE, 0, register::SingleEightBit::C, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register c")] #[test_case(0x99, 0xFE, 1, register::SingleEightBit::C, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register c")] #[test_case(0x99, 0x00, 0, register::SingleEightBit::C, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag, register c")] #[test_case(0x99, 0x01, 1, register::SingleEightBit::C, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit, register c")] #[test_case(0x99, 0x02, 0, register::SingleEightBit::C, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no, register c")] #[test_case(0x99, 0xF0, 0, register::SingleEightBit::C, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit, register c")] #[test_case(0x99, 0x0F, 0, register::SingleEightBit::C, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit, register c")] #[test_case(0x99, 0xF0, 1, register::SingleEightBit::C, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit, register c")] #[test_case(0x99, 0x0F, 1, register::SingleEightBit::C, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit, register c")] // Register D #[test_case(0x9A, 0xFE, 0, register::SingleEightBit::D, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register d")] #[test_case(0x9A, 0xFE, 1, register::SingleEightBit::D, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register d")] #[test_case(0x9A, 0x00, 0, register::SingleEightBit::D, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag, register d")] #[test_case(0x9A, 0x01, 1, register::SingleEightBit::D, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit, register d")] #[test_case(0x9A, 0x02, 0, register::SingleEightBit::D, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no, register d")] #[test_case(0x9A, 0xF0, 0, register::SingleEightBit::D, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit, register d")] #[test_case(0x9A, 0x0F, 0, register::SingleEightBit::D, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit, register d")] #[test_case(0x9A, 0xF0, 1, register::SingleEightBit::D, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit, register d")] #[test_case(0x9A, 0x0F, 1, register::SingleEightBit::D, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit, register d")] // Register E #[test_case(0x9B, 0xFE, 0, register::SingleEightBit::E, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register e")] #[test_case(0x9B, 0xFE, 1, register::SingleEightBit::E, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register e")] #[test_case(0x9B, 0x00, 0, register::SingleEightBit::E, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag, register e")] #[test_case(0x9B, 0x01, 1, register::SingleEightBit::E, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit, register e")] #[test_case(0x9B, 0x02, 0, register::SingleEightBit::E, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no, register e")] #[test_case(0x9B, 0xF0, 0, register::SingleEightBit::E, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit, register e")] #[test_case(0x9B, 0x0F, 0, register::SingleEightBit::E, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit, register e")] #[test_case(0x9B, 0xF0, 1, register::SingleEightBit::E, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit, register e")] #[test_case(0x9B, 0x0F, 1, register::SingleEightBit::E, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit, register e")] // Register H #[test_case(0x9C, 0xFE, 0, register::SingleEightBit::H, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register h")] #[test_case(0x9C, 0xFE, 1, register::SingleEightBit::H, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register h")] #[test_case(0x9C, 0x00, 0, register::SingleEightBit::H, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag, register h")] #[test_case(0x9C, 0x01, 1, register::SingleEightBit::H, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit, register h")] #[test_case(0x9C, 0x02, 0, register::SingleEightBit::H, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no, register h")] #[test_case(0x9C, 0xF0, 0, register::SingleEightBit::H, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit, register h")] #[test_case(0x9C, 0x0F, 0, register::SingleEightBit::H, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit, register h")] #[test_case(0x9C, 0xF0, 1, register::SingleEightBit::H, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit, register h")] #[test_case(0x9C, 0x0F, 1, register::SingleEightBit::H, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit, register h")] // Register L #[test_case(0x9D, 0xFE, 0, register::SingleEightBit::L, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags, register l")] #[test_case(0x9D, 0xFE, 1, register::SingleEightBit::L, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags, register l")] #[test_case(0x9D, 0x00, 0, register::SingleEightBit::L, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag, register l")] #[test_case(0x9D, 0x01, 1, register::SingleEightBit::L, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit, register l")] #[test_case(0x9D, 0x02, 0, register::SingleEightBit::L, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no, register l")] #[test_case(0x9D, 0xF0, 0, register::SingleEightBit::L, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit, register l")] #[test_case(0x9D, 0x0F, 0, register::SingleEightBit::L, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit, register l")] #[test_case(0x9D, 0xF0, 1, register::SingleEightBit::L, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit, register l")] #[test_case(0x9D, 0x0F, 1, register::SingleEightBit::L, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit, register l")] fn test_sub_register_from_carry_from_a_flags( opcode: u8, initial_value: u8, carry_bit: u8, operand_register: register::SingleEightBit, operand: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = initial_value; processor .registers .set_single_8bit_register(operand_register, operand); // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); // ...except for the carry bit, which we must set for the test processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [opcode, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test] fn test_subtract_immediate_from_a_value() { let mut processor = Processor::default(); processor.registers.a = 0xFF; let data = [0xD6, 0x0F, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(processor.registers.a, 0xF0); } #[test_case(0xFF, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")] #[test_case(0xF0, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")] #[test_case(0x0F, 0xF0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")] #[test_case(0x01, 0x01, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")] fn test_sub_immediate_from_a_flags( initial_value: u8, operand: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = initial_value; // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); let data = [0xD6, operand, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0xFF, 0x0F, 0, 0xF0; "no carry")] #[test_case(0xFF, 0x0F, 1, 0xEF; "carry")] fn test_sub_immediate_with_carry_from_a_value( a_value: u8, operand: u8, carry_bit: u8, expected: u8, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0xDE, operand, 0x02]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x02]); processor.run_instruction(&ins); assert_eq!(processor.registers.a, expected); } #[test_case(0xFE, 0, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry bit set, results in no flags")] #[test_case(0xFE, 1, 0x1, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "carry bit set, results in no flags")] #[test_case(0x00, 0, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "all zero operands give zero flag")] #[test_case(0x01, 1, 0x0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 1 gives zero flag with carry bit")] #[test_case(0x02, 0, 0x2, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtracting from 2 gives zero flag with no")] #[test_case(0xF0, 0, 0x0F, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "no carry bit set, half carry bit")] #[test_case(0x0F, 0, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "no carry bit set, full carry bit")] #[test_case(0xF0, 1, 0x0E, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "carry bit set, half carry bit")] #[test_case(0x0F, 1, 0xEE, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "carry bit set, full carry bit")] fn test_sub_immediate_from_carry_from_a_flags( initial_value: u8, carry_bit: u8, operand: u8, expected_flags: AdditionOperationFlags, ) { let mut processor = Processor::default(); processor.registers.a = initial_value; // Set all the register to the opposite we expect to ensure they all get set testutil::set_opposite_of_expected_flags( &mut processor, ( expected_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); // ...except for the carry bit, which we must set for the test processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0xDE, operand, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0, 0; "subtract from itself")] #[test_case(1, 0xFF; "subtract from itself with carry bit")] fn test_sub_a_register_from_itself_with_carry_value(carry_bit: u8, expected: u8) { let mut processor = Processor::default(); processor.registers.a = 0xEE; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0x9F, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(expected, processor.registers.a); } #[test_case(0, AdditionOperationFlags{zero: 1, half_carry: 0, carry: 0}; "subtract from itself")] #[test_case(1, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 1}; "subtract from itself with carry bit")] fn test_sub_a_register_from_itself_with_carry_flags( carry_bit: u8, expected_flags: AdditionOperationFlags, ) { 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_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor.registers.a = 0xEE; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); let data = [0x9F, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test] fn test_sub_hl_addr_from_a_value() { let mut processor = Processor::default(); processor.registers.a = 0xBA; processor .memory .set(0xABCD, 0xA9) .expect("failed to set memory value"); processor .registers .set_combined_register(register::Combined::HL, 0xABCD); let data = [0x96, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(0x11, processor.registers.a); } #[test_case(0xFF, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")] #[test_case(0xF0, 0x0F, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")] #[test_case(0x0F, 0xF0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")] #[test_case(0x01, 0x01, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")] fn test_sub_hl_addr_from_a_flags( a_value: u8, operand_value: u8, expected_flags: AdditionOperationFlags, ) { 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_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor.registers.a = a_value; processor .memory .set(0xABCD, operand_value) .expect("failed to set memory value"); processor .registers .set_combined_register(register::Combined::HL, 0xABCD); let data = [0x96, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0xFE, 0xED, 0, 0x11; "no carry bit")] #[test_case(0xFE, 0xED, 1, 0x10; "carry bit")] fn test_sub_hl_from_a_flags(a_value: u8, operand_value: u8, carry_bit: u8, expected_value: u8) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); processor .memory .set(0xABCD, operand_value) .expect("failed to set memory value"); processor .registers .set_combined_register(register::Combined::HL, 0xABCD); let data = [0x9E, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(expected_value, processor.registers.a); } #[test_case(0xFF, 0x0F, 0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags")] #[test_case(0xFF, 0x0E, 1, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 0 }; "no flags with carry bit")] #[test_case(0xF0, 0x0F, 0, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry")] #[test_case(0xF0, 0x0E, 1, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 0 }; "half carry with carry bit")] #[test_case(0x0F, 0xF0, 0, AdditionOperationFlags { zero: 0, half_carry: 0, carry: 1 }; "full carry")] #[test_case(0x0F, 0xEF, 1, AdditionOperationFlags { zero: 0, half_carry: 1, carry: 1 }; "full carry with carry bit")] #[test_case(0x01, 0x01, 0, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero")] #[test_case(0x01, 0x00, 1, AdditionOperationFlags { zero: 1, half_carry: 0, carry: 0 }; "zero with carry bit")] fn test_sub_hl_addr_from_a_with_carry_flags( a_value: u8, operand_value: u8, carry_bit: u8, expected_flags: AdditionOperationFlags, ) { 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_flags.zero, 0, expected_flags.half_carry, expected_flags.carry, ), ); processor .registers .set_flag_bit(register::Flag::Carry, carry_bit); processor.registers.a = a_value; processor .memory .set(0xABCD, operand_value) .expect("failed to set memory value"); processor .registers .set_combined_register(register::Combined::HL, 0xABCD); let data = [0x9E, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_flags.zero), (register::Flag::Subtract, 1), (register::Flag::HalfCarry, expected_flags.half_carry), (register::Flag::Carry, expected_flags.carry), ); } #[test_case(0xA0, 0xFB, register::SingleEightBit::B, 0x0F, 0x0B; "AND with B")] #[test_case(0xA1, 0xFC, register::SingleEightBit::C, 0x0F, 0x0C; "AND with C")] #[test_case(0xA2, 0xFD, register::SingleEightBit::D, 0x0F, 0x0D; "AND with D")] #[test_case(0xA3, 0xFE, register::SingleEightBit::E, 0x0F, 0x0E; "AND with E")] #[test_case(0xA4, 0xF0, register::SingleEightBit::H, 0x0F, 0x00; "AND with H")] #[test_case(0xA5, 0xF1, register::SingleEightBit::L, 0x0F, 0x01; "AND with L")] fn test_and_single_register_with_a_value( opcode: u8, a_value: u8, operand_register: register::SingleEightBit, operand: u8, expected_value: u8, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_single_8bit_register(operand_register, operand); let data = [opcode, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(expected_value, processor.registers.a); } #[test_case(0xA0, 0xFA, register::SingleEightBit::B, 0x0F, 0; "AND with B, not zero")] #[test_case(0xA0, 0xFA, register::SingleEightBit::B, 0x00, 1; "AND with B, zero")] #[test_case(0xA1, 0xFA, register::SingleEightBit::C, 0x0F, 0; "AND with C, not zero")] #[test_case(0xA1, 0xFA, register::SingleEightBit::C, 0x00, 1; "AND with C, zero")] #[test_case(0xA2, 0xFA, register::SingleEightBit::D, 0x0F, 0; "AND with D, not zero")] #[test_case(0xA2, 0xFA, register::SingleEightBit::D, 0x00, 1; "AND with D, zero")] #[test_case(0xA3, 0xFA, register::SingleEightBit::E, 0x0F, 0; "AND with E, not zero")] #[test_case(0xA3, 0xFA, register::SingleEightBit::E, 0x00, 1; "AND with E, zero")] fn test_and_single_register_with_a_flags( opcode: u8, a_value: u8, operand_register: register::SingleEightBit, operand: u8, expected_zero_flag: u8, ) { 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, 1, 0)); processor.registers.a = a_value; processor .registers .set_single_8bit_register(operand_register, operand); let data = [opcode, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_zero_flag), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, 1), (register::Flag::Carry, 0), ); } #[test] fn test_and_a_with_itself_value() { let mut processor = Processor::default(); processor.registers.a = 0xAB; let data = [0xA7, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(0xAB, processor.registers.a); } #[test] fn test_and_a_with_itself_flags() { 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, (0, 0, 1, 0)); processor.registers.a = 0xAB; let data = [0xA7, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, 0), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, 1), (register::Flag::Carry, 0), ); } #[test] fn test_and_hl_value_with_a_value() { let mut processor = Processor::default(); processor.registers.a = 0xAB; processor .registers .set_combined_register(register::Combined::HL, 0x00FF); processor .memory .set(0x00FF, 0xF0) .expect("failed to set value"); let data = [0xA6, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(0xA0, processor.registers.a); } #[test_case(0xFA, 0x0F, 0; "not zero")] #[test_case(0xFA, 0x00, 1; "zero")] fn test_and_hl_value_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_combined_register(register::Combined::HL, 0x00FF); processor .memory .set(0x00FF, operand) .expect("failed to set value"); // 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, 0)); let data = [0xA6, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_zero_flag), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, 1), (register::Flag::Carry, 0), ); } #[test] fn test_and_immediate_with_a_value() { let mut processor = Processor::default(); processor.registers.a = 0xAB; let data = [0xE6, 0x0F, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); assert_eq!(0x0B, processor.registers.a); } #[test_case(0xFA, 0x0F, 0; "not zero")] #[test_case(0xFA, 0x00, 1; "zero")] fn test_and_immediate_with_a_flags(a_value: u8, operand: u8, expected_zero_flag: u8) { let mut processor = Processor::default(); processor.registers.a = a_value; // 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, 0)); let data = [0xE6, operand, 0x01]; let (ins, extra_data) = RunnableInstruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x01]); processor.run_instruction(&ins); testutil::assert_flags_eq!( processor, (register::Flag::Zero, expected_zero_flag), (register::Flag::Subtract, 0), (register::Flag::HalfCarry, 1), (register::Flag::Carry, 0), ); }