mirror of
https://github.com/TomHarte/CLK.git
synced 2024-08-17 07:29:04 +00:00
146 lines
4.4 KiB
C++
146 lines
4.4 KiB
C++
//
|
||
// Executor.hpp
|
||
// Clock Signal
|
||
//
|
||
// Created by Thomas Harte on 29/04/2022.
|
||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
||
//
|
||
|
||
#ifndef InstructionSets_M68k_Executor_hpp
|
||
#define InstructionSets_M68k_Executor_hpp
|
||
|
||
#include "Decoder.hpp"
|
||
#include "Instruction.hpp"
|
||
#include "Model.hpp"
|
||
#include "Perform.hpp"
|
||
#include "Status.hpp"
|
||
|
||
namespace InstructionSet {
|
||
namespace M68k {
|
||
|
||
/// Maps the 68k function codes such that bits 0, 1 and 2 represent
|
||
/// FC0, FC1 and FC2 respectively.
|
||
enum class FunctionCode {
|
||
UserData = 0b001,
|
||
UserProgram = 0b010,
|
||
SupervisorData = 0b101,
|
||
SupervisorProgram = 0b110,
|
||
InterruptAcknowledge = 0b111,
|
||
};
|
||
|
||
struct BusHandler {
|
||
template <typename IntT> void write(uint32_t address, IntT value, FunctionCode function);
|
||
template <typename IntT> IntT read(uint32_t address, FunctionCode function);
|
||
void reset();
|
||
int acknowlege_interrupt(int);
|
||
};
|
||
|
||
/// 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: public NullFlowController {
|
||
public:
|
||
Executor(BusHandler &);
|
||
|
||
/// Executes the number of instructions specified;
|
||
/// other events — such as initial reset or branching
|
||
/// to exceptions — may be zero costed, and interrupts
|
||
/// will not necessarily take effect immediately when signalled.
|
||
void run_for_instructions(int);
|
||
|
||
/// Call this at any time to interrupt processing with a bus error;
|
||
/// the function code and address must be provided. Internally
|
||
/// this will raise a C++ exception, and therefore doesn't return.
|
||
[[noreturn]] void signal_bus_error(FunctionCode, uint32_t address);
|
||
|
||
/// Sets the current input interrupt level.
|
||
void set_interrupt_level(int);
|
||
|
||
// Flow control; Cf. Perform.hpp.
|
||
template <bool use_current_instruction_pc = true> void raise_exception(int);
|
||
|
||
void did_update_status();
|
||
|
||
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset);
|
||
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset);
|
||
void bsr(uint32_t offset);
|
||
void jmp(uint32_t);
|
||
void jsr(uint32_t offset);
|
||
void rtr();
|
||
void rts();
|
||
void rte();
|
||
void stop();
|
||
void reset();
|
||
|
||
void link(Preinstruction instruction, uint32_t offset);
|
||
void unlink(uint32_t &address);
|
||
void pea(uint32_t address);
|
||
|
||
void move_to_usp(uint32_t address);
|
||
void move_from_usp(uint32_t &address);
|
||
|
||
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest);
|
||
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t source, uint32_t dest);
|
||
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t source, uint32_t dest);
|
||
|
||
void tas(Preinstruction instruction, uint32_t address);
|
||
|
||
// TODO: ownership of this shouldn't be here.
|
||
struct Registers {
|
||
uint32_t data[8], address[7];
|
||
uint32_t user_stack_pointer;
|
||
uint32_t supervisor_stack_pointer;
|
||
uint16_t status;
|
||
uint32_t program_counter;
|
||
};
|
||
Registers get_state();
|
||
void set_state(const Registers &);
|
||
|
||
private:
|
||
void run(int &);
|
||
|
||
BusHandler &bus_handler_;
|
||
Predecoder<model> decoder_;
|
||
|
||
void reset_processor();
|
||
struct EffectiveAddress {
|
||
CPU::SlicedInt32 value;
|
||
bool requires_fetch;
|
||
};
|
||
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
|
||
|
||
void read(DataSize size, uint32_t address, CPU::SlicedInt32 &value);
|
||
void write(DataSize size, uint32_t address, CPU::SlicedInt32 value);
|
||
template <typename IntT> IntT read(uint32_t address, bool is_from_pc = false);
|
||
template <typename IntT> void write(uint32_t address, IntT value);
|
||
|
||
template <typename IntT> IntT read_pc();
|
||
|
||
uint32_t index_8bitdisplacement();
|
||
|
||
// Processor state.
|
||
Status status_;
|
||
CPU::SlicedInt32 program_counter_;
|
||
CPU::SlicedInt32 registers_[16]; // D0–D8, followed by A0–A8.
|
||
CPU::SlicedInt32 stack_pointers_[2];
|
||
uint32_t instruction_address_;
|
||
uint16_t instruction_opcode_;
|
||
int active_stack_pointer_ = 0;
|
||
|
||
// Bus state.
|
||
int interrupt_input_ = 0;
|
||
|
||
// A lookup table to ensure that A7 is adjusted by 2 rather than 1 in
|
||
// postincrement and predecrement mode.
|
||
static constexpr uint32_t byte_increments[] = {
|
||
1, 1, 1, 1, 1, 1, 1, 2
|
||
};
|
||
};
|
||
|
||
}
|
||
}
|
||
|
||
#include "Implementation/ExecutorImplementation.hpp"
|
||
|
||
#endif /* InstructionSets_M68k_Executor_hpp */
|