diff --git a/InstructionSets/ARM/Executor.hpp b/InstructionSets/ARM/Executor.hpp index 6bc8b1d03..9371429d4 100644 --- a/InstructionSets/ARM/Executor.hpp +++ b/InstructionSets/ARM/Executor.hpp @@ -152,8 +152,9 @@ struct Executor { if constexpr (flags.set_condition_codes()) { // "For a subtraction, including the comparison instruction CMP, C is set to 0 if // the subtraction produced a borrow (that is, an unsigned underflow), and to 1 otherwise." - registers_.set_c(!Numeric::carried_out(lhs, rhs, conditions)); - registers_.set_v(Numeric::overflow(lhs, rhs, conditions)); + using NumOp = Numeric::Operation; + registers_.set_c(!Numeric::carried_out(lhs, rhs, conditions)); + registers_.set_v(Numeric::overflow(lhs, rhs, conditions)); } if constexpr (!is_comparison(flags.operation())) { @@ -185,8 +186,9 @@ struct Executor { } if constexpr (flags.set_condition_codes()) { - registers_.set_c(Numeric::carried_out(operand1, operand2, conditions)); - registers_.set_v(Numeric::overflow(operand1, operand2, conditions)); + using NumOp = Numeric::Operation; + registers_.set_c(Numeric::carried_out(operand1, operand2, conditions)); + registers_.set_v(Numeric::overflow(operand1, operand2, conditions)); } if constexpr (!is_comparison(flags.operation())) { diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index cf597ff54..2ec702b0e 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -28,10 +28,12 @@ namespace Primitive { /// Performs an add or subtract (as per @c is_add) between @c source and @c destination, /// updating @c status. @c is_extend indicates whether this is an extend operation (e.g. ADDX) /// or a plain one (e.g. ADD). -template +template static void add_sub(const IntT source, IntT &destination, Status &status) { static_assert(!std::numeric_limits::is_signed); + static_assert(operation == Numeric::Operation::Add || operation == Numeric::Operation::Subtract); + constexpr bool is_add = operation == Numeric::Operation::Add; IntT result = is_add ? destination + source : destination - source; @@ -59,7 +61,7 @@ static void add_sub(const IntT source, IntT &destination, Status &status) { status.zero_result = Status::FlagT(result); } status.set_negative(result); - status.overflow_flag = Numeric::overflow(destination, source, result); + status.overflow_flag = Numeric::overflow(destination, source, result); destination = result; } @@ -121,7 +123,7 @@ void compare(const IntT source, const IntT destination, Status &status) { const IntT result = destination - source; status.carry_flag = result > destination; status.set_neg_zero(result); - status.overflow_flag = Numeric::overflow(destination, source, result); + status.overflow_flag = Numeric::overflow(destination, source, result); } /// @returns the name of the bit to be used as a mask for BCLR, BCHG, BSET or BTST for @@ -279,7 +281,7 @@ template void negative(IntT &source, Status &sta } status.extend_flag = status.carry_flag = result; // i.e. any value other than 0 will result in carry. status.set_negative(result); - status.overflow_flag = Numeric::overflow(IntT(0), source, result); + status.overflow_flag = Numeric::overflow(IntT(0), source, result); source = result; } @@ -519,6 +521,7 @@ template < Status &status, FlowController &flow_controller ) { + using NumOp = Numeric::Operation; switch((operation != Operation::Undefined) ? operation : instruction.operation) { /* ABCD adds the lowest bytes from the source and destination using BCD arithmetic, @@ -551,20 +554,20 @@ template < // ADD and ADDA add two quantities, the latter sign extending and without setting any flags; // ADDQ and SUBQ act as ADD and SUB, but taking the second argument from the instruction code. - case Operation::ADDb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::SUBb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::ADDXb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::SUBXb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::ADDb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::SUBb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::ADDXb: Primitive::add_sub(src.b, dest.b, status); break; + case Operation::SUBXb: Primitive::add_sub(src.b, dest.b, status); break; - case Operation::ADDw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::SUBw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::ADDXw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::SUBXw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::ADDw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::SUBw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::ADDXw: Primitive::add_sub(src.w, dest.w, status); break; + case Operation::SUBXw: Primitive::add_sub(src.w, dest.w, status); break; - case Operation::ADDl: Primitive::add_sub(src.l, dest.l, status); break; - case Operation::SUBl: Primitive::add_sub(src.l, dest.l, status); break; - case Operation::ADDXl: Primitive::add_sub(src.l, dest.l, status); break; - case Operation::SUBXl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::ADDl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::SUBl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::ADDXl: Primitive::add_sub(src.l, dest.l, status); break; + case Operation::SUBXl: Primitive::add_sub(src.l, dest.l, status); break; case Operation::ADDAw: dest.l += u_extend16(src.w); break; case Operation::ADDAl: dest.l += src.l; break; diff --git a/InstructionSets/x86/Implementation/Arithmetic.hpp b/InstructionSets/x86/Implementation/Arithmetic.hpp index 4c09dc5c4..ac7cafaa0 100644 --- a/InstructionSets/x86/Implementation/Arithmetic.hpp +++ b/InstructionSets/x86/Implementation/Arithmetic.hpp @@ -30,12 +30,13 @@ void add( */ const IntT result = destination + source + (with_carry ? context.flags.template carry_bit() : 0); + using NumOp = Numeric::Operation; context.flags.template set_from( - Numeric::carried_out() - 1>(destination, source, result)); + Numeric::carried_out() - 1>(destination, source, result)); context.flags.template set_from( Numeric::carried_in<4>(destination, source, result)); context.flags.template set_from( - Numeric::overflow(destination, source, result)); + Numeric::overflow(destination, source, result)); context.flags.template set_from(result); @@ -56,12 +57,13 @@ void sub( */ const IntT result = destination - source - (with_borrow ? context.flags.template carry_bit() : 0); + using NumOp = Numeric::Operation; context.flags.template set_from( - Numeric::carried_out() - 1>(destination, source, result)); + Numeric::carried_out() - 1>(destination, source, result)); context.flags.template set_from( Numeric::carried_in<4>(destination, source, result)); context.flags.template set_from( - Numeric::overflow(destination, source, result)); + Numeric::overflow(destination, source, result)); context.flags.template set_from(result); diff --git a/Numeric/CRC.hpp b/Numeric/CRC.hpp index 76fa186a9..80561b1c7 100644 --- a/Numeric/CRC.hpp +++ b/Numeric/CRC.hpp @@ -8,6 +8,9 @@ #pragma once +#include "Carry.hpp" + +#include #include #include @@ -26,30 +29,41 @@ constexpr uint8_t reverse_byte(uint8_t byte) { } /*! Provides a class capable of generating a CRC from source data. */ -template +template < + typename IntType, + IntType polynomial, + IntType reset_value, + IntType output_xor, + bool reflect_input, + bool reflect_output +> class Generator { public: /*! Instantiates a CRC16 that will compute the CRC16 specified by the supplied @c polynomial and @c reset_value. */ - constexpr Generator(IntType polynomial) noexcept: value_(reset_value) { - const IntType top_bit = IntType(~(IntType(~0) >> 1)); - for(int c = 0; c < 256; c++) { - IntType shift_value = IntType(c << multibyte_shift); - for(int b = 0; b < 8; b++) { - IntType exclusive_or = (shift_value&top_bit) ? polynomial : 0; - shift_value = IntType(shift_value << 1) ^ exclusive_or; - } - xor_table[c] = shift_value; - } - } + constexpr Generator() noexcept: value_(reset_value) {} /// Resets the CRC to the reset value. void reset() { value_ = reset_value; } /// Updates the CRC to include @c byte. void add(uint8_t byte) { + static constexpr std::array xor_table = [] { + std::array table{}; + constexpr IntType top_bit = Numeric::top_bit(); + for(size_t c = 0; c < 256; c++) { + IntType shift_value = IntType(c << multibyte_shift); + for(int b = 0; b < 8; b++) { + const IntType exclusive_or = (shift_value & top_bit) ? polynomial : 0; + shift_value = IntType(shift_value << 1) ^ exclusive_or; + } + table[c] = shift_value; + } + return table; + } (); + if constexpr (reflect_input) byte = reverse_byte(byte); value_ = IntType((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]); } @@ -100,7 +114,6 @@ public: private: static constexpr int multibyte_shift = (sizeof(IntType) * 8) - 8; - IntType xor_table[256]; IntType value_; }; @@ -108,15 +121,11 @@ private: Provides a generator of 16-bit CCITT CRCs, which amongst other uses are those used by the FM and MFM disk encodings. */ -struct CCITT: public Generator { - CCITT(): Generator(0x1021) {} -}; +using CCITT = Generator; /*! Provides a generator of "standard 32-bit" CRCs. */ -struct CRC32: public Generator { - CRC32(): Generator(0x04c11db7) {} -}; +using CRC32 = Generator; } diff --git a/Numeric/Carry.hpp b/Numeric/Carry.hpp index 0cae0061f..95614ec34 100644 --- a/Numeric/Carry.hpp +++ b/Numeric/Carry.hpp @@ -12,11 +12,17 @@ namespace Numeric { +enum class Operation { + Add, + Subtract, +}; + /// @returns @c true if from @c bit there was: /// • carry after calculating @c lhs + @c rhs if @c is_add is true; or /// • borrow after calculating @c lhs - @c rhs if @c is_add is false; /// producing @c result. -template bool carried_out(IntT lhs, IntT rhs, IntT result) { +template +constexpr bool carried_out(const IntT lhs, const IntT rhs, const IntT result) { // Additive: // // 0 and 0 => didn't. @@ -28,11 +34,10 @@ template bool carried_out(IntT lhs, IntT r // 1 and 0 => didn't // 1 and 1 or 0 and 0 => did if 1. // 0 and 1 => did. - if constexpr (!is_add) { - rhs = ~rhs; - } - const bool carry = IntT(1 << bit) & (lhs | rhs) & ((lhs & rhs) | ~result); - if constexpr (!is_add) { + static_assert(operation == Operation::Add || operation == Operation::Subtract); + const auto adj_rhs = (operation == Operation::Subtract) ? ~rhs : rhs; + const bool carry = IntT(1 << bit) & (lhs | adj_rhs) & ((lhs & adj_rhs) | ~result); + if constexpr (operation == Operation::Subtract) { return !carry; } else { return carry; @@ -43,21 +48,24 @@ template bool carried_out(IntT lhs, IntT r /// • @c lhs + @c rhs; or /// • @c lhs - @c rhs; /// producing @c result. -template bool carried_in(IntT lhs, IntT rhs, IntT result) { +template +constexpr bool carried_in(const IntT lhs, const IntT rhs, const 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) & (lhs ^ rhs ^ result); } /// @returns An int of type @c IntT with only the most-significant bit set. -template constexpr IntT top_bit() { +template +constexpr IntT top_bit() { static_assert(!std::numeric_limits::is_signed); constexpr IntT max = std::numeric_limits::max(); return max - (max >> 1); } /// @returns The number of bits in @c IntT. -template constexpr int bit_size() { +template +constexpr int bit_size() { return sizeof(IntT) * 8; } @@ -65,12 +73,13 @@ template constexpr int bit_size() { /// • @c lhs + @c rhs (if @c is_add is true); or /// • @c lhs - @c rhs (if @c is_add is false) /// and the result was @c result. All other bits will be clear. -template -IntT overflow(IntT lhs, IntT rhs, IntT result) { +template +constexpr IntT overflow(const IntT lhs, const IntT rhs, const IntT result) { const IntT output_changed = result ^ lhs; const IntT input_differed = lhs ^ rhs; - if constexpr (is_add) { + static_assert(operation == Operation::Add || operation == Operation::Subtract); + if constexpr (operation == Operation::Add) { return top_bit() & output_changed & ~input_differed; } else { return top_bit() & output_changed & input_differed; diff --git a/Processors/6502/Implementation/6502Implementation.hpp b/Processors/6502/Implementation/6502Implementation.hpp index cb2fc512b..79b7d953b 100644 --- a/Processors/6502/Implementation/6502Implementation.hpp +++ b/Processors/6502/Implementation/6502Implementation.hpp @@ -368,7 +368,7 @@ void Processor::run_for(const Cycles cycles) { // All flags are set based only on the decimal result. flags_.zero_result = result; - flags_.carry = Numeric::carried_out(a_, operand_, result); + flags_.carry = Numeric::carried_out(a_, operand_, result); flags_.negative_result = result; flags_.overflow = (( (result ^ a_) & (result ^ operand_) ) & 0x80) >> 1; @@ -418,7 +418,7 @@ void Processor::run_for(const Cycles cycles) { if(flags_.decimal && has_decimal_mode(personality)) { uint8_t result = a_ + operand_ + flags_.carry; flags_.zero_result = result; - flags_.carry = Numeric::carried_out(a_, operand_, result); + flags_.carry = Numeric::carried_out(a_, operand_, result); // General ADC logic: // diff --git a/Storage/Tape/Parsers/Acorn.cpp b/Storage/Tape/Parsers/Acorn.cpp index 11c3c8f62..8b2fb0f61 100644 --- a/Storage/Tape/Parsers/Acorn.cpp +++ b/Storage/Tape/Parsers/Acorn.cpp @@ -14,7 +14,7 @@ namespace { constexpr int PLLClockRate = 1920000; } -Parser::Parser(): crc_(0x1021) { +Parser::Parser() { shifter_.set_delegate(this); } diff --git a/Storage/Tape/Parsers/Acorn.hpp b/Storage/Tape/Parsers/Acorn.hpp index 810c552c8..0d731ca51 100644 --- a/Storage/Tape/Parsers/Acorn.hpp +++ b/Storage/Tape/Parsers/Acorn.hpp @@ -58,7 +58,7 @@ private: void process_pulse(const Storage::Tape::Pulse &) override; bool did_update_shifter(int new_value, int length); - CRC::Generator crc_; + CRC::Generator crc_; Shifter shifter_; };