use crate::testutil::{self, assert_flags_eq}; use ferris_boi::{ cpu::{instructions::Instruction, Processor}, register, }; use test_case::test_case; struct DAAInputFlags { subtract: bool, half_carry: bool, full_carry: bool, } struct DAAOutputFlags { zero: bool, full_carry: bool, } #[test_case(1)] #[test_case(0)] fn test_set_carry_flag_always_sets_to_1(starting_value: u8) { let mut processor = Processor::default(); processor .registers .set_flag_bit(register::Flag::Carry, starting_value); let data = [0x37, 0x03]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x03]); processor.run_instruction(ins); let new_carry_bit = processor.registers.get_flag_bit(register::Flag::Carry); assert_eq!(1, new_carry_bit); } #[test_case(1, 0)] #[test_case(0, 1)] fn test_complement_carry_bit(starting_value: u8, expected_value: u8) { let mut processor = Processor::default(); processor .registers .set_flag_bit(register::Flag::Carry, starting_value); let data = [0x3F, 0x03]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x03]); processor.run_instruction(ins); let new_carry_bit = processor.registers.get_flag_bit(register::Flag::Carry); assert_eq!(expected_value, new_carry_bit); } #[test_case(0x37)] #[test_case(0x3F)] fn test_all_carry_bit_instructions_adjust_flags(opcode: u8) { let mut processor = Processor::default(); testutil::set_opposite_of_expected_flags(&mut processor, (0, 0, 0, 1)); let data = [opcode, 0x03]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x03]); processor.run_instruction(ins); testutil::assert_flags_eq!( processor, // Always zero (register::Flag::HalfCarry, 0), (register::Flag::Carry, 1), (register::Flag::Subtract, 0), // Value is preserved (register::Flag::Zero, 1), ); } #[test] fn test_complement_a_register_value() { let mut processor = Processor::default(); processor.registers.a = 0xF0; let data = [0x2F, 0x03]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x03]); processor.run_instruction(ins); assert_eq!(0x0F, processor.registers.a); } #[test] fn test_complement_a_register_flags() { let mut processor = Processor::default(); processor.registers.a = 0xF0; testutil::set_opposite_of_expected_flags(&mut processor, (0, 1, 1, 0)); let data = [0x2F, 0x03]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x03]); processor.run_instruction(ins); testutil::assert_flags_eq!( processor, // Always 1 (register::Flag::HalfCarry, 1), (register::Flag::Subtract, 1), // Value is preserved (register::Flag::Carry, 1), (register::Flag::Zero, 1), ); } #[test_case(0x22, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x22, DAAOutputFlags{zero: false, full_carry: false}; "both digits less than 9 should not adjust A register")] #[test_case(0x0A, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x10, DAAOutputFlags{zero: false, full_carry: false}; "adjust result by 0x06 if lower nibble is greater than 9")] #[test_case(0xA5, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x05, DAAOutputFlags{zero: false, full_carry: true}; "adjust result by 0x60 if greater than 99")] #[test_case(0x20, DAAInputFlags{subtract: false, half_carry: true, full_carry: false}, 0x26, DAAOutputFlags{zero: false, full_carry: false}; "adjust result by 0x06 if half carry was performed")] #[test_case(0x33, DAAInputFlags{subtract: false, half_carry: false, full_carry: true}, 0x93, DAAOutputFlags{zero: false, full_carry: true}; "adjust result by 0x60 if full carry was performed")] #[test_case(0x26, DAAInputFlags{subtract: true, half_carry: true, full_carry: false}, 0x20, DAAOutputFlags{zero: false, full_carry: false}; "adjust result by -0x06 if half carry and a subtract were performed")] #[test_case(0x93, DAAInputFlags{subtract: true, half_carry: false, full_carry: true}, 0x33, DAAOutputFlags{zero: false, full_carry: true}; "adjust result by -0x60 if full carry and a subtract were performed")] #[test_case(0x00, DAAInputFlags{subtract: false, half_carry: false, full_carry: false}, 0x00, DAAOutputFlags{zero: true, full_carry: false}; "zero flag is true if result is zero")] #[test_case(0x9C, DAAInputFlags{subtract: false, half_carry: true, full_carry: false}, 0x02, DAAOutputFlags{zero: false, full_carry: true}; "just because the upper nibble is 9 does not mean that 0x06 is added")] fn test_daa( a_value: u8, flags: DAAInputFlags, expected_a_value: u8, expected_flags: DAAOutputFlags, ) { let mut processor = Processor::default(); processor.registers.a = a_value; processor .registers .set_flag_bit(register::Flag::Carry, flags.full_carry.into()); processor .registers .set_flag_bit(register::Flag::HalfCarry, flags.half_carry.into()); processor .registers .set_flag_bit(register::Flag::Subtract, flags.subtract.into()); processor .registers // Set the opposite of expected so we know we're setting it right .set_flag_bit(register::Flag::Zero, (!expected_flags.zero).into()); let data = [0x27, 0x06]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x06]); processor.run_instruction(ins); assert_eq!(expected_a_value, processor.registers.a); assert_flags_eq!( processor, (register::Flag::Carry, u8::from(expected_flags.full_carry)), (register::Flag::Zero, u8::from(expected_flags.zero)) ); } #[test] fn test_nop_executes_successfully() { let mut processor = Processor::default(); let data = [0x00, 0x06]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x06]); processor.run_instruction(ins); // uhhh it does nothing assert_eq!(processor.num_cycles, 4); } #[test] fn test_enable_interrupts_enables_interrupts_on_the_following_instruction() { let mut processor = Processor::default(); [ // Enable interrupts 0xFB, // Nop 0x00, ] .iter() .copied() .enumerate() .for_each(|(idx, opcode)| { processor .memory .set( usize::from(processor.registers.program_counter) + idx, opcode, ) .expect("could not program data"); }); assert!(!processor.interrupts_enabled()); processor.run_next_instruction(); assert!(!processor.interrupts_enabled()); processor.run_next_instruction(); assert!(processor.interrupts_enabled()); } #[test] fn test_disable_interrupts_disables_interrupts_once_enabled() { let mut processor = Processor::default(); [ // Enable interrupts 0xFB, // Nop 0x00, 0xF3, ] .iter() .copied() .enumerate() .for_each(|(idx, opcode)| { processor .memory .set( usize::from(processor.registers.program_counter) + idx, opcode, ) .expect("could not program data"); }); assert!(!processor.interrupts_enabled()); processor.run_next_instruction(); assert!(!processor.interrupts_enabled()); processor.run_next_instruction(); assert!(processor.interrupts_enabled()); processor.run_next_instruction(); assert!(!processor.interrupts_enabled()); }