From d0e02cd9cd4c9e660ada2e17d11b25509b45d6cc Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Mon, 21 Nov 2022 21:51:13 -0500 Subject: [PATCH] set overflow flag for adc and sbc --- snes-core/src/cpu/instructions.rs | 12 ++- snes-core/src/utils/alu.rs | 133 +++++++++++++++++++----------- snes-core/src/utils/num_trait.rs | 22 ++++- 3 files changed, 111 insertions(+), 56 deletions(-) diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index 129deca..ff6e55d 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -34,7 +34,7 @@ impl CPU { match is_8bit { true => { let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, is_carry, is_negative, is_zero) = match is_decimal_mode { + let (result, is_carry, is_negative, is_zero, is_overflow) = match is_decimal_mode { true => alu::adc8bcd(target as u8, value, carry_flag), false => alu::adc_bin(target as u8, value, carry_flag), }; @@ -42,10 +42,11 @@ impl CPU { self.registers.set_carry_flag(is_carry); self.registers.set_negative_flag(is_negative); self.registers.set_zero_flag(is_zero); + self.registers.set_overflow_flag(is_overflow); }, false => { let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, is_carry, is_negative, is_zero) = match is_decimal_mode { + let (result, is_carry, is_negative, is_zero, is_overflow) = match is_decimal_mode { true => alu::adc16bcd(target, value, carry_flag), false => alu::adc_bin(target, value, carry_flag), }; @@ -53,6 +54,7 @@ impl CPU { self.registers.set_carry_flag(is_carry); self.registers.set_negative_flag(is_negative); self.registers.set_zero_flag(is_zero); + self.registers.set_overflow_flag(is_overflow); } }; self.increment_cycles_arithmetic(addressing_mode); @@ -68,7 +70,7 @@ impl CPU { match is_8bit { true => { let value = self.get_8bit_from_address(bus, addressing_mode); - let (result, is_carry, is_negative, is_zero) = match is_decimal_mode { + let (result, is_carry, is_negative, is_zero, is_overflow) = match is_decimal_mode { true => alu::sbc8bcd(target as u8, value, carry_flag), false => alu::sbc_bin(target as u8, value, carry_flag), }; @@ -76,10 +78,11 @@ impl CPU { self.registers.set_carry_flag(is_carry); self.registers.set_negative_flag(is_negative); self.registers.set_zero_flag(is_zero); + self.registers.set_overflow_flag(is_overflow); }, false => { let value = self.get_16bit_from_address(bus, addressing_mode); - let (result, is_carry, is_negative, is_zero) = match is_decimal_mode { + let (result, is_carry, is_negative, is_zero, is_overflow) = match is_decimal_mode { true => alu::sbc16bcd(target, value, carry_flag), false => alu::sbc_bin(target, value, carry_flag), }; @@ -87,6 +90,7 @@ impl CPU { self.registers.set_carry_flag(is_carry); self.registers.set_negative_flag(is_negative); self.registers.set_zero_flag(is_zero); + self.registers.set_overflow_flag(is_overflow); } }; self.increment_cycles_arithmetic(addressing_mode); diff --git a/snes-core/src/utils/alu.rs b/snes-core/src/utils/alu.rs index 9f25cd7..97e666a 100644 --- a/snes-core/src/utils/alu.rs +++ b/snes-core/src/utils/alu.rs @@ -1,14 +1,15 @@ use super::num_trait::SnesNum; -pub fn adc_bin(target: T, value: T, carry: bool) -> (T, bool, bool, bool) { +pub fn adc_bin(target: T, value: T, carry: bool) -> (T, bool, 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) + let is_overflow = target.is_overflow(value, result); + (result, is_carry, is_negative, is_zero, is_overflow) } -pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { +pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool, bool) { let mut is_carry = carry; let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u8); if result > 9 { @@ -21,10 +22,11 @@ pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { is_carry = result > 0x9F; let is_negative = (result >> 7) == 1; let is_zero = result == 0; - (result, is_carry, is_negative, is_zero) + let is_overflow = target.is_overflow(value, result); + (result, is_carry, is_negative, is_zero, is_overflow) } -pub fn adc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) { +pub fn adc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool, bool) { let mut is_carry = carry; let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u16); if result > 9 { @@ -41,18 +43,19 @@ pub fn adc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) is_carry = result > 0x9FFF; let is_negative = (result >> 15) == 1; let is_zero = result == 0; - (result, is_carry, is_negative, is_zero) + (result, is_carry, is_negative, is_zero, false) // TODO: return overflow } -pub fn sbc_bin(target: T, value: T, carry: bool) -> (T, bool, bool, bool) { - let is_carry = target.sub_will_carry(value, carry); - let result = target.sub_snes(value, carry); +pub fn sbc_bin(target: T, value: T, carry: bool) -> (T, bool, bool, bool, bool) { + let is_carry = target.sbc_will_carry(value, carry); + let result = target.sbc_snes(value, carry); let is_negative = result.is_negative(); let is_zero = result.is_zero(); - (result, is_carry, is_negative, is_zero) + let is_overflow = target.is_overflow(value, result); + (result, is_carry, is_negative, is_zero, is_overflow) } -pub fn sbc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { +pub fn sbc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool, bool) { let mut is_carry = carry; let target = target as u16; let value = !(value as u16); @@ -69,10 +72,10 @@ pub fn sbc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { let result = result as u8; let is_negative = (result >> 7) == 1; let is_zero = result == 0; - (result, is_carry, is_negative, is_zero) + (result, is_carry, is_negative, is_zero, false) // TODO: return overflow } -pub fn sbc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) { +pub fn sbc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool, bool) { let mut is_carry = carry; let target = target as u32; let value = !(value as u32); @@ -100,7 +103,7 @@ pub fn sbc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) let result = result as u16; let is_negative = (result >> 15) == 1; let is_zero = result == 0; - (result, is_carry, is_negative, is_zero) + (result, is_carry, is_negative, is_zero, false) // TODO: return overflow } pub fn and(target: T, value: T) -> (T, bool, bool) { @@ -125,225 +128,259 @@ mod alu_tests { #[test] fn test_adc_bin() { // 8 bits - let (result, carry, negative, zero) = adc_bin(0_u8, 0_u8, false); + let (result, carry, negative, zero, overflow) = adc_bin(0_u8, 0_u8, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(0_u8, 50_u8, false); + let (result, carry, negative, zero, overflow) = adc_bin(0_u8, 50_u8, false); assert_eq!(result, 50); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(200_u8, 155_u8, false); + let (result, carry, negative, zero, overflow) = adc_bin(200_u8, 155_u8, false); assert_eq!(result, 99); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(200_u8, 155_u8, true); + let (result, carry, negative, zero, overflow) = adc_bin(200_u8, 155_u8, true); assert_eq!(result, 100); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(200_u8, 54_u8, true); + let (result, carry, negative, zero, overflow) = adc_bin(200_u8, 54_u8, true); assert_eq!(result, 255); assert_eq!(carry, false); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(200_u8, 54_u8, true); + let (result, carry, negative, zero, overflow) = adc_bin(200_u8, 54_u8, true); assert_eq!(result, 255); assert_eq!(carry, false); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); // 16 bits - let (result, carry, negative, zero) = adc_bin(0_u16, 0_u16, false); + let (result, carry, negative, zero, overflow) = adc_bin(0_u16, 0_u16, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(0_u16, 50_u16, false); + let (result, carry, negative, zero, overflow) = adc_bin(0_u16, 50_u16, false); assert_eq!(result, 50); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc_bin(65530_u16, 10_u16, false); + let (result, carry, negative, zero, overflow) = adc_bin(65530_u16, 10_u16, false); assert_eq!(result, 4); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, true); - let (result, carry, negative, zero) = adc_bin(65530_u16, 10_u16, true); + let (result, carry, negative, zero, overflow) = adc_bin(65530_u16, 10_u16, true); assert_eq!(result, 5); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, true); - let (result, carry, negative, zero) = adc_bin(65530_u16, 4_u16, true); + let (result, carry, negative, zero, overflow) = adc_bin(65530_u16, 4_u16, true); assert_eq!(result, 65535); assert_eq!(carry, false); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); } #[test] fn test_adc_bcd() { // 8 bits - let (result, carry, negative, zero) = adc8bcd(5, 5, false); + let (result, carry, negative, zero, overflow) = adc8bcd(5, 5, false); assert_eq!(result, 0b0001_0000); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc8bcd(7, 9, false); + let (result, carry, negative, zero, overflow) = adc8bcd(7, 9, false); assert_eq!(result, 0b0001_0110); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc8bcd(5, 4, true); + let (result, carry, negative, zero, overflow) = adc8bcd(5, 4, true); assert_eq!(result, 0b0001_0000); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc8bcd(7, 8, true); + let (result, carry, negative, zero, overflow) = adc8bcd(7, 8, true); assert_eq!(result, 0b0001_0110); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc8bcd(0, 0, false); + let (result, carry, negative, zero, overflow) = adc8bcd(0, 0, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc8bcd(0b0001_1001, 0b0010_1000, false); + let (result, carry, negative, zero, overflow) = adc8bcd(0b0001_1001, 0b0010_1000, false); assert_eq!(result, 0b0100_0111); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); // 16 bits - let (result, carry, negative, zero) = adc16bcd(5, 5, false); + let (result, carry, negative, zero, overflow) = adc16bcd(5, 5, false); assert_eq!(result, 0b0001_0000); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc16bcd(7, 9, false); + let (result, carry, negative, zero, overflow) = adc16bcd(7, 9, false); assert_eq!(result, 0b0001_0110); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc16bcd(5, 4, true); + let (result, carry, negative, zero, overflow) = adc16bcd(5, 4, true); assert_eq!(result, 0b0001_0000); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc16bcd(7, 8, true); + let (result, carry, negative, zero, overflow) = adc16bcd(7, 8, true); assert_eq!(result, 0b0001_0110); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc16bcd(0, 0, false); + let (result, carry, negative, zero, overflow) = adc16bcd(0, 0, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc16bcd(0x0500, 0x0500, false); + let (result, carry, negative, zero, overflow) = adc16bcd(0x0500, 0x0500, false); assert_eq!(result, 0b0001_0000_0000_0000); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = adc16bcd(0b0001_1001, 0b0010_1000, false); + let (result, carry, negative, zero, overflow) = adc16bcd(0b0001_1001, 0b0010_1000, false); assert_eq!(result, 0b0100_0111); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, false); + assert_eq!(overflow, false); } #[test] fn test_dec_bin() { // 8 bit - let (result, carry, negative, zero) = sbc_bin(1_u8, 1_u8, false); + let (result, carry, negative, zero, overflow) = sbc_bin(1_u8, 1_u8, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = sbc_bin(0_u8, 1_u8, false); + let (result, carry, negative, zero, overflow) = sbc_bin(0_u8, 1_u8, false); assert_eq!(result, 0b11111111); assert_eq!(carry, true); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = sbc_bin(0_u8, 1_u8, true); + let (result, carry, negative, zero, overflow) = sbc_bin(0_u8, 1_u8, true); assert_eq!(result, 0b11111110); assert_eq!(carry, true); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); // 16 bit - let (result, carry, negative, zero) = sbc_bin(1_u16, 1_u16, false); + let (result, carry, negative, zero, overflow) = sbc_bin(1_u16, 1_u16, false); assert_eq!(result, 0); assert_eq!(carry, false); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = sbc_bin(0_u16, 1_u16, false); + let (result, carry, negative, zero, overflow) = sbc_bin(0_u16, 1_u16, false); assert_eq!(result, 0b11111111_11111111); assert_eq!(carry, true); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = sbc_bin(0_u16, 1_u16, true); + let (result, carry, negative, zero, overflow) = sbc_bin(0_u16, 1_u16, true); assert_eq!(result, 0b11111111_11111110); assert_eq!(carry, true); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); } #[test] fn test_dec_bcd() { // 8 bit - let (result, carry, negative, zero) = sbc8bcd(0x49, 0x48, false); + let (result, carry, negative, zero, overflow) = sbc8bcd(0x49, 0x48, false); assert_eq!(result, 0x00); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = sbc8bcd(0x49, 0x50, true); + let (result, carry, negative, zero, overflow) = sbc8bcd(0x49, 0x50, true); assert_eq!(result, 0x99); assert_eq!(carry, false); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); // 16 bit - let (result, carry, negative, zero) = sbc16bcd(0x4999, 0x4998, false); + let (result, carry, negative, zero, overflow) = sbc16bcd(0x4999, 0x4998, false); assert_eq!(result, 0x0000); assert_eq!(carry, true); assert_eq!(negative, false); assert_eq!(zero, true); + assert_eq!(overflow, false); - let (result, carry, negative, zero) = sbc16bcd(0x4999, 0x5000, true); + let (result, carry, negative, zero, overflow) = sbc16bcd(0x4999, 0x5000, true); assert_eq!(result, 0x9999); assert_eq!(carry, false); assert_eq!(negative, true); assert_eq!(zero, false); + assert_eq!(overflow, false); } #[test] diff --git a/snes-core/src/utils/num_trait.rs b/snes-core/src/utils/num_trait.rs index 76705ed..888324c 100644 --- a/snes-core/src/utils/num_trait.rs +++ b/snes-core/src/utils/num_trait.rs @@ -1,8 +1,9 @@ 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 sbc_will_carry(&self, v: Self, carry: bool) -> bool; + fn is_overflow(&self, v: Self, r: Self) -> bool; fn add_snes(&self, v: Self, carry: bool) -> Self; - fn sub_snes(&self, v: Self, carry: bool) -> Self; + fn sbc_snes(&self, v: Self, carry: bool) -> Self; fn and(&self, v: Self) -> Self; fn asl(&self) -> Self; fn is_negative(&self) -> bool; @@ -31,14 +32,27 @@ macro_rules! define_operation { } } +macro_rules! define_is_overflow { + ($t:ty) => { + fn is_overflow(&self, v: $t, r: $t) -> bool { + let target = (*self).is_negative(); + let value = v.is_negative(); + let result = r.is_negative(); + (target ^ result) && (target ^ value) + } + } +} + 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_will_carry!($t, sbc_will_carry, checked_sub); define_operation!($t, add_snes, wrapping_add); - define_operation!($t, sub_snes, wrapping_sub); + define_operation!($t, sbc_snes, wrapping_sub); + + define_is_overflow!($t); fn and(&self, v: $t) -> $t { (* self) & v