ferris-boi/tests/cpu/load8.rs

329 lines
13 KiB
Rust

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);
}