diff --git a/src/run.rs b/src/run.rs index f3d8a4b..1761bb3 100644 --- a/src/run.rs +++ b/src/run.rs @@ -26,12 +26,16 @@ pub enum Instruction { src: register::Single, }, // 3.3.1.2/3.3.1.3, only the loading from (nn) instructions - LDFromAddress { + LDFromRegisterAddress { // src, unlike some of the other instructions, is a 16bit combined register that will be // dereferenced to get the value to place into the dst register src: register::Combined, dst: register::Single, }, + LDFromImmediateAddress { + src_address: u16, + dst: register::Single, + }, // 3.3.1.2, only the loading to (hl) instructions LDToHLAddress { src: register::Single, @@ -70,49 +74,77 @@ impl RunnableInstruction { 0x7B => Self::make_ldr1r2_data(register::Single::A, register::Single::E, data), 0x7C => Self::make_ldr1r2_data(register::Single::A, register::Single::H, data), 0x7D => Self::make_ldr1r2_data(register::Single::A, register::Single::L, data), - 0x7E => Self::make_ld_from_address(register::Combined::HL, register::Single::A, data), + 0x7E => Self::make_ld_from_register_address( + register::Combined::HL, + register::Single::A, + data, + ), 0x40 => Self::make_ldr1r2_data(register::Single::B, register::Single::B, data), 0x41 => Self::make_ldr1r2_data(register::Single::B, register::Single::C, data), 0x42 => Self::make_ldr1r2_data(register::Single::B, register::Single::D, data), 0x43 => Self::make_ldr1r2_data(register::Single::B, register::Single::E, data), 0x44 => Self::make_ldr1r2_data(register::Single::B, register::Single::H, data), 0x45 => Self::make_ldr1r2_data(register::Single::B, register::Single::L, data), - 0x46 => Self::make_ld_from_address(register::Combined::HL, register::Single::B, data), + 0x46 => Self::make_ld_from_register_address( + register::Combined::HL, + register::Single::B, + data, + ), 0x48 => Self::make_ldr1r2_data(register::Single::C, register::Single::B, data), 0x49 => Self::make_ldr1r2_data(register::Single::C, register::Single::C, data), 0x4A => Self::make_ldr1r2_data(register::Single::C, register::Single::D, data), 0x4B => Self::make_ldr1r2_data(register::Single::C, register::Single::E, data), 0x4C => Self::make_ldr1r2_data(register::Single::C, register::Single::H, data), 0x4D => Self::make_ldr1r2_data(register::Single::C, register::Single::L, data), - 0x4E => Self::make_ld_from_address(register::Combined::HL, register::Single::C, data), + 0x4E => Self::make_ld_from_register_address( + register::Combined::HL, + register::Single::C, + data, + ), 0x50 => Self::make_ldr1r2_data(register::Single::D, register::Single::B, data), 0x51 => Self::make_ldr1r2_data(register::Single::D, register::Single::C, data), 0x52 => Self::make_ldr1r2_data(register::Single::D, register::Single::D, data), 0x53 => Self::make_ldr1r2_data(register::Single::D, register::Single::E, data), 0x54 => Self::make_ldr1r2_data(register::Single::D, register::Single::H, data), 0x55 => Self::make_ldr1r2_data(register::Single::D, register::Single::L, data), - 0x56 => Self::make_ld_from_address(register::Combined::HL, register::Single::D, data), + 0x56 => Self::make_ld_from_register_address( + register::Combined::HL, + register::Single::D, + data, + ), 0x58 => Self::make_ldr1r2_data(register::Single::E, register::Single::B, data), 0x59 => Self::make_ldr1r2_data(register::Single::E, register::Single::C, data), 0x5A => Self::make_ldr1r2_data(register::Single::E, register::Single::D, data), 0x5B => Self::make_ldr1r2_data(register::Single::E, register::Single::E, data), 0x5C => Self::make_ldr1r2_data(register::Single::E, register::Single::H, data), 0x5D => Self::make_ldr1r2_data(register::Single::E, register::Single::L, data), - 0x5E => Self::make_ld_from_address(register::Combined::HL, register::Single::E, data), + 0x5E => Self::make_ld_from_register_address( + register::Combined::HL, + register::Single::E, + data, + ), 0x60 => Self::make_ldr1r2_data(register::Single::H, register::Single::B, data), 0x61 => Self::make_ldr1r2_data(register::Single::H, register::Single::C, data), 0x62 => Self::make_ldr1r2_data(register::Single::H, register::Single::D, data), 0x63 => Self::make_ldr1r2_data(register::Single::H, register::Single::E, data), 0x64 => Self::make_ldr1r2_data(register::Single::H, register::Single::H, data), 0x65 => Self::make_ldr1r2_data(register::Single::H, register::Single::L, data), - 0x66 => Self::make_ld_from_address(register::Combined::HL, register::Single::H, data), + 0x66 => Self::make_ld_from_register_address( + register::Combined::HL, + register::Single::H, + data, + ), 0x68 => Self::make_ldr1r2_data(register::Single::L, register::Single::B, data), 0x69 => Self::make_ldr1r2_data(register::Single::L, register::Single::C, data), 0x6A => Self::make_ldr1r2_data(register::Single::L, register::Single::D, data), 0x6B => Self::make_ldr1r2_data(register::Single::L, register::Single::E, data), 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_address(register::Combined::HL, register::Single::L, data), + 0x6E => Self::make_ld_from_register_address( + register::Combined::HL, + 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), @@ -121,8 +153,17 @@ impl RunnableInstruction { 0x75 => Self::make_ld_to_hl_address(register::Single::L, data), 0x36 => Self::make_ld_n_to_hl_address(data), - 0x0A => Self::make_ld_from_address(register::Combined::BC, register::Single::A, data), - 0x1A => Self::make_ld_from_address(register::Combined::DE, register::Single::A, data), + 0xFA => Self::make_ld_from_immediate_address(register::Single::A, data), + 0x0A => Self::make_ld_from_register_address( + register::Combined::BC, + register::Single::A, + data, + ), + 0x1A => Self::make_ld_from_register_address( + register::Combined::DE, + register::Single::A, + data, + ), _ => panic!("invalid opcode {opcode:0x}"), } @@ -155,20 +196,35 @@ impl RunnableInstruction { ) } - fn make_ld_from_address( + fn make_ld_from_register_address( src: register::Combined, dst: register::Single, data: &[u8], ) -> (RunnableInstruction, &[u8]) { ( RunnableInstruction { - instruction: Instruction::LDFromAddress { src, dst }, + instruction: Instruction::LDFromRegisterAddress { src, dst }, cycles: 8, }, &data[1..], ) } + fn make_ld_from_immediate_address( + dst: register::Single, + data: &[u8], + ) -> (RunnableInstruction, &[u8]) { + // The manual states that the LSB of the address is specified first (i.e. little endian) + let src_address = u16::from_le_bytes([data[0], data[1]]); + ( + RunnableInstruction { + instruction: Instruction::LDFromImmediateAddress { src_address, dst }, + cycles: 16, + }, + &data[3..], + ) + } + fn make_ld_to_hl_address(src: register::Single, data: &[u8]) -> (RunnableInstruction, &[u8]) { ( RunnableInstruction { @@ -202,7 +258,7 @@ impl Processor { self.registers.set_single_register(dst, src_value); } - Instruction::LDFromAddress { src, dst } => { + Instruction::LDFromRegisterAddress { src, dst } => { let src_address = self.registers.get_combined_register(src); let memory_value = self.memory.get(src_address.into()); if memory_value.is_none() { @@ -213,6 +269,16 @@ impl Processor { .set_single_register(dst, memory_value.unwrap()); } + Instruction::LDFromImmediateAddress { src_address, dst } => { + let memory_value = self.memory.get(src_address.into()); + if memory_value.is_none() { + panic!("invalid address immediate: {src_address}") + } + + 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); @@ -403,4 +469,17 @@ mod tests { processor.run(&ins); assert_eq!(Some(0x1D), processor.memory.get(0xCCDE)); } + + #[test] + fn test_load_from_immediate_address() { + let mut processor = Processor::default(); + processor.memory.set(0xCCDE, 105); + + let data = [0xFA, 0xDE, 0xCC, 0x00]; + let (ins, extra_data) = RunnableInstruction::from_data(&data); + + assert_eq!(extra_data, &[0x00]); + processor.run(&ins); + assert_eq!(Some(105), processor.memory.get(0xCCDE)); + } }