Add support for ADC (HL), A
parent
2ade899fc6
commit
c05f8c57e9
|
@ -10,4 +10,5 @@ pub enum EightBitArithmeticInstruction {
|
|||
AddHLAddressToA,
|
||||
AddSingleRegisterToAWithCarry { src: register::SingleEightBit },
|
||||
AddImmediateToAWithCarry { n: u8 },
|
||||
AddHLAddressToAWithCarry,
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ impl OpcodeParser for EightBitAddParser {
|
|||
)),
|
||||
|
||||
0xCE => Ok(build_add_immediate_to_a_with_carry_data(data)),
|
||||
0x8E => Ok(build_add_hl_address_to_a_with_carry_data()),
|
||||
|
||||
_ => Err(Error::UnknownOpcode(opcode)),
|
||||
}
|
||||
}
|
||||
|
@ -115,3 +117,15 @@ fn build_add_immediate_to_a_with_carry_data(data: &View) -> ParseOutput {
|
|||
2,
|
||||
)
|
||||
}
|
||||
|
||||
fn build_add_hl_address_to_a_with_carry_data() -> ParseOutput {
|
||||
(
|
||||
RunnableInstruction {
|
||||
instruction: Instruction::EightBitArithmetic(
|
||||
EightBitArithmeticInstruction::AddHLAddressToAWithCarry,
|
||||
),
|
||||
cycles: 8,
|
||||
},
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@ use super::{arithutil::CarryingAdd, InstructionRunner};
|
|||
pub(super) struct EightBitArithmeticRunner;
|
||||
|
||||
impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunner {
|
||||
// TODO: Fix this
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn run_instruction(
|
||||
processor: &mut Processor,
|
||||
instruction: &EightBitArithmeticInstruction,
|
||||
|
@ -115,6 +117,37 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitArithmeticInstruction::AddHLAddressToAWithCarry => {
|
||||
let a_value = processor.registers.a;
|
||||
let src_address = 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 stored_value = processor.memory.get(src_address.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 carried_operand = arithutil::CarriedNumber::new(
|
||||
stored_value,
|
||||
processor.registers.get_flag_bit(register::Flag::Carry)
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
||||
})?;
|
||||
|
||||
let (result, half_carry, carry) = a_value.add_with_carry(carried_operand);
|
||||
processor.registers.a = result;
|
||||
set_addition_flags(processor, result, half_carry, carry);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -458,3 +458,69 @@ fn test_add_immediate_with_carry_to_a_flags(
|
|||
(register::Flag::Carry, expected_flags.carry),
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0, 0xFE; "no carry bit")]
|
||||
#[test_case(1, 0xFF; "carry bit")]
|
||||
fn test_add_hl_addr_to_a_with_carry_value(carry_flag: u8, expected: u8) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.memory
|
||||
.set(0xFFFE, 0xF0)
|
||||
.expect("expected to be able to set 0xFF00");
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFFFE);
|
||||
processor.registers.a = 0x0E;
|
||||
processor.registers.set_flag_bit(register::Flag::Carry, carry_flag);
|
||||
|
||||
let data = [0x8E, 0x02];
|
||||
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x02]);
|
||||
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
assert_eq!(expected, processor.registers.a);
|
||||
}
|
||||
|
||||
#[test_case(0x01, 0, 0x02, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 0}; "no carry")]
|
||||
#[test_case(0xFF, 1, 0x00, AdditionOperationFlags{zero: 1, half_carry: 1, carry: 1}; "all flags")]
|
||||
#[test_case(0x0F, 1, 0x80, AdditionOperationFlags{zero: 0, half_carry: 1, carry: 0}; "half carry flag")]
|
||||
#[test_case(0xF0, 1, 0xF0, AdditionOperationFlags{zero: 0, half_carry: 0, carry: 1}; "full carry flag")]
|
||||
fn test_add_hl_addr_to_a_with_carry_flags(
|
||||
initial_value: u8,
|
||||
carry_bit: u8,
|
||||
operand: u8,
|
||||
expected_flags: AdditionOperationFlags,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.memory
|
||||
.set(0xFFFE, operand)
|
||||
.expect("expected to be able to set 0xFF00");
|
||||
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFFFE);
|
||||
|
||||
processor.registers.a = initial_value;
|
||||
processor.registers.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
|
||||
let data = [0x8E, 0x02];
|
||||
|
||||
let (ins, extra_data) =
|
||||
RunnableInstruction::from_data(&data).expect("could not parse instruction");
|
||||
assert_eq!(extra_data, &[0x02]);
|
||||
|
||||
processor.run_instruction(&ins);
|
||||
|
||||
testutil::assert_flags_eq!(
|
||||
processor,
|
||||
(register::Flag::Zero, expected_flags.zero),
|
||||
(register::Flag::Subtract, 0),
|
||||
(register::Flag::HalfCarry, expected_flags.half_carry),
|
||||
(register::Flag::Carry, expected_flags.carry),
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue