From 58396f0c5280c21114f6d369e34a8c14ee71671b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 10 Oct 2022 22:21:13 -0400 Subject: [PATCH 01/35] Perform a prima facie conversion of ADD/SUB[/X] from macros to templates. --- .../Implementation/PerformImplementation.hpp | 259 +++++++----------- 1 file changed, 97 insertions(+), 162 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 66af70912..89fd1dfb8 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -20,14 +20,83 @@ namespace M68k { #define u_extend16(x) uint32_t(int16_t(x)) #define s_extend16(x) int32_t(int16_t(x)) +namespace Primitive { + +/// Provides a type alias, @c type, which is an unsigned int bigger than @c IntT. +template struct BiggerInt {}; +template <> struct BiggerInt { + using type = uint16_t; +}; +template <> struct BiggerInt { + using type = uint32_t; +}; +template <> struct BiggerInt { + using type = uint64_t; +}; + +/// @returns An int of type @c IntT with only the most-significant bit set. +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_count() { + return sizeof(IntT) * 8; +} + +/// @returns An int with the top bit indicating whether overflow occurred when @c source and @c destination +/// were either added (if @c is_add is true) or subtracted (if @c is_add is false) and the result was @c result. +/// All other bits will be clear. +template +static Status::FlagT overflow(IntT source, IntT destination, IntT result) { + const IntT output_changed = result ^ destination; + const IntT input_differed = source ^ destination; + + if constexpr (is_add) { + return top_bit() & output_changed & ~input_differed; + } else { + return top_bit() & output_changed & input_differed; + } +} + +/// 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 +static void add_sub(IntT source, IntT &destination, Status &status) { + static_assert(!std::numeric_limits::is_signed); + using BigIntT = typename BiggerInt::type; + + const BigIntT extend = (is_extend && status.extend_flag) ? 1 : 0; + const BigIntT result = is_add ? + (BigIntT(destination) + BigIntT(source) + extend) : + (BigIntT(destination) - BigIntT(source) - extend); + + // Extend operations can reset the zero flag only; non-extend operations + // can either set it or reset it. Which in the reverse-logic world of + // zero_result means ORing or storing. + if constexpr (is_extend) { + status.zero_result |= IntT(result); + } else { + status.zero_result = IntT(result); + } + status.extend_flag = + status.carry_flag = Status::FlagT(result >> bit_count()); + status.negative_flag = Status::FlagT(result & top_bit()); + status.overflow_flag = overflow(source, destination, IntT(result)); + destination = IntT(result); +} + +} + template < Model model, typename FlowController, Operation operation = Operation::Undefined > void perform(Preinstruction instruction, CPU::SlicedInt32 &src, CPU::SlicedInt32 &dest, Status &status, FlowController &flow_controller) { -#define sub_overflow() ((result ^ destination) & (destination ^ source)) -#define add_overflow() ((result ^ destination) & ~(destination ^ source)) switch((operation != Operation::Undefined) ? operation : instruction.operation) { /* ABCD adds the lowest bytes from the source and destination using BCD arithmetic, @@ -58,159 +127,27 @@ template < dest.b = uint8_t(result); } break; -#define addop(a, b, x) a + b + (x ? 1 : 0) -#define subop(a, b, x) a - b - (x ? 1 : 0) -#define z_set(a, b) a = b -#define z_or(a, b) a |= b - -#define addsubb(a, b, op, overflow, x, zero_op) \ - const int source = a; \ - const int destination = b; \ - const auto result = op(destination, source, x); \ - \ - b = uint8_t(result); \ - zero_op(status.zero_result, b); \ - status.extend_flag = status.carry_flag = uint_fast32_t(result & ~0xff); \ - status.negative_flag = result & 0x80; \ - status.overflow_flag = overflow() & 0x80; - -#define addsubw(a, b, op, overflow, x, zero_op) \ - const int source = a; \ - const int destination = b; \ - const auto result = op(destination, source, x); \ - \ - b = uint16_t(result); \ - zero_op(status.zero_result, b); \ - status.extend_flag = status.carry_flag = uint_fast32_t(result & ~0xffff); \ - status.negative_flag = result & 0x8000; \ - status.overflow_flag = overflow() & 0x8000; - -#define addsubl(a, b, op, overflow, x, zero_op) \ - const uint64_t source = a; \ - const uint64_t destination = b; \ - const auto result = op(destination, source, x); \ - \ - b = uint32_t(result); \ - zero_op(status.zero_result, b); \ - status.extend_flag = status.carry_flag = uint_fast32_t(result >> 32); \ - status.negative_flag = result & 0x80000000; \ - status.overflow_flag = overflow() & 0x80000000; - -#define addb(a, b, x, z) addsubb(a, b, addop, add_overflow, x, z) -#define subb(a, b, x, z) addsubb(a, b, subop, sub_overflow, x, z) -#define addw(a, b, x, z) addsubw(a, b, addop, add_overflow, x, z) -#define subw(a, b, x, z) addsubw(a, b, subop, sub_overflow, x, z) -#define addl(a, b, x, z) addsubl(a, b, addop, add_overflow, x, z) -#define subl(a, b, x, z) addsubl(a, b, subop, sub_overflow, x, z) - -#define no_extend(op, a, b) op(a, b, 0, z_set) -#define extend(op, a, b) op(a, b, status.extend_flag, z_or) - // 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: { - no_extend( addb, - src.b, - dest.b); - } 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::ADDXb: { - extend( addb, - src.b, - dest.b); - } 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: { - no_extend( addw, - src.w, - dest.w); - } 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::ADDXw: { - extend( addw, - src.w, - dest.w); - } break; - - case Operation::ADDl: { - no_extend( addl, - src.l, - dest.l); - } break; - - case Operation::ADDXl: { - extend( addl, - src.l, - dest.l); - } break; - - case Operation::SUBb: { - no_extend( subb, - src.b, - dest.b); - } break; - - case Operation::SUBXb: { - extend( subb, - src.b, - dest.b); - } break; - - case Operation::SUBw: { - no_extend( subw, - src.w, - dest.w); - } break; - - case Operation::SUBXw: { - extend( subw, - src.w, - dest.w); - } break; - - case Operation::SUBl: { - no_extend( subl, - src.l, - dest.l); - } break; - - case Operation::SUBXl: { - extend( subl, - src.l, - dest.l); - } break; - -#undef addl -#undef addw -#undef addb -#undef subl -#undef subw -#undef subb -#undef addsubl -#undef addsubw -#undef addsubb -#undef z_set -#undef z_or -#undef no_extend -#undef extend -#undef addop -#undef subop - - case Operation::ADDAw: - dest.l += u_extend16(src.w); - break; - - case Operation::ADDAl: - dest.l += src.l; - break; - - case Operation::SUBAw: - dest.l -= u_extend16(src.w); - break; - - case Operation::SUBAl: - dest.l -= src.l; - break; + case Operation::ADDAw: dest.l += u_extend16(src.w); break; + case Operation::ADDAl: dest.l += src.l; break; + case Operation::SUBAw: dest.l -= u_extend16(src.w); break; + case Operation::SUBAl: dest.l -= src.l; break; #define get_mask() \ const uint32_t mask_size = (instruction.mode<1>() == AddressingMode::DataRegisterDirect) ? 31 : 7; \ @@ -331,7 +268,7 @@ template < status.zero_result = result & 0xff; status.carry_flag = Status::FlagT(result & ~0xff); status.negative_flag = result & 0x80; - status.overflow_flag = sub_overflow() & 0x80; + status.overflow_flag = Primitive::overflow(source, destination, uint8_t(result)); } break; case Operation::CMPw: { @@ -342,7 +279,7 @@ template < status.zero_result = result & 0xffff; status.carry_flag = Status::FlagT(result & ~0xffff); status.negative_flag = result & 0x8000; - status.overflow_flag = sub_overflow() & 0x8000; + status.overflow_flag = Primitive::overflow(source, destination, uint16_t(result)); } break; case Operation::CMPAw: { @@ -353,7 +290,7 @@ template < status.zero_result = uint32_t(result); status.carry_flag = result >> 32; status.negative_flag = result & 0x80000000; - status.overflow_flag = sub_overflow() & 0x80000000; + status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); } break; // TODO: is there any benefit to keeping both of these? @@ -366,7 +303,7 @@ template < status.zero_result = uint32_t(result); status.carry_flag = result >> 32; status.negative_flag = result & 0x80000000; - status.overflow_flag = sub_overflow() & 0x80000000; + status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); } break; // JMP: copies EA(0) to the program counter. @@ -604,7 +541,7 @@ template < status.zero_result = result & 0xff; status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xff); status.negative_flag = result & 0x80; - status.overflow_flag = sub_overflow() & 0x80; + status.overflow_flag = Primitive::overflow(uint8_t(source), uint8_t(destination), uint8_t(result)); } break; case Operation::NEGw: { @@ -616,7 +553,7 @@ template < status.zero_result = result & 0xffff; status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xffff); status.negative_flag = result & 0x8000; - status.overflow_flag = sub_overflow() & 0x8000; + status.overflow_flag = Primitive::overflow(uint16_t(source), uint16_t(destination), uint16_t(result)); } break; case Operation::NEGl: { @@ -628,7 +565,7 @@ template < status.zero_result = uint_fast32_t(result); status.extend_flag = status.carry_flag = result >> 32; status.negative_flag = result & 0x80000000; - status.overflow_flag = sub_overflow() & 0x80000000; + status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); } break; /* @@ -643,7 +580,7 @@ template < status.zero_result |= result & 0xff; status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xff); status.negative_flag = result & 0x80; - status.overflow_flag = sub_overflow() & 0x80; + status.overflow_flag = Primitive::overflow(uint8_t(source), uint8_t(destination), uint8_t(result)); } break; case Operation::NEGXw: { @@ -655,7 +592,7 @@ template < status.zero_result |= result & 0xffff; status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xffff); status.negative_flag = result & 0x8000; - status.overflow_flag = sub_overflow() & 0x8000; + status.overflow_flag = Primitive::overflow(uint16_t(source), uint16_t(destination), uint16_t(result)); } break; case Operation::NEGXl: { @@ -667,7 +604,7 @@ template < status.zero_result |= uint_fast32_t(result); status.extend_flag = status.carry_flag = result >> 32; status.negative_flag = result & 0x80000000; - status.overflow_flag = sub_overflow() & 0x80000000; + status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); } break; /* @@ -1153,8 +1090,6 @@ template < assert(false); break; } -#undef sub_overflow -#undef add_overflow #undef u_extend16 #undef s_extend16 From ec5d57fefe9328397cb45c465eafd69602cf5b34 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 10:33:28 -0400 Subject: [PATCH 02/35] Eliminate 64-bit work. --- .../Implementation/PerformImplementation.hpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 89fd1dfb8..a214e31ee 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -67,26 +67,25 @@ static Status::FlagT overflow(IntT source, IntT destination, IntT result) { template static void add_sub(IntT source, IntT &destination, Status &status) { static_assert(!std::numeric_limits::is_signed); - using BigIntT = typename BiggerInt::type; - const BigIntT extend = (is_extend && status.extend_flag) ? 1 : 0; - const BigIntT result = is_add ? - (BigIntT(destination) + BigIntT(source) + extend) : - (BigIntT(destination) - BigIntT(source) - extend); + const IntT extend = (is_extend && status.extend_flag) ? 1 : 0; + const IntT result = is_add ? + (destination + source + extend) : + (destination - source - extend); // Extend operations can reset the zero flag only; non-extend operations // can either set it or reset it. Which in the reverse-logic world of // zero_result means ORing or storing. if constexpr (is_extend) { - status.zero_result |= IntT(result); + status.zero_result |= Status::FlagT(result); } else { - status.zero_result = IntT(result); + status.zero_result = Status::FlagT(result); } status.extend_flag = - status.carry_flag = Status::FlagT(result >> bit_count()); + status.carry_flag = is_add ? result < destination : result > destination; status.negative_flag = Status::FlagT(result & top_bit()); - status.overflow_flag = overflow(source, destination, IntT(result)); - destination = IntT(result); + status.overflow_flag = overflow(source, destination, result); + destination = result; } } From 77bc60bf86b8338093aa03dc8c8ca3260a9aec49 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 10:47:55 -0400 Subject: [PATCH 03/35] Consolidate BCLR, BCHG and BSET into a macro. --- .../Implementation/PerformImplementation.hpp | 59 ++++++++----------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index a214e31ee..2de88e6ee 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -88,6 +88,24 @@ static void add_sub(IntT source, IntT &destination, Status &status) { destination = result; } +inline uint32_t mask_bit(const Preinstruction &instruction, uint32_t source) { + return source & (instruction.mode<1>() == AddressingMode::DataRegisterDirect ? 31 : 7); +} + +template +void bit_manipulate(const Preinstruction &instruction, uint32_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { + static_assert(operation == Operation::BCLR || operation == Operation::BCHG || operation == Operation::BSET); + + const auto bit = mask_bit(instruction, source); + status.zero_result = destination & (1 << bit); + switch(operation) { + case Operation::BCLR: destination &= ~(1 << bit); break; + case Operation::BCHG: destination ^= (1 << bit); break; + case Operation::BSET: destination |= (1 << bit); break; + } + flow_controller.did_bit_op(int(bit)); +} + } template < @@ -148,42 +166,13 @@ template < case Operation::SUBAw: dest.l -= u_extend16(src.w); break; case Operation::SUBAl: dest.l -= src.l; break; -#define get_mask() \ - const uint32_t mask_size = (instruction.mode<1>() == AddressingMode::DataRegisterDirect) ? 31 : 7; \ - const uint32_t bit_position = src.l & mask_size; \ - const uint32_t bit_mask = 1 << bit_position - // BTST/BCLR/etc: modulo for the mask depends on whether memory or a data register is the target. - case Operation::BTST: { - get_mask(); - status.zero_result = dest.l & bit_mask; - } break; - - case Operation::BCLR: { - get_mask(); - - status.zero_result = dest.l & bit_mask; - dest.l &= ~bit_mask; - flow_controller.did_bit_op(int(bit_position)); - } break; - - case Operation::BCHG: { - get_mask(); - - status.zero_result = dest.l & bit_mask; - dest.l ^= bit_mask; - flow_controller.did_bit_op(int(bit_position)); - } break; - - case Operation::BSET: { - get_mask(); - - status.zero_result = dest.l & bit_mask; - dest.l |= bit_mask; - flow_controller.did_bit_op(int(bit_position)); - } break; - -#undef get_mask + case Operation::BTST: + status.zero_result = dest.l & (1 << Primitive::mask_bit(instruction, src.l)); + break; + case Operation::BCLR: Primitive::bit_manipulate(instruction, src.l, dest.l, status, flow_controller); break; + case Operation::BCHG: Primitive::bit_manipulate(instruction, src.l, dest.l, status, flow_controller); break; + case Operation::BSET: Primitive::bit_manipulate(instruction, src.l, dest.l, status, flow_controller); break; case Operation::Bccb: flow_controller.template complete_bcc( From f3f23f90a3432ea6847515b36df0a62ea7f6f703 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 11:22:34 -0400 Subject: [PATCH 04/35] Consolidate repetition in CLR. --- .../Implementation/PerformImplementation.hpp | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 2de88e6ee..fd9b968be 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -88,10 +88,14 @@ static void add_sub(IntT source, IntT &destination, Status &status) { destination = result; } +/// @returns the name of the bit to be used as a mask for BCLR, BCHG, BSET or BTST for +/// @c instruction given @c source. inline uint32_t mask_bit(const Preinstruction &instruction, uint32_t source) { return source & (instruction.mode<1>() == AddressingMode::DataRegisterDirect ? 31 : 7); } +/// Performs a BCLR, BCHG or BSET as specified by @c operation and described by @c instruction, @c source and @c destination, updating @c destination and @c status. +/// Also makes an appropriate notification to the @c flow_controller. template void bit_manipulate(const Preinstruction &instruction, uint32_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { static_assert(operation == Operation::BCLR || operation == Operation::BCHG || operation == Operation::BSET); @@ -106,6 +110,12 @@ void bit_manipulate(const Preinstruction &instruction, uint32_t source, uint32_t flow_controller.did_bit_op(int(bit)); } +/// Sets @c destination to 0, clears the overflow, carry and negative flags, sets the zero flag. +template void clear(IntT &destination, Status &status) { + destination = 0; + status.negative_flag = status.overflow_flag = status.carry_flag = status.zero_result = 0; +} + } template < @@ -229,20 +239,9 @@ template < CLRs: store 0 to the destination, set the zero flag, and clear negative, overflow and carry. */ - case Operation::CLRb: - src.b = 0; - status.negative_flag = status.overflow_flag = status.carry_flag = status.zero_result = 0; - break; - - case Operation::CLRw: - src.w = 0; - status.negative_flag = status.overflow_flag = status.carry_flag = status.zero_result = 0; - break; - - case Operation::CLRl: - src.l = 0; - status.negative_flag = status.overflow_flag = status.carry_flag = status.zero_result = 0; - break; + case Operation::CLRb: Primitive::clear(src.b, status); break; + case Operation::CLRw: Primitive::clear(src.w, status); break; + case Operation::CLRl: Primitive::clear(src.l, status); break; /* CMP.b, CMP.l and CMP.w: sets the condition flags (other than extend) based on a subtraction From 8305a3b46a9410a27efb6d6f5f3a586ef9d9c0b1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 12:57:02 -0400 Subject: [PATCH 05/35] Consolidate compare logic. --- .../Implementation/PerformImplementation.hpp | 58 +++++-------------- 1 file changed, 14 insertions(+), 44 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index fd9b968be..2577bedfb 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -88,6 +88,16 @@ static void add_sub(IntT source, IntT &destination, Status &status) { destination = result; } +/// Performs a compare of @c source to @c destination, setting zero, carry, negative and overflow flags. +template +static void compare(IntT source, IntT destination, Status &status) { + const IntT result = destination - source; + status.zero_result = result; + status.carry_flag = result > destination; + status.negative_flag = result & top_bit(); + status.overflow_flag = Primitive::overflow(source, destination, result); +} + /// @returns the name of the bit to be used as a mask for BCLR, BCHG, BSET or BTST for /// @c instruction given @c source. inline uint32_t mask_bit(const Preinstruction &instruction, uint32_t source) { @@ -247,51 +257,11 @@ template < CMP.b, CMP.l and CMP.w: sets the condition flags (other than extend) based on a subtraction of the source from the destination; the result of the subtraction is not stored. */ - case Operation::CMPb: { - const uint8_t source = src.b; - const uint8_t destination = dest.b; - const int result = destination - source; - - status.zero_result = result & 0xff; - status.carry_flag = Status::FlagT(result & ~0xff); - status.negative_flag = result & 0x80; - status.overflow_flag = Primitive::overflow(source, destination, uint8_t(result)); - } break; - - case Operation::CMPw: { - const uint16_t source = src.w; - const uint16_t destination = dest.w; - const int result = destination - source; - - status.zero_result = result & 0xffff; - status.carry_flag = Status::FlagT(result & ~0xffff); - status.negative_flag = result & 0x8000; - status.overflow_flag = Primitive::overflow(source, destination, uint16_t(result)); - } break; - - case Operation::CMPAw: { - const auto source = uint64_t(u_extend16(src.w)); - const uint64_t destination = dest.l; - const auto result = destination - source; - - status.zero_result = uint32_t(result); - status.carry_flag = result >> 32; - status.negative_flag = result & 0x80000000; - status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); - } break; - - // TODO: is there any benefit to keeping both of these? + case Operation::CMPb: Primitive::compare(src.b, dest.b, status); break; + case Operation::CMPw: Primitive::compare(src.w, dest.w, status); break; + case Operation::CMPAw: Primitive::compare(u_extend16(src.w), dest.l, status); break; case Operation::CMPAl: - case Operation::CMPl: { - const auto source = uint64_t(src.l); - const auto destination = uint64_t(dest.l); - const auto result = destination - source; - - status.zero_result = uint32_t(result); - status.carry_flag = result >> 32; - status.negative_flag = result & 0x80000000; - status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); - } break; + case Operation::CMPl: Primitive::compare(src.l, dest.l, status); break; // JMP: copies EA(0) to the program counter. case Operation::JMP: From b2f005da1b8283fd9726cb48fa1d4d0dffd0d218 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 15:53:11 -0400 Subject: [PATCH 06/35] Collapse SR/CCR bitwise operations into a template. --- .../Implementation/PerformImplementation.hpp | 80 +++++++++++-------- 1 file changed, 47 insertions(+), 33 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 2577bedfb..46e48e743 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -108,7 +108,10 @@ inline uint32_t mask_bit(const Preinstruction &instruction, uint32_t source) { /// Also makes an appropriate notification to the @c flow_controller. template void bit_manipulate(const Preinstruction &instruction, uint32_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { - static_assert(operation == Operation::BCLR || operation == Operation::BCHG || operation == Operation::BSET); + static_assert( + operation == Operation::BCLR || + operation == Operation::BCHG || + operation == Operation::BSET); const auto bit = mask_bit(instruction, source); status.zero_result = destination & (1 << bit); @@ -126,6 +129,43 @@ template void clear(IntT &destination, Status &status) { status.negative_flag = status.overflow_flag = status.carry_flag = status.zero_result = 0; } +template +void apply_sr_ccr(uint16_t source, Status &status, FlowController &flow_controller) { + static_assert( + operation == Operation::ANDItoSR || operation == Operation::ANDItoCCR || + operation == Operation::EORItoSR || operation == Operation::EORItoCCR || + operation == Operation::ORItoSR || operation == Operation::ORItoCCR + ); + + auto sr = status.status(); + switch(operation) { + case Operation::ANDItoSR: case Operation::ANDItoCCR: + sr &= source; + break; + case Operation::EORItoSR: case Operation::EORItoCCR: + sr ^= source; + break; + case Operation::ORItoSR: case Operation::ORItoCCR: + sr |= source; + break; + } + + switch(operation) { + case Operation::ANDItoSR: + case Operation::EORItoSR: + case Operation::ORItoSR: + status.set_status(sr); + flow_controller.did_update_status(); + break; + + case Operation::ANDItoCCR: + case Operation::EORItoCCR: + case Operation::ORItoCCR: + status.set_ccr(sr); + break; + } +} + } template < @@ -355,38 +395,12 @@ template < status.negative_flag = status.zero_result & 0x80000000; break; -#define and_op(a, b) a &= b -#define or_op(a, b) a |= b -#define eor_op(a, b) a ^= b - -#define apply(op, func) { \ - auto sr = status.status(); \ - op(sr, src.w); \ - func(sr); \ -} - -#define set_status(x) status.set_status(x); flow_controller.did_update_status() -#define set_ccr(x) status.set_ccr(x) - -#define apply_op_sr(op) apply(op, set_status) -#define apply_op_ccr(op) apply(op, set_ccr) - - case Operation::ANDItoSR: apply_op_sr(and_op); break; - case Operation::EORItoSR: apply_op_sr(eor_op); break; - case Operation::ORItoSR: apply_op_sr(or_op); break; - - case Operation::ANDItoCCR: apply_op_ccr(and_op); break; - case Operation::EORItoCCR: apply_op_ccr(eor_op); break; - case Operation::ORItoCCR: apply_op_ccr(or_op); break; - -#undef apply_op_ccr -#undef apply_op_sr -#undef set_ccr -#undef set_status -#undef apply -#undef eor_op -#undef or_op -#undef and_op + case Operation::ANDItoSR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; + case Operation::EORItoSR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; + case Operation::ORItoSR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; + case Operation::ANDItoCCR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; + case Operation::EORItoCCR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; + case Operation::ORItoCCR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; /* Multiplications. From eb206a08d9c0372078cce57e366139ae06f5717e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 16:02:20 -0400 Subject: [PATCH 07/35] Templatise MULU/MULS. --- .../Implementation/PerformImplementation.hpp | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 46e48e743..6236676b4 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -166,6 +166,24 @@ void apply_sr_ccr(uint16_t source, Status &status, FlowController &flow_controll } } +template +void multiply(uint16_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { + if constexpr (is_mulu) { + destination = source * uint16_t(destination); + } else { + destination = u_extend16(source) * u_extend16(uint16_t(destination)); + } + status.carry_flag = status.overflow_flag = 0; + status.zero_result = destination; + status.negative_flag = status.zero_result & top_bit(); + + if constexpr (is_mulu) { + flow_controller.did_mulu(source); + } else { + flow_controller.did_muls(source); + } +} + } template < @@ -406,22 +424,8 @@ template < Multiplications. */ - case Operation::MULU: - dest.l = dest.w * src.w; - status.carry_flag = status.overflow_flag = 0; - status.zero_result = dest.l; - status.negative_flag = status.zero_result & 0x80000000; - flow_controller.did_mulu(src.w); - break; - - case Operation::MULS: - dest.l = - u_extend16(dest.w) * u_extend16(src.w); - status.carry_flag = status.overflow_flag = 0; - status.zero_result = dest.l; - status.negative_flag = status.zero_result & 0x80000000; - flow_controller.did_muls(src.w); - break; + case Operation::MULU: Primitive::multiply(src.w, dest.l, status, flow_controller); break; + case Operation::MULS: Primitive::multiply(src.w, dest.l, status, flow_controller); break; /* Divisions. From 28093196b98150f0844600722b2a9f6b7de3d5b7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 16:16:53 -0400 Subject: [PATCH 08/35] Convert DIVU/DIVS logic to a template. --- .../Implementation/PerformImplementation.hpp | 76 ++++++++++--------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 6236676b4..9452acaa1 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -184,6 +184,46 @@ void multiply(uint16_t source, uint32_t &destination, Status &status, FlowContro } } +template +void did_divide(IntT dividend, IntT divisor, FlowController &flow_controller) { + if constexpr (is_divu) { + flow_controller.template did_divu(dividend, divisor); + } else { + flow_controller.template did_divs(dividend, divisor); + } +} + +template +void divide(uint16_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { + status.carry_flag = 0; + + const auto dividend = Int32(destination); + const auto divisor = Int32(Int16(source)); + + if(!divisor) { + status.negative_flag = status.overflow_flag = 0; + status.zero_result = 1; + flow_controller.raise_exception(Exception::IntegerDivideByZero); + did_divide(dividend, divisor, flow_controller); + return; + } + + const auto quotient = int64_t(dividend) / int64_t(divisor); + if(quotient != Int32(Int16(quotient))) { + status.overflow_flag = 1; + did_divide(dividend, divisor, flow_controller); + return; + } + + const auto remainder = Int16(dividend % divisor); + destination = uint32_t((uint32_t(remainder) << 16) | uint16_t(quotient)); + + status.overflow_flag = 0; + status.zero_result = Status::FlagT(quotient); + status.negative_flag = status.zero_result & 0x8000; + did_divide(dividend, divisor, flow_controller); +} + } template < @@ -431,40 +471,8 @@ template < Divisions. */ -#define DIV(Type16, Type32, flow_function) { \ - status.carry_flag = 0; \ - \ - const auto dividend = Type32(dest.l); \ - const auto divisor = Type32(Type16(src.w)); \ - \ - if(!divisor) { \ - status.negative_flag = status.overflow_flag = 0; \ - status.zero_result = 1; \ - flow_controller.raise_exception(Exception::IntegerDivideByZero); \ - flow_controller.template flow_function(dividend, divisor); \ - return; \ - } \ - \ - const auto quotient = int64_t(dividend) / int64_t(divisor); \ - if(quotient != Type32(Type16(quotient))) { \ - status.overflow_flag = 1; \ - flow_controller.template flow_function(dividend, divisor); \ - return; \ - } \ - \ - const auto remainder = Type16(dividend % divisor); \ - dest.l = uint32_t((uint32_t(remainder) << 16) | uint16_t(quotient)); \ - \ - status.overflow_flag = 0; \ - status.zero_result = Status::FlagT(quotient); \ - status.negative_flag = status.zero_result & 0x8000; \ - flow_controller.template flow_function(dividend, divisor); \ -} - - case Operation::DIVU: DIV(uint16_t, uint32_t, did_divu); break; - case Operation::DIVS: DIV(int16_t, int32_t, did_divs); break; - -#undef DIV + case Operation::DIVU: Primitive::divide(src.w, dest.l, status, flow_controller); break; + case Operation::DIVS: Primitive::divide(src.w, dest.l, status, flow_controller); break; // TRAP, which is a nicer form of ILLEGAL. case Operation::TRAP: From 1f19141746c0edc881a21120e171bfee4fb3c25b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 16:19:47 -0400 Subject: [PATCH 09/35] Eliminate `BiggerInt`. --- .../Implementation/PerformImplementation.hpp | 12 ----------- .../68000ComparativeTests.mm | 21 ++++++++++++------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 9452acaa1..b2b3cf090 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -22,18 +22,6 @@ namespace M68k { namespace Primitive { -/// Provides a type alias, @c type, which is an unsigned int bigger than @c IntT. -template struct BiggerInt {}; -template <> struct BiggerInt { - using type = uint16_t; -}; -template <> struct BiggerInt { - using type = uint32_t; -}; -template <> struct BiggerInt { - using type = uint64_t; -}; - /// @returns An int of type @c IntT with only the most-significant bit set. template constexpr IntT top_bit() { static_assert(!std::numeric_limits::is_signed); diff --git a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm index 94c6cddce..47b30b9b3 100644 --- a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm @@ -307,7 +307,8 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler { if(!address || !value) break; XCTAssertEqual(test68000->ram[address.integerValue ^ 1], value.integerValue, @"%@: Memory at location %@ inconsistent", name, address); - if(test68000->ram[address.integerValue ^ 1] != value.integerValue) [_failures addObject:name]; + if(test68000->ram[address.integerValue ^ 1] != value.integerValue) + [_failures addObject:name]; } // Consider collating extra detail. @@ -359,7 +360,8 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler { NSNumber *const value = [enumerator nextObject]; if(!address || !value) break; - if(_testExecutor->ram[address.integerValue] != value.integerValue) [_failures addObject:name]; + if(_testExecutor->ram[address.integerValue] != value.integerValue) + [_failures addObject:name]; } // If this test is now in the failures set, add the corresponding opcode for @@ -425,12 +427,17 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler { const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c]; const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c]; - if(registers.data[c] != [finalState[dX] integerValue]) [_failures addObject:name]; - if(c < 7 && registers.address[c] != [finalState[aX] integerValue]) [_failures addObject:name]; + if(registers.data[c] != [finalState[dX] integerValue]) + [_failures addObject:name]; + if(c < 7 && registers.address[c] != [finalState[aX] integerValue]) + [_failures addObject:name]; } - if(registers.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) [_failures addObject:name]; - if(registers.user_stack_pointer != [finalState[@"usp"] integerValue]) [_failures addObject:name]; - if(registers.program_counter + pcOffset != [finalState[@"pc"] integerValue]) [_failures addObject:name]; + if(registers.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) + [_failures addObject:name]; + if(registers.user_stack_pointer != [finalState[@"usp"] integerValue]) + [_failures addObject:name]; + if(registers.program_counter + pcOffset != [finalState[@"pc"] integerValue]) + [_failures addObject:name]; const uint16_t correctSR = [finalState[@"sr"] integerValue]; if(registers.status != correctSR) { From eff9a09b9f9458bbeee57985ff679ec39fbb2318 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 21:27:18 -0400 Subject: [PATCH 10/35] Collapse MOVE and NEG[X] similarities. --- .../Implementation/PerformImplementation.hpp | 118 +++++------------- 1 file changed, 31 insertions(+), 87 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index b2b3cf090..dc17ac7e0 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -212,6 +212,28 @@ void divide(uint16_t source, uint32_t &destination, Status &status, FlowControll did_divide(dividend, divisor, flow_controller); } +template void move(IntT source, IntT &destination, Status &status) { + destination = source; + status.zero_result = Status::FlagT(source); + status.negative_flag = status.zero_result & top_bit(); + status.overflow_flag = status.carry_flag = 0; +} + +template void negative(IntT &source, Status &status) { + const IntT result = -source - (is_extend && status.extend_flag ? 1 : 0); + + if constexpr (is_extend) { + status.zero_result |= result; + } else { + status.zero_result = result; + } + status.extend_flag = status.carry_flag = result; // i.e. any value other than 0 will result in carry. + status.negative_flag = result & top_bit(); + status.overflow_flag = Primitive::overflow(source, IntT(0), result); + + source = result; +} + } template < @@ -363,23 +385,9 @@ template < MOVE.b, MOVE.l and MOVE.w: move the least significant byte or word, or the entire long word, and set negative, zero, overflow and carry as appropriate. */ - case Operation::MOVEb: - status.zero_result = dest.b = src.b; - status.negative_flag = status.zero_result & 0x80; - status.overflow_flag = status.carry_flag = 0; - break; - - case Operation::MOVEw: - status.zero_result = dest.w = src.w; - status.negative_flag = status.zero_result & 0x8000; - status.overflow_flag = status.carry_flag = 0; - break; - - case Operation::MOVEl: - status.zero_result = dest.l = src.l; - status.negative_flag = status.zero_result & 0x80000000; - status.overflow_flag = status.carry_flag = 0; - break; + case Operation::MOVEb: Primitive::move(src.b, dest.b, status); break; + case Operation::MOVEw: Primitive::move(src.w, dest.w, status); break; + case Operation::MOVEl: Primitive::move(src.l, dest.l, status); break; /* MOVEA.l: move the entire long word; @@ -503,80 +511,16 @@ template < and SUB calculates `destination - source`, the NEGs deliberately label 'source' and 'destination' differently from Motorola. */ - case Operation::NEGb: { - const int destination = 0; - const int source = src.b; - const auto result = destination - source; - src.b = uint8_t(result); - - status.zero_result = result & 0xff; - status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xff); - status.negative_flag = result & 0x80; - status.overflow_flag = Primitive::overflow(uint8_t(source), uint8_t(destination), uint8_t(result)); - } break; - - case Operation::NEGw: { - const int destination = 0; - const int source = src.w; - const auto result = destination - source; - src.w = uint16_t(result); - - status.zero_result = result & 0xffff; - status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xffff); - status.negative_flag = result & 0x8000; - status.overflow_flag = Primitive::overflow(uint16_t(source), uint16_t(destination), uint16_t(result)); - } break; - - case Operation::NEGl: { - const uint64_t destination = 0; - const uint64_t source = src.l; - const auto result = destination - source; - src.l = uint32_t(result); - - status.zero_result = uint_fast32_t(result); - status.extend_flag = status.carry_flag = result >> 32; - status.negative_flag = result & 0x80000000; - status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); - } break; + case Operation::NEGb: Primitive::negative(src.b, status); break; + case Operation::NEGw: Primitive::negative(src.w, status); break; + case Operation::NEGl: Primitive::negative(src.l, status); break; /* NEGXs: NEG, with extend. */ - case Operation::NEGXb: { - const int source = src.b; - const int destination = 0; - const auto result = destination - source - (status.extend_flag ? 1 : 0); - src.b = uint8_t(result); - - status.zero_result |= result & 0xff; - status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xff); - status.negative_flag = result & 0x80; - status.overflow_flag = Primitive::overflow(uint8_t(source), uint8_t(destination), uint8_t(result)); - } break; - - case Operation::NEGXw: { - const int source = src.w; - const int destination = 0; - const auto result = destination - source - (status.extend_flag ? 1 : 0); - src.w = uint16_t(result); - - status.zero_result |= result & 0xffff; - status.extend_flag = status.carry_flag = Status::FlagT(result & ~0xffff); - status.negative_flag = result & 0x8000; - status.overflow_flag = Primitive::overflow(uint16_t(source), uint16_t(destination), uint16_t(result)); - } break; - - case Operation::NEGXl: { - const uint64_t source = src.l; - const uint64_t destination = 0; - const auto result = destination - source - (status.extend_flag ? 1 : 0); - src.l = uint32_t(result); - - status.zero_result |= uint_fast32_t(result); - status.extend_flag = status.carry_flag = result >> 32; - status.negative_flag = result & 0x80000000; - status.overflow_flag = Primitive::overflow(uint32_t(source), uint32_t(destination), uint32_t(result)); - } break; + case Operation::NEGXb: Primitive::negative(src.b, status); break; + case Operation::NEGXw: Primitive::negative(src.w, status); break; + case Operation::NEGXl: Primitive::negative(src.l, status); break; /* The no-op. From 06dbb7167b2ccc1d61a87bc2fd8eb63b945b1ccf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 11 Oct 2022 21:31:14 -0400 Subject: [PATCH 11/35] Unify TST. --- .../Implementation/PerformImplementation.hpp | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index dc17ac7e0..b7954fbc4 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -234,6 +234,12 @@ template void negative(IntT &source, Status &sta source = result; } +template void test(IntT source, Status &status) { + status.carry_flag = status.overflow_flag = 0; + status.zero_result = Status::FlagT(source); + status.negative_flag = status.zero_result & top_bit(); +} + } template < @@ -970,23 +976,9 @@ template < TSTs: compare to zero. */ - case Operation::TSTb: - status.carry_flag = status.overflow_flag = 0; - status.zero_result = src.b; - status.negative_flag = status.zero_result & 0x80; - break; - - case Operation::TSTw: - status.carry_flag = status.overflow_flag = 0; - status.zero_result = src.w; - status.negative_flag = status.zero_result & 0x8000; - break; - - case Operation::TSTl: - status.carry_flag = status.overflow_flag = 0; - status.zero_result = src.l; - status.negative_flag = status.zero_result & 0x80000000; - break; + case Operation::TSTb: Primitive::test(src.b, status); break; + case Operation::TSTw: Primitive::test(src.w, status); break; + case Operation::TSTl: Primitive::test(src.l, status); break; case Operation::STOP: status.set_status(src.w); From 0a9c392371d023aeec78fb729a3e519e2302cf15 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Thu, 13 Oct 2022 15:01:06 -0400 Subject: [PATCH 12/35] Remove unused `bit_count`. --- .../M68k/Implementation/PerformImplementation.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index b7954fbc4..1a73ffe74 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -29,11 +29,6 @@ template constexpr IntT top_bit() { return max - (max >> 1); } -/// @returns The number of bits in @c IntT. -template constexpr int bit_count() { - return sizeof(IntT) * 8; -} - /// @returns An int with the top bit indicating whether overflow occurred when @c source and @c destination /// were either added (if @c is_add is true) or subtracted (if @c is_add is false) and the result was @c result. /// All other bits will be clear. From fee072b4041958273709f1bc9b78cb9bde5562a1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 16 Oct 2022 12:06:28 -0400 Subject: [PATCH 13/35] Commute ROXL and ROXR into a template. --- .../Implementation/PerformImplementation.hpp | 95 ++++++++++++------- 1 file changed, 60 insertions(+), 35 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 1a73ffe74..ef7b27334 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -235,6 +235,59 @@ template void test(IntT source, Status &status) { status.negative_flag = status.zero_result & top_bit(); } +template int shift_count(uint8_t source, FlowController &flow_controller) { + const int count = source & 63; + flow_controller.template did_shift(count); + return count; +} + +/// @returns The number of bits in @c IntT. +template constexpr int bit_size() { + return sizeof(IntT) * 8; +} + +template void set_neg_zero(IntT result, Status &status) { + status.zero_result = Status::FlagT(result); + status.negative_flag = result & top_bit(); +} + +template void set_neg_zero_overflow(IntT result, IntT destination, Status &status) { + set_neg_zero(result, status); + status.overflow_flag = Status::FlagT((destination ^ result) & top_bit()); +} + +/// Perform a rotate-through-extend, i.e. any of ROX[L/R].[b/w/l]. +template void rox(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { + static_assert( + operation == Operation::ROXLb || operation == Operation::ROXLw || operation == Operation::ROXLl || + operation == Operation::ROXRb || operation == Operation::ROXRw || operation == Operation::ROXRl + ); + + const auto size = bit_size(); + const auto shift = shift_count(uint8_t(source), flow_controller) % (size + 1); + + uint64_t compound = + uint64_t(destination) | + (status.extend_flag ? (1ull << size) : 0); + + switch(operation) { + case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: + compound = + (compound << shift) | + (compound >> (size + 1 - shift)); + break; + case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl: + compound = + (compound >> shift) | + (compound << (size + 1 - shift)); + break; + } + + status.carry_flag = status.extend_flag = Status::FlagT((compound >> size) & 1); + set_neg_zero_overflow(IntT(compound), destination, status); + destination = IntT(compound); +} + } template < @@ -865,19 +918,7 @@ template < case Operation::RORw: ror(dest.w, 16); break; case Operation::RORl: ror(dest.l, 32); break; -#define roxl(destination, size) { \ - decode_shift_count(decltype(destination)); \ - \ - shift_count %= (size + 1); \ - uint64_t compound = uint64_t(destination) | (status.extend_flag ? (1ull << size) : 0); \ - compound = \ - (compound << shift_count) | \ - (compound >> (size + 1 - shift_count)); \ - status.carry_flag = status.extend_flag = Status::FlagT((compound >> size) & 1); \ - destination = decltype(destination)(compound); \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + // --- case Operation::ROXLm: { const auto value = src.w; @@ -885,23 +926,9 @@ template < status.extend_flag = value & 0x8000; set_flags_w(0x8000); } break; - case Operation::ROXLb: roxl(dest.b, 8); break; - case Operation::ROXLw: roxl(dest.w, 16); break; - case Operation::ROXLl: roxl(dest.l, 32); break; - -#define roxr(destination, size) { \ - decode_shift_count(decltype(destination)); \ - \ - shift_count %= (size + 1); \ - uint64_t compound = uint64_t(destination) | (status.extend_flag ? (1ull << size) : 0); \ - compound = \ - (compound >> shift_count) | \ - (compound << (size + 1 - shift_count)); \ - status.carry_flag = status.extend_flag = Status::FlagT((compound >> size) & 1); \ - destination = decltype(destination)(compound); \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + case Operation::ROXLb: Primitive::rox(src.l, dest.b, status, flow_controller); break; + case Operation::ROXLw: Primitive::rox(src.l, dest.w, status, flow_controller); break; + case Operation::ROXLl: Primitive::rox(src.l, dest.l, status, flow_controller); break; case Operation::ROXRm: { const auto value = src.w; @@ -909,12 +936,10 @@ template < status.extend_flag = value & 0x0001; set_flags_w(0x0001); } break; - case Operation::ROXRb: roxr(dest.b, 8); break; - case Operation::ROXRw: roxr(dest.w, 16); break; - case Operation::ROXRl: roxr(dest.l, 32); break; + case Operation::ROXRb: Primitive::rox(src.l, dest.b, status, flow_controller); break; + case Operation::ROXRw: Primitive::rox(src.l, dest.w, status, flow_controller); break; + case Operation::ROXRl: Primitive::rox(src.l, dest.l, status, flow_controller); break; -#undef roxr -#undef roxl #undef ror #undef rol #undef asr From 17c1e512312a18c510ec8e41de241c5d70c0edc1 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 16 Oct 2022 12:19:09 -0400 Subject: [PATCH 14/35] Commute ROL/ROR to templates. --- .../Implementation/PerformImplementation.hpp | 88 +++++++++---------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index ef7b27334..3167623c7 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -256,6 +256,42 @@ template void set_neg_zero_overflow(IntT result, IntT destinatio status.overflow_flag = Status::FlagT((destination ^ result) & top_bit()); } +/// Perform a rotate without extend, i.e. any of RO[L/R].[b/w/l]. +template void rotate(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { + static_assert( + operation == Operation::ROLb || operation == Operation::ROLw || operation == Operation::ROLl || + operation == Operation::RORb || operation == Operation::RORw || operation == Operation::RORl + ); + + const auto size = bit_size(); + const auto shift = shift_count(uint8_t(source), flow_controller) & (size - 1); + IntT result; + + if(!shift) { + result = destination; + status.carry_flag = 0; + } else { + switch(operation) { + case Operation::ROLb: case Operation::ROLw: case Operation::ROLl: + result = IntT( + (destination << shift) | + (destination >> (size - shift)) + ); + break; + case Operation::RORb: case Operation::RORw: case Operation::RORl: + result = IntT( + (destination >> shift) | + (destination << (size - shift)) + ); + break; + } + status.carry_flag = Status::FlagT(result & 1); + } + + set_neg_zero_overflow(result, destination, status); + destination = IntT(result); +} + /// Perform a rotate-through-extend, i.e. any of ROX[L/R].[b/w/l]. template void rox(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { static_assert( @@ -862,51 +898,17 @@ template < case Operation::LSRw: lsr(dest.w, 16); break; case Operation::LSRl: lsr(dest.l, 32); break; -#define rol(destination, size) { \ - decode_shift_count(decltype(destination)); \ - const auto value = destination; \ - \ - if(!shift_count) { \ - status.carry_flag = 0; \ - } else { \ - shift_count &= (size - 1); \ - destination = decltype(destination)( \ - (value << shift_count) | \ - (value >> (size - shift_count)) \ - ); \ - status.carry_flag = Status::FlagT(destination & 1); \ - } \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + // --- case Operation::ROLm: { const auto value = src.w; src.w = uint16_t((value << 1) | (value >> 15)); - status.carry_flag = src.w & 1; + status.carry_flag = src.w & 0x0001; set_neg_zero_overflow(src.w, 0x8000); } break; - case Operation::ROLb: rol(dest.b, 8); break; - case Operation::ROLw: rol(dest.w, 16); break; - case Operation::ROLl: rol(dest.l, 32); break; - -#define ror(destination, size) { \ - decode_shift_count(decltype(destination)); \ - const auto value = destination; \ - \ - if(!shift_count) { \ - status.carry_flag = 0; \ - } else { \ - shift_count &= (size - 1); \ - destination = decltype(destination)(\ - (value >> shift_count) | \ - (value << (size - shift_count)) \ - );\ - status.carry_flag = destination & Status::FlagT(1 << (size - 1)); \ - } \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + case Operation::ROLb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; + case Operation::ROLw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; + case Operation::ROLl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; case Operation::RORm: { const auto value = src.w; @@ -914,11 +916,9 @@ template < status.carry_flag = src.w & 0x8000; set_neg_zero_overflow(src.w, 0x8000); } break; - case Operation::RORb: ror(dest.b, 8); break; - case Operation::RORw: ror(dest.w, 16); break; - case Operation::RORl: ror(dest.l, 32); break; - - // --- + case Operation::RORb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; + case Operation::RORw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; + case Operation::RORl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; case Operation::ROXLm: { const auto value = src.w; From d5ceb934d26165466aaa8a135d44761fa37980ae Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 16 Oct 2022 21:52:00 -0400 Subject: [PATCH 15/35] Fix overflow flags, avoid bigger-word usage. --- .../Implementation/PerformImplementation.hpp | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 3167623c7..5dc0cc0b3 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -251,11 +251,6 @@ template void set_neg_zero(IntT result, Status &status) { status.negative_flag = result & top_bit(); } -template void set_neg_zero_overflow(IntT result, IntT destination, Status &status) { - set_neg_zero(result, status); - status.overflow_flag = Status::FlagT((destination ^ result) & top_bit()); -} - /// Perform a rotate without extend, i.e. any of RO[L/R].[b/w/l]. template void rotate(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { static_assert( @@ -265,31 +260,30 @@ template void rota const auto size = bit_size(); const auto shift = shift_count(uint8_t(source), flow_controller) & (size - 1); - IntT result; if(!shift) { - result = destination; status.carry_flag = 0; } else { switch(operation) { case Operation::ROLb: case Operation::ROLw: case Operation::ROLl: - result = IntT( + destination = IntT( (destination << shift) | (destination >> (size - shift)) ); + status.carry_flag = Status::FlagT(destination & 1); break; case Operation::RORb: case Operation::RORw: case Operation::RORl: - result = IntT( + destination = IntT( (destination >> shift) | (destination << (size - shift)) ); + status.carry_flag = Status::FlagT(destination & top_bit()); break; } - status.carry_flag = Status::FlagT(result & 1); } - set_neg_zero_overflow(result, destination, status); - destination = IntT(result); + set_neg_zero(destination, status); + status.overflow_flag = 0; } /// Perform a rotate-through-extend, i.e. any of ROX[L/R].[b/w/l]. @@ -300,28 +294,34 @@ template void rox( ); const auto size = bit_size(); - const auto shift = shift_count(uint8_t(source), flow_controller) % (size + 1); + auto shift = shift_count(uint8_t(source), flow_controller) % (size + 1); - uint64_t compound = - uint64_t(destination) | - (status.extend_flag ? (1ull << size) : 0); - - switch(operation) { - case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: - compound = - (compound << shift) | - (compound >> (size + 1 - shift)); - break; - case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl: - compound = - (compound >> shift) | - (compound << (size + 1 - shift)); - break; + if(!shift) { + // When shift is zero, extend is unaffected but is copied to carry. + status.carry_flag = status.extend_flag; + } else { + switch(operation) { + case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: + status.carry_flag = status.extend_flag = Status::FlagT((destination >> (size - shift)) & 1); + destination = IntT( + (destination << shift) | + (IntT(status.extend_flag ? 1 : 0) << (shift - 1)) | + (destination >> (size + 1 - shift)) + ); + break; + case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl: + status.carry_flag = status.extend_flag = Status::FlagT(destination & (1 << (shift - 1))); + destination = IntT( + (destination >> shift) | + ((status.extend_flag ? top_bit() : 0) >> (shift - 1)) | + (destination << (size + 1 - shift)) + ); + break; + } } - status.carry_flag = status.extend_flag = Status::FlagT((compound >> size) & 1); - set_neg_zero_overflow(IntT(compound), destination, status); - destination = IntT(compound); + set_neg_zero(destination, status); + status.overflow_flag = 0; } } From 47e8f3c0f17c0b1a8cc10d19ab0ec40500907f80 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 16 Oct 2022 22:21:20 -0400 Subject: [PATCH 16/35] Collapse [A/L]S[L/R].[bwl] into a template. --- .../Implementation/PerformImplementation.hpp | 193 ++++++++---------- 1 file changed, 86 insertions(+), 107 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 5dc0cc0b3..c5bf3f401 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -251,6 +251,74 @@ template void set_neg_zero(IntT result, Status &status) { status.negative_flag = result & top_bit(); } +/// Perform an arithmetic or logical shift, i.e. any of LSL, LSR, ASL or ASR. +template void shift(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { + static_assert( + operation == Operation::ASLb || operation == Operation::ASLw || operation == Operation::ASLl || + operation == Operation::ASRb || operation == Operation::ASRw || operation == Operation::ASRl || + operation == Operation::LSLb || operation == Operation::LSLw || operation == Operation::LSLl || + operation == Operation::LSRb || operation == Operation::LSRw || operation == Operation::LSRl + ); + + const auto size = bit_size(); + const auto shift = shift_count(uint8_t(source), flow_controller); + + if(!shift) { + status.carry_flag = status.overflow_flag = 0; + } else { + enum class Type { + ASL, LSL, ASR, LSR + } type; + switch(operation) { + case Operation::ASLb: case Operation::ASLw: case Operation::ASLl: + type = Type::ASL; + break; + case Operation::LSLb: case Operation::LSLw: case Operation::LSLl: + type = Type::LSL; + break; + case Operation::ASRb: case Operation::ASRw: case Operation::ASRl: + type = Type::ASR; + break; + case Operation::LSRb: case Operation::LSRw: case Operation::LSRl: + type = Type::LSR; + break; + } + + switch(type) { + case Type::ASL: + case Type::LSL: + status.overflow_flag = + type == Type::LSL ? + 0 : (destination ^ (destination << shift)) & top_bit(); + if(shift < size) { + status.carry_flag = status.extend_flag = (destination >> (size - shift)) & 1; + } else { + status.carry_flag = status.extend_flag = 0; + } + destination <<= shift; + break; + + case Type::ASR: + case Type::LSR: { + const IntT sign_word = + type == Type::LSR ? + 0 : (destination & top_bit() ? IntT(~0) : 0); + + status.overflow_flag = 0; + status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1; + + if(shift < size) { + destination = IntT((destination >> shift) | (sign_word << (size - shift))); + } else { + destination = sign_word; + } + } break; + } + } + + set_neg_zero(destination, status); +} + /// Perform a rotate without extend, i.e. any of RO[L/R].[b/w/l]. template void rotate(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { static_assert( @@ -770,62 +838,24 @@ template < set_neg_zero(v, m); \ status.overflow_flag = (Status::FlagT(value) ^ status.zero_result) & Status::FlagT(m); -#define decode_shift_count(type) \ - int shift_count = src.l & 63; \ - flow_controller.template did_shift(shift_count); +#undef set_flags +#define set_flags(v, m, t) \ + status.zero_result = v; \ + status.negative_flag = status.zero_result & (m); \ + status.overflow_flag = 0; \ + status.carry_flag = value & (t); #define set_flags_w(t) set_flags(src.w, 0x8000, t) -#define asl(destination, size) {\ - decode_shift_count(decltype(destination)); \ - const auto value = destination; \ - \ - if(!shift_count) { \ - status.carry_flag = status.overflow_flag = 0; \ - } else { \ - destination = (shift_count < size) ? decltype(destination)(value << shift_count) : 0; \ - status.extend_flag = status.carry_flag = Status::FlagT(value) & Status::FlagT( (1u << (size - 1)) >> (shift_count - 1) ); \ - \ - if(shift_count >= size) status.overflow_flag = value && (value != decltype(value)(-1)); \ - else { \ - const auto mask = decltype(destination)(0xffffffff << (size - shift_count)); \ - status.overflow_flag = mask & value && ((mask & value) != mask); \ - } \ - } \ - \ - set_neg_zero(destination, 1 << (size - 1)); \ -} - case Operation::ASLm: { const auto value = src.w; src.w = uint16_t(value << 1); status.extend_flag = status.carry_flag = value & 0x8000; set_neg_zero_overflow(src.w, 0x8000); } break; - case Operation::ASLb: asl(dest.b, 8); break; - case Operation::ASLw: asl(dest.w, 16); break; - case Operation::ASLl: asl(dest.l, 32); break; - -#define asr(destination, size) {\ - decode_shift_count(decltype(destination)); \ - const auto value = destination; \ - \ - if(!shift_count) { \ - status.carry_flag = 0; \ - } else { \ - destination = (shift_count < size) ? \ - decltype(destination)(\ - (value >> shift_count) | \ - ((value & decltype(value)(1 << (size - 1)) ? 0xffffffff : 0x000000000) << (size - shift_count)) \ - ) : \ - decltype(destination)( \ - (value & decltype(value)(1 << (size - 1))) ? 0xffffffff : 0x000000000 \ - ); \ - status.extend_flag = status.carry_flag = Status::FlagT(value) & Status::FlagT(1 << (shift_count - 1)); \ - } \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + case Operation::ASLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::ASLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::ASLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; case Operation::ASRm: { const auto value = src.w; @@ -833,36 +863,9 @@ template < status.extend_flag = status.carry_flag = value & 1; set_neg_zero_overflow(src.w, 0x8000); } break; - case Operation::ASRb: asr(dest.b, 8); break; - case Operation::ASRw: asr(dest.w, 16); break; - case Operation::ASRl: asr(dest.l, 32); break; - - -#undef set_neg_zero_overflow -#define set_neg_zero_overflow(v, m) \ - set_neg_zero(v, m); \ - status.overflow_flag = 0; - -#undef set_flags -#define set_flags(v, m, t) \ - status.zero_result = v; \ - status.negative_flag = status.zero_result & (m); \ - status.overflow_flag = 0; \ - status.carry_flag = value & (t); - -#define lsl(destination, size) {\ - decode_shift_count(decltype(destination)); \ - const auto value = destination; \ - \ - if(!shift_count) { \ - status.carry_flag = 0; \ - } else { \ - destination = (shift_count < size) ? decltype(destination)(value << shift_count) : 0; \ - status.extend_flag = status.carry_flag = Status::FlagT(value) & Status::FlagT( (1u << (size - 1)) >> (shift_count - 1) ); \ - } \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + case Operation::ASRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::ASRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::ASRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; case Operation::LSLm: { const auto value = src.w; @@ -870,23 +873,9 @@ template < status.extend_flag = status.carry_flag = value & 0x8000; set_neg_zero_overflow(src.w, 0x8000); } break; - case Operation::LSLb: lsl(dest.b, 8); break; - case Operation::LSLw: lsl(dest.w, 16); break; - case Operation::LSLl: lsl(dest.l, 32); break; - -#define lsr(destination, size) {\ - decode_shift_count(decltype(destination)); \ - const auto value = destination; \ - \ - if(!shift_count) { \ - status.carry_flag = 0; \ - } else { \ - destination = (shift_count < size) ? (value >> shift_count) : 0; \ - status.extend_flag = status.carry_flag = value & Status::FlagT(1 << (shift_count - 1)); \ - } \ - \ - set_neg_zero_overflow(destination, 1 << (size - 1)); \ -} + case Operation::LSLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::LSLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::LSLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; case Operation::LSRm: { const auto value = src.w; @@ -894,11 +883,9 @@ template < status.extend_flag = status.carry_flag = value & 1; set_neg_zero_overflow(src.w, 0x8000); } break; - case Operation::LSRb: lsr(dest.b, 8); break; - case Operation::LSRw: lsr(dest.w, 16); break; - case Operation::LSRl: lsr(dest.l, 32); break; - - // --- + case Operation::LSRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::LSRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::LSRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; case Operation::ROLm: { const auto value = src.w; @@ -940,15 +927,7 @@ template < case Operation::ROXRw: Primitive::rox(src.l, dest.w, status, flow_controller); break; case Operation::ROXRl: Primitive::rox(src.l, dest.l, status, flow_controller); break; -#undef ror -#undef rol -#undef asr -#undef lsr -#undef lsl -#undef asl - #undef set_flags -#undef decode_shift_count #undef set_flags_w #undef set_neg_zero_overflow #undef set_neg_zero From cc55f0586dddd5b36f1f9c15ff8f9097daadf76b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 11:18:10 -0400 Subject: [PATCH 17/35] Clean up ASL/ASR/LSL/LSRm. --- .../Implementation/PerformImplementation.hpp | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index c5bf3f401..0888c01f1 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -830,6 +830,34 @@ template < /* Shifts and rotates. */ + case Operation::ASLm: + status.extend_flag = status.carry_flag = src.w & Primitive::top_bit(); + status.overflow_flag = (src.w ^ (src.w << 1)) & Primitive::top_bit(); + src.w <<= 1; + Primitive::set_neg_zero(src.w, status); + break; + + case Operation::LSLm: + status.extend_flag = status.carry_flag = src.w & Primitive::top_bit(); + status.overflow_flag = 0; + src.w <<= 1; + Primitive::set_neg_zero(src.w, status); + break; + + case Operation::ASRm: + status.extend_flag = status.carry_flag = src.w & 1; + status.overflow_flag = 0; + src.w = (src.w & Primitive::top_bit()) | (src.w >> 1); + Primitive::set_neg_zero(src.w, status); + break; + + case Operation::LSRm: + status.extend_flag = status.carry_flag = src.w & 1; + status.overflow_flag = 0; + src.w >>= 1; + Primitive::set_neg_zero(src.w, status); + break; + #define set_neg_zero(v, m) \ status.zero_result = Status::FlagT(v); \ status.negative_flag = status.zero_result & Status::FlagT(m); @@ -847,42 +875,18 @@ template < #define set_flags_w(t) set_flags(src.w, 0x8000, t) - case Operation::ASLm: { - const auto value = src.w; - src.w = uint16_t(value << 1); - status.extend_flag = status.carry_flag = value & 0x8000; - set_neg_zero_overflow(src.w, 0x8000); - } break; case Operation::ASLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; case Operation::ASLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; case Operation::ASLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::ASRm: { - const auto value = src.w; - src.w = (value&0x8000) | (value >> 1); - status.extend_flag = status.carry_flag = value & 1; - set_neg_zero_overflow(src.w, 0x8000); - } break; case Operation::ASRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; case Operation::ASRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; case Operation::ASRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::LSLm: { - const auto value = src.w; - src.w = uint16_t(value << 1); - status.extend_flag = status.carry_flag = value & 0x8000; - set_neg_zero_overflow(src.w, 0x8000); - } break; case Operation::LSLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; case Operation::LSLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; case Operation::LSLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::LSRm: { - const auto value = src.w; - src.w = value >> 1; - status.extend_flag = status.carry_flag = value & 1; - set_neg_zero_overflow(src.w, 0x8000); - } break; case Operation::LSRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; case Operation::LSRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; case Operation::LSRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; From ce98ca4bdd2e77c1a1b6b7f1ae418f72198bff14 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 11:27:04 -0400 Subject: [PATCH 18/35] Pull RO[L/R][X]m out of their macro stupor. --- .../Implementation/PerformImplementation.hpp | 68 +++++++------------ 1 file changed, 26 insertions(+), 42 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 0888c01f1..9574dfb8c 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -858,22 +858,35 @@ template < Primitive::set_neg_zero(src.w, status); break; -#define set_neg_zero(v, m) \ - status.zero_result = Status::FlagT(v); \ - status.negative_flag = status.zero_result & Status::FlagT(m); + case Operation::ROLm: + src.w = (src.w << 1) | (src.w >> 15); + status.carry_flag = src.w & 0x0001; + status.overflow_flag = 0; + Primitive::set_neg_zero(src.w, status); + break; -#define set_neg_zero_overflow(v, m) \ - set_neg_zero(v, m); \ - status.overflow_flag = (Status::FlagT(value) ^ status.zero_result) & Status::FlagT(m); + case Operation::RORm: + src.w = (src.w >> 1) | (src.w << 15); + status.carry_flag = src.w & Primitive::top_bit(); + status.overflow_flag = 0; + Primitive::set_neg_zero(src.w, status); + break; -#undef set_flags -#define set_flags(v, m, t) \ - status.zero_result = v; \ - status.negative_flag = status.zero_result & (m); \ - status.overflow_flag = 0; \ - status.carry_flag = value & (t); + case Operation::ROXLm: + status.carry_flag = src.w & Primitive::top_bit(); + src.w = (src.w << 1) | (status.extend_flag ? 0x0001 : 0x0000); + status.extend_flag = status.carry_flag; + status.overflow_flag = 0; + Primitive::set_neg_zero(src.w, status); + break; -#define set_flags_w(t) set_flags(src.w, 0x8000, t) + case Operation::ROXRm: + status.carry_flag = src.w & 0x0001; + src.w = (src.w >> 1) | (status.extend_flag ? 0x8000 : 0x0000); + status.extend_flag = status.carry_flag; + status.overflow_flag = 0; + Primitive::set_neg_zero(src.w, status); + break; case Operation::ASLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; case Operation::ASLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; @@ -891,51 +904,22 @@ template < case Operation::LSRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; case Operation::LSRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::ROLm: { - const auto value = src.w; - src.w = uint16_t((value << 1) | (value >> 15)); - status.carry_flag = src.w & 0x0001; - set_neg_zero_overflow(src.w, 0x8000); - } break; case Operation::ROLb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; case Operation::ROLw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; case Operation::ROLl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; - case Operation::RORm: { - const auto value = src.w; - src.w = uint16_t((value >> 1) | (value << 15)); - status.carry_flag = src.w & 0x8000; - set_neg_zero_overflow(src.w, 0x8000); - } break; case Operation::RORb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; case Operation::RORw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; case Operation::RORl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; - case Operation::ROXLm: { - const auto value = src.w; - src.w = uint16_t((value << 1) | (status.extend_flag ? 0x0001 : 0x0000)); - status.extend_flag = value & 0x8000; - set_flags_w(0x8000); - } break; case Operation::ROXLb: Primitive::rox(src.l, dest.b, status, flow_controller); break; case Operation::ROXLw: Primitive::rox(src.l, dest.w, status, flow_controller); break; case Operation::ROXLl: Primitive::rox(src.l, dest.l, status, flow_controller); break; - case Operation::ROXRm: { - const auto value = src.w; - src.w = (value >> 1) | (status.extend_flag ? 0x8000 : 0x0000); - status.extend_flag = value & 0x0001; - set_flags_w(0x0001); - } break; case Operation::ROXRb: Primitive::rox(src.l, dest.b, status, flow_controller); break; case Operation::ROXRw: Primitive::rox(src.l, dest.w, status, flow_controller); break; case Operation::ROXRl: Primitive::rox(src.l, dest.l, status, flow_controller); break; -#undef set_flags -#undef set_flags_w -#undef set_neg_zero_overflow -#undef set_neg_zero - case Operation::MOVEPl: flow_controller.template movep(instruction, src.l, dest.l); break; From da03cd58c117c790ebb35500ddc7ad9503e905b0 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 15:04:28 -0400 Subject: [PATCH 19/35] Add overt casting. --- .../M68k/Implementation/PerformImplementation.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 9574dfb8c..fca167125 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -859,14 +859,14 @@ template < break; case Operation::ROLm: - src.w = (src.w << 1) | (src.w >> 15); + src.w = uint16_t((src.w << 1) | (src.w >> 15)); status.carry_flag = src.w & 0x0001; status.overflow_flag = 0; Primitive::set_neg_zero(src.w, status); break; case Operation::RORm: - src.w = (src.w >> 1) | (src.w << 15); + src.w = uint16_t((src.w >> 1) | (src.w << 15)); status.carry_flag = src.w & Primitive::top_bit(); status.overflow_flag = 0; Primitive::set_neg_zero(src.w, status); @@ -874,7 +874,7 @@ template < case Operation::ROXLm: status.carry_flag = src.w & Primitive::top_bit(); - src.w = (src.w << 1) | (status.extend_flag ? 0x0001 : 0x0000); + src.w = uint16_t((src.w << 1) | (status.extend_flag ? 0x0001 : 0x0000)); status.extend_flag = status.carry_flag; status.overflow_flag = 0; Primitive::set_neg_zero(src.w, status); @@ -882,7 +882,7 @@ template < case Operation::ROXRm: status.carry_flag = src.w & 0x0001; - src.w = (src.w >> 1) | (status.extend_flag ? 0x8000 : 0x0000); + src.w = uint16_t((src.w >> 1) | (status.extend_flag ? 0x8000 : 0x0000)); status.extend_flag = status.carry_flag; status.overflow_flag = 0; Primitive::set_neg_zero(src.w, status); From aff1caed1590a9c8e1109dca5a3e28cc7de7bbcb Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 15:05:23 -0400 Subject: [PATCH 20/35] Clean up formatting. --- .../Implementation/PerformImplementation.hpp | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index fca167125..4d0ff071e 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -888,29 +888,29 @@ template < Primitive::set_neg_zero(src.w, status); break; - case Operation::ASLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; - case Operation::ASLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; - case Operation::ASLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; + case Operation::ASLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::ASLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::ASLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::ASRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; - case Operation::ASRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; - case Operation::ASRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; + case Operation::ASRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::ASRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::ASRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::LSLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; - case Operation::LSLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; - case Operation::LSLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; + case Operation::LSLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::LSLw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::LSLl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::LSRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; - case Operation::LSRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; - case Operation::LSRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; + case Operation::LSRb: Primitive::shift(src.l, dest.b, status, flow_controller); break; + case Operation::LSRw: Primitive::shift(src.l, dest.w, status, flow_controller); break; + case Operation::LSRl: Primitive::shift(src.l, dest.l, status, flow_controller); break; - case Operation::ROLb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; - case Operation::ROLw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; - case Operation::ROLl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; + case Operation::ROLb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; + case Operation::ROLw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; + case Operation::ROLl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; - case Operation::RORb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; - case Operation::RORw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; - case Operation::RORl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; + case Operation::RORb: Primitive::rotate(src.l, dest.b, status, flow_controller); break; + case Operation::RORw: Primitive::rotate(src.l, dest.w, status, flow_controller); break; + case Operation::RORl: Primitive::rotate(src.l, dest.l, status, flow_controller); break; case Operation::ROXLb: Primitive::rox(src.l, dest.b, status, flow_controller); break; case Operation::ROXLw: Primitive::rox(src.l, dest.w, status, flow_controller); break; From ee3a3df0b558b70ac6fe9b74b68ec416f0fcb283 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 15:12:38 -0400 Subject: [PATCH 21/35] Eliminate SBCD macro. --- .../Implementation/PerformImplementation.hpp | 61 +++++++++---------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 4d0ff071e..86d9b1a79 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -71,6 +71,28 @@ static void add_sub(IntT source, IntT &destination, Status &status) { destination = result; } +inline void sbcd(uint8_t rhs, uint8_t lhs, uint8_t &destination, Status &status) { + const int extend = (status.extend_flag ? 1 : 0); + const int unadjusted_result = lhs - rhs - extend; + + const int top = (lhs & 0xf0) - (rhs & 0xf0) - (0x60 & (unadjusted_result >> 4)); + + int result = (lhs & 0xf) - (rhs & 0xf) - extend; + const int low_adjustment = 0x06 & (result >> 4); + status.extend_flag = status.carry_flag = Status::FlagT( + (unadjusted_result - low_adjustment) & 0x300 + ); + result = result + top - low_adjustment; + + /* Store the result. */ + destination = uint8_t(result); + + /* Set all remaining flags essentially as if this were normal subtraction. */ + status.zero_result |= destination; + status.negative_flag = result & 0x80; + status.overflow_flag = unadjusted_result & ~result & 0x80; +} + /// Performs a compare of @c source to @c destination, setting zero, carry, negative and overflow flags. template static void compare(IntT source, IntT destination, Status &status) { @@ -765,48 +787,21 @@ template < status.overflow_flag = status.carry_flag = 0; break; -#define sbcd(d) \ - const int extend = (status.extend_flag ? 1 : 0); \ - const int unadjusted_result = destination - source - extend; \ - \ - const int top = (destination & 0xf0) - (source & 0xf0) - (0x60 & (unadjusted_result >> 4)); \ - \ - int result = (destination & 0xf) - (source & 0xf) - extend; \ - const int low_adjustment = 0x06 & (result >> 4); \ - status.extend_flag = status.carry_flag = Status::FlagT( \ - (unadjusted_result - low_adjustment) & 0x300 \ - ); \ - result = result + top - low_adjustment; \ - \ - /* Store the result. */ \ - d = uint8_t(result); \ - \ - /* Set all remaining flags essentially as if this were normal subtraction. */ \ - status.zero_result |= d; \ - status.negative_flag = result & 0x80; \ - status.overflow_flag = unadjusted_result & ~result & 0x80; \ - /* SBCD subtracts the lowest byte of the source from that of the destination using BCD arithmetic, obeying the extend flag. */ - case Operation::SBCD: { - const uint8_t source = src.b; - const uint8_t destination = dest.b; - sbcd(dest.b); - } break; + case Operation::SBCD: + Primitive::sbcd(src.b, dest.b, dest.b, status); + break; /* NBCD is like SBCD except that the result is 0 - source rather than destination - source. */ - case Operation::NBCD: { - const uint8_t source = src.b; - const uint8_t destination = 0; - sbcd(src.b); - } break; - -#undef sbcd + case Operation::NBCD: + Primitive::sbcd(src.b, 0, src.b, status); + break; // EXG and SWAP exchange/swap words or long words. From f095bba1ca16803b909a72f5e0090ee11e7ba4f2 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 15:21:54 -0400 Subject: [PATCH 22/35] Eliminate bitwise macros. --- .../Implementation/PerformImplementation.hpp | 65 ++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 86d9b1a79..262808d1f 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -93,9 +93,34 @@ inline void sbcd(uint8_t rhs, uint8_t lhs, uint8_t &destination, Status &status) status.overflow_flag = unadjusted_result & ~result & 0x80; } +template +void bitwise(IntT source, IntT &destination, Status &status) { + static_assert( + operation == Operation::ANDb || operation == Operation::ANDw || operation == Operation::ANDl || + operation == Operation::ORb || operation == Operation::ORw || operation == Operation::ORl || + operation == Operation::EORb || operation == Operation::EORw || operation == Operation::EORl + ); + + switch(operation) { + case Operation::ANDb: case Operation::ANDw: case Operation::ANDl: + destination &= source; + break; + case Operation::ORb: case Operation::ORw: case Operation::ORl: + destination |= source; + break; + case Operation::EORb: case Operation::EORw: case Operation::EORl: + destination ^= source; + break; + } + + status.overflow_flag = status.carry_flag = 0; + status.zero_result = destination; + status.negative_flag = destination & top_bit(); +} + /// Performs a compare of @c source to @c destination, setting zero, carry, negative and overflow flags. template -static void compare(IntT source, IntT destination, Status &status) { +void compare(IntT source, IntT destination, Status &status) { const IntT result = destination - source; status.zero_result = result; status.carry_flag = result > destination; @@ -733,37 +758,17 @@ template < Bitwise operators: AND, OR and EOR. All three clear the overflow and carry flags, and set zero and negative appropriately. */ -#define op_and(x, y) x &= y -#define op_or(x, y) x |= y -#define op_eor(x, y) x ^= y + case Operation::ANDb: Primitive::bitwise(src.b, dest.b, status); break; + case Operation::ANDw: Primitive::bitwise(src.w, dest.w, status); break; + case Operation::ANDl: Primitive::bitwise(src.l, dest.l, status); break; -#define bitwise(source, dest, sign_mask, operator) \ - operator(dest, source); \ - status.overflow_flag = status.carry_flag = 0; \ - status.zero_result = dest; \ - status.negative_flag = dest & sign_mask; + case Operation::ORb: Primitive::bitwise(src.b, dest.b, status); break; + case Operation::ORw: Primitive::bitwise(src.w, dest.w, status); break; + case Operation::ORl: Primitive::bitwise(src.l, dest.l, status); break; -#define andx(source, dest, sign_mask) bitwise(source, dest, sign_mask, op_and) -#define eorx(source, dest, sign_mask) bitwise(source, dest, sign_mask, op_eor) -#define orx(source, dest, sign_mask) bitwise(source, dest, sign_mask, op_or) - -#define op_bwl(name, op) \ - case Operation::name##b: op(src.b, dest.b, 0x80); break; \ - case Operation::name##w: op(src.w, dest.w, 0x8000); break; \ - case Operation::name##l: op(src.l, dest.l, 0x80000000); break; - - op_bwl(AND, andx); - op_bwl(EOR, eorx); - op_bwl(OR, orx); - -#undef op_bwl -#undef orx -#undef eorx -#undef andx -#undef bitwise -#undef op_eor -#undef op_or -#undef op_and + case Operation::EORb: Primitive::bitwise(src.b, dest.b, status); break; + case Operation::EORw: Primitive::bitwise(src.w, dest.w, status); break; + case Operation::EORl: Primitive::bitwise(src.l, dest.l, status); break; // NOTs: take the logical inverse, affecting the negative and zero flags. case Operation::NOTb: From 8148397f62e56a04c3e52e3e41cef695c3fd9333 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 15:37:13 -0400 Subject: [PATCH 23/35] Fill in comments, eliminate u/s_extend16 macros. --- .../Implementation/PerformImplementation.hpp | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 262808d1f..40e024307 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -17,8 +17,11 @@ namespace InstructionSet { namespace M68k { -#define u_extend16(x) uint32_t(int16_t(x)) -#define s_extend16(x) int32_t(int16_t(x)) +/// Sign-extend @c x to 32 bits and return as an unsigned 32-bit int. +inline uint32_t u_extend16(uint16_t x) { return uint32_t(int16_t(x)); } + +/// Sign-extend @c x to 32 bits and return as a signed 32-bit int. +inline int32_t s_extend16(uint16_t x) { return int32_t(int16_t(x)); } namespace Primitive { @@ -71,6 +74,10 @@ static void add_sub(IntT source, IntT &destination, Status &status) { destination = result; } +/// Perform an SBCD of @c lhs - @c rhs, storing the result to @c destination and updating @c status. +/// +/// @discussion The slightly awkward abandonment of source, destination permits the use of this for both +/// SBCD and NBCD. inline void sbcd(uint8_t rhs, uint8_t lhs, uint8_t &destination, Status &status) { const int extend = (status.extend_flag ? 1 : 0); const int unadjusted_result = lhs - rhs - extend; @@ -93,6 +100,8 @@ inline void sbcd(uint8_t rhs, uint8_t lhs, uint8_t &destination, Status &status) status.overflow_flag = unadjusted_result & ~result & 0x80; } +/// Perform the bitwise operation defined by @c operation on @c source and @c destination and update @c status. +/// Bitwise operations are any of the byte, word or long versions of AND, OR and EOR. template void bitwise(IntT source, IntT &destination, Status &status) { static_assert( @@ -118,7 +127,7 @@ void bitwise(IntT source, IntT &destination, Status &status) { status.negative_flag = destination & top_bit(); } -/// Performs a compare of @c source to @c destination, setting zero, carry, negative and overflow flags. +/// Compare of @c source to @c destination, setting zero, carry, negative and overflow flags. template void compare(IntT source, IntT destination, Status &status) { const IntT result = destination - source; @@ -134,7 +143,7 @@ inline uint32_t mask_bit(const Preinstruction &instruction, uint32_t source) { return source & (instruction.mode<1>() == AddressingMode::DataRegisterDirect ? 31 : 7); } -/// Performs a BCLR, BCHG or BSET as specified by @c operation and described by @c instruction, @c source and @c destination, updating @c destination and @c status. +/// Perform a BCLR, BCHG or BSET as specified by @c operation and described by @c instruction, @c source and @c destination, updating @c destination and @c status. /// Also makes an appropriate notification to the @c flow_controller. template void bit_manipulate(const Preinstruction &instruction, uint32_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { @@ -159,6 +168,7 @@ template void clear(IntT &destination, Status &status) { status.negative_flag = status.overflow_flag = status.carry_flag = status.zero_result = 0; } +/// Perform an ANDI, EORI or ORI to either SR or CCR, notifying @c flow_controller if appropriate. template void apply_sr_ccr(uint16_t source, Status &status, FlowController &flow_controller) { static_assert( @@ -196,6 +206,7 @@ void apply_sr_ccr(uint16_t source, Status &status, FlowController &flow_controll } } +/// Perform a MULU or MULS between @c source and @c destination, updating @c status and notifying @c flow_controller. template void multiply(uint16_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { if constexpr (is_mulu) { @@ -214,6 +225,7 @@ void multiply(uint16_t source, uint32_t &destination, Status &status, FlowContro } } +/// Announce a DIVU or DIVS to @c flow_controller. template void did_divide(IntT dividend, IntT divisor, FlowController &flow_controller) { if constexpr (is_divu) { @@ -223,6 +235,7 @@ void did_divide(IntT dividend, IntT divisor, FlowController &flow_controller) { } } +/// Perform a DIVU or DIVS between @c source and @c destination, updating @c status and notifying @c flow_controller. template void divide(uint16_t source, uint32_t &destination, Status &status, FlowController &flow_controller) { status.carry_flag = 0; @@ -254,6 +267,7 @@ void divide(uint16_t source, uint32_t &destination, Status &status, FlowControll did_divide(dividend, divisor, flow_controller); } +/// Move @c source to @c destination, updating @c status. template void move(IntT source, IntT &destination, Status &status) { destination = source; status.zero_result = Status::FlagT(source); @@ -261,6 +275,7 @@ template void move(IntT source, IntT &destination, Status &statu status.overflow_flag = status.carry_flag = 0; } +/// Perform NEG.[b/l/w] on @c source, updating @c status. template void negative(IntT &source, Status &status) { const IntT result = -source - (is_extend && status.extend_flag ? 1 : 0); @@ -276,12 +291,14 @@ template void negative(IntT &source, Status &sta source = result; } +/// Perform TST.[b/l/w] with @c source, updating @c status. template void test(IntT source, Status &status) { status.carry_flag = status.overflow_flag = 0; status.zero_result = Status::FlagT(source); status.negative_flag = status.zero_result & top_bit(); } +/// Decodes the proper shift distance from @c source, notifying the @c flow_controller. template int shift_count(uint8_t source, FlowController &flow_controller) { const int count = source & 63; flow_controller.template did_shift(count); @@ -293,6 +310,7 @@ template constexpr int bit_size() { return sizeof(IntT) * 8; } +/// Set the zero and negative flags on @c status according to @c result. template void set_neg_zero(IntT result, Status &status) { status.zero_result = Status::FlagT(result); status.negative_flag = result & top_bit(); @@ -985,9 +1003,6 @@ template < break; } -#undef u_extend16 -#undef s_extend16 - } } From 555250dbd92da578494d3363da82ba491abb822a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 22:19:35 -0400 Subject: [PATCH 24/35] Don't trample on X before use. --- .../M68k/Implementation/PerformImplementation.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 40e024307..88d0af4e6 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -435,20 +435,22 @@ template void rox( } else { switch(operation) { case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: - status.carry_flag = status.extend_flag = Status::FlagT((destination >> (size - shift)) & 1); + status.carry_flag = Status::FlagT((destination >> (size - shift)) & 1); destination = IntT( (destination << shift) | (IntT(status.extend_flag ? 1 : 0) << (shift - 1)) | (destination >> (size + 1 - shift)) ); + status.extend_flag = status.carry_flag; break; case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl: - status.carry_flag = status.extend_flag = Status::FlagT(destination & (1 << (shift - 1))); + status.carry_flag = Status::FlagT(destination & (1 << (shift - 1))); destination = IntT( (destination >> shift) | ((status.extend_flag ? top_bit() : 0) >> (shift - 1)) | (destination << (size + 1 - shift)) ); + status.extend_flag = status.carry_flag; break; } } From abb19e6670bfc278b95b30a1b5b438ee1f983ee5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 22:57:21 -0400 Subject: [PATCH 25/35] Populate carry whenever count != 0, regardless of modulo. --- InstructionSets/M68k/Implementation/PerformImplementation.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 88d0af4e6..84e0ea7b5 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -392,11 +392,13 @@ template void rota ); const auto size = bit_size(); - const auto shift = shift_count(uint8_t(source), flow_controller) & (size - 1); + auto shift = shift_count(uint8_t(source), flow_controller); if(!shift) { status.carry_flag = 0; } else { + shift &= size - 1; + switch(operation) { case Operation::ROLb: case Operation::ROLw: case Operation::ROLl: destination = IntT( From fb2b7969a247d9f9b48f8f5d327b08699913a9d8 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 23:14:14 -0400 Subject: [PATCH 26/35] Add TODO to self on undefined behaviour. --- .../M68k/Implementation/PerformImplementation.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 84e0ea7b5..b93826faa 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -435,6 +435,13 @@ template void rox( // When shift is zero, extend is unaffected but is copied to carry. status.carry_flag = status.extend_flag; } else { + // TODO: if the value of the right operand is negative or is greater or equal to + // the number of bits in the promoted left operand, the behavior is undefined. + // + // i.e. for a long if shift >= 32, + // or size + 1 - shift >= 32, i.e. 1 - shift >= 0, 1 >= shift; i.e. shift <= 1 + // ... the code below is undefined behaviour. + switch(operation) { case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: status.carry_flag = Status::FlagT((destination >> (size - shift)) & 1); From a364499d17be20e9f2b1db7199e7bfd66f172181 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 17 Oct 2022 23:15:45 -0400 Subject: [PATCH 27/35] Revert inadvertent commits. --- .../68000ComparativeTests.mm | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm index 47b30b9b3..94c6cddce 100644 --- a/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm +++ b/OSBindings/Mac/Clock SignalTests/68000ComparativeTests.mm @@ -307,8 +307,7 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler { if(!address || !value) break; XCTAssertEqual(test68000->ram[address.integerValue ^ 1], value.integerValue, @"%@: Memory at location %@ inconsistent", name, address); - if(test68000->ram[address.integerValue ^ 1] != value.integerValue) - [_failures addObject:name]; + if(test68000->ram[address.integerValue ^ 1] != value.integerValue) [_failures addObject:name]; } // Consider collating extra detail. @@ -360,8 +359,7 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler { NSNumber *const value = [enumerator nextObject]; if(!address || !value) break; - if(_testExecutor->ram[address.integerValue] != value.integerValue) - [_failures addObject:name]; + if(_testExecutor->ram[address.integerValue] != value.integerValue) [_failures addObject:name]; } // If this test is now in the failures set, add the corresponding opcode for @@ -427,17 +425,12 @@ struct TestProcessor: public CPU::MC68000Mk2::BusHandler { const NSString *dX = [@"d" stringByAppendingFormat:@"%d", c]; const NSString *aX = [@"a" stringByAppendingFormat:@"%d", c]; - if(registers.data[c] != [finalState[dX] integerValue]) - [_failures addObject:name]; - if(c < 7 && registers.address[c] != [finalState[aX] integerValue]) - [_failures addObject:name]; + if(registers.data[c] != [finalState[dX] integerValue]) [_failures addObject:name]; + if(c < 7 && registers.address[c] != [finalState[aX] integerValue]) [_failures addObject:name]; } - if(registers.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) - [_failures addObject:name]; - if(registers.user_stack_pointer != [finalState[@"usp"] integerValue]) - [_failures addObject:name]; - if(registers.program_counter + pcOffset != [finalState[@"pc"] integerValue]) - [_failures addObject:name]; + if(registers.supervisor_stack_pointer != [finalState[@"a7"] integerValue]) [_failures addObject:name]; + if(registers.user_stack_pointer != [finalState[@"usp"] integerValue]) [_failures addObject:name]; + if(registers.program_counter + pcOffset != [finalState[@"pc"] integerValue]) [_failures addObject:name]; const uint16_t correctSR = [finalState[@"sr"] integerValue]; if(registers.status != correctSR) { From a1ae7c28b28c65f5e1f084165d47663b6750cd69 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 18 Oct 2022 11:30:40 -0400 Subject: [PATCH 28/35] Add various insurances against undefined behaviour. --- .../Implementation/PerformImplementation.hpp | 121 ++++++++++++------ 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index b93826faa..d1d62eb68 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -325,7 +325,7 @@ template void shif operation == Operation::LSRb || operation == Operation::LSRw || operation == Operation::LSRl ); - const auto size = bit_size(); + constexpr auto size = bit_size(); const auto shift = shift_count(uint8_t(source), flow_controller); if(!shift) { @@ -352,30 +352,46 @@ template void shif switch(type) { case Type::ASL: case Type::LSL: - status.overflow_flag = - type == Type::LSL ? - 0 : (destination ^ (destination << shift)) & top_bit(); - if(shift < size) { - status.carry_flag = status.extend_flag = (destination >> (size - shift)) & 1; - } else { + if(shift > size) { status.carry_flag = status.extend_flag = 0; + } else { + status.carry_flag = status.extend_flag = (destination << (shift - 1)) & top_bit(); + } + + if(type == Type::LSL) { + status.overflow_flag = 0; + } else { + if(shift >= size) { + status.overflow_flag = destination & top_bit(); + } else { + status.overflow_flag = (destination ^ (destination << shift)) & top_bit(); + } + } + + if(shift >= size) { + destination = 0; + } else { + destination <<= shift; } - destination <<= shift; break; case Type::ASR: case Type::LSR: { + if(shift > size) { + status.carry_flag = status.extend_flag = 0; + } else { + status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1; + } + status.overflow_flag = 0; + const IntT sign_word = type == Type::LSR ? 0 : (destination & top_bit() ? IntT(~0) : 0); - status.overflow_flag = 0; - status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1; - - if(shift < size) { - destination = IntT((destination >> shift) | (sign_word << (size - shift))); - } else { + if(shift >= size) { destination = sign_word; + } else { + destination = IntT((destination >> shift) | (sign_word << (size - shift))); } } break; } @@ -391,7 +407,7 @@ template void rota operation == Operation::RORb || operation == Operation::RORw || operation == Operation::RORl ); - const auto size = bit_size(); + constexpr auto size = bit_size(); auto shift = shift_count(uint8_t(source), flow_controller); if(!shift) { @@ -401,17 +417,21 @@ template void rota switch(operation) { case Operation::ROLb: case Operation::ROLw: case Operation::ROLl: - destination = IntT( - (destination << shift) | - (destination >> (size - shift)) - ); + if(shift) { + destination = IntT( + (destination << shift) | + (destination >> (size - shift)) + ); + } status.carry_flag = Status::FlagT(destination & 1); break; case Operation::RORb: case Operation::RORw: case Operation::RORl: - destination = IntT( - (destination >> shift) | - (destination << (size - shift)) - ); + if(shift) { + destination = IntT( + (destination >> shift) | + (destination << (size - shift)) + ); + } status.carry_flag = Status::FlagT(destination & top_bit()); break; } @@ -428,37 +448,56 @@ template void rox( operation == Operation::ROXRb || operation == Operation::ROXRw || operation == Operation::ROXRl ); - const auto size = bit_size(); + constexpr auto size = bit_size(); auto shift = shift_count(uint8_t(source), flow_controller) % (size + 1); if(!shift) { // When shift is zero, extend is unaffected but is copied to carry. status.carry_flag = status.extend_flag; } else { - // TODO: if the value of the right operand is negative or is greater or equal to - // the number of bits in the promoted left operand, the behavior is undefined. - // - // i.e. for a long if shift >= 32, - // or size + 1 - shift >= 32, i.e. 1 - shift >= 0, 1 >= shift; i.e. shift <= 1 - // ... the code below is undefined behaviour. - switch(operation) { case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: status.carry_flag = Status::FlagT((destination >> (size - shift)) & 1); - destination = IntT( - (destination << shift) | - (IntT(status.extend_flag ? 1 : 0) << (shift - 1)) | - (destination >> (size + 1 - shift)) - ); + + if(shift == bit_size()) { + destination = IntT( + (status.extend_flag ? top_bit() : 0) | + (destination >> 1) + ); + } else if(shift == 1) { + destination = IntT( + (destination << 1) | + IntT(status.extend_flag ? 1 : 0) + ); + } else { + destination = IntT( + (destination << shift) | + (IntT(status.extend_flag ? 1 : 0) << (shift - 1)) | + (destination >> (size + 1 - shift)) + ); + } status.extend_flag = status.carry_flag; break; case Operation::ROXRb: case Operation::ROXRw: case Operation::ROXRl: status.carry_flag = Status::FlagT(destination & (1 << (shift - 1))); - destination = IntT( - (destination >> shift) | - ((status.extend_flag ? top_bit() : 0) >> (shift - 1)) | - (destination << (size + 1 - shift)) - ); + + if(shift == bit_size()) { + destination = IntT( + (status.extend_flag ? 1 : 0) | + (destination << 1) + ); + } else if(shift == 1) { + destination = IntT( + (destination >> 1) | + (status.extend_flag ? top_bit() : 0) + ); + } else { + destination = IntT( + (destination >> shift) | + ((status.extend_flag ? top_bit() : 0) >> (shift - 1)) | + (destination << (size + 1 - shift)) + ); + } status.extend_flag = status.carry_flag; break; } From 5560a0ed3961fd7d165b2eadac330d95fda99992 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 18 Oct 2022 11:47:36 -0400 Subject: [PATCH 29/35] Fix overflow test for ASL. --- .../M68k/Implementation/PerformImplementation.hpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index d1d62eb68..47e6000d3 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -361,10 +361,16 @@ template void shif if(type == Type::LSL) { status.overflow_flag = 0; } else { + // Overflow records whether the top bit changed at any point during the operation. if(shift >= size) { - status.overflow_flag = destination & top_bit(); + // The result is going to be all bits evacuated through the top giving a net + // result of 0, so overflow is set if any bit was originally set. + status.overflow_flag = destination; } else { - status.overflow_flag = (destination ^ (destination << shift)) & top_bit(); + // For a shift of n places, overflow will be set if the top n bits were not + // all the same value. + const auto affected_bits = IntT(~0 << shift); + status.overflow_flag = (destination & affected_bits) && (destination & affected_bits) != affected_bits; } } @@ -382,7 +388,7 @@ template void shif } else { status.carry_flag = status.extend_flag = (destination >> (shift - 1)) & 1; } - status.overflow_flag = 0; + status.overflow_flag = 0; // The top bit can't change during an ASR, and LSR always clears overflow. const IntT sign_word = type == Type::LSR ? From b31b4a5d10ca5b63388656a9c8ab7241b42eda6b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 18 Oct 2022 12:17:55 -0400 Subject: [PATCH 30/35] Reformulate NOT in terms of EOR, and clean up elsewhere. --- .../Implementation/PerformImplementation.hpp | 53 ++++--------------- 1 file changed, 11 insertions(+), 42 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 47e6000d3..751224a14 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -11,6 +11,7 @@ #include "../ExceptionVectors.hpp" +#include #include #include @@ -810,7 +811,6 @@ template < LINK and UNLINK help with stack frames, allowing a certain amount of stack space to be allocated or deallocated. */ - case Operation::LINKw: flow_controller.link(instruction, uint32_t(int16_t(dest.w))); break; @@ -820,10 +820,8 @@ template < break; /* - TAS: sets zero and negative depending on the current value of the destination, - and sets the high bit, using a specialised atomic bus cycle. + TAS: requiring a specialised bus cycle, just kick this out to the flow controller. */ - case Operation::TAS: flow_controller.tas(instruction, src.l); break; @@ -844,27 +842,9 @@ template < case Operation::EORw: Primitive::bitwise(src.w, dest.w, status); break; case Operation::EORl: Primitive::bitwise(src.l, dest.l, status); break; - // NOTs: take the logical inverse, affecting the negative and zero flags. - case Operation::NOTb: - src.b ^= 0xff; - status.zero_result = src.b; - status.negative_flag = status.zero_result & 0x80; - status.overflow_flag = status.carry_flag = 0; - break; - - case Operation::NOTw: - src.w ^= 0xffff; - status.zero_result = src.w; - status.negative_flag = status.zero_result & 0x8000; - status.overflow_flag = status.carry_flag = 0; - break; - - case Operation::NOTl: - src.l ^= 0xffffffff; - status.zero_result = src.l; - status.negative_flag = status.zero_result & 0x80000000; - status.overflow_flag = status.carry_flag = 0; - break; + case Operation::NOTb: Primitive::bitwise(uint8_t(~0), src.b, status); break; + case Operation::NOTw: Primitive::bitwise(uint16_t(~0), src.w, status); break; + case Operation::NOTl: Primitive::bitwise(uint32_t(~0), src.l, status); break; /* SBCD subtracts the lowest byte of the source from that of the destination using @@ -892,12 +872,10 @@ template < case Operation::SWAP: { uint16_t *const words = reinterpret_cast(&src.l); - const auto temporary = words[0]; - words[0] = words[1]; - words[1] = temporary; + std::swap(words[0], words[1]); status.zero_result = src.l; - status.negative_flag = temporary & 0x8000; + status.negative_flag = src.l & Primitive::top_bit(); status.overflow_flag = status.carry_flag = 0; } break; @@ -1019,24 +997,15 @@ template < break; /* - RTE and RTR share an implementation. + RTE, RTR and RTS defer to the flow controller. */ - case Operation::RTR: - flow_controller.rtr(); - break; - - case Operation::RTE: - flow_controller.rte(); - break; - - case Operation::RTS: - flow_controller.rts(); - break; + case Operation::RTR: flow_controller.rtr(); break; + case Operation::RTE: flow_controller.rte(); break; + case Operation::RTS: flow_controller.rts(); break; /* TSTs: compare to zero. */ - case Operation::TSTb: Primitive::test(src.b, status); break; case Operation::TSTw: Primitive::test(src.w, status); break; case Operation::TSTl: Primitive::test(src.l, status); break; From d09473b66f6709331d21ab5b86e344aae92c23a6 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 18 Oct 2022 14:51:51 -0400 Subject: [PATCH 31/35] Move common negative and zero logic into Status. --- .../Implementation/PerformImplementation.hpp | 63 +++++++------------ InstructionSets/M68k/Status.hpp | 12 ++++ 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 751224a14..db3babcd6 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -70,7 +70,7 @@ static void add_sub(IntT source, IntT &destination, Status &status) { } status.extend_flag = status.carry_flag = is_add ? result < destination : result > destination; - status.negative_flag = Status::FlagT(result & top_bit()); + status.set_negative(result); status.overflow_flag = overflow(source, destination, result); destination = result; } @@ -97,7 +97,7 @@ inline void sbcd(uint8_t rhs, uint8_t lhs, uint8_t &destination, Status &status) /* Set all remaining flags essentially as if this were normal subtraction. */ status.zero_result |= destination; - status.negative_flag = result & 0x80; + status.set_negative(destination); status.overflow_flag = unadjusted_result & ~result & 0x80; } @@ -124,17 +124,15 @@ void bitwise(IntT source, IntT &destination, Status &status) { } status.overflow_flag = status.carry_flag = 0; - status.zero_result = destination; - status.negative_flag = destination & top_bit(); + status.set_neg_zero(destination); } /// Compare of @c source to @c destination, setting zero, carry, negative and overflow flags. template void compare(IntT source, IntT destination, Status &status) { const IntT result = destination - source; - status.zero_result = result; status.carry_flag = result > destination; - status.negative_flag = result & top_bit(); + status.set_neg_zero(result); status.overflow_flag = Primitive::overflow(source, destination, result); } @@ -216,8 +214,7 @@ void multiply(uint16_t source, uint32_t &destination, Status &status, FlowContro destination = u_extend16(source) * u_extend16(uint16_t(destination)); } status.carry_flag = status.overflow_flag = 0; - status.zero_result = destination; - status.negative_flag = status.zero_result & top_bit(); + status.set_neg_zero(destination); if constexpr (is_mulu) { flow_controller.did_mulu(source); @@ -264,15 +261,14 @@ void divide(uint16_t source, uint32_t &destination, Status &status, FlowControll status.overflow_flag = 0; status.zero_result = Status::FlagT(quotient); - status.negative_flag = status.zero_result & 0x8000; + status.set_negative(uint16_t(quotient)); did_divide(dividend, divisor, flow_controller); } /// Move @c source to @c destination, updating @c status. template void move(IntT source, IntT &destination, Status &status) { destination = source; - status.zero_result = Status::FlagT(source); - status.negative_flag = status.zero_result & top_bit(); + status.set_neg_zero(destination); status.overflow_flag = status.carry_flag = 0; } @@ -286,7 +282,7 @@ template void negative(IntT &source, Status &sta status.zero_result = result; } status.extend_flag = status.carry_flag = result; // i.e. any value other than 0 will result in carry. - status.negative_flag = result & top_bit(); + status.set_negative(result); status.overflow_flag = Primitive::overflow(source, IntT(0), result); source = result; @@ -295,8 +291,7 @@ template void negative(IntT &source, Status &sta /// Perform TST.[b/l/w] with @c source, updating @c status. template void test(IntT source, Status &status) { status.carry_flag = status.overflow_flag = 0; - status.zero_result = Status::FlagT(source); - status.negative_flag = status.zero_result & top_bit(); + status.set_neg_zero(source); } /// Decodes the proper shift distance from @c source, notifying the @c flow_controller. @@ -311,12 +306,6 @@ template constexpr int bit_size() { return sizeof(IntT) * 8; } -/// Set the zero and negative flags on @c status according to @c result. -template void set_neg_zero(IntT result, Status &status) { - status.zero_result = Status::FlagT(result); - status.negative_flag = result & top_bit(); -} - /// Perform an arithmetic or logical shift, i.e. any of LSL, LSR, ASL or ASR. template void shift(uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) { static_assert( @@ -404,7 +393,7 @@ template void shif } } - set_neg_zero(destination, status); + status.set_neg_zero(destination); } /// Perform a rotate without extend, i.e. any of RO[L/R].[b/w/l]. @@ -444,7 +433,7 @@ template void rota } } - set_neg_zero(destination, status); + status.set_neg_zero(destination); status.overflow_flag = 0; } @@ -510,7 +499,7 @@ template void rox( } } - set_neg_zero(destination, status); + status.set_neg_zero(destination); status.overflow_flag = 0; } @@ -545,7 +534,7 @@ template < // Set all flags essentially as if this were normal addition. status.zero_result |= result & 0xff; status.extend_flag = status.carry_flag = uint_fast32_t(result & ~0xff); - status.negative_flag = result & 0x80; + status.set_negative(uint8_t(result)); status.overflow_flag = ~unadjusted_result & result & 0x80; // Store the result. @@ -718,15 +707,13 @@ template < case Operation::EXTbtow: src.w = uint16_t(int8_t(src.b)); status.overflow_flag = status.carry_flag = 0; - status.zero_result = src.w; - status.negative_flag = status.zero_result & 0x8000; + status.set_neg_zero(src.w); break; case Operation::EXTwtol: src.l = u_extend16(src.w); status.overflow_flag = status.carry_flag = 0; - status.zero_result = src.l; - status.negative_flag = status.zero_result & 0x80000000; + status.set_neg_zero(src.l); break; case Operation::ANDItoSR: Primitive::apply_sr_ccr(src.w, status, flow_controller); break; @@ -873,9 +860,7 @@ template < case Operation::SWAP: { uint16_t *const words = reinterpret_cast(&src.l); std::swap(words[0], words[1]); - - status.zero_result = src.l; - status.negative_flag = src.l & Primitive::top_bit(); + status.set_neg_zero(src.l); status.overflow_flag = status.carry_flag = 0; } break; @@ -886,42 +871,42 @@ template < status.extend_flag = status.carry_flag = src.w & Primitive::top_bit(); status.overflow_flag = (src.w ^ (src.w << 1)) & Primitive::top_bit(); src.w <<= 1; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::LSLm: status.extend_flag = status.carry_flag = src.w & Primitive::top_bit(); status.overflow_flag = 0; src.w <<= 1; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::ASRm: status.extend_flag = status.carry_flag = src.w & 1; status.overflow_flag = 0; src.w = (src.w & Primitive::top_bit()) | (src.w >> 1); - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::LSRm: status.extend_flag = status.carry_flag = src.w & 1; status.overflow_flag = 0; src.w >>= 1; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::ROLm: src.w = uint16_t((src.w << 1) | (src.w >> 15)); status.carry_flag = src.w & 0x0001; status.overflow_flag = 0; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::RORm: src.w = uint16_t((src.w >> 1) | (src.w << 15)); status.carry_flag = src.w & Primitive::top_bit(); status.overflow_flag = 0; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::ROXLm: @@ -929,7 +914,7 @@ template < src.w = uint16_t((src.w << 1) | (status.extend_flag ? 0x0001 : 0x0000)); status.extend_flag = status.carry_flag; status.overflow_flag = 0; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::ROXRm: @@ -937,7 +922,7 @@ template < src.w = uint16_t((src.w >> 1) | (status.extend_flag ? 0x8000 : 0x0000)); status.extend_flag = status.carry_flag; status.overflow_flag = 0; - Primitive::set_neg_zero(src.w, status); + status.set_neg_zero(src.w); break; case Operation::ASLb: Primitive::shift(src.l, dest.b, status, flow_controller); break; diff --git a/InstructionSets/M68k/Status.hpp b/InstructionSets/M68k/Status.hpp index 583d4fe3b..22f15c52d 100644 --- a/InstructionSets/M68k/Status.hpp +++ b/InstructionSets/M68k/Status.hpp @@ -54,6 +54,18 @@ struct Status { FlagT overflow_flag = 0; // The overflow flag is set if and only if this value is non-zero. FlagT negative_flag = 0; // The negative flag is set if and only this value is non-zero. + /// Sets the negative flag per @c value + template void set_negative(IntT value) { + constexpr auto top_bit = IntT(1 << ((sizeof(IntT) * 8) - 1)); + negative_flag = value & top_bit; + } + + /// Sets both the negative and zero flags according to @c value. + template void set_neg_zero(IntT value) { + zero_result = value; + set_negative(value); + } + /// Gets the current condition codes. constexpr uint16_t ccr() const { return From 979bf42541eb1e790f543ee5159b1d2b41af8fca Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 18 Oct 2022 22:43:17 -0400 Subject: [PATCH 32/35] Fix ASL overflow test. --- InstructionSets/M68k/Implementation/PerformImplementation.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index db3babcd6..0dc00c7fb 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -359,7 +359,9 @@ template void shif } else { // For a shift of n places, overflow will be set if the top n bits were not // all the same value. - const auto affected_bits = IntT(~0 << shift); + const auto affected_bits = IntT( + ~((top_bit() >> shift) - 1) + ); status.overflow_flag = (destination & affected_bits) && (destination & affected_bits) != affected_bits; } } From bc9ddacb8db1361534541866d96431f25e715f3e Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Oct 2022 14:40:29 -0400 Subject: [PATCH 33/35] Improve commentary. --- .../M68k/Implementation/PerformImplementation.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 0dc00c7fb..6e38892c9 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -357,11 +357,13 @@ template void shif // result of 0, so overflow is set if any bit was originally set. status.overflow_flag = destination; } else { - // For a shift of n places, overflow will be set if the top n bits were not + // For a shift of n places, overflow will be set if the top n+1 bits were not // all the same value. const auto affected_bits = IntT( ~((top_bit() >> shift) - 1) - ); + ); // e.g. shift = 1 => ~((0x80 >> 1) - 1) = ~(0x40 - 1) = ~0x3f = 0xc0, i.e. if shift is + // 1 then the top two bits are relevant to whether there was overflow. If they have the + // same value, i.e. are both 0 or are both 1, then there wasn't. Otherwise there was. status.overflow_flag = (destination & affected_bits) && (destination & affected_bits) != affected_bits; } } From df7f94f3624b90eddd4f687a8c8a59b462f21b3a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Oct 2022 14:41:08 -0400 Subject: [PATCH 34/35] Include MacintoshVolume in test build. --- OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 0f3880616..25161def6 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -316,6 +316,7 @@ 4B7136911F789C93008B8ED9 /* SegmentParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B71368F1F789C93008B8ED9 /* SegmentParser.cpp */; }; 4B74CF812312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; }; 4B74CF822312FA9C00500CE8 /* HFV.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B74CF802312FA9C00500CE8 /* HFV.cpp */; }; + 4B75EBFE28FF9CA20088AB22 /* MacintoshVolume.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4C81C928B56CF800F84AE9 /* MacintoshVolume.cpp */; }; 4B75F979280D7C5100121055 /* 68000DecoderTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B75F978280D7C5100121055 /* 68000DecoderTests.mm */; }; 4B75F97B280D7C7700121055 /* 68000 Decoding in Resources */ = {isa = PBXBuildFile; fileRef = 4B75F97A280D7C7700121055 /* 68000 Decoding */; }; 4B7752A628217DF80073E2C5 /* Dave.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BFEA2ED2682A7B900EBF94C /* Dave.cpp */; }; @@ -6183,6 +6184,7 @@ 4BEE4BD425A26E2B00011BD2 /* x86DecoderTests.mm in Sources */, 4B778F3623A5F1040000D260 /* Target.cpp in Sources */, 4B1D08061E0F7A1100763741 /* TimeTests.mm in Sources */, + 4B75EBFE28FF9CA20088AB22 /* MacintoshVolume.cpp in Sources */, 4B778F3D23A5F1750000D260 /* ncr5380.cpp in Sources */, 4BF701A026FFD32300996424 /* AmigaBlitterTests.mm in Sources */, 4B7752B428217ECB0073E2C5 /* ZXSpectrumTAP.cpp in Sources */, From ec728ad573193af500abebf2ada3b0c14ff7a950 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Wed, 19 Oct 2022 22:17:51 -0400 Subject: [PATCH 35/35] Fix ADD/SUBX carry. --- .../Implementation/PerformImplementation.hpp | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/InstructionSets/M68k/Implementation/PerformImplementation.hpp b/InstructionSets/M68k/Implementation/PerformImplementation.hpp index 6e38892c9..2043cbd9c 100644 --- a/InstructionSets/M68k/Implementation/PerformImplementation.hpp +++ b/InstructionSets/M68k/Implementation/PerformImplementation.hpp @@ -55,10 +55,23 @@ template static void add_sub(IntT source, IntT &destination, Status &status) { static_assert(!std::numeric_limits::is_signed); - const IntT extend = (is_extend && status.extend_flag) ? 1 : 0; - const IntT result = is_add ? - (destination + source + extend) : - (destination - source - extend); + IntT result = is_add ? + destination + source : + destination - source; + status.carry_flag = is_add ? result < destination : result > destination; + + // If this is an extend operation, there's a second opportunity to create carry, + // which requires a second test. + if(is_extend && status.extend_flag) { + if constexpr (is_add) { + ++result; + status.carry_flag |= result == 0; + } else { + status.carry_flag |= result == 0; + --result; + } + } + status.extend_flag = status.carry_flag; // Extend operations can reset the zero flag only; non-extend operations // can either set it or reset it. Which in the reverse-logic world of @@ -68,8 +81,6 @@ static void add_sub(IntT source, IntT &destination, Status &status) { } else { status.zero_result = Status::FlagT(result); } - status.extend_flag = - status.carry_flag = is_add ? result < destination : result > destination; status.set_negative(result); status.overflow_flag = overflow(source, destination, result); destination = result;