Implement add A to HL instruction
parent
733c18614c
commit
f0c0e818c9
98
src/cpu.rs
98
src/cpu.rs
|
@ -661,6 +661,8 @@ mod tests {
|
|||
assert_eq!(20, processor.registers.a);
|
||||
}
|
||||
|
||||
// TODO: These add tests are almost entirely copy paste - we should find some way to break them up
|
||||
|
||||
#[test_case(0x80, register::SingleEightBit::B)]
|
||||
#[test_case(0x81, register::SingleEightBit::C)]
|
||||
#[test_case(0x82, register::SingleEightBit::D)]
|
||||
|
@ -750,6 +752,102 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_hl_addr_to_a_value() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.memory
|
||||
.set(0xFF00, 0x34)
|
||||
.expect("expected to be able to set 0xFF00");
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFF00);
|
||||
processor.registers.a = 0x12;
|
||||
|
||||
let data = [0x86, 0x02];
|
||||
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x02]);
|
||||
|
||||
processor.run(&ins);
|
||||
|
||||
assert_eq!(0x46, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0x00, 0x00, 1, 0, 0; "zero flag for zero value")]
|
||||
#[test_case(0x00, 0x01, 0, 0, 0; "no zero flag for non-zero value")]
|
||||
#[test_case(0x0F, 0x01, 0, 1, 0; "half carry flag")]
|
||||
#[test_case(0x80, 0x80, 0, 0, 1; "full carry flag")]
|
||||
#[test_case(0xFF, 0x01, 0, 1, 1; "both full and half carry flag")]
|
||||
fn test_add_hl_addr_to_a_flags(
|
||||
a_value: u8,
|
||||
hl_addr_value: u8,
|
||||
zero_flag: u8,
|
||||
half_carry_flag: u8,
|
||||
carry_flag: u8,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor.registers.a = a_value;
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFF00);
|
||||
|
||||
processor
|
||||
.memory
|
||||
.set(0xFF00, hl_addr_value)
|
||||
.expect("expected to set address 0xFF00 but could not");
|
||||
|
||||
// Set all the register to the opposite we expect to ensure they all get set
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Zero, if zero_flag == 1 { 0 } else { 1 });
|
||||
|
||||
processor.registers.set_flag_bit(
|
||||
register::Flag::HalfCarry,
|
||||
if half_carry_flag == 1 { 0 } else { 1 },
|
||||
);
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, if carry_flag == 1 { 0 } else { 1 });
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Subtract, 1);
|
||||
|
||||
let data = [0x86, 0x01];
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x01]);
|
||||
|
||||
processor.run(&ins);
|
||||
|
||||
assert_eq!(
|
||||
zero_flag,
|
||||
processor.registers.get_flag_bit(register::Flag::Zero),
|
||||
"zero flag did not match",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
0,
|
||||
processor.registers.get_flag_bit(register::Flag::Subtract),
|
||||
"sub flag did not match",
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
half_carry_flag,
|
||||
processor.registers.get_flag_bit(register::Flag::HalfCarry),
|
||||
"half carry flag did not match"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
carry_flag,
|
||||
processor.registers.get_flag_bit(register::Flag::Carry),
|
||||
"carry flag did not match"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_immediate_to_a_value() {
|
||||
let mut processor = Processor::default();
|
||||
|
|
|
@ -7,4 +7,5 @@ use crate::register;
|
|||
pub enum EightBitArithmeticInstruction {
|
||||
AddSingleRegisterToA { src: register::SingleEightBit },
|
||||
AddImmediateToA { n: u8 },
|
||||
AddHLAddressToA,
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ impl OpcodeParser for EightBitAddParser {
|
|||
0x83 => build_add_register_to_a_data(register::SingleEightBit::E, data),
|
||||
0x84 => build_add_register_to_a_data(register::SingleEightBit::H, data),
|
||||
0x85 => build_add_register_to_a_data(register::SingleEightBit::L, data),
|
||||
0x86 => build_add_hl_address_to_a_data(data),
|
||||
0xC6 => build_add_immediate_to_a_data(data),
|
||||
_ => Err(Error::UnknownOpcode(opcode)),
|
||||
}
|
||||
|
@ -41,6 +42,22 @@ fn build_add_register_to_a_data(src: register::SingleEightBit, data: &[u8]) -> P
|
|||
.ok_or(Error::NoData)
|
||||
}
|
||||
|
||||
fn build_add_hl_address_to_a_data(data: &[u8]) -> ParseResult {
|
||||
data.get(1..)
|
||||
.map(|remaining_data| {
|
||||
(
|
||||
RunnableInstruction {
|
||||
instruction: Instruction::EightBitArithmetic(
|
||||
EightBitArithmeticInstruction::AddHLAddressToA,
|
||||
),
|
||||
cycles: 8,
|
||||
},
|
||||
remaining_data,
|
||||
)
|
||||
})
|
||||
.ok_or(Error::NoData)
|
||||
}
|
||||
|
||||
fn build_add_immediate_to_a_data(data: &[u8]) -> ParseResult {
|
||||
let opcode = parse::get_opcode_from_data(data)?;
|
||||
let n = data.get(1).copied().ok_or(Error::NotEnoughArgs(opcode))?;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::cpu::{instructions::arith8::EightBitArithmeticInstruction, run::Error, Processor};
|
||||
use crate::register;
|
||||
use crate::{memory, register};
|
||||
|
||||
use super::{arithutil::CarryingAdd, InstructionRunner};
|
||||
|
||||
|
@ -27,6 +27,7 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitArithmeticInstruction::AddImmediateToA { n } => {
|
||||
let (total, half_carry, carry) = processor
|
||||
.registers
|
||||
|
@ -41,6 +42,40 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitArithmeticInstruction::AddHLAddressToA => {
|
||||
let src_addr = processor
|
||||
.registers
|
||||
.get_combined_register(register::Combined::HL);
|
||||
|
||||
// While this is true, we really do want a wildcard match in map_err
|
||||
#[allow(clippy::match_wildcard_for_single_variants)]
|
||||
let hl_addr_value =
|
||||
processor
|
||||
.memory
|
||||
.get(src_addr.into())
|
||||
.map_err(|err| match err {
|
||||
memory::Error::GetInvalidAddress(bad_addr) => {
|
||||
Error::InvalidRegisterAddress(
|
||||
register::SixteenBit::Combined(register::Combined::HL),
|
||||
bad_addr,
|
||||
)
|
||||
}
|
||||
err => Error::Unknown(Box::new(err)),
|
||||
})?;
|
||||
|
||||
let (total, half_carry, carry) = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A)
|
||||
.add_with_carry(hl_addr_value);
|
||||
|
||||
set_addition_flags(processor, total, half_carry, carry);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,10 +89,9 @@ mod tests {
|
|||
#[test]
|
||||
fn test_get_invalid_address() {
|
||||
let memory = Memory::new();
|
||||
let get_val = memory.get(0xCC_CC_CC_CC_CC);
|
||||
match memory.get(0xCC_CC_CC_CC_CC) {
|
||||
Err(Error::GetInvalidAddress(addr)) => {
|
||||
assert_eq!(0xCC_CC_CC_CC_CC, addr)
|
||||
assert_eq!(0xCC_CC_CC_CC_CC, addr);
|
||||
}
|
||||
Ok(_) => panic!("should not have succeeded in setting invalid address"),
|
||||
Err(_) => panic!("unexpected error variant returned"),
|
||||
|
@ -104,7 +103,7 @@ mod tests {
|
|||
let mut memory = Memory::new();
|
||||
match memory.set(0xCC_CC_CC_CC_CC, 100) {
|
||||
Err(Error::SetInvalidAddress(addr, value)) => {
|
||||
assert_eq!((0xCC_CC_CC_CC_CC, 100), (addr, value))
|
||||
assert_eq!((0xCC_CC_CC_CC_CC, 100), (addr, value));
|
||||
}
|
||||
Ok(_) => panic!("should not have succeeded in setting invalid address"),
|
||||
Err(_) => panic!("unexpected error variant returned"),
|
||||
|
|
Loading…
Reference in New Issue