From dcaf0588de1f6fc3899bcb4cd5d5a48243033410 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Mon, 4 Apr 2022 23:34:34 -0400 Subject: [PATCH] Add load to (hl) instructions --- src/run.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/run.rs b/src/run.rs index 7810ef6..dae5297 100644 --- a/src/run.rs +++ b/src/run.rs @@ -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)); + } }