Add support for ADC (HL), A

old-bit-manip
Nick Krichevsky 2023-05-01 00:24:52 -04:00
parent 2ade899fc6
commit c05f8c57e9
5 changed files with 114 additions and 0 deletions

View File

@ -10,4 +10,5 @@ pub enum EightBitArithmeticInstruction {
AddHLAddressToA,
AddSingleRegisterToAWithCarry { src: register::SingleEightBit },
AddImmediateToAWithCarry { n: u8 },
AddHLAddressToAWithCarry,
}

View File

@ -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,
)
}

View File

@ -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(())
}
}
}
}

View File

@ -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),
);
}