mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-17 22:38:59 +00:00
Improve formatting, const
ness in 68k and ARM instruction set implementations.
This commit is contained in:
parent
9d87296316
commit
394fe0f1f1
@ -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 1–16 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 ®(bool force_user_mode, uint32_t offset) {
|
||||
uint32_t ®(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]; // D0–D7 followed by A0–A7.
|
||||
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]; // D0–D7 followed by A0–A7.
|
||||
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 0–7 to indicate data registers 0 to 7, or 8–15 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 0–7 to indicate data registers 0 to 7, or 8–15 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 &);
|
||||
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user