Implement add A to HL instruction

jsmoo
Nick Krichevsky 2022-04-23 23:46:44 -04:00
parent 733c18614c
commit f0c0e818c9
5 changed files with 154 additions and 4 deletions

View File

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

View File

@ -7,4 +7,5 @@ use crate::register;
pub enum EightBitArithmeticInstruction {
AddSingleRegisterToA { src: register::SingleEightBit },
AddImmediateToA { n: u8 },
AddHLAddressToA,
}

View File

@ -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))?;

View File

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

View File

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