From 1dc176fd74d084b62b90ea60a1c4d8cd95c2c1d5 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Mon, 4 Apr 2022 23:11:18 -0400 Subject: [PATCH] Add load from (hl) instructions --- src/memory.rs | 2 +- src/run.rs | 74 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/memory.rs b/src/memory.rs index 29bc9b1..0e23166 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -5,7 +5,7 @@ const MAX_MEMORY_ADDRESS: usize = 0xFFFF; /// In its current state, this implementation ignores literally all properties. /// of the Gameboy's memory, and should not be relied upon for anything other than development #[derive(Debug, Clone)] -struct Memory { +pub struct Memory { data: [u8; MAX_MEMORY_ADDRESS], } diff --git a/src/run.rs b/src/run.rs index 38ad94d..7810ef6 100644 --- a/src/run.rs +++ b/src/run.rs @@ -1,11 +1,15 @@ use std::time::Instant; -use crate::register::{self, Registers}; +use crate::{ + memory::Memory, + register::{self, Registers}, +}; #[derive(Debug, Default, Clone)] pub struct Processor { num_cycles: u64, registers: Registers, + memory: Memory, } // these are indexed with the instruction numbers in this manual @@ -21,6 +25,10 @@ pub enum Instruction { dst: register::Single, src: register::Single, }, + // 3.3.1.2, only the loading from (hl) instructions + LDFromHLAddress { + dst: register::Single, + }, } struct RunnableInstruction { @@ -50,48 +58,49 @@ 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 skipped + 0x7E => Self::make_ld_from_hl_address(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 skiipped + 0x46 => Self::make_ld_from_hl_address(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 skipped + 0x4E => Self::make_ld_from_hl_address(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 skipped + 0x56 => Self::make_ld_from_hl_address(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 skipped + 0x5E => Self::make_ld_from_hl_address(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 skipped + 0x66 => Self::make_ld_from_hl_address(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_hl_address(register::Single::L, data), _ => panic!("invalid opcode {opcode:0x}"), } } @@ -122,6 +131,16 @@ impl RunnableInstruction { &data[1..], ) } + + fn make_ld_from_hl_address(dst: register::Single, data: &[u8]) -> (RunnableInstruction, &[u8]) { + ( + RunnableInstruction { + instruction: Instruction::LDFromHLAddress { dst }, + cycles: 8, + }, + &data[1..], + ) + } } impl Processor { @@ -130,11 +149,22 @@ impl Processor { Instruction::LDnnn { value, register } => { self.registers.set_single_register(register, value); } + Instruction::LDr1r2 { dst, src } => { - dbg!(dst, src); let src_value = self.registers.get_single_register(src); self.registers.set_single_register(dst, src_value); } + + Instruction::LDFromHLAddress { dst } => { + let src_address = self.registers.get_combined_register(register::Combined::HL); + let memory_value = self.memory.get(src_address.into()); + if memory_value.is_none() { + panic!("invalid address stored in register (hl): {src_address}") + } + + self.registers + .set_single_register(dst, memory_value.unwrap()); + } } self.num_cycles += u64::from(instruction.cycles); @@ -229,4 +259,32 @@ mod tests { processor.run(&ins); assert_eq!(0x45, processor.registers.get_single_register(dst_register)); } + + #[test_case(0x7E, register::Single::A)] + #[test_case(0x46, register::Single::B)] + #[test_case(0x4E, register::Single::C)] + #[test_case(0x56, register::Single::D)] + #[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) { + let mut processor = Processor::default(); + let set_val = processor.memory.set(0xCCDD, 105); + assert_eq!(Some(105), set_val); + + processor + .registers + .set_combined_register(register::Combined::HL, 0xCCDD); + + let data = [opcode, 0x10, 0x20]; + let (ins, extra_data) = RunnableInstruction::from_data(&data); + + assert_eq!(extra_data, &[0x10, 0x20]); + + processor.run(&ins); + assert_eq!( + 105, + processor.registers.get_single_register(expected_reigster) + ); + } }