mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-04 01:57:54 +00:00
Fully formulate and document the flow controller.
This commit is contained in:
parent
c635720a09
commit
477979c275
@ -36,7 +36,7 @@ struct BusHandler {
|
||||
/// Ties together the decoder, sequencer and performer to provide an executor for 680x0 instruction streams.
|
||||
/// As is standard for these executors, no bus- or cache-level fidelity to any real 680x0 is attempted. This is
|
||||
/// simply an executor of 680x0 code.
|
||||
template <Model model, typename BusHandler> class Executor {
|
||||
template <Model model, typename BusHandler> class Executor: public NullFlowController {
|
||||
public:
|
||||
Executor(BusHandler &);
|
||||
|
||||
@ -46,17 +46,10 @@ template <Model model, typename BusHandler> class Executor {
|
||||
/// will not necessarily take effect immediately when signalled.
|
||||
void run_for_instructions(int);
|
||||
|
||||
// Flow control.
|
||||
// Flow control; Cf. Perform.hpp.
|
||||
template <bool use_current_instruction_pc = true> void raise_exception(int);
|
||||
|
||||
void did_update_status();
|
||||
template <typename IntT> void did_mulu(IntT) {}
|
||||
template <typename IntT> void did_muls(IntT) {}
|
||||
void did_chk(bool was_under, bool was_over) {}
|
||||
void did_shift(int bit_count) {}
|
||||
template <bool did_overflow> void did_divu(uint32_t dividend, uint32_t divisor) {}
|
||||
template <bool did_overflow> void did_divs(int32_t dividend, int32_t divisor) {}
|
||||
void did_bit_op(int bit) {}
|
||||
|
||||
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset);
|
||||
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset);
|
||||
|
@ -18,14 +18,127 @@ namespace InstructionSet {
|
||||
namespace M68k {
|
||||
|
||||
struct NullFlowController {
|
||||
void raise_exception(int) {}
|
||||
void consume_cycles(int) {}
|
||||
//
|
||||
// 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.
|
||||
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) {}
|
||||
|
||||
/// 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();
|
||||
|
||||
/// 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);
|
||||
|
||||
/// 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 instruction, 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([[maybe_unused]] int vector);
|
||||
};
|
||||
|
||||
/// Performs @c instruction using @c source and @c dest (which mmay be ignored as per the semantics of the operation).
|
||||
/// And change in processor status will be applied to @c status. If this operation raises an exception, causes a
|
||||
/// branch, or consumes additional cycles due to the particular value of the operands (on the 68000, think DIV or MUL),
|
||||
/// that'll be notified to the @c flow_controller.
|
||||
/// 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
|
||||
|
Loading…
Reference in New Issue
Block a user