Add load to (hl) instructions
This commit is contained in:
parent
1dc176fd74
commit
dcaf0588de
62
src/run.rs
62
src/run.rs
|
@ -29,6 +29,10 @@ pub enum Instruction {
|
||||||
LDFromHLAddress {
|
LDFromHLAddress {
|
||||||
dst: register::Single,
|
dst: register::Single,
|
||||||
},
|
},
|
||||||
|
// 3.3.1.2, only the loading to (hl) instructions
|
||||||
|
LDToHLAddress {
|
||||||
|
src: register::Single,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RunnableInstruction {
|
struct RunnableInstruction {
|
||||||
|
@ -101,6 +105,12 @@ impl RunnableInstruction {
|
||||||
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),
|
0x6E => Self::make_ld_from_hl_address(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),
|
||||||
|
0x73 => Self::make_ld_to_hl_address(register::Single::E, data),
|
||||||
|
0x74 => Self::make_ld_to_hl_address(register::Single::H, data),
|
||||||
|
0x75 => Self::make_ld_to_hl_address(register::Single::L, data),
|
||||||
_ => panic!("invalid opcode {opcode:0x}"),
|
_ => panic!("invalid opcode {opcode:0x}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,6 +151,16 @@ impl RunnableInstruction {
|
||||||
&data[1..],
|
&data[1..],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_ld_to_hl_address(src: register::Single, data: &[u8]) -> (RunnableInstruction, &[u8]) {
|
||||||
|
(
|
||||||
|
RunnableInstruction {
|
||||||
|
instruction: Instruction::LDToHLAddress { src },
|
||||||
|
cycles: 8,
|
||||||
|
},
|
||||||
|
&data[1..],
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Processor {
|
impl Processor {
|
||||||
|
@ -165,6 +185,16 @@ impl Processor {
|
||||||
self.registers
|
self.registers
|
||||||
.set_single_register(dst, memory_value.unwrap());
|
.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);
|
||||||
|
|
||||||
|
let memory_value = self.memory.set(dst_address.into(), value);
|
||||||
|
if memory_value.is_none() {
|
||||||
|
panic!("invalid address stored in (hl): {dst_address}")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.num_cycles += u64::from(instruction.cycles);
|
self.num_cycles += u64::from(instruction.cycles);
|
||||||
|
@ -267,7 +297,7 @@ mod tests {
|
||||||
#[test_case(0x5E, register::Single::E)]
|
#[test_case(0x5E, register::Single::E)]
|
||||||
#[test_case(0x66, register::Single::H)]
|
#[test_case(0x66, register::Single::H)]
|
||||||
#[test_case(0x6E, register::Single::L)]
|
#[test_case(0x6E, register::Single::L)]
|
||||||
fn test_load_from_memory(opcode: u8, expected_reigster: register::Single) {
|
fn test_load_from_memory(opcode: u8, expected_register: register::Single) {
|
||||||
let mut processor = Processor::default();
|
let mut processor = Processor::default();
|
||||||
let set_val = processor.memory.set(0xCCDD, 105);
|
let set_val = processor.memory.set(0xCCDD, 105);
|
||||||
assert_eq!(Some(105), set_val);
|
assert_eq!(Some(105), set_val);
|
||||||
|
@ -284,7 +314,35 @@ mod tests {
|
||||||
processor.run(&ins);
|
processor.run(&ins);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
105,
|
105,
|
||||||
processor.registers.get_single_register(expected_reigster)
|
processor.registers.get_single_register(expected_register)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test_case(0x70, register::Single::B)]
|
||||||
|
#[test_case(0x71, register::Single::C)]
|
||||||
|
#[test_case(0x72, register::Single::D)]
|
||||||
|
#[test_case(0x73, register::Single::E)]
|
||||||
|
#[test_case(0x74, register::Single::H)]
|
||||||
|
#[test_case(0x75, register::Single::L)]
|
||||||
|
fn test_load_to_memory(opcode: u8, expected_register: register::Single) {
|
||||||
|
let mut processor = Processor::default();
|
||||||
|
// This test does a bit of a sneaky - because we want to use CC as the high/low addresses
|
||||||
|
// in the H/L variants of these tests, but we also want to use it store addreses, we must
|
||||||
|
// be careful that the stored immediate value is also part of the address in these cases.
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_single_register(expected_register, 0xCC);
|
||||||
|
|
||||||
|
processor
|
||||||
|
.registers
|
||||||
|
.set_combined_register(register::Combined::HL, 0xCCCC);
|
||||||
|
|
||||||
|
let data = [opcode, 0x10, 0x20];
|
||||||
|
let (ins, extra_data) = RunnableInstruction::from_data(&data);
|
||||||
|
|
||||||
|
assert_eq!(extra_data, &[0x10, 0x20]);
|
||||||
|
|
||||||
|
processor.run(&ins);
|
||||||
|
assert_eq!(Some(0xCC), processor.memory.get(0xCCCC));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue