Add load from (hl) instructions
parent
4c6eec264a
commit
1dc176fd74
|
@ -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],
|
||||
}
|
||||
|
||||
|
|
74
src/run.rs
74
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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue