From 139a1a2accf06b9c504bd64e06c52ca612f14f75 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 30 Aug 2023 23:04:38 -0400 Subject: [PATCH] Clean up decimal ADC. --- Numeric/Carry.hpp | 31 +++++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 2 ++ Processors/6502/6502.hpp | 1 + .../Implementation/6502Implementation.hpp | 34 ++++++++++++++----- 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 Numeric/Carry.hpp diff --git a/Numeric/Carry.hpp b/Numeric/Carry.hpp new file mode 100644 index 000000000..fd17ed357 --- /dev/null +++ b/Numeric/Carry.hpp @@ -0,0 +1,31 @@ +// +// Carry.hpp +// Clock Signal +// +// Created by Thomas Harte on 30/08/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef Carry_hpp +#define Carry_hpp + +namespace Numeric { + +/// @returns @c true if there was carry out of @c bit when @c source1 and @c source2 were added, producing @c result. +template bool carried_out(IntT source1, IntT source2, IntT result) { + // 0 and 0 => didn't. + // 0 and 1 or 1 and 0 => did if 0. + // 1 and 1 => did. + return IntT(1 << bit) & (source1 | source2) & ((source1 & source2) | ~result); +} + +/// @returns @c true if there was carry into @c bit when @c source1 and @c source2 were added, producing @c result. +template bool carried_in(IntT source1, IntT source2, IntT result) { + // 0 and 0 or 1 and 1 => did if 1 + // 0 and 1 or 1 and 0 => did if 0 + return IntT(1 << bit) & (source1 ^ source2 ^ result); +} + +} + +#endif /* Carry_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 2803a2130..40a9812c3 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1101,6 +1101,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 4281572E2AA0334300E16AA1 /* Carry.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Carry.hpp; sourceTree = ""; }; 428168372A16C25C008ECD27 /* LineLayout.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = LineLayout.hpp; sourceTree = ""; }; 428168392A37AFB4008ECD27 /* DispatcherTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatcherTests.mm; sourceTree = ""; }; 42AD552E2A0C4D5000ACE410 /* 68000.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = 68000.hpp; sourceTree = ""; }; @@ -3313,6 +3314,7 @@ 4B66E1A8297719270057ED0F /* NumericCoder.hpp */, 4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */, 4BFEA2F12682A90200EBF94C /* Sizes.hpp */, + 4281572E2AA0334300E16AA1 /* Carry.hpp */, ); name = Numeric; path = ../../Numeric; diff --git a/Processors/6502/6502.hpp b/Processors/6502/6502.hpp index 15edc54cd..56f6d01bc 100644 --- a/Processors/6502/6502.hpp +++ b/Processors/6502/6502.hpp @@ -15,6 +15,7 @@ #include "../6502Esque/6502Esque.hpp" #include "../6502Esque/Implementation/LazyFlags.hpp" +#include "../../Numeric/Carry.hpp" #include "../../Numeric/RegisterSizes.hpp" #include "../../ClockReceiver/ClockReceiver.hpp" diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index 80b0d7a00..e274addf7 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -317,18 +317,34 @@ template void Proces case OperationADC: if(flags_.decimal && has_decimal_mode(personality)) { - const uint16_t decimalResult = uint16_t(a_) + uint16_t(operand_) + uint16_t(flags_.carry); + uint8_t result = a_ + operand_ + flags_.carry; + flags_.zero_result = result; + flags_.carry = Numeric::carried_out<7>(a_, operand_, result); - uint8_t low_nibble = (a_ & 0xf) + (operand_ & 0xf) + flags_.carry; - if(low_nibble >= 0xa) low_nibble = ((low_nibble + 0x6) & 0xf) + 0x10; - uint16_t result = uint16_t(a_ & 0xf0) + uint16_t(operand_ & 0xf0) + uint16_t(low_nibble); + // The following effects the rule that only a single bit of carry can flow from the + // bottom nibble to the top: if that carry already happened, fix up the bottom without + // permitting another; otherwise if a fix up is required anyway due to the low digit nibble + // being out of range, permit the carry to happen (and check whether carry then left bit 7). + if(Numeric::carried_in<4>(a_, operand_, result)) { + result = (result & 0xf0) | ((result + 0x06) & 0x0f); + } else if((result & 0xf) > 0x9) { + flags_.carry |= result >= 0x100 - 0x6; + result += 0x06; + } + + // 6502 quirk: N and V are set before the full result is computed but after the low nibble + // has been corrected. flags_.negative_result = uint8_t(result); - flags_.overflow = (( (result^a_)&(result^operand_) )&0x80) >> 1; - if(result >= 0xa0) result += 0x60; + flags_.overflow = (( (result ^ a_) & (result ^ operand_) ) & 0x80) >> 1; - flags_.carry = (result >> 8) ? 1 : 0; - a_ = uint8_t(result); - flags_.zero_result = uint8_t(decimalResult); + if(Numeric::carried_out<7>(a_, operand_, result)) { + result += 0x60; + } else if (result >= 0xa0) { + flags_.carry = 1; // There'll now definitely be carry. + result += 0x60; + } + + a_ = result; if(is_65c02(personality)) { flags_.set_nz(a_);