diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index c2089c1..95a5093 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -38,7 +38,7 @@ impl CPU { true => alu::adc8bcd(target as u8, value, carry_flag), false => alu::adc8bin(target as u8, value, carry_flag), }; - self.registers.a = result as u16; + self.registers.a = (self.registers.a & 0x00FF) | (result as u16); self.registers.set_carry_flag(is_carry); self.registers.set_negative_flag(is_negative); self.registers.set_zero_flag(is_zero); @@ -99,4 +99,21 @@ mod cpu_instructions_tests { assert_eq!(cpu.registers.a, 0x40); assert!(!cpu.registers.get_carry_flag()); } + + #[test] + fn test_dec() { + /* + let mut cpu = CPU::new(); + let mut bus = Bus::new(); + cpu.registers.a = 0x0000; + cpu.registers.pbr = 0x00; + cpu.registers.pc = 0x0000; + cpu.registers.set_memory_select_flag(true); + bus.write(0x000001, 1); + cpu.dec(&bus, AddressingMode::Immediate); + assert_eq!(cpu.registers.a, 1); + assert!(!cpu.registers.get_carry_flag()); + assert!(!cpu.registers.get_zero_flag()); + */ + } } diff --git a/snes-core/src/utils/alu.rs b/snes-core/src/utils/alu.rs index 64c890d..00d96a4 100644 --- a/snes-core/src/utils/alu.rs +++ b/snes-core/src/utils/alu.rs @@ -1,3 +1,5 @@ +/// 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, @@ -66,6 +68,72 @@ pub fn adc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) (result, is_carry, is_negative, is_zero) } +pub fn dec8bin(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { + let is_carry = match target.checked_sub(value) { + None => true, + Some(res) => match res.checked_sub(carry as u8) { + None => true, + Some(_) => false, + }, + }; + let result = target + .wrapping_sub(value) + .wrapping_sub(carry as u8); + let is_negative = (result >> 7) == 1; + let is_zero = result == 0; + (result, is_carry, is_negative, is_zero) +} + +pub fn dec16bin(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) { + let is_carry = match target.checked_sub(value) { + None => true, + Some(res) => match res.checked_sub(carry as u16) { + None => true, + Some(_) => false, + }, + }; + let result = target + .wrapping_sub(value) + .wrapping_sub(carry as u16); + let is_negative = (result >> 15) == 1; + let is_zero = result == 0; + (result, is_carry, is_negative, is_zero) +} + +pub fn dec8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { + let mut is_carry = carry; + let value = (!value).wrapping_add(1); // complement of value + let six_complement = (!6 as u8).wrapping_add(1); + let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u8) + 1; + if result > 0xF { + result = result.wrapping_add(six_complement); + } + result = result.wrapping_add(target & 0xF0).wrapping_add(value & 0xF0).wrapping_add(1); + is_carry = result < 0xFF; + let is_negative = (result >> 7) == 1; + let is_zero = result == 0; + (result, is_carry, is_negative, is_zero) +} + +pub fn dec16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) { + let mut is_carry = carry; + let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u16); + if result > 9 { + result += 6; + } + result += (target & 0xF0) + (value & 0xF0); + if result > 0x9F { + result += 0x60; + } + result += (target & 0xF00) + (value & 0xF00); + if result > 0x9FF { + result += 0x600; + } + is_carry = result > 0x9FFF; + let is_negative = (result >> 15) == 1; + let is_zero = result == 0; + (result, is_carry, is_negative, is_zero) +} #[cfg(test)] mod alu_tests { @@ -220,4 +288,64 @@ mod alu_tests { assert_eq!(negative, false); assert_eq!(zero, false); } + + #[test] + fn test_dec8bin() { + let (result, carry, negative, zero) = dec8bin(1, 1, false); + assert_eq!(result, 0); + assert_eq!(carry, false); + assert_eq!(negative, false); + assert_eq!(zero, true); + + let (result, carry, negative, zero) = dec8bin(0, 1, false); + assert_eq!(result, 0b11111111); + assert_eq!(carry, true); + assert_eq!(negative, true); + assert_eq!(zero, false); + + let (result, carry, negative, zero) = dec8bin(0, 1, true); + assert_eq!(result, 0b11111110); + assert_eq!(carry, true); + assert_eq!(negative, true); + assert_eq!(zero, false); + } + + #[test] + fn test_dec16bin() { + let (result, carry, negative, zero) = dec16bin(1, 1, false); + assert_eq!(result, 0); + assert_eq!(carry, false); + assert_eq!(negative, false); + assert_eq!(zero, true); + + let (result, carry, negative, zero) = dec16bin(0, 1, false); + assert_eq!(result, 0b11111111_11111111); + assert_eq!(carry, true); + assert_eq!(negative, true); + assert_eq!(zero, false); + + let (result, carry, negative, zero) = dec16bin(0, 1, true); + assert_eq!(result, 0b11111111_11111110); + assert_eq!(carry, true); + assert_eq!(negative, true); + assert_eq!(zero, false); + } + + #[test] + fn test_dec8bcd() { + let (result, carry, negative, zero) = dec8bcd(0b0100_0111, 0b0010_1000, false); + assert_eq!(result, 0b0001_1001); + assert_eq!(carry, false); + assert_eq!(negative, false); + assert_eq!(zero, false); + } + + #[test] + fn test_dec16bcd() { + let (result, carry, negative, zero) = dec16bcd(0b0100_0111, 0b0010_1000, false); + assert_eq!(result, 0b0001_1001); + assert_eq!(carry, false); + assert_eq!(negative, false); + assert_eq!(zero, false); + } }