Refactor eight bit arithmetic to extract operand collection
parent
01a68e1415
commit
4fb9144c6d
|
@ -1,12 +1,90 @@
|
|||
use crate::cpu::Processor;
|
||||
use crate::register;
|
||||
use crate::{cpu::run::Error, cpu::Processor, memory, register};
|
||||
|
||||
pub use add::EightBitAddRunner;
|
||||
pub use sub::EightBitSubRunner;
|
||||
|
||||
use super::arithutil::{self, CarriedNumber};
|
||||
|
||||
mod add;
|
||||
mod sub;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum EightBitArithmeticOperation {
|
||||
SingleRegisterToA { src: register::SingleEightBit },
|
||||
ImmediateToA { n: u8 },
|
||||
HLAddressToA,
|
||||
}
|
||||
|
||||
fn gather_operands(
|
||||
processor: &mut Processor,
|
||||
operation: EightBitArithmeticOperation,
|
||||
) -> Result<(u8, u8), Error> {
|
||||
match operation {
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src } => {
|
||||
let a_value = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A);
|
||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||
|
||||
Ok((a_value, src_register_value))
|
||||
}
|
||||
|
||||
EightBitArithmeticOperation::ImmediateToA { n } => {
|
||||
let a_value = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A);
|
||||
|
||||
Ok((a_value, n))
|
||||
}
|
||||
|
||||
EightBitArithmeticOperation::HLAddressToA => {
|
||||
let a_value = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A);
|
||||
|
||||
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)),
|
||||
})?;
|
||||
|
||||
Ok((a_value, hl_addr_value))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn gather_operands_with_carry(
|
||||
processor: &mut Processor,
|
||||
operation: EightBitArithmeticOperation,
|
||||
) -> Result<(u8, CarriedNumber<u8>), Error> {
|
||||
let (lhs, no_carry_rhs) = gather_operands(processor, operation)?;
|
||||
let rhs = attach_carry_bit_to_operand(processor, no_carry_rhs)?;
|
||||
|
||||
Ok((lhs, rhs))
|
||||
}
|
||||
|
||||
fn attach_carry_bit_to_operand(
|
||||
processor: &mut Processor,
|
||||
operand: u8,
|
||||
) -> Result<CarriedNumber<u8>, Error> {
|
||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||
|
||||
CarriedNumber::new(operand, carry_bit).map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
||||
})
|
||||
}
|
||||
|
||||
fn set_subtraction_flags(processor: &mut Processor, total: u8, half_carry: bool, carry: bool) {
|
||||
set_addition_flags(processor, total, half_carry, carry);
|
||||
processor
|
||||
|
@ -31,3 +109,200 @@ fn set_addition_flags(processor: &mut Processor, total: u8, half_carry: bool, ca
|
|||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry.into());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test_case::test_case;
|
||||
|
||||
#[test_case(register::SingleEightBit::B)]
|
||||
#[test_case(register::SingleEightBit::C)]
|
||||
#[test_case(register::SingleEightBit::D)]
|
||||
#[test_case(register::SingleEightBit::E)]
|
||||
#[test_case(register::SingleEightBit::H)]
|
||||
#[test_case(register::SingleEightBit::L)]
|
||||
fn test_gather_operands_from_single_register_to_a(src_register: register::SingleEightBit) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(src_register, 0xBB);
|
||||
|
||||
let (lhs, rhs) = gather_operands(
|
||||
&mut processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src: src_register },
|
||||
)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!((0xAA, 0xBB), (lhs, rhs));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gather_operands_from_single_register_to_a_with_itself() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
|
||||
let (lhs, rhs) = gather_operands(
|
||||
&mut processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA {
|
||||
src: register::SingleEightBit::A,
|
||||
},
|
||||
)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!((0xAA, 0xAA), (lhs, rhs));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gather_operands_from_immediate_to_a() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
let (lhs, rhs) = gather_operands(
|
||||
&mut processor,
|
||||
EightBitArithmeticOperation::ImmediateToA { n: 0x45 },
|
||||
)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!((0xAA, 0x45), (lhs, rhs));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gather_operands_from_hl_value_a() {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFFAA);
|
||||
processor
|
||||
.memory
|
||||
.set(0xFFAA, 0xBB)
|
||||
.expect("failed to set memory value");
|
||||
|
||||
let (lhs, rhs) = gather_operands(&mut processor, EightBitArithmeticOperation::HLAddressToA)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!((0xAA, 0xBB), (lhs, rhs));
|
||||
}
|
||||
|
||||
#[test_case(register::SingleEightBit::B, 0; "register b, no carry bit")]
|
||||
#[test_case(register::SingleEightBit::B, 1; "register b, carry bit")]
|
||||
#[test_case(register::SingleEightBit::C, 0; "register c, no carry bit")]
|
||||
#[test_case(register::SingleEightBit::C, 1; "register c, carry bit")]
|
||||
#[test_case(register::SingleEightBit::D, 0; "register d, no carry bit")]
|
||||
#[test_case(register::SingleEightBit::D, 1; "register d, carry bit")]
|
||||
#[test_case(register::SingleEightBit::E, 0; "register e, carry bit")]
|
||||
#[test_case(register::SingleEightBit::E, 1; "register e, no carry bit")]
|
||||
#[test_case(register::SingleEightBit::H, 0; "register h, carry bit")]
|
||||
#[test_case(register::SingleEightBit::H, 1; "register h, no carry bit")]
|
||||
#[test_case(register::SingleEightBit::L, 0; "register l, carry bit")]
|
||||
#[test_case(register::SingleEightBit::L, 1; "register l, no carry bit")]
|
||||
fn test_gather_operands_with_carry_from_single_register_to_a(
|
||||
src_register: register::SingleEightBit,
|
||||
carry_bit: u8,
|
||||
) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(src_register, 0xBB);
|
||||
|
||||
let (lhs, rhs) = gather_operands_with_carry(
|
||||
&mut processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src: src_register },
|
||||
)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!(
|
||||
(0xAA, CarriedNumber::new(0xBB, carry_bit).unwrap()),
|
||||
(lhs, rhs)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0; "no carry bit")]
|
||||
#[test_case(1; "carry bit")]
|
||||
fn test_gather_operands_with_carry_from_single_register_to_a_with_itself(carry_bit: u8) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
|
||||
let (lhs, rhs) = gather_operands_with_carry(
|
||||
&mut processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA {
|
||||
src: register::SingleEightBit::A,
|
||||
},
|
||||
)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!(
|
||||
(0xAA, CarriedNumber::new(0xAA, carry_bit).unwrap()),
|
||||
(lhs, rhs)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0; "no carry bit")]
|
||||
#[test_case(1; "carry bit")]
|
||||
fn test_gather_operands_with_carry_from_immediate_to_a(carry_bit: u8) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
let (lhs, rhs) = gather_operands_with_carry(
|
||||
&mut processor,
|
||||
EightBitArithmeticOperation::ImmediateToA { n: 0x45 },
|
||||
)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!(
|
||||
(0xAA, CarriedNumber::new(0x45, carry_bit).unwrap()),
|
||||
(lhs, rhs)
|
||||
);
|
||||
}
|
||||
|
||||
#[test_case(0; "no carry bit")]
|
||||
#[test_case(1; "carry bit")]
|
||||
fn test_gather_with_carry_operands_from_hl_value_a(carry_bit: u8) {
|
||||
let mut processor = Processor::default();
|
||||
processor
|
||||
.registers
|
||||
.set_flag_bit(register::Flag::Carry, carry_bit);
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, 0xAA);
|
||||
processor
|
||||
.registers
|
||||
.set_combined_register(register::Combined::HL, 0xFFAA);
|
||||
processor
|
||||
.memory
|
||||
.set(0xFFAA, 0xBB)
|
||||
.expect("failed to set memory value");
|
||||
|
||||
let (lhs, rhs) =
|
||||
gather_operands_with_carry(&mut processor, EightBitArithmeticOperation::HLAddressToA)
|
||||
.expect("failed to gather operands");
|
||||
|
||||
assert_eq!(
|
||||
(0xAA, CarriedNumber::new(0xBB, carry_bit).unwrap()),
|
||||
(lhs, rhs)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,37 @@
|
|||
use crate::{
|
||||
cpu::{
|
||||
instructions::arith8::EightBitAddInstruction,
|
||||
run::{
|
||||
arithutil::{self, CarriedNumber, CarryingAdd},
|
||||
Error, InstructionRunner,
|
||||
},
|
||||
run::{arithutil::CarryingAdd, Error, InstructionRunner},
|
||||
Processor,
|
||||
},
|
||||
memory, register,
|
||||
register,
|
||||
};
|
||||
|
||||
use super::EightBitArithmeticOperation;
|
||||
|
||||
pub struct EightBitAddRunner;
|
||||
|
||||
impl EightBitAddRunner {
|
||||
fn do_add<T>(processor: &mut Processor, lhs: u8, rhs: T)
|
||||
where
|
||||
u8: CarryingAdd<T, Output = u8>,
|
||||
{
|
||||
let result = lhs.add_with_carry(rhs);
|
||||
Self::store_addition_result(processor, result);
|
||||
}
|
||||
|
||||
fn store_addition_result(
|
||||
processor: &mut Processor,
|
||||
(total, half_carry, carry): (u8, bool, bool),
|
||||
) {
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||
|
||||
super::set_addition_flags(processor, total, half_carry, carry);
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionRunner<EightBitAddInstruction> for EightBitAddRunner {
|
||||
fn run_instruction(
|
||||
processor: &mut Processor,
|
||||
|
@ -19,134 +39,68 @@ impl InstructionRunner<EightBitAddInstruction> for EightBitAddRunner {
|
|||
) -> Result<(), Error> {
|
||||
match *instruction {
|
||||
EightBitAddInstruction::AddSingleRegisterToA { src } => {
|
||||
let a_value = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A);
|
||||
let src_value = processor.registers.get_single_8bit_register(src);
|
||||
let (lhs, rhs) = super::gather_operands(
|
||||
processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||
)?;
|
||||
|
||||
let result = a_value.add_with_carry(src_value);
|
||||
store_addition_result(processor, result);
|
||||
Self::do_add(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitAddInstruction::AddImmediateToA { n } => {
|
||||
let result = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A)
|
||||
.add_with_carry(n);
|
||||
let (lhs, rhs) = super::gather_operands(
|
||||
processor,
|
||||
EightBitArithmeticOperation::ImmediateToA { n },
|
||||
)?;
|
||||
|
||||
store_addition_result(processor, result);
|
||||
Self::do_add(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitAddInstruction::AddHLAddressToA => {
|
||||
let src_addr = processor
|
||||
.registers
|
||||
.get_combined_register(register::Combined::HL);
|
||||
let (lhs, rhs) =
|
||||
super::gather_operands(processor, EightBitArithmeticOperation::HLAddressToA)?;
|
||||
|
||||
// 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 result = processor
|
||||
.registers
|
||||
.get_single_8bit_register(register::SingleEightBit::A)
|
||||
.add_with_carry(hl_addr_value);
|
||||
|
||||
store_addition_result(processor, result);
|
||||
Self::do_add(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitAddInstruction::AddSingleRegisterToAWithCarry { src } => {
|
||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||
processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||
)?;
|
||||
|
||||
let carried_operand = CarriedNumber::new(
|
||||
src_register_value,
|
||||
processor.registers.get_flag_bit(register::Flag::Carry),
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => Error::InvalidCarryFlagValue(value),
|
||||
})?;
|
||||
|
||||
let result = processor.registers.a.add_with_carry(carried_operand);
|
||||
store_addition_result(processor, result);
|
||||
Self::do_add(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitAddInstruction::AddImmediateToAWithCarry { n } => {
|
||||
let a_value = processor.registers.a;
|
||||
let carried_operand =
|
||||
CarriedNumber::new(n, processor.registers.get_flag_bit(register::Flag::Carry))
|
||||
.map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => {
|
||||
Error::InvalidCarryFlagValue(value)
|
||||
}
|
||||
})?;
|
||||
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||
processor,
|
||||
EightBitArithmeticOperation::ImmediateToA { n },
|
||||
)?;
|
||||
|
||||
let result = a_value.add_with_carry(carried_operand);
|
||||
store_addition_result(processor, result);
|
||||
Self::do_add(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitAddInstruction::AddHLAddressToAWithCarry => {
|
||||
let a_value = processor.registers.a;
|
||||
let src_address = processor
|
||||
.registers
|
||||
.get_combined_register(register::Combined::HL);
|
||||
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||
processor,
|
||||
EightBitArithmeticOperation::HLAddressToA,
|
||||
)?;
|
||||
|
||||
// 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 = 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 = a_value.add_with_carry(carried_operand);
|
||||
store_addition_result(processor, result);
|
||||
Self::do_add(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn store_addition_result(processor: &mut Processor, (total, half_carry, carry): (u8, bool, bool)) {
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||
|
||||
super::set_addition_flags(processor, total, half_carry, carry);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,37 @@
|
|||
use crate::{
|
||||
cpu::{
|
||||
instructions::arith8::EightBitSubInstruction,
|
||||
run::{
|
||||
arithutil::{self, CarriedNumber, CarryingSub},
|
||||
Error, InstructionRunner,
|
||||
},
|
||||
run::{arithutil::CarryingSub, Error, InstructionRunner},
|
||||
Processor,
|
||||
},
|
||||
memory, register,
|
||||
register,
|
||||
};
|
||||
|
||||
use super::EightBitArithmeticOperation;
|
||||
|
||||
pub struct EightBitSubRunner;
|
||||
|
||||
impl EightBitSubRunner {
|
||||
fn do_sub<T>(processor: &mut Processor, lhs: u8, rhs: T)
|
||||
where
|
||||
u8: CarryingSub<T, Output = u8>,
|
||||
{
|
||||
let result = lhs.sub_with_carry(rhs);
|
||||
Self::store_subtraction_result(processor, result);
|
||||
}
|
||||
|
||||
fn store_subtraction_result(
|
||||
processor: &mut Processor,
|
||||
(total, half_carry, carry): (u8, bool, bool),
|
||||
) {
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||
|
||||
super::set_subtraction_flags(processor, total, half_carry, carry);
|
||||
}
|
||||
}
|
||||
|
||||
impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
||||
fn run_instruction(
|
||||
processor: &mut Processor,
|
||||
|
@ -19,127 +39,68 @@ impl InstructionRunner<EightBitSubInstruction> for EightBitSubRunner {
|
|||
) -> Result<(), Error> {
|
||||
match *instruction {
|
||||
EightBitSubInstruction::SubSingleRegisterFromA { src } => {
|
||||
let a_value = processor.registers.a;
|
||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||
let result = a_value.sub_with_carry(src_register_value);
|
||||
store_subtraction_result(processor, result);
|
||||
let (lhs, rhs) = super::gather_operands(
|
||||
processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||
)?;
|
||||
|
||||
Self::do_sub(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitSubInstruction::SubImmediateFromA { n } => {
|
||||
let a_value = processor.registers.a;
|
||||
let (lhs, rhs) = super::gather_operands(
|
||||
processor,
|
||||
EightBitArithmeticOperation::ImmediateToA { n },
|
||||
)?;
|
||||
|
||||
let result = a_value.sub_with_carry(n);
|
||||
store_subtraction_result(processor, result);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitSubInstruction::SubSingleRegisterFromAWithCarry { src } => {
|
||||
let a_value = processor.registers.a;
|
||||
let src_register_value = processor.registers.get_single_8bit_register(src);
|
||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||
let carried_operand =
|
||||
CarriedNumber::new(src_register_value, carry_bit).map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => {
|
||||
Error::InvalidCarryFlagValue(value)
|
||||
}
|
||||
})?;
|
||||
|
||||
let result = a_value.sub_with_carry(carried_operand);
|
||||
store_subtraction_result(processor, result);
|
||||
Self::do_sub(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitSubInstruction::SubHLAddressFromA => {
|
||||
let a_value = processor.registers.a;
|
||||
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 (lhs, rhs) =
|
||||
super::gather_operands(processor, EightBitArithmeticOperation::HLAddressToA)?;
|
||||
|
||||
let result = a_value.sub_with_carry(hl_addr_value);
|
||||
store_subtraction_result(processor, result);
|
||||
Self::do_sub(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
EightBitSubInstruction::SubHLAddressFromAWithCarry => {
|
||||
let a_value = processor.registers.a;
|
||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||
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 carried_operand =
|
||||
CarriedNumber::new(hl_addr_value, carry_bit).map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => {
|
||||
Error::InvalidCarryFlagValue(value)
|
||||
}
|
||||
})?;
|
||||
EightBitSubInstruction::SubSingleRegisterFromAWithCarry { src } => {
|
||||
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||
processor,
|
||||
EightBitArithmeticOperation::SingleRegisterToA { src },
|
||||
)?;
|
||||
|
||||
let result = a_value.sub_with_carry(carried_operand);
|
||||
store_subtraction_result(processor, result);
|
||||
Self::do_sub(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitSubInstruction::SubImmediateFromAWithCarry { n } => {
|
||||
let a_value = processor.registers.a;
|
||||
let carry_bit = processor.registers.get_flag_bit(register::Flag::Carry);
|
||||
let carried_operand =
|
||||
CarriedNumber::new(n, carry_bit).map_err(|err| match err {
|
||||
arithutil::Error::InvalidCarryBit(value) => {
|
||||
Error::InvalidCarryFlagValue(value)
|
||||
}
|
||||
})?;
|
||||
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||
processor,
|
||||
EightBitArithmeticOperation::ImmediateToA { n },
|
||||
)?;
|
||||
|
||||
let result = a_value.sub_with_carry(carried_operand);
|
||||
store_subtraction_result(processor, result);
|
||||
Self::do_sub(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
EightBitSubInstruction::SubHLAddressFromAWithCarry => {
|
||||
let (lhs, rhs) = super::gather_operands_with_carry(
|
||||
processor,
|
||||
EightBitArithmeticOperation::HLAddressToA,
|
||||
)?;
|
||||
|
||||
Self::do_sub(processor, lhs, rhs);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn store_subtraction_result(
|
||||
processor: &mut Processor,
|
||||
(total, half_carry, carry): (u8, bool, bool),
|
||||
) {
|
||||
processor
|
||||
.registers
|
||||
.set_single_8bit_register(register::SingleEightBit::A, total);
|
||||
|
||||
super::set_subtraction_flags(processor, total, half_carry, carry);
|
||||
}
|
||||
|
|
|
@ -50,25 +50,29 @@ where
|
|||
|
||||
/// `CarryingAdd` describes a type that can perform addition to produce the carry flags needed for the operation
|
||||
/// of the gameboy
|
||||
pub trait CarryingAdd<R, O> {
|
||||
pub trait CarryingAdd<R = Self> {
|
||||
type Output;
|
||||
|
||||
/// `add_with_carry` will perform an add operation, and then return the result, whether or not a half carry was
|
||||
/// performed, and whether or not a full carry was performed. This can be thought of analgous to
|
||||
/// [`u8::overflowing_add`] (or `overflowing_add` on any of the other numeric types), but with the addition of
|
||||
/// the half carry flag
|
||||
fn add_with_carry(self, rhs: R) -> (O, bool, bool);
|
||||
fn add_with_carry(self, rhs: R) -> (Self::Output, bool, bool);
|
||||
}
|
||||
|
||||
/// `CarryingSub` describes a type that can perform addition to produce the carry flags needed for the operation
|
||||
/// of the gameboy
|
||||
pub trait CarryingSub<R, O> {
|
||||
pub trait CarryingSub<R = Self> {
|
||||
type Output;
|
||||
/// `sub_with_carry` will perform a sub operation, and then return the result, whether or not a half carry was
|
||||
/// performed, and whether or not a full carry was performed. This can be thought of analgous to
|
||||
/// [`u8::overflowing_sub`] (or `overflowing_sub` on any of the other numeric types), but with the addition of
|
||||
/// the half carry flag
|
||||
fn sub_with_carry(self, rhs: R) -> (O, bool, bool);
|
||||
fn sub_with_carry(self, rhs: R) -> (Self::Output, bool, bool);
|
||||
}
|
||||
|
||||
impl CarryingAdd<u8, u8> for u8 {
|
||||
impl CarryingAdd for u8 {
|
||||
type Output = u8;
|
||||
fn add_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
||||
let (total, carry) = self.overflowing_add(rhs);
|
||||
|
||||
|
@ -76,7 +80,9 @@ impl CarryingAdd<u8, u8> for u8 {
|
|||
}
|
||||
}
|
||||
|
||||
impl CarryingAdd<u8, u16> for u16 {
|
||||
impl CarryingAdd<u8> for u16 {
|
||||
type Output = u16;
|
||||
|
||||
fn add_with_carry(self, rhs: u8) -> (u16, bool, bool) {
|
||||
let (total, _16_bit_carry) = self.overflowing_add(rhs.into());
|
||||
let half_carry = did_8bit_add_half_carry(self, rhs);
|
||||
|
@ -86,7 +92,9 @@ impl CarryingAdd<u8, u16> for u16 {
|
|||
}
|
||||
}
|
||||
|
||||
impl CarryingAdd<i8, u16> for u16 {
|
||||
impl CarryingAdd<i8> for u16 {
|
||||
type Output = u16;
|
||||
|
||||
fn add_with_carry(self, rhs: i8) -> (u16, bool, bool) {
|
||||
let [upper_8_bits, lower_8_bits] = self.to_be_bytes();
|
||||
let operand = twos_comp_abs(rhs);
|
||||
|
@ -111,7 +119,9 @@ impl CarryingAdd<i8, u16> for u16 {
|
|||
}
|
||||
}
|
||||
|
||||
impl CarryingAdd<CarriedNumber<u8>, u8> for u8 {
|
||||
impl CarryingAdd<CarriedNumber<u8>> for u8 {
|
||||
type Output = u8;
|
||||
|
||||
fn add_with_carry(self, rhs: CarriedNumber<u8>) -> (u8, bool, bool) {
|
||||
let total = rhs.value() + u16::from(self);
|
||||
// Given this is adding two u8s, the largest value we can produce is 0x01FF (both operands 0xFF, plus a carry bit)
|
||||
|
@ -126,7 +136,9 @@ impl CarryingAdd<CarriedNumber<u8>, u8> for u8 {
|
|||
}
|
||||
}
|
||||
|
||||
impl CarryingSub<u8, u8> for u8 {
|
||||
impl CarryingSub<u8> for u8 {
|
||||
type Output = u8;
|
||||
|
||||
fn sub_with_carry(self, rhs: u8) -> (u8, bool, bool) {
|
||||
let (total, carry) = self.overflowing_sub(rhs);
|
||||
let half_carry = did_8bit_sub_half_carry(self, rhs);
|
||||
|
@ -135,7 +147,9 @@ impl CarryingSub<u8, u8> for u8 {
|
|||
}
|
||||
}
|
||||
|
||||
impl CarryingSub<CarriedNumber<u8>, u8> for u8 {
|
||||
impl CarryingSub<CarriedNumber<u8>> for u8 {
|
||||
type Output = u8;
|
||||
|
||||
fn sub_with_carry(self, rhs: CarriedNumber<u8>) -> (u8, bool, bool) {
|
||||
let total = self.wrapping_sub(rhs.num).wrapping_sub(rhs.carry_bit);
|
||||
let half_carry = did_8bit_sub_half_carry_including_carry_bit(self, rhs.num, rhs.carry_bit);
|
||||
|
|
Loading…
Reference in New Issue