1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-17 22:38:59 +00:00

Improve formatting, constness in 68k and ARM instruction set implementations.

This commit is contained in:
Thomas Harte 2024-12-01 08:20:24 -05:00
parent 9d87296316
commit 394fe0f1f1
13 changed files with 508 additions and 495 deletions

@ -31,7 +31,7 @@ template <> struct Carry<false> {
///
/// Shift amounts of 0 are given the meaning attributed to them for immediate shift counts.
template <ShiftType type, bool set_carry, bool is_immediate_shift>
void shift(uint32_t &source, uint32_t amount, typename Carry<set_carry>::type carry) {
void shift(uint32_t &source, uint32_t amount, const typename Carry<set_carry>::type carry) {
switch(type) {
case ShiftType::LogicalLeft:
if(amount > 32) {
@ -113,7 +113,7 @@ void shift(uint32_t &source, uint32_t amount, typename Carry<set_carry>::type ca
/// Acts as per @c shift above, but applies runtime shift-type selection.
template <bool set_carry, bool is_immediate_shift>
void shift(ShiftType type, uint32_t &source, uint32_t amount, typename Carry<set_carry>::type carry) {
void shift(const ShiftType type, uint32_t &source, const uint32_t amount, const typename Carry<set_carry>::type carry) {
switch(type) {
case ShiftType::LogicalLeft:
shift<ShiftType::LogicalLeft, set_carry, is_immediate_shift>(source, amount, carry);

@ -74,7 +74,7 @@ struct Instruction {
bool sets_flags = false;
bool is_byte = false;
std::string to_string(uint32_t address) const {
std::string to_string(const uint32_t address) const {
std::ostringstream result;
// Treat all nevers as nops.
@ -161,17 +161,17 @@ struct Instruction {
/// able to vend it later via @c last().
template <Model model>
struct Disassembler {
Instruction last() {
Instruction last() const {
return instruction_;
}
bool should_schedule(Condition condition) {
bool should_schedule(const Condition condition) {
instruction_ = Instruction();
instruction_.condition = condition;
return true;
}
template <Flags f> void perform(DataProcessing fields) {
template <Flags f> void perform(const DataProcessing fields) {
constexpr DataProcessingFlags flags(f);
instruction_.operand1.type = Operand::Type::Register;
@ -214,7 +214,7 @@ struct Disassembler {
}
template <Flags> void perform(Multiply) {}
template <Flags f> void perform(SingleDataTransfer fields) {
template <Flags f> void perform(const SingleDataTransfer fields) {
constexpr SingleDataTransferFlags flags(f);
instruction_.operation =
(flags.operation() == SingleDataTransferFlags::Operation::STR) ?
@ -226,7 +226,7 @@ struct Disassembler {
instruction_.operand1.type = Operand::Type::Register;
instruction_.operand1.value = fields.base();
}
template <Flags f> void perform(BlockDataTransfer fields) {
template <Flags f> void perform(const BlockDataTransfer fields) {
constexpr BlockDataTransferFlags flags(f);
instruction_.operation =
(flags.operation() == BlockDataTransferFlags::Operation::STM) ?
@ -238,7 +238,7 @@ struct Disassembler {
instruction_.operand1.type = Operand::Type::RegisterList;
instruction_.operand1.value = fields.register_list();
}
template <Flags f> void perform(Branch fields) {
template <Flags f> void perform(const Branch fields) {
constexpr BranchFlags flags(f);
instruction_.operation =
(flags.operation() == BranchFlags::Operation::BL) ?
@ -264,7 +264,6 @@ struct Disassembler {
private:
Instruction instruction_;
};
}

@ -18,7 +18,7 @@ namespace InstructionSet::ARM {
/// Maps from a semantic ARM read of type @c SourceT to either the 8- or 32-bit value observed
/// by watching the low 8 bits or all 32 bits of the data bus.
template <typename DestinationT, typename SourceT>
DestinationT read_bus(SourceT value) {
DestinationT read_bus(const SourceT value) {
if constexpr (std::is_same_v<DestinationT, SourceT>) {
return value;
}
@ -57,14 +57,14 @@ struct Executor {
/// @returns @c true if @c condition implies an appropriate perform call should be made for this instruction,
/// @c false otherwise.
bool should_schedule(Condition condition) {
bool should_schedule(const Condition condition) {
// This short-circuit of registers_.test provides the necessary compiler clue that
// Condition::AL is not only [[likely]] but [[exceedingly likely]].
return condition == Condition::AL ? true : registers_.test(condition);
}
template <bool allow_register, bool set_carry, typename FieldsT>
uint32_t decode_shift(FieldsT fields, uint32_t &rotate_carry, uint32_t pc_offset) {
uint32_t decode_shift(const FieldsT fields, uint32_t &rotate_carry, const uint32_t pc_offset) {
// "When R15 appears in the Rm position it will give the value of the PC together
// with the PSR flags to the barrel shifter. ...
//
@ -105,7 +105,7 @@ struct Executor {
return operand2;
}
template <Flags f> void perform(DataProcessing fields) {
template <Flags f> void perform(const DataProcessing fields) {
constexpr DataProcessingFlags flags(f);
const bool shift_by_register = !flags.operand2_is_immediate() && fields.shift_count_is_register();
@ -142,7 +142,10 @@ struct Executor {
const auto sub = [&](uint32_t lhs, uint32_t rhs) {
conditions = lhs - rhs;
if constexpr (flags.operation() == DataProcessingOperation::SBC || flags.operation() == DataProcessingOperation::RSC) {
if constexpr (
flags.operation() == DataProcessingOperation::SBC ||
flags.operation() == DataProcessingOperation::RSC
) {
conditions += registers_.c() - 1;
}
@ -225,7 +228,7 @@ struct Executor {
}
}
template <Flags f> void perform(Multiply fields) {
template <Flags f> void perform(const Multiply fields) {
constexpr MultiplyFlags flags(f);
// R15 rules:
@ -252,7 +255,7 @@ struct Executor {
}
}
template <Flags f> void perform(Branch branch) {
template <Flags f> void perform(const Branch branch) {
constexpr BranchFlags flags(f);
if constexpr (flags.operation() == BranchFlags::Operation::BL) {
@ -261,7 +264,7 @@ struct Executor {
set_pc<true>(registers_.pc(4) + branch.offset());
}
template <Flags f> void perform(SingleDataTransfer transfer) {
template <Flags f> void perform(const SingleDataTransfer transfer) {
constexpr SingleDataTransferFlags flags(f);
// Calculate offset.
@ -386,7 +389,7 @@ struct Executor {
}
}
}
template <Flags f> void perform(BlockDataTransfer transfer) {
template <Flags f> void perform(const BlockDataTransfer transfer) {
constexpr BlockDataTransferFlags flags(f);
constexpr bool is_ldm = flags.operation() == BlockDataTransferFlags::Operation::LDM;
@ -573,7 +576,7 @@ struct Executor {
}
}
void software_interrupt(SoftwareInterrupt swi) {
void software_interrupt(const SoftwareInterrupt swi) {
if(control_flow_handler_.should_swi(swi.comment())) {
exception<Registers::Exception::SoftwareInterrupt>();
}
@ -614,7 +617,7 @@ struct Executor {
///
/// By default this is not forwarded to the control-flow handler.
template <bool notify = false>
void set_pc(uint32_t pc) {
void set_pc(const uint32_t pc) {
registers_.set_pc(pc);
if constexpr (notify) {
control_flow_handler_.did_set_pc();
@ -637,7 +640,7 @@ private:
control_flow_handler_.did_set_pc();
}
void set_status(uint32_t status) {
void set_status(const uint32_t status) {
registers_.set_status(status);
control_flow_handler_.did_set_status();
}
@ -650,7 +653,7 @@ private:
ControlFlowHandlerTStorage control_flow_handler_;
Registers registers_;
static bool is_invalid_address(uint32_t address) {
static bool is_invalid_address(const uint32_t address) {
if constexpr (model == Model::ARMv2with32bitAddressing) {
return false;
}
@ -661,7 +664,7 @@ private:
/// Executes the instruction @c instruction which should have been fetched from @c executor.pc(),
/// modifying @c executor.
template <Model model, typename MemoryT, typename StatusObserverT>
void execute(uint32_t instruction, Executor<model, MemoryT, StatusObserverT> &executor) {
void execute(const uint32_t instruction, Executor<model, MemoryT, StatusObserverT> &executor) {
executor.set_pc(executor.pc() + 4);
dispatch<model>(instruction, executor);
}

@ -35,7 +35,7 @@ static constexpr int FlagsStartBit = 20;
using Flags = uint8_t;
template <int position>
constexpr bool flag_bit(uint8_t flags) {
constexpr bool flag_bit(const uint8_t flags) {
static_assert(position >= 20 && position < 28);
return flags & (1 << (position - FlagsStartBit));
}
@ -44,7 +44,7 @@ constexpr bool flag_bit(uint8_t flags) {
// Methods common to data processing and data transfer.
//
struct WithShiftControlBits {
constexpr WithShiftControlBits(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr WithShiftControlBits(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The operand 2 register index if @c operand2_is_immediate() is @c false; meaningless otherwise.
uint32_t operand2() const { return opcode_ & 0xf; }
@ -65,7 +65,7 @@ protected:
// Branch (i.e. B and BL).
//
struct BranchFlags {
constexpr BranchFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr BranchFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
B, /// Add offset to PC; programmer allows for PC being two words ahead.
@ -82,7 +82,7 @@ private:
};
struct Branch {
constexpr Branch(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr Branch(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The 26-bit offset to add to the PC.
uint32_t offset() const { return (opcode_ & 0xff'ffff) << 2; }
@ -113,7 +113,7 @@ enum class DataProcessingOperation {
MVN, /// Rd = NOT Op2.
};
constexpr bool is_logical(DataProcessingOperation operation) {
constexpr bool is_logical(const DataProcessingOperation operation) {
switch(operation) {
case DataProcessingOperation::AND:
case DataProcessingOperation::EOR:
@ -129,7 +129,7 @@ constexpr bool is_logical(DataProcessingOperation operation) {
}
}
constexpr bool is_comparison(DataProcessingOperation operation) {
constexpr bool is_comparison(const DataProcessingOperation operation) {
switch(operation) {
case DataProcessingOperation::TST:
case DataProcessingOperation::TEQ:
@ -142,7 +142,7 @@ constexpr bool is_comparison(DataProcessingOperation operation) {
}
struct DataProcessingFlags {
constexpr DataProcessingFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr DataProcessingFlags(const uint8_t flags) noexcept : flags_(flags) {}
/// @returns The operation to apply.
constexpr DataProcessingOperation operation() const {
@ -183,7 +183,7 @@ struct DataProcessing: public WithShiftControlBits {
// MUL and MLA.
//
struct MultiplyFlags {
constexpr MultiplyFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr MultiplyFlags(const uint8_t flags) noexcept : flags_(flags) {}
/// @c true if the status register should be updated; @c false otherwise.
constexpr bool set_condition_codes() const { return flag_bit<20>(flags_); }
@ -203,7 +203,7 @@ private:
};
struct Multiply {
constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr Multiply(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The destination register index. i.e. 'Rd'.
uint32_t destination() const { return (opcode_ >> 16) & 0xf; }
@ -225,7 +225,7 @@ private:
// Single data transfer (LDR, STR).
//
struct SingleDataTransferFlags {
constexpr SingleDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr SingleDataTransferFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
LDR, /// Read single byte or word from [base + offset], possibly mutating the base.
@ -266,7 +266,7 @@ struct SingleDataTransfer: public WithShiftControlBits {
// Block data transfer (LDR, STR).
//
struct BlockDataTransferFlags {
constexpr BlockDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr BlockDataTransferFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
LDM, /// Read 116 words from [base], possibly mutating it.
@ -311,7 +311,7 @@ struct BlockDataTransfer: public WithShiftControlBits {
// Coprocessor data operation.
//
struct CoprocessorDataOperationFlags {
constexpr CoprocessorDataOperationFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr CoprocessorDataOperationFlags(const uint8_t flags) noexcept : flags_(flags) {}
constexpr int coprocessor_operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0xf; }
@ -320,7 +320,7 @@ private:
};
struct CoprocessorDataOperation {
constexpr CoprocessorDataOperation(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr CoprocessorDataOperation(const uint32_t opcode) noexcept : opcode_(opcode) {}
uint32_t operand1() const { return (opcode_ >> 16) & 0xf; }
uint32_t operand2() const { return opcode_ & 0xf; }
@ -336,7 +336,7 @@ private:
// Coprocessor register transfer.
//
struct CoprocessorRegisterTransferFlags {
constexpr CoprocessorRegisterTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr CoprocessorRegisterTransferFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
MRC, /// Move from coprocessor register to ARM register.
@ -353,7 +353,7 @@ private:
};
struct CoprocessorRegisterTransfer {
constexpr CoprocessorRegisterTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr CoprocessorRegisterTransfer(const uint32_t opcode) noexcept : opcode_(opcode) {}
uint32_t operand1() const { return (opcode_ >> 16) & 0xf; }
uint32_t operand2() const { return opcode_ & 0xf; }
@ -369,7 +369,7 @@ private:
// Coprocessor data transfer.
//
struct CoprocessorDataTransferFlags {
constexpr CoprocessorDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr CoprocessorDataTransferFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
LDC, /// Coprocessor data transfer load.
@ -392,7 +392,7 @@ private:
// Software interrupt.
//
struct SoftwareInterrupt {
constexpr SoftwareInterrupt(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr SoftwareInterrupt(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The 24-bit comment field, often decoded by the receiver of an SWI.
uint32_t comment() const { return opcode_ & 0xff'ffff; }
@ -402,7 +402,7 @@ private:
};
struct CoprocessorDataTransfer {
constexpr CoprocessorDataTransfer(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr CoprocessorDataTransfer(const uint32_t opcode) noexcept : opcode_(opcode) {}
int base() const { return (opcode_ >> 16) & 0xf; }
@ -419,12 +419,12 @@ private:
/// Operation mapper; use the free function @c dispatch as defined below.
template <Model>
struct OperationMapper {
static Condition condition(uint32_t instruction) {
static Condition condition(const uint32_t instruction) {
return Condition(instruction >> 28);
}
template <int i, typename SchedulerT>
static void dispatch(uint32_t instruction, SchedulerT &scheduler) {
static void dispatch(const uint32_t instruction, SchedulerT &scheduler) {
// Put the 8-bit segment of instruction back into its proper place;
// this allows all the tests below to be written so as to coordinate
// properly with the data sheet, and since it's all compile-time work
@ -507,7 +507,7 @@ struct OperationMapper {
struct SampleScheduler {
/// @returns @c true if the rest of the instruction should be decoded and supplied
/// to the scheduler as defined below; @c false otherwise.
bool should_schedule(Condition condition);
bool should_schedule(Condition);
// Template argument:
//
@ -537,7 +537,7 @@ struct SampleScheduler {
/// Decodes @c instruction, making an appropriate call into @c scheduler.
///
/// In lieu of C++20, see the sample definition of SampleScheduler above for the expected interface.
template <Model model, typename SchedulerT> void dispatch(uint32_t instruction, SchedulerT &scheduler) {
template <Model model, typename SchedulerT> void dispatch(const uint32_t instruction, SchedulerT &scheduler) {
OperationMapper<model> mapper;
// Test condition.

@ -52,12 +52,12 @@ struct Registers {
Registers() = default;
/// Sets the N and Z flags according to the value of @c result.
void set_nz(uint32_t value) {
void set_nz(const uint32_t value) {
zero_result_ = negative_flag_ = value;
}
/// Sets C if @c value is non-zero; resets it otherwise.
void set_c(uint32_t value) {
void set_c(const uint32_t value) {
carry_flag_ = value;
}
@ -67,7 +67,7 @@ struct Registers {
}
/// Sets V if the highest bit of @c value is set; resets it otherwise.
void set_v(uint32_t value) {
void set_v(const uint32_t value) {
overflow_flag_ = value;
}
@ -83,14 +83,14 @@ struct Registers {
}
/// @returns The full PC + status bits.
uint32_t pc_status(uint32_t offset) const {
uint32_t pc_status(const uint32_t offset) const {
return
((active_[15] + offset) & ConditionCode::Address) |
status();
}
/// Sets status bits only, subject to mode.
void set_status(uint32_t status) {
void set_status(const uint32_t status) {
// ... in user mode the other flags (I, F, M1, M0) are protected from direct change
// but in non-user modes these will also be affected, accepting copies of bits 27, 26,
// 1 and 0 of the result respectively.
@ -112,12 +112,12 @@ struct Registers {
}
/// Sets a new PC.
void set_pc(uint32_t value) {
void set_pc(const uint32_t value) {
active_[15] = value & ConditionCode::Address;
}
/// @returns The stored PC plus @c offset limited to 26 bits.
uint32_t pc(uint32_t offset) const {
uint32_t pc(const uint32_t offset) const {
return (active_[15] + offset) & ConditionCode::Address;
}
@ -143,7 +143,7 @@ struct Registers {
/// The FIQ went low at least one cycle ago and ConditionCode::FIQDisable was not set.
FIQ = 0x1c,
};
static constexpr uint32_t pc_offset_during(Exception exception) {
static constexpr uint32_t pc_offset_during(const Exception exception) {
// The below is somewhat convoluted by the assumed execution model:
//
// * exceptions occuring during execution of an instruction are taken
@ -231,7 +231,7 @@ struct Registers {
// MARK: - Condition tests.
/// @returns @c true if @c condition tests as true; @c false otherwise.
bool test(Condition condition) const {
bool test(const Condition condition) const {
const auto ne = [&]() -> bool {
return zero_result_;
};
@ -279,7 +279,7 @@ struct Registers {
}
/// Sets current execution mode.
void set_mode(Mode target_mode) {
void set_mode(const Mode target_mode) {
if(mode_ == target_mode) {
return;
}
@ -335,18 +335,18 @@ struct Registers {
mode_ = target_mode;
}
uint32_t &operator[](uint32_t offset) {
uint32_t &operator[](const uint32_t offset) {
return active_[static_cast<size_t>(offset)];
}
uint32_t operator[](uint32_t offset) const {
uint32_t operator[](const uint32_t offset) const {
return active_[static_cast<size_t>(offset)];
}
/// @returns A reference to the register at @c offset. If @c force_user_mode is true,
/// this will the the user-mode register. Otherwise it'll be that for the current mode. These references
/// are guaranteed to remain valid only until the next mode change.
uint32_t &reg(bool force_user_mode, uint32_t offset) {
uint32_t &reg(const bool force_user_mode, const uint32_t offset) {
switch(mode_) {
default:
case Mode::User: return active_[offset];

@ -44,50 +44,50 @@ template <
/// Indicates whether instructions should be treated as ephemeral or included in the cache.
bool retain_instructions
> class CachingExecutor {
public:
using Performer = void (Executor::*)();
using PerformerIndex = typename MinIntTypeValue<max_performer_count>::type;
using ProgramCounterType = typename MinIntTypeValue<max_address>::type;
public:
using Performer = void (Executor::*)();
using PerformerIndex = typename MinIntTypeValue<max_performer_count>::type;
using ProgramCounterType = typename MinIntTypeValue<max_address>::type;
// MARK: - Parser call-ins.
// MARK: - Parser call-ins.
void announce_overflow(ProgramCounterType) {
/*
Should be impossible for now; this is intended to provide information
when page caching.
*/
}
void announce_instruction(ProgramCounterType, InstructionType instruction) {
// Dutifully map the instruction to a performer and keep it.
program_.push_back(static_cast<Executor *>(this)->action_for(instruction));
if constexpr (retain_instructions) {
// TODO.
}
}
protected:
// Storage for the statically-allocated list of performers. It's a bit more
// work for executors to fill this array, but subsequently performers can be
// indexed by array position, which is a lot more compact than a generic pointer.
std::array<Performer, max_performer_count+1> performers_;
ProgramCounterType program_counter_;
/*!
Moves the current point of execution to @c address, updating necessary performer caches
and doing any translation as is necessary.
void announce_overflow(ProgramCounterType) {
/*
Should be impossible for now; this is intended to provide information
when page caching.
*/
void set_program_counter(ProgramCounterType address) {
// Set flag to terminate any inner loop currently running through
// previously-parsed content.
has_branched_ = true;
program_counter_ = address;
}
void announce_instruction(ProgramCounterType, const InstructionType instruction) {
// Dutifully map the instruction to a performer and keep it.
program_.push_back(static_cast<Executor *>(this)->action_for(instruction));
// Temporary implementation: just interpret.
program_.clear();
program_index_ = 0;
static_cast<Executor *>(this)->parse(address, ProgramCounterType(max_address));
if constexpr (retain_instructions) {
// TODO.
}
}
protected:
// Storage for the statically-allocated list of performers. It's a bit more
// work for executors to fill this array, but subsequently performers can be
// indexed by array position, which is a lot more compact than a generic pointer.
std::array<Performer, max_performer_count+1> performers_;
ProgramCounterType program_counter_;
/*!
Moves the current point of execution to @c address, updating necessary performer caches
and doing any translation as is necessary.
*/
void set_program_counter(ProgramCounterType address) {
// Set flag to terminate any inner loop currently running through
// previously-parsed content.
has_branched_ = true;
program_counter_ = address;
// Temporary implementation: just interpret.
program_.clear();
program_index_ = 0;
static_cast<Executor *>(this)->parse(address, ProgramCounterType(max_address));
// const auto page = find_page(address);
// const auto entry = page->entry_points.find(address);
@ -96,102 +96,102 @@ template <
// // within the recently translated list and otherwise
// // translate it.
// }
}
/*!
Indicates whether the processor is currently 'stopped', i.e. whether all attempts to run
should produce no activity. Some processors have such a state when waiting for
interrupts or for a reset.
*/
void set_is_stopped(bool) {}
/*!
Executes up to the next branch.
*/
void run_to_branch() {
has_branched_ = false;
for(auto index: program_) {
const auto performer = performers_[index];
(static_cast<Executor *>(this)->*performer)();
if(has_branched_) break;
}
}
/*!
Indicates whether the processor is currently 'stopped', i.e. whether all attempts to run
should produce no activity. Some processors have such a state when waiting for
interrupts or for a reset.
*/
void set_is_stopped(bool) {}
/*!
Runs for @c duration; the intention is that subclasses provide a method
that is clear about units, and call this to count down in whatever units they
count down in.
*/
void run_for(int duration) {
remaining_duration_ += duration;
/*!
Executes up to the next branch.
*/
void run_to_branch() {
while(remaining_duration_ > 0) {
has_branched_ = false;
for(auto index: program_) {
const auto performer = performers_[index];
(static_cast<Executor *>(this)->*performer)();
if(has_branched_) break;
Executor *const executor = static_cast<Executor *>(this);
while(remaining_duration_ > 0 && !has_branched_) {
const auto performer = performers_[program_[program_index_]];
++program_index_;
(executor->*performer)();
}
}
}
/*!
Runs for @c duration; the intention is that subclasses provide a method
that is clear about units, and call this to count down in whatever units they
count down in.
*/
void run_for(int duration) {
remaining_duration_ += duration;
/*!
Should be called by a specific executor to subtract from the remaining
running duration.
*/
inline void subtract_duration(int duration) {
remaining_duration_ -= duration;
}
while(remaining_duration_ > 0) {
has_branched_ = false;
Executor *const executor = static_cast<Executor *>(this);
while(remaining_duration_ > 0 && !has_branched_) {
const auto performer = performers_[program_[program_index_]];
++program_index_;
private:
bool has_branched_ = false;
int remaining_duration_ = 0;
std::vector<PerformerIndex> program_;
size_t program_index_ = 0;
(executor->*performer)();
}
}
}
/*!
Should be called by a specific executor to subtract from the remaining
running duration.
*/
inline void subtract_duration(int duration) {
remaining_duration_ -= duration;
}
private:
bool has_branched_ = false;
int remaining_duration_ = 0;
std::vector<PerformerIndex> program_;
size_t program_index_ = 0;
/* TODO: almost below here can be shoved off into an LRUCache object, or similar. */
/* TODO: almost below here can be shoved off into an LRUCache object, or similar. */
// static constexpr size_t max_cached_pages = 64;
// struct Page {
// std::map<ProgramCounterType, PerformerIndex> entry_points;
// TODO: can I statically these two? Should I?
// TODO: can I statically these two? Should I?
// std::vector<PerformerIndex> actions_;
// std::vector<typename std::enable_if<!std::is_same<InstructionType, void>::value, InstructionType>::type> instructions_;
// };
// std::array<Page, max_cached_pages> pages_;
// Maps from page numbers to pages.
// Maps from page numbers to pages.
// std::unordered_map<ProgramCounterType, Page *> cached_pages_;
// Maintains an LRU of recently-used pages in case of a need for reuse.
// Maintains an LRU of recently-used pages in case of a need for reuse.
// std::list<ProgramCounterType> touched_pages_;
/*!
Finds or creates the page that contains @c address.
*/
/*!
Finds or creates the page that contains @c address.
*/
/* Page *find_page(ProgramCounterType address) {
// TODO: are 1kb pages always appropriate? Is 64 the correct amount to keep?
const auto page_address = ProgramCounterType(address >> 10);
// TODO: are 1kb pages always appropriate? Is 64 the correct amount to keep?
const auto page_address = ProgramCounterType(address >> 10);
auto page = cached_pages_.find(page_address);
if(page == cached_pages_.end()) {
// Page wasn't found; either allocate a new one or
// reuse one that already exists.
if(cached_pages_.size() == max_cached_pages) {
auto page = cached_pages_.find(page_address);
if(page == cached_pages_.end()) {
// Page wasn't found; either allocate a new one or
// reuse one that already exists.
if(cached_pages_.size() == max_cached_pages) {
} else {
}
} else {
// Page was found; LRU shuffle it.
}
return nullptr;
}*/
}
} else {
// Page was found; LRU shuffle it.
}
return nullptr;
}*/
};
}

@ -28,59 +28,64 @@ template <
/// Provides the addressing range of memory.
typename AddressType
> class Disassembler {
public:
using ProgramCounterType = typename MinIntTypeValue<max_address>::type;
public:
using ProgramCounterType = typename MinIntTypeValue<max_address>::type;
/*!
Adds the result of disassembling @c memory which is @c length @c MemoryWords long from @c start_address
to the current net total of instructions and recorded memory accesses.
*/
void disassemble(const MemoryWord *memory, ProgramCounterType location, ProgramCounterType length, ProgramCounterType start_address) {
// TODO: possibly, move some of this stuff to instruction-set specific disassemblers, analogous to
// the Executor's ownership of the Parser. That would allow handling of stateful parsing.
ParserType<decltype(*this), true> parser;
pending_entry_points_.push_back(start_address);
entry_points_.insert(start_address);
/*!
Adds the result of disassembling @c memory which is @c length @c MemoryWords long from @c start_address
to the current net total of instructions and recorded memory accesses.
*/
void disassemble(
const MemoryWord *const memory,
const ProgramCounterType location,
const ProgramCounterType length,
const ProgramCounterType start_address
) {
// TODO: possibly, move some of this stuff to instruction-set specific disassemblers, analogous to
// the Executor's ownership of the Parser. That would allow handling of stateful parsing.
ParserType<decltype(*this), true> parser;
pending_entry_points_.push_back(start_address);
entry_points_.insert(start_address);
while(!pending_entry_points_.empty()) {
const auto next_entry_point = pending_entry_points_.front();
pending_entry_points_.pop_front();
while(!pending_entry_points_.empty()) {
const auto next_entry_point = pending_entry_points_.front();
pending_entry_points_.pop_front();
if(next_entry_point >= location) {
parser.parse(*this, memory - location, next_entry_point & max_address, length + location);
}
if(next_entry_point >= location) {
parser.parse(*this, memory - location, next_entry_point & max_address, length + location);
}
}
}
const std::map<ProgramCounterType, InstructionType> &instructions() const {
return instructions_;
}
const std::map<ProgramCounterType, InstructionType> &instructions() const {
return instructions_;
}
const std::set<ProgramCounterType> &entry_points() const {
return entry_points_;
}
const std::set<ProgramCounterType> &entry_points() const {
return entry_points_;
}
void announce_overflow(ProgramCounterType) {}
void announce_instruction(ProgramCounterType address, InstructionType instruction) {
instructions_[address] = instruction;
}
void add_entry(ProgramCounterType address) {
if(entry_points_.find(address) == entry_points_.end()) {
pending_entry_points_.push_back(address);
entry_points_.insert(address);
}
}
void add_access(AddressType address, AccessType access_type) {
// TODO.
(void)address;
(void)access_type;
void announce_overflow(ProgramCounterType) {}
void announce_instruction(const ProgramCounterType address, const InstructionType instruction) {
instructions_[address] = instruction;
}
void add_entry(const ProgramCounterType address) {
if(entry_points_.find(address) == entry_points_.end()) {
pending_entry_points_.push_back(address);
entry_points_.insert(address);
}
}
void add_access(const AddressType address, const AccessType access_type) {
// TODO.
(void)address;
(void)access_type;
}
private:
std::map<ProgramCounterType, InstructionType> instructions_;
std::set<ProgramCounterType> entry_points_;
private:
std::map<ProgramCounterType, InstructionType> instructions_;
std::set<ProgramCounterType> entry_points_;
std::list<ProgramCounterType> pending_entry_points_;
std::list<ProgramCounterType> pending_entry_points_;
};
}

@ -140,7 +140,8 @@ constexpr Operation Predecoder<model>::operation(const OpT op) {
}
template <Model model>
template <typename Predecoder<model>::OpT op> uint32_t Predecoder<model>::invalid_operands() {
template <typename Predecoder<model>::OpT op>
constexpr uint32_t Predecoder<model>::invalid_operands() {
constexpr auto Dn = Mask< AddressingMode::DataRegisterDirect >::value;
constexpr auto An = Mask< AddressingMode::AddressRegisterDirect >::value;
constexpr auto Ind = Mask< AddressingMode::AddressRegisterIndirect >::value;
@ -162,24 +163,25 @@ template <typename Predecoder<model>::OpT op> uint32_t Predecoder<model>::invali
//
// All modes: the complete set (other than Quick).
//
static constexpr auto AllModes = Dn | An | Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl | Imm | d16PC | d8PCXn;
static constexpr auto AllModesNoAn = AllModes & ~An;
constexpr auto AllModes = Dn | An | Ind | PostInc | PreDec | d16An |
d8AnXn | XXXw | XXXl | Imm | d16PC | d8PCXn;
constexpr auto AllModesNoAn = AllModes & ~An;
//
// Alterable addressing modes (with and without AddressRegisterDirect).
//
static constexpr auto AlterableAddressingModes = Dn | An | Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl;
static constexpr auto AlterableAddressingModesNoAn = AlterableAddressingModes & ~An;
constexpr auto AlterableAddressingModes = Dn | An | Ind | PostInc | PreDec | d16An | d8AnXn | XXXw | XXXl;
constexpr auto AlterableAddressingModesNoAn = AlterableAddressingModes & ~An;
//
// Control [flow] addressing modes.
//
static constexpr auto ControlAddressingModes = Ind | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn;
constexpr auto ControlAddressingModes = Ind | d16An | d8AnXn | XXXw | XXXl | d16PC | d8PCXn;
//
// An invalid response, used as the default case.
//
static constexpr auto InvalidOperands = uint32_t(~0);
constexpr auto InvalidOperands = uint32_t(~0);
switch(op) {
default: break;
@ -620,7 +622,8 @@ template <typename Predecoder<model>::OpT op> uint32_t Predecoder<model>::invali
/// Provides a post-decoding validation step — primarily ensures that the prima facie addressing modes are supported by the operation.
template <Model model>
template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Predecoder<model>::validated(
template <typename Predecoder<model>::OpT op, bool validate>
constexpr Preinstruction Predecoder<model>::validated(
const AddressingMode op1_mode, const int op1_reg,
const AddressingMode op2_mode, const int op2_reg,
const Condition condition,
@ -656,7 +659,10 @@ template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Pred
/// Decodes the fields within an instruction and constructs a `Preinstruction`, given that the operation has already been
/// decoded. Optionally applies validation
template <Model model>
template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Predecoder<model>::decode(const uint16_t instruction) {
template <typename Predecoder<model>::OpT op, bool validate>
constexpr Preinstruction Predecoder<model>::decode(
const uint16_t instruction
) {
// Fields used pervasively below.
//
// Underlying assumption: the compiler will discard whatever of these
@ -1269,11 +1275,11 @@ template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Pred
// MARK: - Page decoders.
#define Decode(y) return decode<OpT(y)>(instruction)
#define Decode(y) return decode<OpT(y)>(instruction)
#define DecodeReq(x, y) if constexpr (x) Decode(y); break;
template <Model model>
Preinstruction Predecoder<model>::decode0(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode0(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0xfff) {
@ -1386,7 +1392,7 @@ Preinstruction Predecoder<model>::decode0(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode1(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode1(const uint16_t instruction) {
using Op = Operation;
// 4-116 (p220)
@ -1394,7 +1400,7 @@ Preinstruction Predecoder<model>::decode1(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode2(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode2(const uint16_t instruction) {
using Op = Operation;
// 4-116 (p220)
@ -1405,7 +1411,7 @@ Preinstruction Predecoder<model>::decode2(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode3(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode3(const uint16_t instruction) {
using Op = Operation;
// 4-116 (p220)
@ -1416,7 +1422,7 @@ Preinstruction Predecoder<model>::decode3(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode4(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode4(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0xfff) {
@ -1539,7 +1545,7 @@ Preinstruction Predecoder<model>::decode4(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode5(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode5(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0x1f8) {
@ -1617,7 +1623,7 @@ Preinstruction Predecoder<model>::decode5(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode6(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode6(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0xf00) {
@ -1649,7 +1655,7 @@ Preinstruction Predecoder<model>::decode6(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode7(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode7(const uint16_t instruction) {
// 4-134 (p238)
if(!(instruction & 0x100)) {
Decode(MOVEQ);
@ -1659,7 +1665,7 @@ Preinstruction Predecoder<model>::decode7(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode8(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode8(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0x1f0) {
@ -1689,7 +1695,7 @@ Preinstruction Predecoder<model>::decode8(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decode9(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decode9(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0x1f0) {
@ -1721,12 +1727,12 @@ Preinstruction Predecoder<model>::decode9(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decodeA(const uint16_t) {
constexpr Preinstruction Predecoder<model>::decodeA(const uint16_t) {
return Preinstruction();
}
template <Model model>
Preinstruction Predecoder<model>::decodeB(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decodeB(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0x1f8) {
@ -1760,7 +1766,7 @@ Preinstruction Predecoder<model>::decodeB(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decodeC(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decodeC(const uint16_t instruction) {
using Op = Operation;
// 4-105 (p209)
@ -1795,7 +1801,7 @@ Preinstruction Predecoder<model>::decodeC(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decodeD(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decodeD(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0x1f0) {
@ -1827,7 +1833,7 @@ Preinstruction Predecoder<model>::decodeD(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decodeE(const uint16_t instruction) {
constexpr Preinstruction Predecoder<model>::decodeE(const uint16_t instruction) {
using Op = Operation;
switch(instruction & 0xfc0) {
@ -1900,7 +1906,7 @@ Preinstruction Predecoder<model>::decodeE(const uint16_t instruction) {
}
template <Model model>
Preinstruction Predecoder<model>::decodeF(const uint16_t) {
constexpr Preinstruction Predecoder<model>::decodeF(const uint16_t) {
return Preinstruction();
}

@ -26,91 +26,91 @@ namespace InstructionSet::M68k {
But it does not yet decode any operations which were not present on the 68000.
*/
template <Model model> class Predecoder {
public:
Preinstruction decode(uint16_t instruction);
public:
static Preinstruction decode(uint16_t);
private:
// Page by page decoders; each gets a bit ad hoc so
// it is neater to separate them.
Preinstruction decode0(uint16_t instruction);
Preinstruction decode1(uint16_t instruction);
Preinstruction decode2(uint16_t instruction);
Preinstruction decode3(uint16_t instruction);
Preinstruction decode4(uint16_t instruction);
Preinstruction decode5(uint16_t instruction);
Preinstruction decode6(uint16_t instruction);
Preinstruction decode7(uint16_t instruction);
Preinstruction decode8(uint16_t instruction);
Preinstruction decode9(uint16_t instruction);
Preinstruction decodeA(uint16_t instruction);
Preinstruction decodeB(uint16_t instruction);
Preinstruction decodeC(uint16_t instruction);
Preinstruction decodeD(uint16_t instruction);
Preinstruction decodeE(uint16_t instruction);
Preinstruction decodeF(uint16_t instruction);
private:
// Page by page decoders; each gets a bit ad hoc so
// it is neater to separate them.
static constexpr Preinstruction decode0(uint16_t);
static constexpr Preinstruction decode1(uint16_t);
static constexpr Preinstruction decode2(uint16_t);
static constexpr Preinstruction decode3(uint16_t);
static constexpr Preinstruction decode4(uint16_t);
static constexpr Preinstruction decode5(uint16_t);
static constexpr Preinstruction decode6(uint16_t);
static constexpr Preinstruction decode7(uint16_t);
static constexpr Preinstruction decode8(uint16_t);
static constexpr Preinstruction decode9(uint16_t);
static constexpr Preinstruction decodeA(uint16_t);
static constexpr Preinstruction decodeB(uint16_t);
static constexpr Preinstruction decodeC(uint16_t);
static constexpr Preinstruction decodeD(uint16_t);
static constexpr Preinstruction decodeE(uint16_t);
static constexpr Preinstruction decodeF(uint16_t);
// Yuckiness here: 67 is a count of the number of things contained below in
// ExtendedOperation; this acts to ensure ExtendedOperation is the minimum
// integer size large enough to hold all actual operations plus the ephemeral
// ones used here. Intention is to support table-based decoding, which will mean
// making those integers less ephemeral, hence the desire to pick a minimum size.
using OpT = typename MinIntTypeValue<
uint64_t(OperationMax<model>::value) + 67
>::type;
static constexpr auto OpMax = OpT(OperationMax<model>::value);
// Yuckiness here: 67 is a count of the number of things contained below in
// ExtendedOperation; this acts to ensure ExtendedOperation is the minimum
// integer size large enough to hold all actual operations plus the ephemeral
// ones used here. Intention is to support table-based decoding, which will mean
// making those integers less ephemeral, hence the desire to pick a minimum size.
using OpT = typename MinIntTypeValue<
uint64_t(OperationMax<model>::value) + 67
>::type;
static constexpr auto OpMax = OpT(OperationMax<model>::value);
// Specific instruction decoders.
template <OpT operation, bool validate = true> Preinstruction decode(uint16_t instruction);
template <OpT operation, bool validate> Preinstruction validated(
AddressingMode op1_mode = AddressingMode::None, int op1_reg = 0,
AddressingMode op2_mode = AddressingMode::None, int op2_reg = 0,
Condition condition = Condition::True,
int further_extension_words = 0
);
template <OpT operation> uint32_t invalid_operands();
// Specific instruction decoders.
template <OpT operation, bool validate = true> static constexpr Preinstruction decode(uint16_t instruction);
template <OpT operation, bool validate> static constexpr Preinstruction validated(
AddressingMode op1_mode = AddressingMode::None, int op1_reg = 0,
AddressingMode op2_mode = AddressingMode::None, int op2_reg = 0,
Condition condition = Condition::True,
int further_extension_words = 0
);
template <OpT operation> static constexpr uint32_t invalid_operands();
// Extended operation list; collapses into a single byte enough information to
// know both the type of operation and how to decode the operands. Most of the
// time that's knowable from the Operation alone, hence the rather awkward
// extension of @c Operation.
enum ExtendedOperation: OpT {
MOVEPtoRl = OpMax + 1, MOVEPtoRw,
MOVEPtoMl, MOVEPtoMw,
// Extended operation list; collapses into a single byte enough information to
// know both the type of operation and how to decode the operands. Most of the
// time that's knowable from the Operation alone, hence the rather awkward
// extension of @c Operation.
enum ExtendedOperation: OpT {
MOVEPtoRl = OpMax + 1, MOVEPtoRw,
MOVEPtoMl, MOVEPtoMw,
MOVEQ,
MOVEQ,
ADDQb, ADDQw, ADDQl,
ADDQAw, ADDQAl,
SUBQb, SUBQw, SUBQl,
SUBQAw, SUBQAl,
ADDQb, ADDQw, ADDQl,
ADDQAw, ADDQAl,
SUBQb, SUBQw, SUBQl,
SUBQAw, SUBQAl,
ADDIb, ADDIw, ADDIl,
ORIb, ORIw, ORIl,
SUBIb, SUBIw, SUBIl,
ANDIb, ANDIw, ANDIl,
EORIb, EORIw, EORIl,
CMPIb, CMPIw, CMPIl,
ADDIb, ADDIw, ADDIl,
ORIb, ORIw, ORIl,
SUBIb, SUBIw, SUBIl,
ANDIb, ANDIw, ANDIl,
EORIb, EORIw, EORIl,
CMPIb, CMPIw, CMPIl,
BTSTI, BCHGI, BCLRI, BSETI,
BTSTI, BCHGI, BCLRI, BSETI,
CMPMb, CMPMw, CMPMl,
CMPMb, CMPMw, CMPMl,
ADDtoMb, ADDtoMw, ADDtoMl,
ADDtoRb, ADDtoRw, ADDtoRl,
ADDtoMb, ADDtoMw, ADDtoMl,
ADDtoRb, ADDtoRw, ADDtoRl,
SUBtoMb, SUBtoMw, SUBtoMl,
SUBtoRb, SUBtoRw, SUBtoRl,
SUBtoMb, SUBtoMw, SUBtoMl,
SUBtoRb, SUBtoRw, SUBtoRl,
ANDtoMb, ANDtoMw, ANDtoMl,
ANDtoRb, ANDtoRw, ANDtoRl,
ANDtoMb, ANDtoMw, ANDtoMl,
ANDtoRb, ANDtoRw, ANDtoRl,
ORtoMb, ORtoMw, ORtoMl,
ORtoRb, ORtoRw, ORtoRl,
ORtoMb, ORtoMw, ORtoMl,
ORtoRb, ORtoRw, ORtoRl,
EXGRtoR, EXGAtoA, EXGRtoA,
};
EXGRtoR, EXGAtoA, EXGRtoA,
};
static constexpr Operation operation(OpT op);
static constexpr Operation operation(OpT op);
};
}

@ -37,12 +37,12 @@ struct BusHandler {
/// Write @c value of type/size @c IntT to @c address with the processor signalling
/// a FunctionCode of @c function. @c IntT will be one of @c uint8_t, @c uint16_t
/// or @c uint32_t.
template <typename IntT> void write(uint32_t address, IntT value, FunctionCode function);
template <typename IntT> void write(uint32_t address, IntT value, FunctionCode);
/// Read and return a value of type/size @c IntT from @c address with the processor signalling
/// a FunctionCode of @c function. @c IntT will be one of @c uint8_t, @c uint16_t
/// or @c uint32_t.
template <typename IntT> IntT read(uint32_t address, FunctionCode function);
template <typename IntT> IntT read(uint32_t address, FunctionCode);
/// React to the processor programmatically strobing its RESET output.
void reset();
@ -59,106 +59,106 @@ struct BusHandler {
/// 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:
Executor(BusHandler &);
public:
Executor(BusHandler &);
/// Reset the processor, back to a state as if just externally reset.
void reset();
/// Reset the processor, back to a state as if just externally reset.
void reset();
/// 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);
/// 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);
/// 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);
/// Sets the current input interrupt level.
void set_interrupt_level(int);
// State for the executor is just the register set.
RegisterSet get_state();
void set_state(const RegisterSet &);
// State for the executor is just the register set.
RegisterSet get_state() const;
void set_state(const RegisterSet &);
private:
class State: public NullFlowController {
public:
State(BusHandler &handler) : bus_handler_(handler) {}
private:
class State: public NullFlowController {
public:
State(BusHandler &handler) : bus_handler_(handler) {}
void run(int &);
bool stopped = false;
void run(int &);
bool stopped = false;
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);
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();
template <typename IntT> IntT read_pc();
// Processor state.
Status status;
CPU::SlicedInt32 program_counter;
CPU::SlicedInt32 registers[16]; // D0D7 followed by A0A7.
CPU::SlicedInt32 stack_pointers[2];
uint32_t instruction_address;
uint16_t instruction_opcode;
// Processor state.
Status status;
CPU::SlicedInt32 program_counter;
CPU::SlicedInt32 registers[16]; // D0D7 followed by A0A7.
CPU::SlicedInt32 stack_pointers[2];
uint32_t instruction_address;
uint16_t instruction_opcode;
// Things that are ephemerally duplicative of Status.
int active_stack_pointer = 0;
Status::FlagT should_trace = 0;
// Things that are ephemerally duplicative of Status.
int active_stack_pointer = 0;
Status::FlagT should_trace = 0;
// Bus state.
int interrupt_input = 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
};
// 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
};
// Flow control; Cf. Perform.hpp.
template <bool use_current_instruction_pc = true> void raise_exception(int);
// Flow control; Cf. Perform.hpp.
template <bool use_current_instruction_pc = true> void raise_exception(int);
void did_update_status();
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();
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 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);
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);
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);
void tas(Preinstruction instruction, uint32_t address);
private:
BusHandler &bus_handler_;
Predecoder<model> decoder_;
private:
BusHandler &bus_handler_;
Predecoder<model> decoder_;
struct EffectiveAddress {
CPU::SlicedInt32 value;
bool requires_fetch;
};
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
uint32_t index_8bitdisplacement(uint32_t);
} state_;
struct EffectiveAddress {
CPU::SlicedInt32 value;
bool requires_fetch;
};
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
uint32_t index_8bitdisplacement(uint32_t);
} state_;
};
}

@ -101,7 +101,7 @@ void Executor<model, BusHandler>::run_for_instructions(int count) {
}
template <Model model, typename BusHandler>
RegisterSet Executor<model, BusHandler>::get_state() {
RegisterSet Executor<model, BusHandler>::get_state() const {
RegisterSet result;
for(int c = 0; c < 8; c++) {

@ -367,133 +367,133 @@ static constexpr int AddressingModeCount = 0b10'110;
the notes on @c AddressingMode for potential aliasing.
*/
class Preinstruction {
public:
Operation operation = Operation::Undefined;
public:
Operation operation = Operation::Undefined;
// Instructions come with 0, 1 or 2 operands;
// the getters below act to provide a list of operands
// that is terminated by an AddressingMode::None.
//
// For two-operand instructions, argument 0 is a source
// and argument 1 is a destination.
//
// For one-operand instructions, only argument 0 will
// be provided, and will be a source and/or destination as
// per the semantics of the operation.
//
// The versions templated on index do a range check;
// if using the runtime versions then results for indices
// other than 0 and 1 are undefined.
// Instructions come with 0, 1 or 2 operands;
// the getters below act to provide a list of operands
// that is terminated by an AddressingMode::None.
//
// For two-operand instructions, argument 0 is a source
// and argument 1 is a destination.
//
// For one-operand instructions, only argument 0 will
// be provided, and will be a source and/or destination as
// per the semantics of the operation.
//
// The versions templated on index do a range check;
// if using the runtime versions then results for indices
// other than 0 and 1 are undefined.
AddressingMode mode(const int index) const {
return AddressingMode(operands_[index] >> 3);
AddressingMode mode(const int index) const {
return AddressingMode(operands_[index] >> 3);
}
template <int index> AddressingMode mode() const {
if constexpr (index > 1) {
return AddressingMode::None;
}
template <int index> AddressingMode mode() const {
if constexpr (index > 1) {
return AddressingMode::None;
}
return mode(index);
}
int reg(const int index) const {
return operands_[index] & 7;
}
template <int index> int reg() const {
if constexpr (index > 1) {
return 0;
}
return reg(index);
return mode(index);
}
int reg(const int index) const {
return operands_[index] & 7;
}
template <int index> int reg() const {
if constexpr (index > 1) {
return 0;
}
return reg(index);
}
/// @returns 07 to indicate data registers 0 to 7, or 815 to indicate address registers 0 to 7 respectively.
/// Provides undefined results if the addressing mode is not either @c DataRegisterDirect or
/// @c AddressRegisterDirect.
int lreg(const int index) const {
return operands_[index] & 0xf;
}
/// @returns 07 to indicate data registers 0 to 7, or 815 to indicate address registers 0 to 7 respectively.
/// Provides undefined results if the addressing mode is not either @c DataRegisterDirect or
/// @c AddressRegisterDirect.
int lreg(const int index) const {
return operands_[index] & 0xf;
}
/// @returns @c true if this instruction requires supervisor privileges; @c false otherwise.
bool requires_supervisor() const {
return flags_ & Flags::IsSupervisor;
}
/// @returns @c true if this instruction requires supervisor privileges; @c false otherwise.
bool requires_supervisor() const {
return flags_ & Flags::IsSupervisor;
}
/// @returns @c true if this instruction will require further fetching than can be encoded in a
/// @c Preinstruction. In practice this means it is one of a very small quantity of 68020+
/// instructions; those that can rationalise extension words into one of the two operands will
/// do so. Use the free function @c extension_words(instruction.operation) to
/// look up the number of additional words required.
///
/// (specifically affected, at least: PACK, UNPK, CAS, CAS2)
bool requires_further_extension() const {
return flags_ & Flags::RequiresFurtherExtension;
}
/// @returns @c true if this instruction will require further fetching than can be encoded in a
/// @c Preinstruction. In practice this means it is one of a very small quantity of 68020+
/// instructions; those that can rationalise extension words into one of the two operands will
/// do so. Use the free function @c extension_words(instruction.operation) to
/// look up the number of additional words required.
///
/// (specifically affected, at least: PACK, UNPK, CAS, CAS2)
bool requires_further_extension() const {
return flags_ & Flags::RequiresFurtherExtension;
}
/// @returns The number of additional extension words required, beyond those encoded as operands.
int additional_extension_words() const {
return flags_ & Flags::RequiresFurtherExtension ? (flags_ & Flags::ConditionMask) >> Flags::ConditionShift : 0;
}
/// @returns The number of additional extension words required, beyond those encoded as operands.
int additional_extension_words() const {
return flags_ & Flags::RequiresFurtherExtension ? (flags_ & Flags::ConditionMask) >> Flags::ConditionShift : 0;
}
/// @returns The @c DataSize used for operands of this instruction, i.e. byte, word or longword.
DataSize operand_size() const {
return DataSize((flags_ & Flags::SizeMask) >> Flags::SizeShift);
}
/// @returns The @c DataSize used for operands of this instruction, i.e. byte, word or longword.
DataSize operand_size() const {
return DataSize((flags_ & Flags::SizeMask) >> Flags::SizeShift);
}
/// @returns The condition code evaluated by this instruction if applicable. If this instruction is not
/// conditional, the result is undefined.
Condition condition() const {
return Condition((flags_ & Flags::ConditionMask) >> Flags::ConditionShift);
}
/// @returns The condition code evaluated by this instruction if applicable. If this instruction is not
/// conditional, the result is undefined.
Condition condition() const {
return Condition((flags_ & Flags::ConditionMask) >> Flags::ConditionShift);
}
private:
uint8_t operands_[2] = { uint8_t(AddressingMode::None), uint8_t(AddressingMode::None)};
uint8_t flags_ = 0;
private:
uint8_t operands_[2] = { uint8_t(AddressingMode::None), uint8_t(AddressingMode::None)};
uint8_t flags_ = 0;
std::string operand_description(int index, int opcode) const;
std::string operand_description(int index, int opcode) const;
public:
Preinstruction(
Operation operation,
AddressingMode op1_mode, int op1_reg,
AddressingMode op2_mode, int op2_reg,
bool is_supervisor,
int extension_words,
DataSize size,
Condition condition) : operation(operation)
{
operands_[0] = uint8_t((uint8_t(op1_mode) << 3) | op1_reg);
operands_[1] = uint8_t((uint8_t(op2_mode) << 3) | op2_reg);
flags_ = uint8_t(
(is_supervisor ? Flags::IsSupervisor : 0x00) |
(extension_words ? Flags::RequiresFurtherExtension : 0x00) |
(int(condition) << Flags::ConditionShift) |
(extension_words << Flags::ConditionShift) |
(int(size) << Flags::SizeShift)
);
}
public:
Preinstruction(
Operation operation,
AddressingMode op1_mode, int op1_reg,
AddressingMode op2_mode, int op2_reg,
bool is_supervisor,
int extension_words,
DataSize size,
Condition condition) : operation(operation)
{
operands_[0] = uint8_t((uint8_t(op1_mode) << 3) | op1_reg);
operands_[1] = uint8_t((uint8_t(op2_mode) << 3) | op2_reg);
flags_ = uint8_t(
(is_supervisor ? Flags::IsSupervisor : 0x00) |
(extension_words ? Flags::RequiresFurtherExtension : 0x00) |
(int(condition) << Flags::ConditionShift) |
(extension_words << Flags::ConditionShift) |
(int(size) << Flags::SizeShift)
);
}
struct Flags {
static constexpr uint8_t IsSupervisor = 0b1000'0000;
static constexpr uint8_t RequiresFurtherExtension = 0b0100'0000;
static constexpr uint8_t ConditionMask = 0b0011'1100;
static constexpr uint8_t SizeMask = 0b0000'0011;
struct Flags {
static constexpr uint8_t IsSupervisor = 0b1000'0000;
static constexpr uint8_t RequiresFurtherExtension = 0b0100'0000;
static constexpr uint8_t ConditionMask = 0b0011'1100;
static constexpr uint8_t SizeMask = 0b0000'0011;
static constexpr int IsSupervisorShift = 7;
static constexpr int RequiresFurtherExtensionShift = 6;
static constexpr int ConditionShift = 2;
static constexpr int SizeShift = 0;
};
static constexpr int IsSupervisorShift = 7;
static constexpr int RequiresFurtherExtensionShift = 6;
static constexpr int ConditionShift = 2;
static constexpr int SizeShift = 0;
};
Preinstruction() = default;
Preinstruction() = default;
/// Produces a string description of this instruction; if @c opcode
/// is supplied then any quick fields in this instruction will be decoded;
/// otherwise they'll be printed as just 'Q'.
std::string to_string(int opcode = -1) const;
/// Produces a string description of this instruction; if @c opcode
/// is supplied then any quick fields in this instruction will be decoded;
/// otherwise they'll be printed as just 'Q'.
std::string to_string(int opcode = -1) const;
/// Produces a slightly-more-idiomatic version of the operation name than
/// a direct to_string(instruction.operation) would, given that this decoder
/// sometimes aliases operations, disambiguating based on addressing mode
/// (e.g. MOVEQ is MOVE.l with the Q addressing mode).
const char *operation_string() const;
/// Produces a slightly-more-idiomatic version of the operation name than
/// a direct to_string(instruction.operation) would, given that this decoder
/// sometimes aliases operations, disambiguating based on addressing mode
/// (e.g. MOVEQ is MOVE.l with the Q addressing mode).
const char *operation_string() const;
};
}

@ -163,7 +163,7 @@ template <
Model model,
typename FlowController,
Operation operation = Operation::Undefined
> void perform(Preinstruction instruction, CPU::RegisterPair32 &source, CPU::RegisterPair32 &dest, Status &status, FlowController &flow_controller);
> void perform(Preinstruction, CPU::RegisterPair32 &source, CPU::RegisterPair32 &dest, Status &, FlowController &);
}