Add load to (hl) instructions

jsmoo
Nick Krichevsky 2022-04-04 23:34:34 -04:00
parent 1dc176fd74
commit dcaf0588de
1 changed files with 60 additions and 2 deletions

View File

@ -29,6 +29,10 @@ pub enum Instruction {
LDFromHLAddress {
dst: register::Single,
},
// 3.3.1.2, only the loading to (hl) instructions
LDToHLAddress {
src: register::Single,
},
}
struct RunnableInstruction {
@ -101,6 +105,12 @@ impl RunnableInstruction {
0x6C => Self::make_ldr1r2_data(register::Single::L, register::Single::H, data),
0x6D => Self::make_ldr1r2_data(register::Single::L, register::Single::L, data),
0x6E => Self::make_ld_from_hl_address(register::Single::L, data),
0x70 => Self::make_ld_to_hl_address(register::Single::B, data),
0x71 => Self::make_ld_to_hl_address(register::Single::C, data),
0x72 => Self::make_ld_to_hl_address(register::Single::D, data),
0x73 => Self::make_ld_to_hl_address(register::Single::E, data),
0x74 => Self::make_ld_to_hl_address(register::Single::H, data),
0x75 => Self::make_ld_to_hl_address(register::Single::L, data),
_ => panic!("invalid opcode {opcode:0x}"),
}
}
@ -141,6 +151,16 @@ impl RunnableInstruction {
&data[1..],
)
}
fn make_ld_to_hl_address(src: register::Single, data: &[u8]) -> (RunnableInstruction, &[u8]) {
(
RunnableInstruction {
instruction: Instruction::LDToHLAddress { src },
cycles: 8,
},
&data[1..],
)
}
}
impl Processor {
@ -165,6 +185,16 @@ impl Processor {
self.registers
.set_single_register(dst, memory_value.unwrap());
}
Instruction::LDToHLAddress { src } => {
let value = self.registers.get_single_register(src);
let dst_address = self.registers.get_combined_register(register::Combined::HL);
let memory_value = self.memory.set(dst_address.into(), value);
if memory_value.is_none() {
panic!("invalid address stored in (hl): {dst_address}")
}
}
}
self.num_cycles += u64::from(instruction.cycles);
@ -267,7 +297,7 @@ mod tests {
#[test_case(0x5E, register::Single::E)]
#[test_case(0x66, register::Single::H)]
#[test_case(0x6E, register::Single::L)]
fn test_load_from_memory(opcode: u8, expected_reigster: register::Single) {
fn test_load_from_memory(opcode: u8, expected_register: register::Single) {
let mut processor = Processor::default();
let set_val = processor.memory.set(0xCCDD, 105);
assert_eq!(Some(105), set_val);
@ -284,7 +314,35 @@ mod tests {
processor.run(&ins);
assert_eq!(
105,
processor.registers.get_single_register(expected_reigster)
processor.registers.get_single_register(expected_register)
);
}
#[test_case(0x70, register::Single::B)]
#[test_case(0x71, register::Single::C)]
#[test_case(0x72, register::Single::D)]
#[test_case(0x73, register::Single::E)]
#[test_case(0x74, register::Single::H)]
#[test_case(0x75, register::Single::L)]
fn test_load_to_memory(opcode: u8, expected_register: register::Single) {
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_register(expected_register, 0xCC);
processor
.registers
.set_combined_register(register::Combined::HL, 0xCCCC);
let data = [opcode, 0x10, 0x20];
let (ins, extra_data) = RunnableInstruction::from_data(&data);
assert_eq!(extra_data, &[0x10, 0x20]);
processor.run(&ins);
assert_eq!(Some(0xCC), processor.memory.get(0xCCCC));
}
}