use ferris_boi::{ cpu::{instructions::Instruction, Processor}, register::Flag, }; use test_case::test_case; #[test] fn test_can_jump_to_immediate() { let mut processor = Processor::default(); let data = [0xC3, 0x37, 0x13, 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!(0x1337, processor.registers.program_counter); } #[test] fn test_call_adjusts_program_counter_to_given_value() { let mut processor = Processor::default(); let data = [0xCD, 0x37, 0x13, 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!(0x1337, processor.registers.program_counter); } #[test] fn test_call_pushes_pc_onto_stack() { let mut processor = Processor::default(); processor.registers.program_counter = 0xBEEF; let data = [0xCD, 0x37, 0x13, 0x06]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x06]); let old_sp = processor.registers.stack_pointer; processor.run_instruction(ins); assert_eq!(0xBE, processor.memory.get((old_sp - 1).into()).unwrap()); // 0xEF + 3, which is the size of the CALL instruction assert_eq!(0xF2, processor.memory.get((old_sp - 2).into()).unwrap()); assert_eq!(old_sp - 2, processor.registers.stack_pointer); } #[test_case(0xCC, Flag::Zero, 1; "CALL Z")] #[test_case(0xDC, Flag::Carry, 1; "CALL C")] #[test_case(0xC4, Flag::Zero, 0; "CALL NZ")] #[test_case(0xD4, Flag::Carry, 0; "CALL NC")] #[test_case(0xCA, Flag::Zero, 1; "JP Z")] #[test_case(0xDA, Flag::Carry, 1; "JP C")] #[test_case(0xC2, Flag::Zero, 0; "JP NZ")] #[test_case(0xD2, Flag::Carry, 0; "JP NC")] fn test_call_and_jump_jumps_if_condition_matches(opcode: u8, flag: Flag, expected_value: u8) { let mut processor = Processor::default(); processor.registers.set_flag_bit(flag, expected_value); let data = [opcode, 0x37, 0x13, 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!(0x1337, processor.registers.program_counter); } #[test_case(0xCC, Flag::Zero, 1; "CALL Z")] #[test_case(0xDC, Flag::Carry, 1; "CALL C")] #[test_case(0xC4, Flag::Zero, 0; "CALL NZ")] #[test_case(0xD4, Flag::Carry, 0; "CALL NC")] fn test_call_grows_stack_if_condition_matches(opcode: u8, flag: Flag, expected_value: u8) { let mut processor = Processor::default(); processor.registers.set_flag_bit(flag, expected_value); let data = [opcode, 0x37, 0x13, 0x06]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x06]); let old_sp = processor.registers.stack_pointer; processor.run_instruction(ins); assert_eq!(old_sp - 2, processor.registers.stack_pointer); } #[test_case(0xCC, Flag::Zero, 1; "CALL Z")] #[test_case(0xDC, Flag::Carry, 1; "CALL C")] #[test_case(0xC4, Flag::Zero, 0; "CALL NZ")] #[test_case(0xD4, Flag::Carry, 0; "CALL NC")] #[test_case(0xCA, Flag::Zero, 1; "JP Z")] #[test_case(0xDA, Flag::Carry, 1; "JP C")] #[test_case(0xC2, Flag::Zero, 0; "JP NZ")] #[test_case(0xD2, Flag::Carry, 0; "JP NC")] fn test_call_and_jump_does_nothing_if_condition_fails(opcode: u8, flag: Flag, expected_value: u8) { let mut processor = Processor::default(); processor .registers .set_flag_bit(flag, if expected_value == 0 { 1 } else { 0 }); let data = [opcode, 0x37, 0x13, 0x06]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x06]); let old_sp = processor.registers.stack_pointer; let old_pc = processor.registers.program_counter; processor.run_instruction(ins); assert_eq!(old_sp, processor.registers.stack_pointer); // run_instruction does not advance the program counter, so this will be the same. Normally it would be old_pc plus 3 assert_eq!(old_pc, processor.registers.program_counter); }