From 6ac4ed5d7ce4a7638f864c5632a7da23916d08d2 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Fri, 21 Apr 2023 12:29:55 -0400 Subject: [PATCH] add support for ADC with immediate --- src/cpu/instructions/arith8.rs | 1 + src/cpu/parse/arith8/add.rs | 16 ++++++++++ src/cpu/run/arith8.rs | 18 ++++++++++++ tests/cpu/arith8.rs | 54 ++++++++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+) diff --git a/src/cpu/instructions/arith8.rs b/src/cpu/instructions/arith8.rs index dfa90de..aaf63f9 100644 --- a/src/cpu/instructions/arith8.rs +++ b/src/cpu/instructions/arith8.rs @@ -9,4 +9,5 @@ pub enum EightBitArithmeticInstruction { AddImmediateToA { n: u8 }, AddHLAddressToA, AddSingleRegisterToAWithCarry { src: register::SingleEightBit }, + AddImmediateToAWithCarry { n: u8 }, } diff --git a/src/cpu/parse/arith8/add.rs b/src/cpu/parse/arith8/add.rs index 63d2571..6db7681 100644 --- a/src/cpu/parse/arith8/add.rs +++ b/src/cpu/parse/arith8/add.rs @@ -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..], + )) +} diff --git a/src/cpu/run/arith8.rs b/src/cpu/run/arith8.rs index 1b57567..418a417 100644 --- a/src/cpu/run/arith8.rs +++ b/src/cpu/run/arith8.rs @@ -97,6 +97,24 @@ impl InstructionRunner 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(()) + } } } } diff --git a/tests/cpu/arith8.rs b/tests/cpu/arith8.rs index 22b7bbd..753bab7 100644 --- a/tests/cpu/arith8.rs +++ b/tests/cpu/arith8.rs @@ -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), + ); +}