// // Perform.hpp // Clock Signal // // Created by Thomas Harte on 28/04/2022. // Copyright © 2022 Thomas Harte. All rights reserved. // #pragma once #include "Model.hpp" #include "Instruction.hpp" #include "Status.hpp" #include "../../Numeric/RegisterSizes.hpp" namespace InstructionSet::M68k { struct NullFlowController { // // Various operation-specific did-perform notfications; these all relate to operations // with variable timing on a 68000, providing the fields that contribute to that timing. // /// Indicates that a @c MULU was performed, providing the @c source operand. template <typename IntT> void did_mulu(IntT) {} /// Indicates that a @c MULS was performed, providing the @c source operand. template <typename IntT> void did_muls(IntT) {} /// Indicates that a @c CHK was performed, along with whether the result @c was_under zero or @c was_over the source operand. void did_chk([[maybe_unused]] bool was_under, [[maybe_unused]] bool was_over) {} /// Indicates an in-register shift or roll occurred, providing the number of bits shifted by /// and the type shifted. /// /// @c IntT may be uint8_t, uint16_t or uint32_t. template <typename IntT> void did_shift([[maybe_unused]] int bit_count) {} /// Indicates that a @c DIVU was performed, providing the @c dividend and @c divisor. /// If @c did_overflow is @c true then the divide ended in overflow. template <bool did_overflow> void did_divu([[maybe_unused]] uint32_t dividend, [[maybe_unused]] uint32_t divisor) {} /// Indicates that a @c DIVS was performed, providing the @c dividend and @c divisor. /// If @c did_overflow is @c true then the divide ended in overflow. template <bool did_overflow> void did_divs([[maybe_unused]] int32_t dividend, [[maybe_unused]] int32_t divisor) {} /// Indicates that a bit-manipulation operation (i.e. BTST, BCHG or BSET) was performed, affecting the bit at posiition @c bit_position. void did_bit_op([[maybe_unused]] int bit_position) {} /// Indicates that an @c Scc was performed; if @c did_set_ff is true then the condition was true and FF /// written to the operand; otherwise 00 was written. void did_scc([[maybe_unused]] bool did_set_ff) {} /// Provides a notification that the upper byte of the status register has been affected by the current instruction; /// this gives an opportunity to track the supervisor flag. void did_update_status() {} // // Operations that don't fit the reductive load-modify-store pattern; these are requests from perform // that the flow controller do something (and, correspondingly, do not have empty implementations). // // All offsets are the native values as encoded in the corresponding operations. // /// If @c matched_condition is @c true, apply the @c offset to the PC. template <typename IntT> void complete_bcc(bool matched_condition, IntT offset); /// If both @c matched_condition and @c overflowed are @c false, apply @c offset to the PC. void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset); /// Push the program counter of the next instruction to the stack, and add @c offset to the PC. void bsr(uint32_t offset); /// Push the program counter of the next instruction to the stack, and load @c offset to the PC. void jsr(uint32_t address); /// Set the program counter to @c address. void jmp(uint32_t address); /// Pop a word from the stack and use that to set the status condition codes. Then pop a new value for the PC. void rtr(); /// Pop a word from the stack and use that to set the entire status register. Then pop a new value for the PC. void rte(); /// Pop a new value for the PC from the stack. void rts(); /// Put the processor into the stopped state, waiting for interrupts. void stop(); /// Assert the reset output. void reset(); /// Perform LINK using the address register identified by @c instruction and the specified @c offset. void link(Preinstruction instruction, uint32_t offset); /// Perform unlink, with @c address being the target address register. void unlink(uint32_t &address); /// Push @c address to the stack. void pea(uint32_t address); /// Replace the current user stack pointer with @c address. /// The processor is guranteed to be in supervisor mode. void move_to_usp(uint32_t address); /// Put the value of the user stack pointer into @c address. /// The processor is guranteed to be in supervisor mode. void move_from_usp(uint32_t &address); /// Perform an atomic TAS cycle; if @c instruction indicates that this is a TAS Dn then /// perform the TAS directly upon that register; otherwise perform it on the memory at /// @c address. If this is a TAS Dn then @c address will contain the initial value of /// the register. void tas(Preinstruction instruction, uint32_t address); /// Use @c instruction to determine the direction of this MOVEP and perform it; /// @c source is the first operand provided to the MOVEP — either an address or register /// contents — and @c dest is the second. /// /// @c IntT may be either uint16_t or uint32_t. template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest); /// Perform a MOVEM to memory, from registers. @c instruction will indicate the mask as the first operand, /// and the target address and addressing mode as the second; the mask and address are also supplied /// as @c mask and @c address. If the addressing mode is -(An) then the address register will have /// been decremented already. /// /// The receiver is responsible for updating the address register if applicable. /// /// @c IntT may be either uint16_t or uint32_t. template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t mask, uint32_t address); /// Perform a MOVEM to registers, from memory. @c instruction will indicate the mask as the first operand, /// and the target address and addressing mode as the second; the mask and address are also supplied /// as @c mask and @c address. If the addressing mode is (An)+ then the address register will have been /// incremented, but @c address will be its value before that occurred. /// /// The receiver is responsible for updating the address register if applicable. /// /// @c IntT may be either uint16_t or uint32_t. template <typename IntT> void movem_toR(Preinstruction, uint32_t mask, uint32_t address); /// Raises a short-form exception using @c vector. If @c use_current_instruction_pc is @c true, /// the program counter for the current instruction is included in the resulting stack frame. Otherwise the program /// counter for the next instruction is used. template <bool use_current_instruction_pc = true> void raise_exception(int vector); }; /// Performs @c instruction using @c source and @c dest (one or both of which may be ignored as per /// the semantics of the operation). /// /// Any change in processor status will be applied to @c status. If this operation does not fit the reductive model /// of being a read and possibly a modify and possibly a write of up to two operands then the @c flow_controller /// will be asked to fill in the gaps. /// /// If the template parameter @c operation is not @c Operation::Undefined then that operation will be performed, ignoring /// whatever is specifed in @c instruction. This allows selection either at compile time or at run time; per Godbolt all modern /// compilers seem to be smart enough fully to optimise the compile-time case. template < Model model, typename FlowController, Operation operation = Operation::Undefined > void perform(Preinstruction, CPU::RegisterPair32 &source, CPU::RegisterPair32 &dest, Status &, FlowController &); } #include "Implementation/PerformImplementation.hpp"