Add load from (hl) instructions

jsmoo
Nick Krichevsky 2022-04-04 23:11:18 -04:00
parent 4c6eec264a
commit 1dc176fd74
2 changed files with 67 additions and 9 deletions

View File

@ -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],
}

View File

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