diff --git a/src/cpu/run/arithutil.rs b/src/cpu/run/arithutil.rs index a760899..7c71f4a 100644 --- a/src/cpu/run/arithutil.rs +++ b/src/cpu/run/arithutil.rs @@ -1,6 +1,6 @@ //! arithutil contains utilities to generalize the implementation of arithmetic instructions -use std::{marker::PhantomData, ops::Add}; +use std::ops::Add; use thiserror::Error; @@ -16,16 +16,15 @@ pub enum Error { /// `CarriedNumber` is a number combined with its carry bit, intended to be used to perform carry-based arithmetic /// operations such as ADC. #[derive(Debug, Clone, Copy, PartialEq)] -pub struct CarriedNumber { +pub struct CarriedNumber { num: N, carry_bit: u8, - _phantom: PhantomData, } -impl CarriedNumber +impl CarriedNumber where - N: Copy + Clone + PartialEq + UpgradableNumber, - V: From + From + Add, + N: Copy + Clone + PartialEq + UpgradableNumber, + N::Output: From + Add, { /// Create a new [`CarriedNumber`] with a raw value and its carry bit. Note that the carry bit must be 0 or 1, /// or an [`Error::InvalidCarryBit`] will be returned @@ -34,17 +33,13 @@ where return Err(Error::InvalidCarryBit(carry_bit)); } - Ok(Self { - num, - carry_bit, - _phantom: PhantomData, - }) + Ok(Self { num, carry_bit }) } /// Get the combination between the value and the carry bit. The return value is the "upgraded" version of `N`. /// See [`upgrade::UpgradableNumber`] for more details - pub fn value(self) -> V { - self.num.upgrade() + V::from(self.carry_bit) + pub fn value(self) -> N::Output { + self.num.upgrade() + self.carry_bit.into() } /// Get the number and its carry bit directly @@ -116,8 +111,8 @@ impl CarryingAdd for u16 { } } -impl CarryingAdd, u8> for u8 { - fn add_with_carry(self, rhs: CarriedNumber) -> (u8, bool, bool) { +impl CarryingAdd, u8> for 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) // which can never wrap, so we just take the lower byte to convert back to u8 @@ -140,8 +135,8 @@ impl CarryingSub for u8 { } } -impl CarryingSub, u8> for u8 { - fn sub_with_carry(self, rhs: CarriedNumber) -> (u8, bool, bool) { +impl CarryingSub, u8> for 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(self, rhs.value()); let half_carry = did_8bit_sub_half_carry_including_carry_bit(self, rhs.num, rhs.carry_bit); @@ -309,7 +304,7 @@ mod tests { #[test_case(0xFF, CarriedNumber::new(0xFF, 1).unwrap(), (0xFF, true, true))] fn test_add_carried_number_to_u8( value: u8, - carried_number: CarriedNumber, + carried_number: CarriedNumber, expected: (u8, bool, bool), ) { assert_eq!(expected, value.add_with_carry(carried_number)); @@ -332,11 +327,7 @@ mod tests { #[test_case(0xAB, CarriedNumber::new(0x5F, 1).unwrap(), (0x4B, true, false); "half borrow with carry bit and wrap")] #[test_case(0x0F, CarriedNumber::new(0xF0, 0).unwrap(), (0x1F, false, true); "full borrow without carry bit")] #[test_case(0x0F, CarriedNumber::new(0xEE, 1).unwrap(), (0x20, false, true); "full borrow with carry bit")] - fn test_sub_u8_from_u8_with_carry( - lhs: u8, - rhs: CarriedNumber, - expected: (u8, bool, bool), - ) { + fn test_sub_u8_from_u8_with_carry(lhs: u8, rhs: CarriedNumber, expected: (u8, bool, bool)) { assert_eq!(expected, lhs.sub_with_carry(rhs)); } } diff --git a/src/cpu/run/arithutil/upgrade.rs b/src/cpu/run/arithutil/upgrade.rs index ae290f9..58eeba0 100644 --- a/src/cpu/run/arithutil/upgrade.rs +++ b/src/cpu/run/arithutil/upgrade.rs @@ -1,22 +1,37 @@ /// `UpgradableNumber` represents an number that can be "upgraded" to its next largest type. /// This is slightly different than `From`, in that it only allows you to upgrade "one step up". /// You cannot, for instance, upgrade a `u8` to a `u64` directly. -pub trait UpgradableNumber> +pub trait UpgradableNumber where Self: Sized, { - fn upgrade(self) -> U { + type Output: From; + + fn upgrade(self) -> Self::Output { self.into() } } -impl UpgradableNumber for u8 {} -impl UpgradableNumber for u16 {} -impl UpgradableNumber for u32 {} +impl UpgradableNumber for u8 { + type Output = u16; +} +impl UpgradableNumber for u16 { + type Output = u32; +} +impl UpgradableNumber for u32 { + type Output = u64; +} -impl UpgradableNumber for i8 {} -impl UpgradableNumber for i16 {} -impl UpgradableNumber for i32 {} +impl UpgradableNumber for i8 { + type Output = i16; +} +impl UpgradableNumber for i16 { + type Output = i32; +} + +impl UpgradableNumber for i32 { + type Output = i64; +} #[cfg(test)] mod tests {