add support for ADC with immediate

jsmoo
Nick Krichevsky 2023-04-21 12:29:55 -04:00
parent e416e607df
commit 6ac4ed5d7c
4 changed files with 89 additions and 0 deletions

View File

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

View File

@ -27,6 +27,7 @@ impl OpcodeParser for EightBitAddParser {
0x8B => build_add_register_to_a_with_carry_data(register::SingleEightBit::E, data),
0x8C => build_add_register_to_a_with_carry_data(register::SingleEightBit::H, data),
0x8D => build_add_register_to_a_with_carry_data(register::SingleEightBit::L, data),
0xCE => build_add_immediate_to_a_with_carry_data(data),
_ => Err(Error::UnknownOpcode(opcode)),
}
}
@ -98,3 +99,18 @@ fn build_add_immediate_to_a_data(data: &[u8]) -> ParseResult {
&data[2..],
))
}
fn build_add_immediate_to_a_with_carry_data(data: &[u8]) -> ParseResult {
let opcode = parse::get_opcode_from_data(data)?;
let n = data.get(1).copied().ok_or(Error::NotEnoughArgs(opcode))?;
Ok((
RunnableInstruction {
instruction: Instruction::EightBitArithmetic(
EightBitArithmeticInstruction::AddImmediateToAWithCarry { n },
),
cycles: 8,
},
&data[2..],
))
}

View File

@ -97,6 +97,24 @@ impl InstructionRunner<EightBitArithmeticInstruction> for EightBitArithmeticRunn
Ok(())
}
EightBitArithmeticInstruction::AddImmediateToAWithCarry { n } => {
let a_value = processor.registers.a;
let carried_operand = arithutil::CarriedNumber::new(
n,
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

@ -361,3 +361,57 @@ fn test_add_register_with_carry_to_a_flags(
(register::Flag::Carry, expected_flags.carry),
);
}
#[test_case(0x01, 0x00, 0x02, 0x03; "no carry")]
#[test_case(0x01, 0x01, 0x02, 0x04; "carry")]
fn test_add_immediate_with_carry_to_a_value(
initial_value: u8,
carry_bit: u8,
operand: u8,
expected_value: u8,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0xCE, operand, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&ins);
assert_eq!(processor.registers.a, expected_value);
}
#[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_immediate_with_carry_to_a_flags(
initial_value: u8,
carry_bit: u8,
operand: u8,
expected_flags: AdditionOperationFlags,
) {
let mut processor = Processor::default();
processor.registers.a = initial_value;
processor
.registers
.set_flag_bit(register::Flag::Carry, carry_bit);
let data = [0xCE, operand, 0x01];
let (ins, extra_data) =
RunnableInstruction::from_data(&data).expect("could not parse instruction");
assert_eq!(extra_data, &[0x01]);
processor.run(&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),
);
}