Add load from (hl) instructions
This commit is contained in:
parent
4c6eec264a
commit
1dc176fd74
|
@ -5,7 +5,7 @@ const MAX_MEMORY_ADDRESS: usize = 0xFFFF;
|
||||||
/// In its current state, this implementation ignores literally all properties.
|
/// 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
|
/// of the Gameboy's memory, and should not be relied upon for anything other than development
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Memory {
|
pub struct Memory {
|
||||||
data: [u8; MAX_MEMORY_ADDRESS],
|
data: [u8; MAX_MEMORY_ADDRESS],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
74
src/run.rs
74
src/run.rs
|
@ -1,11 +1,15 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use crate::register::{self, Registers};
|
use crate::{
|
||||||
|
memory::Memory,
|
||||||
|
register::{self, Registers},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct Processor {
|
pub struct Processor {
|
||||||
num_cycles: u64,
|
num_cycles: u64,
|
||||||
registers: Registers,
|
registers: Registers,
|
||||||
|
memory: Memory,
|
||||||
}
|
}
|
||||||
|
|
||||||
// these are indexed with the instruction numbers in this manual
|
// these are indexed with the instruction numbers in this manual
|
||||||
|
@ -21,6 +25,10 @@ pub enum Instruction {
|
||||||
dst: register::Single,
|
dst: register::Single,
|
||||||
src: register::Single,
|
src: register::Single,
|
||||||
},
|
},
|
||||||
|
// 3.3.1.2, only the loading from (hl) instructions
|
||||||
|
LDFromHLAddress {
|
||||||
|
dst: register::Single,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RunnableInstruction {
|
struct RunnableInstruction {
|
||||||
|
@ -50,48 +58,49 @@ impl RunnableInstruction {
|
||||||
0x7B => Self::make_ldr1r2_data(register::Single::A, register::Single::E, data),
|
0x7B => Self::make_ldr1r2_data(register::Single::A, register::Single::E, data),
|
||||||
0x7C => Self::make_ldr1r2_data(register::Single::A, register::Single::H, 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),
|
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),
|
0x40 => Self::make_ldr1r2_data(register::Single::B, register::Single::B, data),
|
||||||
0x41 => Self::make_ldr1r2_data(register::Single::B, register::Single::C, 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),
|
0x42 => Self::make_ldr1r2_data(register::Single::B, register::Single::D, data),
|
||||||
0x43 => Self::make_ldr1r2_data(register::Single::B, register::Single::E, 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),
|
0x44 => Self::make_ldr1r2_data(register::Single::B, register::Single::H, data),
|
||||||
0x45 => Self::make_ldr1r2_data(register::Single::B, register::Single::L, 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),
|
0x48 => Self::make_ldr1r2_data(register::Single::C, register::Single::B, data),
|
||||||
0x49 => Self::make_ldr1r2_data(register::Single::C, register::Single::C, 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),
|
0x4A => Self::make_ldr1r2_data(register::Single::C, register::Single::D, data),
|
||||||
0x4B => Self::make_ldr1r2_data(register::Single::C, register::Single::E, 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),
|
0x4C => Self::make_ldr1r2_data(register::Single::C, register::Single::H, data),
|
||||||
0x4D => Self::make_ldr1r2_data(register::Single::C, register::Single::L, 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),
|
0x50 => Self::make_ldr1r2_data(register::Single::D, register::Single::B, data),
|
||||||
0x51 => Self::make_ldr1r2_data(register::Single::D, register::Single::C, 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),
|
0x52 => Self::make_ldr1r2_data(register::Single::D, register::Single::D, data),
|
||||||
0x53 => Self::make_ldr1r2_data(register::Single::D, register::Single::E, 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),
|
0x54 => Self::make_ldr1r2_data(register::Single::D, register::Single::H, data),
|
||||||
0x55 => Self::make_ldr1r2_data(register::Single::D, register::Single::L, 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),
|
0x58 => Self::make_ldr1r2_data(register::Single::E, register::Single::B, data),
|
||||||
0x59 => Self::make_ldr1r2_data(register::Single::E, register::Single::C, 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),
|
0x5A => Self::make_ldr1r2_data(register::Single::E, register::Single::D, data),
|
||||||
0x5B => Self::make_ldr1r2_data(register::Single::E, register::Single::E, 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),
|
0x5C => Self::make_ldr1r2_data(register::Single::E, register::Single::H, data),
|
||||||
0x5D => Self::make_ldr1r2_data(register::Single::E, register::Single::L, 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),
|
0x60 => Self::make_ldr1r2_data(register::Single::H, register::Single::B, data),
|
||||||
0x61 => Self::make_ldr1r2_data(register::Single::H, register::Single::C, 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),
|
0x62 => Self::make_ldr1r2_data(register::Single::H, register::Single::D, data),
|
||||||
0x63 => Self::make_ldr1r2_data(register::Single::H, register::Single::E, 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),
|
0x64 => Self::make_ldr1r2_data(register::Single::H, register::Single::H, data),
|
||||||
0x65 => Self::make_ldr1r2_data(register::Single::H, register::Single::L, 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),
|
0x68 => Self::make_ldr1r2_data(register::Single::L, register::Single::B, data),
|
||||||
0x69 => Self::make_ldr1r2_data(register::Single::L, register::Single::C, 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),
|
0x6A => Self::make_ldr1r2_data(register::Single::L, register::Single::D, data),
|
||||||
0x6B => Self::make_ldr1r2_data(register::Single::L, register::Single::E, 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),
|
0x6C => Self::make_ldr1r2_data(register::Single::L, register::Single::H, data),
|
||||||
0x6D => Self::make_ldr1r2_data(register::Single::L, register::Single::L, 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}"),
|
_ => panic!("invalid opcode {opcode:0x}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -122,6 +131,16 @@ impl RunnableInstruction {
|
||||||
&data[1..],
|
&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 {
|
impl Processor {
|
||||||
|
@ -130,11 +149,22 @@ impl Processor {
|
||||||
Instruction::LDnnn { value, register } => {
|
Instruction::LDnnn { value, register } => {
|
||||||
self.registers.set_single_register(register, value);
|
self.registers.set_single_register(register, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Instruction::LDr1r2 { dst, src } => {
|
Instruction::LDr1r2 { dst, src } => {
|
||||||
dbg!(dst, src);
|
|
||||||
let src_value = self.registers.get_single_register(src);
|
let src_value = self.registers.get_single_register(src);
|
||||||
self.registers.set_single_register(dst, src_value);
|
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);
|
self.num_cycles += u64::from(instruction.cycles);
|
||||||
|
@ -229,4 +259,32 @@ mod tests {
|
||||||
processor.run(&ins);
|
processor.run(&ins);
|
||||||
assert_eq!(0x45, processor.registers.get_single_register(dst_register));
|
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 a new issue