use ferris_boi::{ cpu::{instructions::Instruction, Processor}, register, }; use test_case::test_case; // lol going from a register to itself is kind of a weird thing to test #[test_case(0x7F, register::SingleEightBit::A, register::SingleEightBit::A)] #[test_case(0x78, register::SingleEightBit::A, register::SingleEightBit::B)] #[test_case(0x79, register::SingleEightBit::A, register::SingleEightBit::C)] #[test_case(0x7A, register::SingleEightBit::A, register::SingleEightBit::D)] #[test_case(0x7B, register::SingleEightBit::A, register::SingleEightBit::E)] #[test_case(0x7C, register::SingleEightBit::A, register::SingleEightBit::H)] #[test_case(0x7D, register::SingleEightBit::A, register::SingleEightBit::L)] #[test_case(0x40, register::SingleEightBit::B, register::SingleEightBit::B)] #[test_case(0x41, register::SingleEightBit::B, register::SingleEightBit::C)] #[test_case(0x42, register::SingleEightBit::B, register::SingleEightBit::D)] #[test_case(0x43, register::SingleEightBit::B, register::SingleEightBit::E)] #[test_case(0x44, register::SingleEightBit::B, register::SingleEightBit::H)] #[test_case(0x45, register::SingleEightBit::B, register::SingleEightBit::L)] #[test_case(0x48, register::SingleEightBit::C, register::SingleEightBit::B)] #[test_case(0x49, register::SingleEightBit::C, register::SingleEightBit::C)] #[test_case(0x4A, register::SingleEightBit::C, register::SingleEightBit::D)] #[test_case(0x4B, register::SingleEightBit::C, register::SingleEightBit::E)] #[test_case(0x4C, register::SingleEightBit::C, register::SingleEightBit::H)] #[test_case(0x4D, register::SingleEightBit::C, register::SingleEightBit::L)] #[test_case(0x50, register::SingleEightBit::D, register::SingleEightBit::B)] #[test_case(0x51, register::SingleEightBit::D, register::SingleEightBit::C)] #[test_case(0x52, register::SingleEightBit::D, register::SingleEightBit::D)] #[test_case(0x53, register::SingleEightBit::D, register::SingleEightBit::E)] #[test_case(0x54, register::SingleEightBit::D, register::SingleEightBit::H)] #[test_case(0x55, register::SingleEightBit::D, register::SingleEightBit::L)] #[test_case(0x58, register::SingleEightBit::E, register::SingleEightBit::B)] #[test_case(0x59, register::SingleEightBit::E, register::SingleEightBit::C)] #[test_case(0x5A, register::SingleEightBit::E, register::SingleEightBit::D)] #[test_case(0x5B, register::SingleEightBit::E, register::SingleEightBit::E)] #[test_case(0x5C, register::SingleEightBit::E, register::SingleEightBit::H)] #[test_case(0x5D, register::SingleEightBit::E, register::SingleEightBit::L)] #[test_case(0x60, register::SingleEightBit::H, register::SingleEightBit::B)] #[test_case(0x61, register::SingleEightBit::H, register::SingleEightBit::C)] #[test_case(0x62, register::SingleEightBit::H, register::SingleEightBit::D)] #[test_case(0x63, register::SingleEightBit::H, register::SingleEightBit::E)] #[test_case(0x64, register::SingleEightBit::H, register::SingleEightBit::H)] #[test_case(0x65, register::SingleEightBit::H, register::SingleEightBit::L)] #[test_case(0x68, register::SingleEightBit::L, register::SingleEightBit::B)] #[test_case(0x69, register::SingleEightBit::L, register::SingleEightBit::C)] #[test_case(0x6A, register::SingleEightBit::L, register::SingleEightBit::D)] #[test_case(0x6B, register::SingleEightBit::L, register::SingleEightBit::E)] #[test_case(0x6C, register::SingleEightBit::L, register::SingleEightBit::H)] #[test_case(0x6D, register::SingleEightBit::L, register::SingleEightBit::L)] #[test_case(0x47, register::SingleEightBit::B, register::SingleEightBit::A)] #[test_case(0x4F, register::SingleEightBit::C, register::SingleEightBit::A)] #[test_case(0x57, register::SingleEightBit::D, register::SingleEightBit::A)] #[test_case(0x5F, register::SingleEightBit::E, register::SingleEightBit::A)] #[test_case(0x67, register::SingleEightBit::H, register::SingleEightBit::A)] #[test_case(0x6F, register::SingleEightBit::L, register::SingleEightBit::A)] fn test_load_register( load_opcode: u8, dst_register: register::SingleEightBit, src_register: register::SingleEightBit, ) { let mut processor = Processor::default(); processor .registers .set_single_8bit_register(src_register, 0x45); let data = [load_opcode, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); // assert our extra data after the instruction is returned assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!( 0x45, processor.registers.get_single_8bit_register(dst_register) ); } #[test_case(0x3E, register::SingleEightBit::A)] #[test_case(0x06, register::SingleEightBit::B)] #[test_case(0x0E, register::SingleEightBit::C)] #[test_case(0x16, register::SingleEightBit::D)] #[test_case(0x1E, register::SingleEightBit::E)] #[test_case(0x26, register::SingleEightBit::H)] #[test_case(0x2E, register::SingleEightBit::L)] fn test_load_immediate(load_opcode: u8, expected_register: register::SingleEightBit) { let mut processor = Processor::default(); let data = [load_opcode, 0x23, 0x00, 0x01]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); // assert our extra data after the instruction is returned // this data is just garbage; no idea if it's a valid instruction assert_eq!(extra_data, &[0x00, 0x01]); processor.run_instruction(ins); assert_eq!( 0x23, processor .registers .get_single_8bit_register(expected_register) ); } #[test_case(0x7E, register::Combined::HL, register::SingleEightBit::A)] #[test_case(0x46, register::Combined::HL, register::SingleEightBit::B)] #[test_case(0x4E, register::Combined::HL, register::SingleEightBit::C)] #[test_case(0x56, register::Combined::HL, register::SingleEightBit::D)] #[test_case(0x5E, register::Combined::HL, register::SingleEightBit::E)] #[test_case(0x66, register::Combined::HL, register::SingleEightBit::H)] #[test_case(0x6E, register::Combined::HL, register::SingleEightBit::L)] #[test_case(0x0A, register::Combined::BC, register::SingleEightBit::A)] #[test_case(0x1A, register::Combined::DE, register::SingleEightBit::A)] fn test_load_from_memory( opcode: u8, expected_deref_register: register::Combined, expected_register: register::SingleEightBit, ) { let mut processor = Processor::default(); let set_val = processor.memory.set(0xCCDD, 105); assert_eq!(105, set_val.unwrap()); processor .registers .set_combined_register(expected_deref_register, 0xCCDD); let data = [opcode, 0x10, 0x20]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse isntruction"); assert_eq!(extra_data, &[0x10, 0x20]); processor.run_instruction(ins); assert_eq!( 105, processor .registers .get_single_8bit_register(expected_register) ); } #[test_case(0x70, register::Combined::HL, register::SingleEightBit::B)] #[test_case(0x71, register::Combined::HL, register::SingleEightBit::C)] #[test_case(0x72, register::Combined::HL, register::SingleEightBit::D)] #[test_case(0x73, register::Combined::HL, register::SingleEightBit::E)] #[test_case(0x74, register::Combined::HL, register::SingleEightBit::H)] #[test_case(0x75, register::Combined::HL, register::SingleEightBit::L)] #[test_case(0x02, register::Combined::BC, register::SingleEightBit::A)] #[test_case(0x12, register::Combined::DE, register::SingleEightBit::A)] #[test_case(0x77, register::Combined::HL, register::SingleEightBit::A)] fn test_load_to_memory( opcode: u8, expected_deref_register: register::Combined, expected_register: register::SingleEightBit, ) { let mut processor = Processor::default(); // This test does a bit of a sneaky - because we want to use CC as the high/low addresses // in the H/L variants of these tests, but we also want to use it store addreses, we must // be careful that the stored immediate value is also part of the address in these cases. processor .registers .set_single_8bit_register(expected_register, 0xCC); processor .registers .set_combined_register(expected_deref_register, 0xCCCC); let data = [opcode, 0x10, 0x20]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x10, 0x20]); processor.run_instruction(ins); assert_eq!(0xCC, processor.memory.get(0xCCCC).unwrap()); } #[test] fn test_load_immediate_to_memory() { let mut processor = Processor::default(); processor .registers .set_combined_register(register::Combined::HL, 0xCCDE); let data = [0x36, 0x1D, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!(0x1D, processor.memory.get(0xCCDE).unwrap()); } #[test] fn test_load_from_immediate_address() { let mut processor = Processor::default(); processor .memory .set(0xCCDE, 105) .expect("failed to set memory value"); let data = [0xFA, 0xDE, 0xCC, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!(105, processor.registers.a); } #[test] fn test_load_to_immadiate_address() { let mut processor = Processor::default(); processor.registers.a = 105; let data = [0xEA, 0xDE, 0xCC, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!(105, processor.memory.get(0xCCDE).unwrap()); } #[test] fn test_load_from_io_register_zone() { let mut processor = Processor::default(); processor .memory .set(0xFF64, 10) .expect("could not set memory value"); processor.registers.c = 0x64; let data = [0xF2, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!(10, processor.registers.a); } #[test] fn test_load_to_io_register_zone() { let mut processor = Processor::default(); processor.registers.c = 0x64; processor.registers.a = 10; let data = [0xE2, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!(10, processor.memory.get(0xFF64).unwrap()); } #[test_case(0x3A, 0x64, 0x63)] #[test_case(0x2A, 0x64, 0x65)] fn test_load_from_register_address_then_do_arithmetic( opcode: u8, hl_value_before: u16, hl_value_after: u16, ) { let mut processor = Processor::default(); processor .registers .set_combined_register(register::Combined::HL, hl_value_before); processor .memory .set(hl_value_before.into(), 10) .expect("failed to set memory value"); let data = [opcode, 0x00]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x00]); processor.run_instruction(ins); assert_eq!(10, processor.registers.a); assert_eq!( hl_value_after, processor .registers .get_combined_register(register::Combined::HL) ); } #[test] fn test_load_to_io_register_zone_by_immediate() { let mut processor = Processor::default(); processor.registers.a = 0xAF; let data = [0xE0, 0x05, 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!(0xAF, processor.memory.get(0xFF05).unwrap()); } #[test] fn test_load_from_io_register_zone_by_immediate() { let mut processor = Processor::default(); processor .memory .set(0xFF05, 0xAF) .expect("failed to set memory value"); let data = [0xF0, 0x05, 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!(0xAF, processor.registers.a); } #[test] fn test_load_from_bottom_of_so_to_immediate_address() { let mut processor = Processor::default(); processor.registers.stack_pointer = 0xABCD; let data = [0x08, 0xAA, 0xFF, 0x06]; let (ins, extra_data) = Instruction::from_data(&data).expect("could not parse instruction"); assert_eq!(extra_data, &[0x06]); processor.run_instruction(ins); let bottom_stored_value = processor.memory.get_from(0xFFAA).unwrap().get(); let top_stored_value = processor.memory.get_from(0xFFAB).unwrap().get(); assert_eq!(0xCD, bottom_stored_value); assert_eq!(0xAB, top_stored_value); }