From 4fb9144c6d6ace98cc4e3327b2c0de953e715c70 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Sun, 7 May 2023 16:08:49 -0400 Subject: [PATCH] Refactor eight bit arithmetic to extract operand collection --- src/cpu/run/arith8.rs | 279 +++++++++++++++++++++++++++++++++++++- src/cpu/run/arith8/add.rs | 152 ++++++++------------- src/cpu/run/arith8/sub.rs | 161 +++++++++------------- src/cpu/run/arithutil.rs | 34 +++-- 4 files changed, 415 insertions(+), 211 deletions(-) diff --git a/src/cpu/run/arith8.rs b/src/cpu/run/arith8.rs index 85aa71e..b5b447e 100644 --- a/src/cpu/run/arith8.rs +++ b/src/cpu/run/arith8.rs @@ -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), 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, 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) + ); + } +} diff --git a/src/cpu/run/arith8/add.rs b/src/cpu/run/arith8/add.rs index ce06283..2907d87 100644 --- a/src/cpu/run/arith8/add.rs +++ b/src/cpu/run/arith8/add.rs @@ -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(processor: &mut Processor, lhs: u8, rhs: T) + where + u8: CarryingAdd, + { + 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 for EightBitAddRunner { fn run_instruction( processor: &mut Processor, @@ -19,134 +39,68 @@ impl InstructionRunner 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); -} diff --git a/src/cpu/run/arith8/sub.rs b/src/cpu/run/arith8/sub.rs index 32dd1f6..6d1eabc 100644 --- a/src/cpu/run/arith8/sub.rs +++ b/src/cpu/run/arith8/sub.rs @@ -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(processor: &mut Processor, lhs: u8, rhs: T) + where + u8: CarryingSub, + { + 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 for EightBitSubRunner { fn run_instruction( processor: &mut Processor, @@ -19,127 +39,68 @@ impl InstructionRunner 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); -} diff --git a/src/cpu/run/arithutil.rs b/src/cpu/run/arithutil.rs index 88ba3eb..4c29e0a 100644 --- a/src/cpu/run/arithutil.rs +++ b/src/cpu/run/arithutil.rs @@ -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 { +pub trait CarryingAdd { + 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 { +pub trait CarryingSub { + 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 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 for u8 { } } -impl CarryingAdd for u16 { +impl CarryingAdd 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 for u16 { } } -impl CarryingAdd for u16 { +impl CarryingAdd 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 for u16 { } } -impl CarryingAdd, u8> for u8 { +impl CarryingAdd> for u8 { + type Output = u8; + fn add_with_carry(self, rhs: CarriedNumber) -> (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, u8> for u8 { } } -impl CarryingSub for u8 { +impl CarryingSub 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 for u8 { } } -impl CarryingSub, u8> for u8 { +impl CarryingSub> for u8 { + type Output = u8; + fn sub_with_carry(self, rhs: CarriedNumber) -> (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);