From e1f9542cca6600911e495c33e412e795868e9f46 Mon Sep 17 00:00:00 2001 From: Franco Colmenarez Date: Thu, 13 Oct 2022 00:37:03 -0500 Subject: [PATCH] Refactor opcode execution and adc instruction --- .gitignore | 1 + snes-core/src/cpu/cpu.rs | 133 --------------- snes-core/src/cpu/instructions.rs | 274 ++++++++---------------------- snes-core/src/utils/addressing.rs | 48 +++--- snes-core/src/utils/alu.rs | 72 ++++---- 5 files changed, 134 insertions(+), 394 deletions(-) diff --git a/.gitignore b/.gitignore index 4ef3c07..b0bdad3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /Cargo.lock +/ignore imgui.ini diff --git a/snes-core/src/cpu/cpu.rs b/snes-core/src/cpu/cpu.rs index 35ff5ce..a502e38 100644 --- a/snes-core/src/cpu/cpu.rs +++ b/snes-core/src/cpu/cpu.rs @@ -1,4 +1,3 @@ -use crate::bus::Bus; use super::registers::Registers; pub struct CPU { @@ -13,136 +12,4 @@ impl CPU { cycles: 0, } } - - pub fn get_absolute(&self, bus: &Bus) -> u16 { - let pc = self.registers.get_pc_address(); - (bus.read(pc + 1) as u16) | ((bus.read(pc + 2) as u16) << 8) - } - - pub fn get_absolute_long(&self, bus: &Bus) -> u32 { - let pc = self.registers.get_pc_address(); - (bus.read(pc + 1) as u32) | ((bus.read(pc + 2) as u32) << 8) | ((bus.read(pc + 3) as u32) << 16) - } - - pub fn get_direct_page(&self, bus: &Bus) -> u8 { - let pc = self.registers.get_pc_address(); - bus.read(pc + 1) - } - - pub fn get_immediate(&self, bus: &Bus) -> u16 { - // If the "m" flag is set to 1, read only 8 bits. - // Otherwise, read 16 bits - let address = self.registers.get_pc_address(); - if self.registers.get_memory_select_flag() { - return bus.read(address + 1) as u16; - } else { - return (bus.read(address + 1) as u16) | ((bus.read(address + 2) as u16) << 8); - } - } - - fn execute_opcode(&mut self, opcode: u8, bus: &Bus) { - match opcode { - 0x69 => self.adc_const(bus), - 0x6D => self.adc_addr(bus), - 0x6F => self.adc_long(bus), - 0x65 => self.adc_dp(bus), - 0x72 => self.adc_dp_indirect(bus), - 0x67 => self.adc_dp_indirect_long(bus), - _ => todo!("Missing opcode implementation: {:02X}", opcode), - } - } - - pub fn tick(&mut self, bus: &Bus) { - let address = self.registers.get_pc_address(); - let opcode = bus.read(address); - self.execute_opcode(opcode, bus); - } } - -#[cfg(test)] -mod cpu_tests { - use super::*; - - #[test] - fn test_get_immediate() { - let mut bus = Bus::new(); - let mut cpu = CPU::new(); - cpu.registers.set_memory_select_flag(true); - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - // write to WRAM - bus.write(0x00_0001, 0x01); - bus.write(0x00_0002, 0x02); - assert_eq!(cpu.get_immediate(&bus), 0x0001); - cpu.registers.set_memory_select_flag(false); - assert_eq!(cpu.get_immediate(&bus), 0x0201); - - cpu.registers.set_memory_select_flag(true); - cpu.registers.pc = 0x0010; - cpu.registers.pbr = 0x7E; - // write to WRAM - bus.write(0x7E_0011, 0x01); - bus.write(0x7E_0012, 0x02); - assert_eq!(cpu.get_immediate(&bus), 0x0001); - cpu.registers.set_memory_select_flag(false); - assert_eq!(cpu.get_immediate(&bus), 0x0201); - } - - #[test] - fn test_get_absolute() { - let mut bus = Bus::new(); - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - // write to WRAM - bus.write(0x00_0001, 0x01); - bus.write(0x00_0002, 0x02); - assert_eq!(cpu.get_absolute(&bus), 0x0201); - - cpu.registers.pc = 0x0010; - cpu.registers.pbr = 0x7E; - // write to WRAM - bus.write(0x7E_0011, 0x01); - bus.write(0x7E_0012, 0x02); - assert_eq!(cpu.get_absolute(&bus), 0x0201); - } - - #[test] - fn test_get_absolute_long() { - let mut bus = Bus::new(); - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - // write to WRAM - bus.write(0x00_0001, 0x01); - bus.write(0x00_0002, 0x02); - bus.write(0x00_0003, 0x03); - assert_eq!(cpu.get_absolute_long(&bus), 0x030201); - - cpu.registers.pc = 0x0010; - cpu.registers.pbr = 0x7E; - // write to WRAM - bus.write(0x7E_0011, 0x01); - bus.write(0x7E_0012, 0x02); - bus.write(0x7E_0013, 0x03); - assert_eq!(cpu.get_absolute_long(&bus), 0x030201); - } - - #[test] - fn test_get_direct_page() { - let mut bus = Bus::new(); - let mut cpu = CPU::new(); - cpu.registers.pc = 0x0000; - cpu.registers.pbr = 0x00; - // write to WRAM - bus.write(0x00_0001, 0x01); - assert_eq!(cpu.get_direct_page(&bus), 0x01); - - cpu.registers.pc = 0x0010; - cpu.registers.pbr = 0x7E; - // write to WRAM - bus.write(0x7E_0011, 0x01); - assert_eq!(cpu.get_direct_page(&bus), 0x01); - } -} - diff --git a/snes-core/src/cpu/instructions.rs b/snes-core/src/cpu/instructions.rs index b2115fa..c2089c1 100644 --- a/snes-core/src/cpu/instructions.rs +++ b/snes-core/src/cpu/instructions.rs @@ -1,192 +1,84 @@ use super::cpu::CPU; use crate::bus::Bus; +use crate::utils::addressing::{AddressingMode, IndexRegister}; +use crate::utils::alu; impl CPU { - fn adc8bcd(&mut self, value: u8) { - let carry = self.registers.get_carry_flag(); - let a = self.registers.a as u8; - let carry_result = match a.checked_add(value) { - None => true, - Some(res) => match res.checked_add(carry as u8) { - None => true, - Some(_) => false, - }, - }; - let result = a - .wrapping_add(value) - .wrapping_add(carry as u8); - self.registers.a = (self.registers.a & 0xFF00) | (result as u16); - self.registers.set_carry_flag(carry_result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); + fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 { + addressing_mode.value_8bit( + bus, + self.registers.get_pc_address(), + self.registers.d, + self.registers.sp, + self.registers.x, self.registers.y + ) } - fn adc16bcd(&mut self, value: u16) { - let carry = self.registers.get_carry_flag(); - let carry_result = match self.registers.a.checked_add(value) { - None => true, - Some(res) => match res.checked_add(carry as u16) { - None => true, - Some(_) => false, - }, - }; - let result = self.registers.a - .wrapping_add(value) - .wrapping_add(carry as u16); - self.registers.a = result; - self.registers.set_carry_flag(carry_result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); + fn get_16bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u16 { + addressing_mode.value_16bit( + bus, + self.registers.get_pc_address(), + self.registers.d, + self.registers.sp, + self.registers.x, self.registers.y + ) } - fn adc8(&mut self, value: u8) { - let carry = self.registers.get_carry_flag(); - let a = self.registers.a as u8; - let carry_result = match a.checked_add(value) { - None => true, - Some(res) => match res.checked_add(carry as u8) { - None => true, - Some(_) => false, - }, - }; - let result = a - .wrapping_add(value) - .wrapping_add(carry as u8); - self.registers.a = (self.registers.a & 0xFF00) | (result as u16); - self.registers.set_carry_flag(carry_result); - self.registers.set_negative_flag((result >> 7) == 1); - self.registers.set_zero_flag(result == 0); - } - - fn adc16(&mut self, value: u16) { - let carry = self.registers.get_carry_flag(); - let carry_result = match self.registers.a.checked_add(value) { - None => true, - Some(res) => match res.checked_add(carry as u16) { - None => true, - Some(_) => false, - }, - }; - let result = self.registers.a - .wrapping_add(value) - .wrapping_add(carry as u16); - self.registers.a = result; - self.registers.set_carry_flag(carry_result); - self.registers.set_negative_flag((result >> 15) == 1); - self.registers.set_zero_flag(result == 0); - } - - fn adc(&mut self, value: u16) { + fn adc(&mut self, bus: &Bus, addressing_mode: AddressingMode) { // if the M flag is set, perform 8 bit addition. // Otherwise, 16 bit addition - if self.registers.get_memory_select_flag() { - if self.registers.get_decimal_mode_flag() { - self.adc8bcd(value as u8); - } else { - self.adc8(value as u8); + let carry_flag = self.registers.get_carry_flag(); + let is_decimal_mode = self.registers.get_decimal_mode_flag(); + let is_8bit = self.registers.get_memory_select_flag(); + let target = self.registers.a; + 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 { + 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.set_carry_flag(is_carry); + self.registers.set_negative_flag(is_negative); + self.registers.set_zero_flag(is_zero); + }, + false => { + 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), + }; + self.registers.a = result; + self.registers.set_carry_flag(is_carry); + self.registers.set_negative_flag(is_negative); + self.registers.set_zero_flag(is_zero); } - } else { - if self.registers.get_decimal_mode_flag() { - self.adc16bcd(value); - } else { - self.adc16(value); - } - } + }; } - pub fn adc_const(&mut self, bus: &Bus) { - let value = self.get_immediate(bus); - self.registers.pc = self.registers.pc.wrapping_add(2); - self.cycles += 2; - if self.registers.get_memory_select_flag() { - self.registers.pc = self.registers.pc.wrapping_add(1); - self.cycles += 1; + pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) { + type A = AddressingMode; + type I = IndexRegister; + match opcode { + // ADC + 0x69 => self.adc(bus, A::Immediate), + 0x6D => self.adc(bus, A::Absolute), + 0x6F => self.adc(bus, A::AbsoluteLong), + 0x65 => self.adc(bus, A::DirectPage), + 0x72 => self.adc(bus, A::DirectPageIndirect), + 0x67 => self.adc(bus, A::DirectPageIndirectLong), + 0x7D => self.adc(bus, A::AbsoluteIndexed(I::X)), + 0x7F => self.adc(bus, A::AbsoluteLongIndexed(I::X)), + 0x79 => self.adc(bus, A::AbsoluteIndexed(I::Y)), + 0x75 => self.adc(bus, A::DirectPageIndexed(I::X)), + 0x61 => self.adc(bus, A::DirectPageIndexedIndirect(I::X)), + 0x71 => self.adc(bus, A::DirectPageIndirectIndexed(I::Y)), + 0x77 => self.adc(bus, A::DirectPageIndirectLongIndexed(I::Y)), + 0x63 => self.adc(bus, A::StackRelative), + 0x73 => self.adc(bus, A::StackRelativeIndirectIndexed(I::Y)), + _ => println!("Invalid opcode: {:02X}", opcode), } - if self.registers.get_decimal_mode_flag() { - self.cycles += 1; - } - self.adc(value); - } - - pub fn adc_addr(&mut self, bus: &Bus) { - let address = ((self.registers.pbr as u32) << 16) | (self.get_absolute(bus) as u32); - self.registers.pc = self.registers.pc.wrapping_add(3); - self.cycles += 4; - if self.registers.get_memory_select_flag() { - self.cycles += 1; - } - if self.registers.get_decimal_mode_flag() { - self.cycles += 1; - } - let value = bus.read(address); - self.adc(value as u16); - } - - pub fn adc_long(&mut self, bus: &Bus) { - let address = self.get_absolute_long(bus); - self.registers.pc = self.registers.pc.wrapping_add(4); - self.cycles += 5; - if self.registers.get_memory_select_flag() { - self.cycles += 1; - } - if self.registers.get_decimal_mode_flag() { - self.cycles += 1; - } - let value = bus.read(address); - self.adc(value as u16); - } - - pub fn adc_dp(&mut self, bus: &Bus) { - let address = self.get_direct_page(bus); - self.registers.pc = self.registers.pc.wrapping_add(2); - self.cycles += 3; - if self.registers.get_memory_select_flag() { - self.cycles += 1; - } - if address != 0 { - self.cycles += 1; - } - if self.registers.get_decimal_mode_flag() { - self.cycles += 1; - } - let value = bus.read(address as u32); - self.adc(value as u16); - } - - pub fn adc_dp_indirect(&mut self, bus: &Bus) { - let pointer = self.get_direct_page(bus) as u32; - let address = ((bus.read(pointer) as u32) << 8) | (bus.read(pointer + 1) as u32); - self.registers.pc = self.registers.pc.wrapping_add(2); - self.cycles += 3; - if self.registers.get_memory_select_flag() { - self.cycles += 1; - } - if address != 0 { - self.cycles += 1; - } - if self.registers.get_decimal_mode_flag() { - self.cycles += 1; - } - let value = bus.read(address); - self.adc(value as u16); - } - - pub fn adc_dp_indirect_long(&mut self, bus: &Bus) { - let pointer = self.get_direct_page(bus) as u32; - let address = ((bus.read(pointer) as u32) << 16) | ((bus.read(pointer + 1) as u32) << 8) | (bus.read(pointer + 2) as u32); - self.registers.pc = self.registers.pc.wrapping_add(2); - self.cycles += 3; - if self.registers.get_memory_select_flag() { - self.cycles += 1; - } - if address != 0 { - self.cycles += 1; - } - if self.registers.get_decimal_mode_flag() { - self.cycles += 1; - } - let value = bus.read(address); - self.adc(value as u16); } } @@ -197,32 +89,14 @@ mod cpu_instructions_tests { #[test] fn test_adc() { let mut cpu = CPU::new(); - cpu.registers.a = 0x00; - cpu.adc(0x40); + 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, 0x40); + cpu.adc(&bus, AddressingMode::Immediate); assert_eq!(cpu.registers.a, 0x40); assert!(!cpu.registers.get_carry_flag()); - - cpu.registers.a = 0xFFFF; - cpu.adc(0x01); - assert_eq!(cpu.registers.a, 0x0000); - assert!(cpu.registers.get_carry_flag()); - - cpu.registers.a = 0xFFFF; - cpu.registers.set_carry_flag(true); - cpu.adc(0x01); - assert_eq!(cpu.registers.a, 0x0001); - assert!(cpu.registers.get_carry_flag()); - - cpu.registers.a = 0xFFFD; - cpu.registers.set_carry_flag(true); - cpu.adc(0x01); - assert_eq!(cpu.registers.a, 0xFFFF); - assert!(!cpu.registers.get_carry_flag()); - - cpu.registers.a = 0x0000; - cpu.registers.set_carry_flag(true); - cpu.adc(0x01); - assert_eq!(cpu.registers.a, 0x0002); - assert!(!cpu.registers.get_carry_flag()); } } diff --git a/snes-core/src/utils/addressing.rs b/snes-core/src/utils/addressing.rs index 3c38e89..4f2a05e 100644 --- a/snes-core/src/utils/addressing.rs +++ b/snes-core/src/utils/addressing.rs @@ -86,6 +86,11 @@ pub fn stack_relative_indirect_indexed(bus: &Bus, pc_addr: u32, stack_pointer: u absolute_indexed(bus, pc_addr, stack_pointer) + (xy as u32) } +#[derive(Copy, Clone, PartialEq)] +pub enum IndexRegister { + X, Y, +} + #[derive(Copy, Clone)] pub enum AddressingMode { Immediate, @@ -94,18 +99,19 @@ pub enum AddressingMode { DirectPage, DirectPageIndirect, DirectPageIndirectLong, - AbsoluteIndexed, - AbsoluteLongIndexed, - DirectPageIndexed, - DirectPageIndexedIndirect, - DirectPageIndirectIndexed, - DirectPageIndirectLongIndexed, + AbsoluteIndexed(IndexRegister), + AbsoluteLongIndexed(IndexRegister), + DirectPageIndexed(IndexRegister), + DirectPageIndexedIndirect(IndexRegister), + DirectPageIndirectIndexed(IndexRegister), + DirectPageIndirectLongIndexed(IndexRegister), StackRelative, - StackRelativeIndirectIndexed, + StackRelativeIndirectIndexed(IndexRegister), } impl AddressingMode { - pub fn effective_address(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, xy: u16) -> u32 { + pub fn effective_address(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u32 { + use IndexRegister::X as X; match self { Self::Immediate => immediate(pc_addr), Self::Absolute => absolute(bus, pc_addr), @@ -113,24 +119,24 @@ impl AddressingMode { Self::DirectPage => direct_page(bus, pc_addr, direct_page_register), Self::DirectPageIndirect => direct_page_indirect(bus, pc_addr, direct_page_register), Self::DirectPageIndirectLong => direct_page_indirect_long(bus, pc_addr, direct_page_register), - Self::AbsoluteIndexed => absolute_indexed(bus, pc_addr, xy), - Self::AbsoluteLongIndexed => absolute_long_indexed(bus, pc_addr, xy), - Self::DirectPageIndexed => direct_page_indexed(bus, pc_addr, direct_page_register, xy), - Self::DirectPageIndexedIndirect => direct_page_indexed_indirect(bus, pc_addr, direct_page_register, xy), - Self::DirectPageIndirectIndexed => direct_page_indirect_indexed(bus, pc_addr, direct_page_register, xy), - Self::DirectPageIndirectLongIndexed => direct_page_indirect_long_indexed(bus, pc_addr, direct_page_register, xy), + Self::AbsoluteIndexed(idx) => absolute_indexed(bus, pc_addr, if idx == X {x} else {y}), + Self::AbsoluteLongIndexed(idx) => absolute_long_indexed(bus, pc_addr, if idx == X {x} else {y}), + Self::DirectPageIndexed(idx) => direct_page_indexed(bus, pc_addr, direct_page_register, if idx == X {x} else {y}), + Self::DirectPageIndexedIndirect(idx) => direct_page_indexed_indirect(bus, pc_addr, direct_page_register, if idx == X {x} else {y}), + Self::DirectPageIndirectIndexed(idx) => direct_page_indirect_indexed(bus, pc_addr, direct_page_register, if idx == X {x} else {y}), + Self::DirectPageIndirectLongIndexed(idx) => direct_page_indirect_long_indexed(bus, pc_addr, direct_page_register, if idx == X {x} else {y}), Self::StackRelative => stack_relative(bus, pc_addr, stack_pointer), - Self::StackRelativeIndirectIndexed => stack_relative_indirect_indexed(bus, pc_addr, stack_pointer, xy), + Self::StackRelativeIndirectIndexed(idx) => stack_relative_indirect_indexed(bus, pc_addr, stack_pointer, if idx == X {x} else {y}), } } - pub fn value_8bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, xy: u16) -> u8 { - let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, xy); + pub fn value_8bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u8 { + let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y); return bus.read(address); } - pub fn value_16bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, xy: u16) -> u16 { - let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, xy); + pub fn value_16bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u16 { + let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y); return (bus.read(address) as u16) | ((bus.read(address + 1) as u16) << 8); } } @@ -389,7 +395,7 @@ mod addressing_modes_tests { bus.write(pc_addr + 1, 0x20); bus.write(pc_addr + 2, 0x10); bus.write(0x001020, 0xFE); - let val = AddressingMode::Absolute.value_8bit(&bus, pc_addr, 0x00, 0x00, 0x00); + let val = AddressingMode::Absolute.value_8bit(&bus, pc_addr, 0x00, 0x00, 0x00, 0x00); assert_eq!(val, 0xFE); let mut bus = Bus::new(); @@ -398,7 +404,7 @@ mod addressing_modes_tests { bus.write(pc_addr + 2, 0x10); bus.write(0x001020, 0xFF); bus.write(0x001021, 0xEE); - let val = AddressingMode::Absolute.value_16bit(&bus, pc_addr, 0x00, 0x00, 0x00); + let val = AddressingMode::Absolute.value_16bit(&bus, pc_addr, 0x00, 0x00, 0x00, 0x00); assert_eq!(val, 0xEEFF); } } \ No newline at end of file diff --git a/snes-core/src/utils/alu.rs b/snes-core/src/utils/alu.rs index 7ac0834..4e3a985 100644 --- a/snes-core/src/utils/alu.rs +++ b/snes-core/src/utils/alu.rs @@ -32,26 +32,15 @@ pub fn adc16bin(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { let mut is_carry = carry; - let mut result: u8 = 0; - let mut operand_mask: u8 = 0x0F; - let mut result_mask: u8 = 0x00; - let mut carry_check: u8= 0x09; - let mut carry_add: u8 = 0x06; - let mut is_carry_shifts: u8 = 0; - for _ in 0..2 { - result = (result & result_mask) - .wrapping_add(target & operand_mask) - .wrapping_add(value & operand_mask) - .wrapping_add((is_carry as u8) << is_carry_shifts); - is_carry = result > carry_check; - if is_carry { - result = result.wrapping_add(carry_add); - } - operand_mask = operand_mask << 4; - carry_add = carry_add << 4; - carry_check = (carry_check << 4) | 0x0F; - result_mask = (result_mask << 4) | 0x0F; - is_carry_shifts += 4; + let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u8); + is_carry = result > 9; + if is_carry { + result += 6; + } + result = (result & 0xF0) + (target & 0xF0) + (value & 0xF0) + ((is_carry as u8) << 4); + is_carry = result > 0x9F; + if is_carry { + result = result.wrapping_add(0x60); } let is_negative = (result >> 7) == 1; let is_zero = result == 0; @@ -60,26 +49,29 @@ pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) { pub fn adc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) { let mut is_carry = carry; - let mut result: u16 = 0; - let mut operand_mask: u16 = 0x0F; - let mut result_mask: u16 = 0x00; - let mut carry_check: u16 = 0x09; - let mut carry_add: u16 = 0x06; - let mut is_carry_shifts: u16 = 0; - for _ in 0..4 { - result = (result & result_mask) - .wrapping_add(target & operand_mask) - .wrapping_add(value & operand_mask) - .wrapping_add((is_carry as u16) << is_carry_shifts); - is_carry = result > carry_check; - if is_carry { - result = result.wrapping_add(carry_add); - } - operand_mask = operand_mask << 4; - carry_add = carry_add << 4; - carry_check = (carry_check << 4) | 0x0F; - result_mask = (result_mask << 4) | 0x0F; - is_carry_shifts += 4; + let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u16); + is_carry = false; + if result > 9 { + result += 6; + is_carry = true; + } + result = (result & 0xF0) + (target & 0xF0) + (value & 0xF0) + ((is_carry as u16) << 4); + is_carry = false; + if result > 0x9F { + result += 0x60; + is_carry = true; + } + result = (result & 0xF00) + (target & 0xF00) + (value & 0xF00) + ((is_carry as u16) << 8); + is_carry = false; + if result > 0x9FF { + result += 0x600; + is_carry = true; + } + result = (result & 0xF000) + (target & 0xF000) + (target & 0xF000) + ((is_carry as u16) << 12); + is_carry = false; + if result > 0x9FFF { + result += 0x6000; + is_carry = true; } let is_negative = (result >> 15) == 1; let is_zero = result == 0;