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