// // PerformImplementation.hpp // Clock Signal // // Created by Thomas Harte on 28/04/2022. // Copyright © 2022 Thomas Harte. All rights reserved. // #ifndef InstructionSets_M68k_PerformImplementation_h #define InstructionSets_M68k_PerformImplementation_h #include "../ExceptionVectors.hpp" #include #include #include namespace InstructionSet::M68k { /// 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 { /// @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 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); 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 // zero_result means ORing or storing. if constexpr (is_extend) { status.zero_result |= Status::FlagT(result); } else { status.zero_result = Status::FlagT(result); } status.set_negative(result); status.overflow_flag = overflow(source, destination, result); 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; 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.set_negative(destination); 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( 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.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.carry_flag = result > destination; status.set_neg_zero(result); 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) { return source & (instruction.mode<1>() == AddressingMode::DataRegisterDirect ? 31 : 7); } /// 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) { 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)); } /// 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; } /// 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( 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; } } /// 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) { destination = source * uint16_t(destination); } else { destination = u_extend16(source) * u_extend16(uint16_t(destination)); } status.carry_flag = status.overflow_flag = 0; status.set_neg_zero(destination); if constexpr (is_mulu) { flow_controller.did_mulu(source); } else { flow_controller.did_muls(source); } } /// Announce a DIVU or DIVS to @c flow_controller. 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); } } /// 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; 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.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.set_neg_zero(destination); 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); 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.set_negative(result); status.overflow_flag = Primitive::overflow(source, IntT(0), result); 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.set_neg_zero(source); } /// 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); return count; } /// @returns The number of bits in @c IntT. template constexpr int bit_size() { return sizeof(IntT) * 8; } /// 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 ); constexpr 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: 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 { // Overflow records whether the top bit changed at any point during the operation. if(shift >= size) { // 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 { // 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; } } if(shift >= size) { destination = 0; } else { 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; // The top bit can't change during an ASR, and LSR always clears overflow. const IntT sign_word = type == Type::LSR ? 0 : (destination & top_bit() ? IntT(~0) : 0); if(shift >= size) { destination = sign_word; } else { destination = IntT((destination >> shift) | (sign_word << (size - shift))); } } break; } } status.set_neg_zero(destination); } /// 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 ); constexpr auto size = bit_size(); 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: 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: if(shift) { destination = IntT( (destination >> shift) | (destination << (size - shift)) ); } status.carry_flag = Status::FlagT(destination & top_bit()); break; } } status.set_neg_zero(destination); status.overflow_flag = 0; } /// 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 ); 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 { switch(operation) { case Operation::ROXLb: case Operation::ROXLw: case Operation::ROXLl: status.carry_flag = Status::FlagT((destination >> (size - shift)) & 1); 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))); 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; } } status.set_neg_zero(destination); status.overflow_flag = 0; } } template < Model model, typename FlowController, Operation operation = Operation::Undefined > void perform(Preinstruction instruction, CPU::SlicedInt32 &src, CPU::SlicedInt32 &dest, Status &status, FlowController &flow_controller) { switch((operation != Operation::Undefined) ? operation : instruction.operation) { /* ABCD adds the lowest bytes from the source and destination using BCD arithmetic, obeying the extend flag. */ case Operation::ABCD: { // Pull out the two halves, for simplicity. const uint8_t source = src.b; const uint8_t destination = dest.b; const int extend = (status.extend_flag ? 1 : 0); // Perform the BCD add by evaluating the two nibbles separately. const int unadjusted_result = destination + source + extend; int result = (destination & 0xf) + (source & 0xf) + extend; result += (destination & 0xf0) + (source & 0xf0) + (((9 - result) >> 4) & 0x06); // i.e. ((result > 0x09) ? 0x06 : 0x00) result += ((0x9f - result) >> 4) & 0x60; // i.e. ((result > 0x9f) ? 0x60 : 0x00) // 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.set_negative(uint8_t(result)); status.overflow_flag = ~unadjusted_result & result & 0x80; // Store the result. dest.b = uint8_t(result); } break; // ADD and ADDA add two quantities, the latter sign extending and without setting any flags; // ADDQ and SUBQ act as ADD and SUB, but taking the second argument from the instruction code. case Operation::ADDb: Primitive::add_sub(src.b, dest.b, status); break; case Operation::SUBb: Primitive::add_sub(src.b, dest.b, status); break; case Operation::ADDXb: Primitive::add_sub(src.b, dest.b, status); break; case Operation::SUBXb: Primitive::add_sub(src.b, dest.b, status); break; case Operation::ADDw: Primitive::add_sub(src.w, dest.w, status); break; case Operation::SUBw: Primitive::add_sub(src.w, dest.w, status); break; case Operation::ADDXw: Primitive::add_sub(src.w, dest.w, status); break; case Operation::SUBXw: Primitive::add_sub(src.w, dest.w, status); break; case Operation::ADDl: Primitive::add_sub(src.l, dest.l, status); break; case Operation::SUBl: Primitive::add_sub(src.l, dest.l, status); break; case Operation::ADDXl: Primitive::add_sub(src.l, dest.l, status); break; case Operation::SUBXl: Primitive::add_sub(src.l, dest.l, status); break; case Operation::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; // BTST/BCLR/etc: modulo for the mask depends on whether memory or a data register is the target. 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( status.evaluate_condition(instruction.condition()), int8_t(src.b)); break; case Operation::Bccw: flow_controller.template complete_bcc( status.evaluate_condition(instruction.condition()), int16_t(src.w)); break; case Operation::Bccl: flow_controller.template complete_bcc( status.evaluate_condition(instruction.condition()), int32_t(src.l)); break; case Operation::BSRb: flow_controller.bsr(uint32_t(int8_t(src.b))); break; case Operation::BSRw: flow_controller.bsr(uint32_t(int16_t(src.w))); break; case Operation::BSRl: flow_controller.bsr(src.l); break; case Operation::DBcc: { const bool matched_condition = status.evaluate_condition(instruction.condition()); bool overflowed = false; // Classify the dbcc. if(!matched_condition) { -- src.w; overflowed = src.w == 0xffff; } // Take the branch. flow_controller.complete_dbcc( matched_condition, overflowed, int16_t(dest.w)); } break; case Operation::Scc: { const bool condition = status.evaluate_condition(instruction.condition()); src.b = condition ? 0xff : 0x00; flow_controller.did_scc(condition); } break; /* CLRs: store 0 to the destination, set the zero flag, and clear negative, overflow and carry. */ 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 of the source from the destination; the result of the subtraction is not stored. */ 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: Primitive::compare(src.l, dest.l, status); break; // JMP: copies EA(0) to the program counter. case Operation::JMP: flow_controller.jmp(src.l); break; // JSR: jump to EA(0), pushing the current PC to the stack. case Operation::JSR: flow_controller.jsr(src.l); break; /* 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: 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; MOVEA.w: move the least significant word and sign extend it. Neither sets any flags. */ case Operation::MOVEAw: dest.l = u_extend16(src.w); break; case Operation::MOVEAl: dest.l = src.l; break; case Operation::LEA: dest.l = src.l; break; case Operation::PEA: flow_controller.pea(src.l); break; /* Status word moves and manipulations. */ case Operation::MOVEtoSR: status.set_status(src.w); flow_controller.did_update_status(); break; case Operation::MOVEfromSR: src.w = status.status(); break; case Operation::MOVEtoCCR: status.set_ccr(src.w); break; case Operation::MOVEtoUSP: flow_controller.move_to_usp(src.l); break; case Operation::MOVEfromUSP: flow_controller.move_from_usp(src.l); break; case Operation::EXTbtow: src.w = uint16_t(int8_t(src.b)); status.overflow_flag = status.carry_flag = 0; status.set_neg_zero(src.w); break; case Operation::EXTwtol: src.l = u_extend16(src.w); status.overflow_flag = status.carry_flag = 0; status.set_neg_zero(src.l); break; 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. */ case Operation::MULUw: Primitive::multiply(src.w, dest.l, status, flow_controller); break; case Operation::MULSw: Primitive::multiply(src.w, dest.l, status, flow_controller); break; /* Divisions. */ case Operation::DIVUw: Primitive::divide(src.w, dest.l, status, flow_controller); break; case Operation::DIVSw: Primitive::divide(src.w, dest.l, status, flow_controller); break; // TRAP, which is a nicer form of ILLEGAL. case Operation::TRAP: flow_controller.template raise_exception(int(src.l + Exception::TrapBase)); break; case Operation::TRAPV: { if(status.overflow_flag) { flow_controller.template raise_exception(Exception::TRAPV); } } break; case Operation::CHKw: { const bool is_under = s_extend16(dest.w) < 0; const bool is_over = s_extend16(dest.w) > s_extend16(src.w); status.overflow_flag = status.carry_flag = 0; status.zero_result = dest.w; // Test applied for N: // // if Dn < 0, set negative flag; // otherwise, if Dn > , reset negative flag. if(is_over) status.negative_flag = 0; if(is_under) status.negative_flag = 1; // No exception is the default course of action; deviate only if an // exception is necessary. flow_controller.did_chk(is_under, is_over); if(is_under || is_over) { flow_controller.template raise_exception(Exception::CHK); } } break; /* NEGs: negatives the destination, setting the zero, negative, overflow and carry flags appropriate, and extend. NB: since the same logic as SUB is used to calculate overflow, and SUB calculates `destination - source`, the NEGs deliberately label 'source' and 'destination' differently from Motorola. */ 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: 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. */ case Operation::NOP: break; /* 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; case Operation::UNLINK: flow_controller.unlink(src.l); break; /* TAS: requiring a specialised bus cycle, just kick this out to the flow controller. */ case Operation::TAS: flow_controller.tas(instruction, src.l); break; /* Bitwise operators: AND, OR and EOR. All three clear the overflow and carry flags, and set zero and negative appropriately. */ 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; 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; 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; 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 BCD arithmetic, obeying the extend flag. */ 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: Primitive::sbcd(src.b, 0, src.b, status); break; // EXG and SWAP exchange/swap words or long words. case Operation::EXG: { const auto temporary = src.l; src.l = dest.l; dest.l = temporary; } break; case Operation::SWAP: { uint16_t *const words = reinterpret_cast(&src.l); std::swap(words[0], words[1]); status.set_neg_zero(src.l); status.overflow_flag = status.carry_flag = 0; } break; /* 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; 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; 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); 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; 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; 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; status.set_neg_zero(src.w); break; case Operation::ROXLm: status.carry_flag = src.w & Primitive::top_bit(); src.w = uint16_t((src.w << 1) | (status.extend_flag ? 0x0001 : 0x0000)); status.extend_flag = status.carry_flag; status.overflow_flag = 0; status.set_neg_zero(src.w); break; case Operation::ROXRm: status.carry_flag = src.w & 0x0001; src.w = uint16_t((src.w >> 1) | (status.extend_flag ? 0x8000 : 0x0000)); status.extend_flag = status.carry_flag; status.overflow_flag = 0; status.set_neg_zero(src.w); 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::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::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::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::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; case Operation::MOVEPl: flow_controller.template movep(instruction, src.l, dest.l); break; case Operation::MOVEPw: flow_controller.template movep(instruction, src.l, dest.l); break; case Operation::MOVEMtoRl: flow_controller.template movem_toR(instruction, src.l, dest.l); break; case Operation::MOVEMtoMl: flow_controller.template movem_toM(instruction, src.l, dest.l); break; case Operation::MOVEMtoRw: flow_controller.template movem_toR(instruction, src.l, dest.l); break; case Operation::MOVEMtoMw: flow_controller.template movem_toM(instruction, src.l, dest.l); break; /* 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; /* 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; case Operation::STOP: status.set_status(src.w); flow_controller.did_update_status(); flow_controller.stop(); break; case Operation::RESET: flow_controller.reset(); break; /* Development period debugging. */ default: assert(false); break; } } } #endif /* InstructionSets_M68k_PerformImplementation_h */