diff --git a/snes-core/src/cpu/cycles.rs b/snes-core/src/cpu/cycles.rs index 0a8cadf..c39826b 100644 --- a/snes-core/src/cpu/cycles.rs +++ b/snes-core/src/cpu/cycles.rs @@ -139,6 +139,7 @@ impl CPU { self.registers.increment_pc(bytes); self.cycles += cycles; // Add 2 cycles if m = 1 + // TODO: Consider that this may be adding one byte more self.common_conditions(addressing_mode, &[Condition::MemorySelectFlag]); self.common_conditions(addressing_mode, &[ Condition::MemorySelectFlag, diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index 5762aa1..cee37f7 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -36,7 +36,7 @@ impl CPU { let value = self.get_8bit_from_address(bus, addressing_mode); let (result, is_carry, is_negative, is_zero) = match is_decimal_mode { true => alu::adc8bcd(target as u8, value, carry_flag), - false => alu::adc8bin(target as u8, value, carry_flag), + false => alu::adc_bin(target as u8, value, carry_flag), }; self.registers.set_low_a(result as u8); self.registers.set_carry_flag(is_carry); @@ -47,7 +47,7 @@ impl CPU { let value = self.get_16bit_from_address(bus, addressing_mode); let (result, is_carry, is_negative, is_zero) = match is_decimal_mode { true => alu::adc16bcd(target, value, carry_flag), - false => alu::adc16bin(target, value, carry_flag), + false => alu::adc_bin(target, value, carry_flag), }; self.registers.a = result; self.registers.set_carry_flag(is_carry); diff --git a/snes-core/src/utils/alu.rs b/snes-core/src/utils/alu.rs index 46257f5..c8d9fd1 100644 --- a/snes-core/src/utils/alu.rs +++ b/snes-core/src/utils/alu.rs @@ -1,34 +1,12 @@ /// TODO: refactor functions to work with generic types (either u8 or 16) to reduce duplication -pub fn adc8bin(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { - let is_carry = match target.checked_add(value) { - None => true, - Some(res) => match res.checked_add(carry as u8) { - None => true, - Some(_) => false, - }, - }; - let result = target - .wrapping_add(value) - .wrapping_add(carry as u8); - let is_negative = (result >> 7) == 1; - let is_zero = result == 0; - (result, is_carry, is_negative, is_zero) -} +use super::num_trait::SnesNum; -pub fn adc16bin(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) { - let is_carry = match target.checked_add(value) { - None => true, - Some(res) => match res.checked_add(carry as u16) { - None => true, - Some(_) => false, - }, - }; - let result = target - .wrapping_add(value) - .wrapping_add(carry as u16); - let is_negative = (result >> 15) == 1; - let is_zero = result == 0; +pub fn adc_bin(target: T, value: T, carry: bool) -> (T, bool, bool, bool) { + let is_carry = target.add_will_carry(value, carry); + let result = target.add_snes(value, carry); + let is_negative = result.is_negative(); + let is_zero = result.is_zero(); (result, is_carry, is_negative, is_zero) } @@ -187,31 +165,37 @@ mod alu_tests { #[test] fn test_adc8bin() { - let (result, carry, negative, zero) = adc8bin(0, 0, false); + let (result, carry, negative, zero) = adc_bin(0_u8, 0_u8, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); - let (result, carry, negative, zero) = adc8bin(0, 50, false); + let (result, carry, negative, zero) = adc_bin(0_u8, 50_u8, false); assert_eq!(result, 50); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); - let (result, carry, negative, zero) = adc8bin(200, 155, false); + let (result, carry, negative, zero) = adc_bin(200_u8, 155_u8, false); assert_eq!(result, 99); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); - let (result, carry, negative, zero) = adc8bin(200, 155, true); + let (result, carry, negative, zero) = adc_bin(200_u8, 155_u8, true); assert_eq!(result, 100); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); - let (result, carry, negative, zero) = adc8bin(200, 54, true); + let (result, carry, negative, zero) = adc_bin(200_u8, 54_u8, true); + assert_eq!(result, 255); + assert_eq!(carry, false); + assert_eq!(negative, true); + assert_eq!(zero, false); + + let (result, carry, negative, zero) = adc_bin(200_u8, 54_u8, true); assert_eq!(result, 255); assert_eq!(carry, false); assert_eq!(negative, true); @@ -220,31 +204,31 @@ mod alu_tests { #[test] fn test_adc16bin() { - let (result, carry, negative, zero) = adc16bin(0, 0, false); + let (result, carry, negative, zero) = adc_bin(0_u16, 0_u16, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); - let (result, carry, negative, zero) = adc16bin(0, 50, false); + let (result, carry, negative, zero) = adc_bin(0_u16, 50_u16, false); assert_eq!(result, 50); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); - let (result, carry, negative, zero) = adc16bin(65530, 10, false); + let (result, carry, negative, zero) = adc_bin(65530_u16, 10_u16, false); assert_eq!(result, 4); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); - let (result, carry, negative, zero) = adc16bin(65530, 10, true); + let (result, carry, negative, zero) = adc_bin(65530_u16, 10_u16, true); assert_eq!(result, 5); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); - let (result, carry, negative, zero) = adc16bin(65530, 4, true); + let (result, carry, negative, zero) = adc_bin(65530_u16, 4_u16, true); assert_eq!(result, 65535); assert_eq!(carry, false); assert_eq!(negative, true); diff --git a/snes-core/src/utils/mod.rs b/snes-core/src/utils/mod.rs index e9761e3..6112635 100644 --- a/snes-core/src/utils/mod.rs +++ b/snes-core/src/utils/mod.rs @@ -1,2 +1,3 @@ pub mod alu; pub mod addressing; +pub mod num_trait; diff --git a/snes-core/src/utils/num_trait.rs b/snes-core/src/utils/num_trait.rs new file mode 100644 index 0000000..142b28c --- /dev/null +++ b/snes-core/src/utils/num_trait.rs @@ -0,0 +1,53 @@ +pub trait SnesNum: Copy + Clone + Sized + Eq + PartialEq { + fn add_will_carry(&self, v: Self, carry: bool) -> bool; + fn sub_will_carry(&self, v: Self, carry: bool) -> bool; + fn add_snes(&self, v: Self, carry: bool) -> Self; + fn sub_snes(&self, v: Self, carry: bool) -> Self; + fn is_negative(&self) -> bool; + fn is_zero(&self) -> bool; +} + +macro_rules! define_will_carry { + ($t:ty, $method:ident, $inner_method:ident) => { + fn $method(&self, v: $t, carry: bool) -> bool { + match self.$inner_method(v) { + None => true, + Some(res) => match res.$inner_method(carry as $t) { + None => true, + Some(_) => false, + }, + } + } + } +} + +macro_rules! define_operation { + ($t:ty, $method:ident, $inner_method:ident) => { + fn $method(&self, v: $t, carry: bool) -> $t { + self.$inner_method(v).$inner_method(carry as $t) + } + } +} + +macro_rules! define_impl { + ($t:ty) => { + impl SnesNum for $t { + define_will_carry!($t, add_will_carry, checked_add); + define_will_carry!($t, sub_will_carry, checked_sub); + + define_operation!($t, add_snes, wrapping_add); + define_operation!($t, sub_snes, wrapping_sub); + + fn is_negative(&self) -> bool { + (*self) & !(<$t>::MAX >> 1) != 0 + } + + fn is_zero(&self) -> bool { + (*self) == 0 + } + } + }; +} + +define_impl!(u8); +define_impl!(u16); \ No newline at end of file