mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-09 00:37:27 +00:00
Merge pull request #1424 from TomHarte/InstructionSetFormatting
Improve formatting, `const`ness in instruction sets.
This commit is contained in:
commit
31c878b654
@ -50,7 +50,7 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool is_iie(Target::Model model) {
|
||||
constexpr bool is_iie(const Target::Model model) {
|
||||
return model == Target::Model::IIe || model == Target::Model::EnhancedIIe;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ enum class Personality {
|
||||
EGA, // Extended EGA-style CRTC; uses 16-bit addressing throughout.
|
||||
};
|
||||
|
||||
constexpr bool is_egavga(Personality p) {
|
||||
constexpr bool is_egavga(const Personality p) {
|
||||
return p >= Personality::EGA;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ public:
|
||||
}
|
||||
|
||||
/// @returns @c true if all result bytes are exhausted; @c false otherwise.
|
||||
bool empty() const { return result_.empty(); }
|
||||
bool empty() const { return result_.empty(); }
|
||||
|
||||
/// @returns The next byte of the result.
|
||||
uint8_t next() {
|
||||
|
@ -38,7 +38,7 @@ enum class Status0: uint8_t {
|
||||
|
||||
enum class Status1: uint8_t {
|
||||
EndOfCylinder = 0x80,
|
||||
DataError = 0x20,
|
||||
DataError = 0x20,
|
||||
OverRun = 0x10,
|
||||
NoData = 0x04,
|
||||
NotWriteable = 0x02,
|
||||
@ -47,7 +47,7 @@ enum class Status1: uint8_t {
|
||||
|
||||
enum class Status2: uint8_t {
|
||||
DeletedControlMark = 0x40,
|
||||
DataCRCError = 0x20,
|
||||
DataCRCError = 0x20,
|
||||
WrongCyinder = 0x10,
|
||||
ScanEqualHit = 0x08,
|
||||
ScanNotSatisfied = 0x04,
|
||||
@ -57,7 +57,7 @@ enum class Status2: uint8_t {
|
||||
|
||||
enum class Status3: uint8_t {
|
||||
Fault = 0x80,
|
||||
WriteProtected = 0x40,
|
||||
WriteProtected = 0x40,
|
||||
Ready = 0x20,
|
||||
Track0 = 0x10,
|
||||
TwoSided = 0x08,
|
||||
@ -91,9 +91,9 @@ public:
|
||||
void set(const MainStatus flag, const bool value) {
|
||||
set(uint8_t(flag), value, main_status_);
|
||||
}
|
||||
void start_seek(const int drive) { main_status_ |= 1 << drive; }
|
||||
void start_seek(const int drive) { main_status_ |= 1 << drive; }
|
||||
void set(const Status0 flag) { set(uint8_t(flag), true, status_[0]); }
|
||||
void set(const Status1 flag) { set(uint8_t(flag), true, status_[1]); }
|
||||
void set(const Status1 flag) { set(uint8_t(flag), true, status_[1]); }
|
||||
void set(const Status2 flag) { set(uint8_t(flag), true, status_[2]); }
|
||||
|
||||
void set_status0(uint8_t value) { status_[0] = value; }
|
||||
|
@ -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,10 +320,10 @@ 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; }
|
||||
uint32_t operand2() const { return opcode_ & 0xf; }
|
||||
uint32_t destination() const { return (opcode_ >> 12) & 0xf; }
|
||||
uint32_t coprocessor() const { return (opcode_ >> 8) & 0xf; }
|
||||
uint32_t information() const { return (opcode_ >> 5) & 0x7; }
|
||||
@ -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,10 +353,10 @@ 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; }
|
||||
uint32_t operand2() const { return opcode_ & 0xf; }
|
||||
uint32_t destination() const { return (opcode_ >> 12) & 0xf; }
|
||||
uint32_t coprocessor() const { return (opcode_ >> 8) & 0xf; }
|
||||
uint32_t information() const { return (opcode_ >> 5) & 0x7; }
|
||||
@ -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
|
||||
@ -194,7 +194,7 @@ struct Registers {
|
||||
const auto r14 = pc_status(pc_offset_during(type));
|
||||
switch(type) {
|
||||
case Exception::IRQ: set_mode(Mode::IRQ); break;
|
||||
case Exception::FIQ: set_mode(Mode::FIQ); break;
|
||||
case Exception::FIQ: set_mode(Mode::FIQ); break;
|
||||
default: set_mode(Mode::Supervisor); break;
|
||||
}
|
||||
active_[14] = r14;
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
namespace InstructionSet {
|
||||
namespace M50740 {
|
||||
|
||||
Instruction Decoder::instrucion_for_opcode(uint8_t opcode) {
|
||||
Instruction Decoder::instrucion_for_opcode(const uint8_t opcode) {
|
||||
switch(opcode) {
|
||||
default: return Instruction(opcode);
|
||||
|
||||
@ -231,7 +231,7 @@ Instruction Decoder::instrucion_for_opcode(uint8_t opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, InstructionSet::M50740::Instruction> Decoder::decode(const uint8_t *source, size_t length) {
|
||||
std::pair<int, InstructionSet::M50740::Instruction> Decoder::decode(const uint8_t *source, const size_t length) {
|
||||
const uint8_t *const end = source + length;
|
||||
|
||||
if(phase_ == Phase::Instruction && source != end) {
|
||||
|
@ -16,19 +16,19 @@
|
||||
namespace InstructionSet::M50740 {
|
||||
|
||||
class Decoder {
|
||||
public:
|
||||
std::pair<int, Instruction> decode(const uint8_t *source, size_t length);
|
||||
Instruction instrucion_for_opcode(uint8_t opcode);
|
||||
public:
|
||||
std::pair<int, Instruction> decode(const uint8_t *, size_t length);
|
||||
Instruction instrucion_for_opcode(uint8_t);
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
Instruction,
|
||||
AwaitingOperand,
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
int operand_size_ = 0, operand_bytes_ = 0;
|
||||
int consumed_ = 0;
|
||||
Instruction instr_;
|
||||
private:
|
||||
enum class Phase {
|
||||
Instruction,
|
||||
AwaitingOperand,
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
int operand_size_ = 0, operand_bytes_ = 0;
|
||||
int consumed_ = 0;
|
||||
Instruction instr_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ void Executor::set_rom(const std::vector<uint8_t> &rom) {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Executor::run_for(Cycles cycles) {
|
||||
void Executor::run_for(const Cycles cycles) {
|
||||
// The incoming clock is divided by four; the local cycles_ count
|
||||
// ensures that fractional parts are kept track of.
|
||||
cycles_ += cycles;
|
||||
@ -61,7 +61,7 @@ void Executor::reset() {
|
||||
set_program_counter(uint16_t(memory_[0x1ffe] | (memory_[0x1fff] << 8)));
|
||||
}
|
||||
|
||||
void Executor::set_interrupt_line(bool line) {
|
||||
void Executor::set_interrupt_line(const bool line) {
|
||||
// Super hack: interrupt now, if permitted. Otherwise do nothing.
|
||||
// So this will fail to catch enabling of interrupts while the line
|
||||
// is active, amongst other things.
|
||||
@ -117,12 +117,12 @@ uint8_t Executor::read(uint16_t address) {
|
||||
}
|
||||
}
|
||||
|
||||
void Executor::set_port_output(int port) {
|
||||
void Executor::set_port_output(const int port) {
|
||||
// Force 'output' to a 1 anywhere that a bit is set as input.
|
||||
port_handler_.set_port_output(port, port_outputs_[port] | ~port_directions_[port]);
|
||||
}
|
||||
|
||||
void Executor::write(uint16_t address, uint8_t value) {
|
||||
void Executor::write(uint16_t address, const uint8_t value) {
|
||||
address &= 0x1fff;
|
||||
|
||||
// RAM writes are easy.
|
||||
@ -182,7 +182,7 @@ void Executor::write(uint16_t address, uint8_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Executor::push(uint8_t value) {
|
||||
void Executor::push(const uint8_t value) {
|
||||
write(s_, value);
|
||||
--s_;
|
||||
}
|
||||
@ -192,7 +192,7 @@ uint8_t Executor::pull() {
|
||||
return read(s_);
|
||||
}
|
||||
|
||||
void Executor::set_flags(uint8_t flags) {
|
||||
void Executor::set_flags(const uint8_t flags) {
|
||||
negative_result_ = flags;
|
||||
overflow_result_ = uint8_t(flags << 1);
|
||||
index_mode_ = flags & 0x20;
|
||||
@ -213,7 +213,7 @@ uint8_t Executor::flags() {
|
||||
carry_flag_;
|
||||
}
|
||||
|
||||
template<bool is_brk> inline void Executor::perform_interrupt(uint16_t vector) {
|
||||
template<bool is_brk> inline void Executor::perform_interrupt(const uint16_t vector) {
|
||||
// BRK has an unused operand.
|
||||
++program_counter_;
|
||||
push(uint8_t(program_counter_ >> 8));
|
||||
@ -222,7 +222,7 @@ template<bool is_brk> inline void Executor::perform_interrupt(uint16_t vector) {
|
||||
set_program_counter(uint16_t(memory_[vector] | (memory_[vector+1] << 8)));
|
||||
}
|
||||
|
||||
void Executor::set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector) {
|
||||
void Executor::set_interrupt_request(uint8_t ®, const uint8_t value, const uint16_t vector) {
|
||||
// TODO: this allows interrupts through only if fully enabled at the time they
|
||||
// signal. Which isn't quite correct, albeit that it seems sufficient for the
|
||||
// IIgs ADB controller.
|
||||
@ -797,7 +797,7 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
|
||||
}
|
||||
}
|
||||
|
||||
inline void Executor::subtract_duration(int duration) {
|
||||
inline void Executor::subtract_duration(const int duration) {
|
||||
// Pass along.
|
||||
CachingExecutor::subtract_duration(duration);
|
||||
|
||||
@ -846,7 +846,7 @@ inline void Executor::subtract_duration(int duration) {
|
||||
}
|
||||
}
|
||||
|
||||
inline int Executor::update_timer(Timer &timer, int count) {
|
||||
inline int Executor::update_timer(Timer &timer, const int count) {
|
||||
const int next_value = timer.value - count;
|
||||
if(next_value < 0) {
|
||||
// Determine how many reloads were required to get above zero.
|
||||
@ -859,6 +859,6 @@ inline int Executor::update_timer(Timer &timer, int count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Executor::get_output_mask(int port) {
|
||||
uint8_t Executor::get_output_mask(const int port) {
|
||||
return port_directions_[port];
|
||||
}
|
||||
|
@ -35,143 +35,142 @@ struct PortHandler {
|
||||
* timing is correct to whole-opcode boundaries only.
|
||||
*/
|
||||
class Executor: public CachingExecutor {
|
||||
public:
|
||||
Executor(PortHandler &);
|
||||
void set_rom(const std::vector<uint8_t> &rom);
|
||||
public:
|
||||
Executor(PortHandler &);
|
||||
void set_rom(const std::vector<uint8_t> &rom);
|
||||
|
||||
void reset();
|
||||
void set_interrupt_line(bool);
|
||||
void reset();
|
||||
void set_interrupt_line(bool);
|
||||
|
||||
uint8_t get_output_mask(int port);
|
||||
uint8_t get_output_mask(int port);
|
||||
|
||||
/*!
|
||||
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
|
||||
*/
|
||||
void run_for(Cycles cycles);
|
||||
/*!
|
||||
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
|
||||
*/
|
||||
void run_for(Cycles);
|
||||
|
||||
private:
|
||||
// MARK: - CachingExecutor-facing interface.
|
||||
private:
|
||||
// MARK: - CachingExecutor-facing interface.
|
||||
|
||||
friend CachingExecutor;
|
||||
friend CachingExecutor;
|
||||
|
||||
/*!
|
||||
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
|
||||
*/
|
||||
inline PerformerIndex action_for(Instruction instruction) {
|
||||
// This is a super-simple processor, so the opcode can be used directly to index the performers.
|
||||
return instruction.opcode;
|
||||
}
|
||||
/*!
|
||||
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
|
||||
*/
|
||||
inline PerformerIndex action_for(const Instruction instruction) {
|
||||
// This is a super-simple processor, so the opcode can be used directly to index the performers.
|
||||
return instruction.opcode;
|
||||
}
|
||||
|
||||
/*!
|
||||
Parses from @c start and no later than @c max_address, using the CachingExecutor as a target.
|
||||
*/
|
||||
inline void parse(uint16_t start, uint16_t closing_bound) {
|
||||
Parser<Executor, false> parser;
|
||||
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
|
||||
}
|
||||
/*!
|
||||
Parses from @c start and no later than @c max_address, using the CachingExecutor as a target.
|
||||
*/
|
||||
inline void parse(const uint16_t start, const uint16_t closing_bound) {
|
||||
Parser<Executor, false> parser;
|
||||
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
|
||||
}
|
||||
|
||||
private:
|
||||
// MARK: - Internal framework for generator performers.
|
||||
// MARK: - Internal framework for generator performers.
|
||||
|
||||
/*!
|
||||
Provides dynamic lookup of @c perform(Executor*).
|
||||
*/
|
||||
class PerformerLookup {
|
||||
public:
|
||||
PerformerLookup() {
|
||||
fill<int(MinOperation)>(performers_);
|
||||
/*!
|
||||
Provides dynamic lookup of @c perform(Executor*).
|
||||
*/
|
||||
class PerformerLookup {
|
||||
public:
|
||||
PerformerLookup() {
|
||||
fill<int(MinOperation)>(performers_);
|
||||
}
|
||||
|
||||
Performer performer(const Operation operation, const AddressingMode addressing_mode) {
|
||||
const auto index =
|
||||
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
|
||||
(int(addressing_mode) - MinAddressingMode);
|
||||
return performers_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
|
||||
|
||||
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
|
||||
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
|
||||
|
||||
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
|
||||
fill_operation<operation, addressing_mode+1>(target + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Performer performer(Operation operation, AddressingMode addressing_mode) {
|
||||
const auto index =
|
||||
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
|
||||
(int(addressing_mode) - MinAddressingMode);
|
||||
return performers_[index];
|
||||
template<int operation> void fill(Performer *target) {
|
||||
fill_operation<operation, int(MinAddressingMode)>(target);
|
||||
target += 1 + MaxAddressingMode - MinAddressingMode;
|
||||
|
||||
if constexpr (operation+1 <= MaxOperation) {
|
||||
fill<operation+1>(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
inline static PerformerLookup performer_lookup_;
|
||||
|
||||
private:
|
||||
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
|
||||
/*!
|
||||
Performs @c operation using @c operand as the value fetched from memory, if any.
|
||||
*/
|
||||
template <Operation operation> void perform(uint8_t *operand);
|
||||
|
||||
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
|
||||
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
|
||||
/*!
|
||||
Performs @c operation in @c addressing_mode.
|
||||
*/
|
||||
template <Operation operation, AddressingMode addressing_mode> void perform();
|
||||
|
||||
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
|
||||
fill_operation<operation, addressing_mode+1>(target + 1);
|
||||
}
|
||||
}
|
||||
private:
|
||||
// MARK: - Instruction set state.
|
||||
|
||||
template<int operation> void fill(Performer *target) {
|
||||
fill_operation<operation, int(MinAddressingMode)>(target);
|
||||
target += 1 + MaxAddressingMode - MinAddressingMode;
|
||||
// Memory.
|
||||
std::array<uint8_t, 0x2000> memory_;
|
||||
|
||||
if constexpr (operation+1 <= MaxOperation) {
|
||||
fill<operation+1>(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
inline static PerformerLookup performer_lookup_;
|
||||
// Registers.
|
||||
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
|
||||
|
||||
/*!
|
||||
Performs @c operation using @c operand as the value fetched from memory, if any.
|
||||
*/
|
||||
template <Operation operation> void perform(uint8_t *operand);
|
||||
uint8_t negative_result_ = 0;
|
||||
uint8_t zero_result_ = 0;
|
||||
uint8_t interrupt_disable_ = 0x04;
|
||||
uint8_t carry_flag_ = 0;
|
||||
uint8_t overflow_result_ = 0;
|
||||
bool index_mode_ = false;
|
||||
bool decimal_mode_ = false;
|
||||
|
||||
/*!
|
||||
Performs @c operation in @c addressing_mode.
|
||||
*/
|
||||
template <Operation operation, AddressingMode addressing_mode> void perform();
|
||||
// IO ports.
|
||||
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
|
||||
|
||||
private:
|
||||
// MARK: - Instruction set state.
|
||||
// Timers.
|
||||
struct Timer {
|
||||
uint8_t value = 0xff, reload_value = 0xff;
|
||||
};
|
||||
int timer_divider_ = 0;
|
||||
Timer timers_[3], prescalers_[2];
|
||||
inline int update_timer(Timer &timer, int count);
|
||||
|
||||
// Memory.
|
||||
std::array<uint8_t, 0x2000> memory_;
|
||||
// Interrupt and timer control.
|
||||
uint8_t interrupt_control_ = 0, timer_control_ = 0;
|
||||
bool interrupt_line_ = false;
|
||||
|
||||
// Registers.
|
||||
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
|
||||
// Access helpers.
|
||||
inline uint8_t read(uint16_t address);
|
||||
inline void write(uint16_t address, uint8_t value);
|
||||
inline void push(uint8_t);
|
||||
inline uint8_t pull();
|
||||
inline void set_flags(uint8_t);
|
||||
inline uint8_t flags();
|
||||
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
|
||||
inline void set_port_output(int port);
|
||||
|
||||
uint8_t negative_result_ = 0;
|
||||
uint8_t zero_result_ = 0;
|
||||
uint8_t interrupt_disable_ = 0x04;
|
||||
uint8_t carry_flag_ = 0;
|
||||
uint8_t overflow_result_ = 0;
|
||||
bool index_mode_ = false;
|
||||
bool decimal_mode_ = false;
|
||||
void set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector);
|
||||
|
||||
// IO ports.
|
||||
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
|
||||
// MARK: - Execution time
|
||||
|
||||
// Timers.
|
||||
struct Timer {
|
||||
uint8_t value = 0xff, reload_value = 0xff;
|
||||
};
|
||||
int timer_divider_ = 0;
|
||||
Timer timers_[3], prescalers_[2];
|
||||
inline int update_timer(Timer &timer, int count);
|
||||
|
||||
// Interrupt and timer control.
|
||||
uint8_t interrupt_control_ = 0, timer_control_ = 0;
|
||||
bool interrupt_line_ = false;
|
||||
|
||||
// Access helpers.
|
||||
inline uint8_t read(uint16_t address);
|
||||
inline void write(uint16_t address, uint8_t value);
|
||||
inline void push(uint8_t value);
|
||||
inline uint8_t pull();
|
||||
inline void set_flags(uint8_t);
|
||||
inline uint8_t flags();
|
||||
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
|
||||
inline void set_port_output(int port);
|
||||
|
||||
void set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector);
|
||||
|
||||
// MARK: - Execution time
|
||||
|
||||
Cycles cycles_;
|
||||
Cycles cycles_since_port_handler_;
|
||||
PortHandler &port_handler_;
|
||||
inline void subtract_duration(int duration);
|
||||
Cycles cycles_;
|
||||
Cycles cycles_since_port_handler_;
|
||||
PortHandler &port_handler_;
|
||||
inline void subtract_duration(int);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ enum class AddressingMode {
|
||||
static constexpr auto MaxAddressingMode = int(AddressingMode::ZeroPageRelative);
|
||||
static constexpr auto MinAddressingMode = int(AddressingMode::Implied);
|
||||
|
||||
constexpr int size(AddressingMode mode) {
|
||||
constexpr int size(const AddressingMode mode) {
|
||||
// This is coupled to the AddressingMode list above; be careful!
|
||||
constexpr int sizes[] = {
|
||||
0, 0, 1,
|
||||
@ -92,14 +92,14 @@ enum class Operation: uint8_t {
|
||||
static constexpr auto MaxOperation = int(Operation::STY);
|
||||
static constexpr auto MinOperation = int(Operation::BBC0);
|
||||
|
||||
constexpr AccessType access_type(Operation operation) {
|
||||
constexpr AccessType access_type(const Operation operation) {
|
||||
if(operation < Operation::ADC) return AccessType::None;
|
||||
if(operation < Operation::ASL) return AccessType::Read;
|
||||
if(operation < Operation::LDM) return AccessType::ReadModifyWrite;
|
||||
return AccessType::Write;
|
||||
}
|
||||
|
||||
constexpr bool uses_index_mode(Operation operation) {
|
||||
constexpr bool uses_index_mode(const Operation operation) {
|
||||
return
|
||||
operation == Operation::ADC || operation == Operation::AND ||
|
||||
operation == Operation::CMP || operation == Operation::EOR ||
|
||||
@ -110,7 +110,7 @@ constexpr bool uses_index_mode(Operation operation) {
|
||||
/*!
|
||||
@returns The name of @c operation.
|
||||
*/
|
||||
inline constexpr const char *operation_name(Operation operation) {
|
||||
inline constexpr const char *operation_name(const Operation operation) {
|
||||
#define MAP(x) case Operation::x: return #x;
|
||||
switch(operation) {
|
||||
default: break;
|
||||
@ -133,7 +133,7 @@ inline constexpr const char *operation_name(Operation operation) {
|
||||
return "???";
|
||||
}
|
||||
|
||||
inline std::ostream &operator <<(std::ostream &stream, Operation operation) {
|
||||
inline std::ostream &operator <<(std::ostream &stream, const Operation operation) {
|
||||
stream << operation_name(operation);
|
||||
return stream;
|
||||
}
|
||||
@ -141,7 +141,7 @@ inline std::ostream &operator <<(std::ostream &stream, Operation operation) {
|
||||
/*!
|
||||
@returns The name of @c addressing_mode.
|
||||
*/
|
||||
inline constexpr const char *addressing_mode_name(AddressingMode addressing_mode) {
|
||||
inline constexpr const char *addressing_mode_name(const AddressingMode addressing_mode) {
|
||||
switch(addressing_mode) {
|
||||
default: break;
|
||||
case AddressingMode::Implied: return "";
|
||||
@ -167,7 +167,7 @@ inline constexpr const char *addressing_mode_name(AddressingMode addressing_mode
|
||||
return "???";
|
||||
}
|
||||
|
||||
inline std::ostream &operator <<(std::ostream &stream, AddressingMode mode) {
|
||||
inline std::ostream &operator <<(std::ostream &stream, const AddressingMode mode) {
|
||||
stream << addressing_mode_name(mode);
|
||||
return stream;
|
||||
}
|
||||
@ -177,7 +177,11 @@ inline std::ostream &operator <<(std::ostream &stream, AddressingMode mode) {
|
||||
would appear in an assembler. E.g. '$5a' for that zero page address, or '$5a, x' for zero-page indexed from $5a. This function
|
||||
may access up to three bytes from @c operation onwards.
|
||||
*/
|
||||
inline std::string address(AddressingMode addressing_mode, const uint8_t *operation, uint16_t program_counter) {
|
||||
inline std::string address(
|
||||
const AddressingMode addressing_mode,
|
||||
const uint8_t *operation,
|
||||
const uint16_t program_counter
|
||||
) {
|
||||
std::stringstream output;
|
||||
output << std::hex;
|
||||
|
||||
@ -220,7 +224,8 @@ struct Instruction {
|
||||
AddressingMode addressing_mode = AddressingMode::Implied;
|
||||
uint8_t opcode = 0;
|
||||
|
||||
Instruction(Operation operation, AddressingMode addressing_mode, uint8_t opcode) : operation(operation), addressing_mode(addressing_mode), opcode(opcode) {}
|
||||
Instruction(const Operation operation, const AddressingMode addressing_mode, const uint8_t opcode) :
|
||||
operation(operation), addressing_mode(addressing_mode), opcode(opcode) {}
|
||||
Instruction(uint8_t opcode) : opcode(opcode) {}
|
||||
Instruction() = default;
|
||||
};
|
||||
|
@ -15,7 +15,7 @@
|
||||
namespace InstructionSet::M50740 {
|
||||
|
||||
template<typename Target, bool include_entries_and_accesses> struct Parser {
|
||||
void parse(Target &target, const uint8_t *storage, uint16_t start, uint16_t closing_bound) {
|
||||
void parse(Target &target, const uint8_t *storage, uint16_t start, const uint16_t closing_bound) {
|
||||
Decoder decoder;
|
||||
|
||||
while(start <= closing_bound) {
|
||||
@ -97,7 +97,10 @@ template<typename Target, bool include_entries_and_accesses> struct Parser {
|
||||
// Provide any fixed address accesses.
|
||||
switch(next.second.addressing_mode) {
|
||||
case AddressingMode::Absolute:
|
||||
target.add_access(uint16_t(storage[start + 1] | (storage[start + 2] << 8)), access_type(next.second.operation));
|
||||
target.add_access(
|
||||
uint16_t(storage[start + 1] | (storage[start + 2] << 8)),
|
||||
access_type(next.second.operation)
|
||||
);
|
||||
break;
|
||||
case AddressingMode::ZeroPage: case AddressingMode::ZeroPageRelative:
|
||||
target.add_access(storage[start + 1], access_type(next.second.operation));
|
||||
|
@ -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
|
||||
@ -1000,7 +1006,8 @@ template <typename Predecoder<model>::OpT op, bool validate> Preinstruction Pred
|
||||
// MARK: ASR, LSR, ROXR, ROR, ASL, LSL, ROXL, ROL
|
||||
//
|
||||
// b0–b2: a register to shift (the source here, for consistency with the memory operations);
|
||||
// b8: 0 => b9–b11 are a direct count of bits to shift; 1 => b9–b11 identify a register containing the shift count;
|
||||
// b8: 0 => b9–b11 are a direct count of bits to shift;
|
||||
// 1 => b9–b11 identify register containing the shift count;
|
||||
// b9–b11: either a quick value or a register.
|
||||
//
|
||||
case OpT(Operation::ASRb): case OpT(Operation::ASRw): case OpT(Operation::ASRl):
|
||||
@ -1269,11 +1276,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 +1393,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 +1401,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 +1412,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 +1423,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 +1546,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 +1624,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 +1656,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 +1666,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 +1696,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 +1728,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 +1767,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 +1802,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 +1834,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) {
|
||||
@ -1845,10 +1852,12 @@ Preinstruction Predecoder<model>::decodeE(const uint16_t instruction) {
|
||||
case 0xac0: DecodeReq(model >= Model::M68020, Op::BFCHG); // 4-33 (p137)
|
||||
case 0xbc0: DecodeReq(model >= Model::M68020, Op::BFEXTS); // 4-37 (p141)
|
||||
case 0xcc0: DecodeReq(model >= Model::M68020, Op::BFCLR); // 4-35 (p139)
|
||||
case 0xdc0: DecodeReq(model >= Model::M68020, Op::BFFFO); // 4-43 (p147) [though the given opcode is wrong; listed same as BFEXTU]
|
||||
case 0xdc0: DecodeReq(model >= Model::M68020, Op::BFFFO); // 4-43 (p147)*
|
||||
case 0xec0: DecodeReq(model >= Model::M68020, Op::BFSET); // 4-49 (p153)
|
||||
case 0xfc0: DecodeReq(model >= Model::M68020, Op::BFINS); // 4-46 (p150)
|
||||
|
||||
// * [though the given opcode is wrong; listed same as BFEXTU]
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
@ -1900,7 +1909,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++) {
|
||||
@ -266,7 +266,11 @@ uint32_t Executor<model, BusHandler>::State::index_8bitdisplacement(uint32_t bas
|
||||
|
||||
template <Model model, typename BusHandler>
|
||||
typename Executor<model, BusHandler>::State::EffectiveAddress
|
||||
Executor<model, BusHandler>::State::calculate_effective_address(const Preinstruction instruction, const uint16_t opcode, const int index) {
|
||||
Executor<model, BusHandler>::State::calculate_effective_address(
|
||||
const Preinstruction instruction,
|
||||
const uint16_t opcode,
|
||||
const int index
|
||||
) {
|
||||
EffectiveAddress ea;
|
||||
|
||||
switch(instruction.mode(index)) {
|
||||
@ -531,7 +535,11 @@ template <typename IntT> void Executor<model, BusHandler>::State::complete_bcc(c
|
||||
}
|
||||
|
||||
template <Model model, typename BusHandler>
|
||||
void Executor<model, BusHandler>::State::complete_dbcc(const bool matched_condition, const bool overflowed, const int16_t offset) {
|
||||
void Executor<model, BusHandler>::State::complete_dbcc(
|
||||
const bool matched_condition,
|
||||
const bool overflowed,
|
||||
const int16_t offset
|
||||
) {
|
||||
if(!matched_condition && !overflowed) {
|
||||
program_counter.l = instruction_address + offset + 2;
|
||||
}
|
||||
@ -622,7 +630,11 @@ void Executor<model, BusHandler>::State::move_from_usp(uint32_t &address) {
|
||||
|
||||
template <Model model, typename BusHandler>
|
||||
template <typename IntT>
|
||||
void Executor<model, BusHandler>::State::movep(const Preinstruction instruction, const uint32_t source, const uint32_t dest) {
|
||||
void Executor<model, BusHandler>::State::movep(
|
||||
const Preinstruction instruction,
|
||||
const uint32_t source,
|
||||
const uint32_t dest
|
||||
) {
|
||||
if(instruction.mode<0>() == AddressingMode::DataRegisterDirect) {
|
||||
// Move register to memory.
|
||||
const uint32_t reg = source;
|
||||
|
@ -130,10 +130,18 @@ inline uint32_t mask_bit(const Preinstruction &instruction, const uint32_t sourc
|
||||
return source & (instruction.mode<1>() == AddressingMode::DataRegisterDirect ? 31 : 7);
|
||||
}
|
||||
|
||||
/// Perform a BCLR, BCHG or BSET as specified by @c operation and described by @c instruction, @c source and @c destination, updating @c destination and @c status.
|
||||
/// Perform a BCLR, BCHG or BSET as specified by @c operation and described by @c instruction, @c source
|
||||
/// and @c destination, updating @c destination and @c status.
|
||||
///
|
||||
/// Also makes an appropriate notification to the @c flow_controller.
|
||||
template <Operation operation, typename FlowController>
|
||||
void bit_manipulate(const Preinstruction &instruction, const uint32_t source, uint32_t &destination, Status &status, FlowController &flow_controller) {
|
||||
void bit_manipulate(
|
||||
const Preinstruction &instruction,
|
||||
const uint32_t source,
|
||||
uint32_t &destination,
|
||||
Status &status,
|
||||
FlowController &flow_controller
|
||||
) {
|
||||
static_assert(
|
||||
operation == Operation::BCLR ||
|
||||
operation == Operation::BCHG ||
|
||||
@ -283,14 +291,18 @@ template <typename IntT> void test(const IntT source, Status &status) {
|
||||
}
|
||||
|
||||
/// Decodes the proper shift distance from @c source, notifying the @c flow_controller.
|
||||
template <typename IntT, typename FlowController> int shift_count(const uint8_t source, FlowController &flow_controller) {
|
||||
template <typename IntT, typename FlowController> int shift_count(
|
||||
const uint8_t source,
|
||||
FlowController &flow_controller
|
||||
) {
|
||||
const int count = source & 63;
|
||||
flow_controller.template did_shift<IntT>(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
/// Perform an arithmetic or logical shift, i.e. any of LSL, LSR, ASL or ASR.
|
||||
template <Operation operation, typename IntT, typename FlowController> void shift(const uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) {
|
||||
template <Operation operation, typename IntT, typename FlowController>
|
||||
void shift(const uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) {
|
||||
static_assert(
|
||||
operation == Operation::ASLb || operation == Operation::ASLw || operation == Operation::ASLl ||
|
||||
operation == Operation::ASRb || operation == Operation::ASRw || operation == Operation::ASRl ||
|
||||
@ -347,7 +359,9 @@ template <Operation operation, typename IntT, typename FlowController> void shif
|
||||
); // e.g. shift = 1 => ~((0x80 >> 1) - 1) = ~(0x40 - 1) = ~0x3f = 0xc0, i.e. if shift is
|
||||
// 1 then the top two bits are relevant to whether there was overflow. If they have the
|
||||
// same value, i.e. are both 0 or are both 1, then there wasn't. Otherwise there was.
|
||||
status.overflow_flag = (destination & affected_bits) && (destination & affected_bits) != affected_bits;
|
||||
status.overflow_flag =
|
||||
(destination & affected_bits) &&
|
||||
(destination & affected_bits) != affected_bits;
|
||||
}
|
||||
}
|
||||
|
||||
@ -384,7 +398,8 @@ template <Operation operation, typename IntT, typename FlowController> void shif
|
||||
}
|
||||
|
||||
/// Perform a rotate without extend, i.e. any of RO[L/R].[b/w/l].
|
||||
template <Operation operation, typename IntT, typename FlowController> void rotate(const uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) {
|
||||
template <Operation operation, typename IntT, typename FlowController>
|
||||
void rotate(const uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) {
|
||||
static_assert(
|
||||
operation == Operation::ROLb || operation == Operation::ROLw || operation == Operation::ROLl ||
|
||||
operation == Operation::RORb || operation == Operation::RORw || operation == Operation::RORl
|
||||
@ -425,7 +440,8 @@ template <Operation operation, typename IntT, typename FlowController> void rota
|
||||
}
|
||||
|
||||
/// Perform a rotate-through-extend, i.e. any of ROX[L/R].[b/w/l].
|
||||
template <Operation operation, typename IntT, typename FlowController> void rox(const uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) {
|
||||
template <Operation operation, typename IntT, typename FlowController>
|
||||
void rox(const uint32_t source, IntT &destination, Status &status, FlowController &flow_controller) {
|
||||
static_assert(
|
||||
operation == Operation::ROXLb || operation == Operation::ROXLw || operation == Operation::ROXLl ||
|
||||
operation == Operation::ROXRb || operation == Operation::ROXRw || operation == Operation::ROXRl
|
||||
@ -496,8 +512,13 @@ template <
|
||||
Model model,
|
||||
typename FlowController,
|
||||
Operation operation = Operation::Undefined
|
||||
> void perform(const Preinstruction instruction, CPU::SlicedInt32 &src, CPU::SlicedInt32 &dest, Status &status, FlowController &flow_controller) {
|
||||
|
||||
> void perform(
|
||||
const Preinstruction instruction,
|
||||
CPU::SlicedInt32 &src,
|
||||
CPU::SlicedInt32 &dest,
|
||||
Status &status,
|
||||
FlowController &flow_controller
|
||||
) {
|
||||
switch((operation != Operation::Undefined) ? operation : instruction.operation) {
|
||||
/*
|
||||
ABCD adds the lowest bytes from the source and destination using BCD arithmetic,
|
||||
@ -554,9 +575,15 @@ template <
|
||||
case Operation::BTST:
|
||||
status.zero_result = dest.l & (1 << Primitive::mask_bit(instruction, src.l));
|
||||
break;
|
||||
case Operation::BCLR: Primitive::bit_manipulate<Operation::BCLR>(instruction, src.l, dest.l, status, flow_controller); break;
|
||||
case Operation::BCHG: Primitive::bit_manipulate<Operation::BCHG>(instruction, src.l, dest.l, status, flow_controller); break;
|
||||
case Operation::BSET: Primitive::bit_manipulate<Operation::BSET>(instruction, src.l, dest.l, status, flow_controller); break;
|
||||
case Operation::BCLR:
|
||||
Primitive::bit_manipulate<Operation::BCLR>(instruction, src.l, dest.l, status, flow_controller);
|
||||
break;
|
||||
case Operation::BCHG:
|
||||
Primitive::bit_manipulate<Operation::BCHG>(instruction, src.l, dest.l, status, flow_controller);
|
||||
break;
|
||||
case Operation::BSET:
|
||||
Primitive::bit_manipulate<Operation::BSET>(instruction, src.l, dest.l, status, flow_controller);
|
||||
break;
|
||||
|
||||
case Operation::Bccb:
|
||||
flow_controller.template complete_bcc<int8_t>(
|
||||
@ -703,12 +730,24 @@ template <
|
||||
status.set_neg_zero(src.l);
|
||||
break;
|
||||
|
||||
case Operation::ANDItoSR: Primitive::apply_sr_ccr<Operation::ANDItoSR>(src.w, status, flow_controller); break;
|
||||
case Operation::EORItoSR: Primitive::apply_sr_ccr<Operation::EORItoSR>(src.w, status, flow_controller); break;
|
||||
case Operation::ORItoSR: Primitive::apply_sr_ccr<Operation::ORItoSR>(src.w, status, flow_controller); break;
|
||||
case Operation::ANDItoCCR: Primitive::apply_sr_ccr<Operation::ANDItoCCR>(src.w, status, flow_controller); break;
|
||||
case Operation::EORItoCCR: Primitive::apply_sr_ccr<Operation::EORItoCCR>(src.w, status, flow_controller); break;
|
||||
case Operation::ORItoCCR: Primitive::apply_sr_ccr<Operation::ORItoCCR>(src.w, status, flow_controller); break;
|
||||
case Operation::ANDItoSR:
|
||||
Primitive::apply_sr_ccr<Operation::ANDItoSR>(src.w, status, flow_controller);
|
||||
break;
|
||||
case Operation::EORItoSR:
|
||||
Primitive::apply_sr_ccr<Operation::EORItoSR>(src.w, status, flow_controller);
|
||||
break;
|
||||
case Operation::ORItoSR:
|
||||
Primitive::apply_sr_ccr<Operation::ORItoSR>(src.w, status, flow_controller);
|
||||
break;
|
||||
case Operation::ANDItoCCR:
|
||||
Primitive::apply_sr_ccr<Operation::ANDItoCCR>(src.w, status, flow_controller);
|
||||
break;
|
||||
case Operation::EORItoCCR:
|
||||
Primitive::apply_sr_ccr<Operation::EORItoCCR>(src.w, status, flow_controller);
|
||||
break;
|
||||
case Operation::ORItoCCR:
|
||||
Primitive::apply_sr_ccr<Operation::ORItoCCR>(src.w, status, flow_controller);
|
||||
break;
|
||||
|
||||
/*
|
||||
Multiplications.
|
||||
@ -721,8 +760,12 @@ template <
|
||||
Divisions.
|
||||
*/
|
||||
|
||||
case Operation::DIVUw: Primitive::divide<true, uint16_t, uint32_t>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::DIVSw: Primitive::divide<false, int16_t, int32_t>(src.w, dest.l, status, flow_controller); break;
|
||||
case Operation::DIVUw:
|
||||
Primitive::divide<true, uint16_t, uint32_t>(src.w, dest.l, status, flow_controller);
|
||||
break;
|
||||
case Operation::DIVSw:
|
||||
Primitive::divide<false, int16_t, int32_t>(src.w, dest.l, status, flow_controller);
|
||||
break;
|
||||
|
||||
// TRAP, which is a nicer form of ILLEGAL.
|
||||
case Operation::TRAP:
|
||||
|
@ -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 &);
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,8 @@ using namespace InstructionSet::PowerPC;
|
||||
|
||||
namespace {
|
||||
|
||||
template <Model model, bool validate_reserved_bits, Operation operation> Instruction instruction(uint32_t opcode, bool is_supervisor = false) {
|
||||
template <Model model, bool validate_reserved_bits, Operation operation>
|
||||
Instruction instruction(const uint32_t opcode, const bool is_supervisor = false) {
|
||||
// If validation isn't required, there's nothing to do here.
|
||||
if constexpr (!validate_reserved_bits) {
|
||||
return Instruction(operation, opcode, is_supervisor);
|
||||
@ -243,7 +244,7 @@ template <Model model, bool validate_reserved_bits, Operation operation> Instruc
|
||||
}
|
||||
|
||||
template <Model model, bool validate_reserved_bits>
|
||||
Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
Instruction Decoder<model, validate_reserved_bits>::decode(const uint32_t opcode) {
|
||||
// Quick bluffer's guide to PowerPC instruction encoding:
|
||||
//
|
||||
// There is a six-bit field at the very top of the instruction.
|
||||
@ -332,23 +333,29 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
default: break;
|
||||
|
||||
// 64-bit instructions.
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000110101), ldux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000111010), cntlzdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001000100), td);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001010100), ldarx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0010010101), stdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0010110101), stdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulldx); BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0101010101), lwax);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0101110101), lwaux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1000011011), srdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100011010), sradx);
|
||||
BindConditional(is64bit, SixTen(0b111111, 0b1111011010), extswx);
|
||||
@ -356,16 +363,22 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
// Power instructions; these are all taken from the MPC601 manual rather than
|
||||
// the PowerPC Programmer's Reference Guide, hence the decimal encoding of the
|
||||
// ten-bit field.
|
||||
BindConditional(is601, SixTen(0b011111, 360), absx); BindConditional(is601, SixTen(0b011111, 512 + 360), absx);
|
||||
BindConditional(is601, SixTen(0b011111, 360), absx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 360), absx);
|
||||
BindConditional(is601, SixTen(0b011111, 531), clcs);
|
||||
BindConditional(is601, SixTen(0b011111, 331), divx); BindConditional(is601, SixTen(0b011111, 512 + 331), divx);
|
||||
BindConditional(is601, SixTen(0b011111, 363), divsx); BindConditional(is601, SixTen(0b011111, 512 + 363), divsx);
|
||||
BindConditional(is601, SixTen(0b011111, 264), dozx); BindConditional(is601, SixTen(0b011111, 512 + 264), dozx);
|
||||
BindConditional(is601, SixTen(0b011111, 331), divx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 331), divx);
|
||||
BindConditional(is601, SixTen(0b011111, 363), divsx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 363), divsx);
|
||||
BindConditional(is601, SixTen(0b011111, 264), dozx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 264), dozx);
|
||||
BindConditional(is601, SixTen(0b011111, 277), lscbxx);
|
||||
BindConditional(is601, SixTen(0b011111, 29), maskgx);
|
||||
BindConditional(is601, SixTen(0b011111, 541), maskirx);
|
||||
BindConditional(is601, SixTen(0b011111, 107), mulx); BindConditional(is601, SixTen(0b011111, 512 + 107), mulx);
|
||||
BindConditional(is601, SixTen(0b011111, 488), nabsx); BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx);
|
||||
BindConditional(is601, SixTen(0b011111, 107), mulx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 107), mulx);
|
||||
BindConditional(is601, SixTen(0b011111, 488), nabsx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx);
|
||||
BindConditional(is601, SixTen(0b011111, 537), rribx);
|
||||
BindConditional(is601, SixTen(0b011111, 153), slex);
|
||||
BindConditional(is601, SixTen(0b011111, 217), sleqx);
|
||||
@ -553,17 +566,22 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
if(is64bit(model)) {
|
||||
switch(opcode & 0b111111'00000'00000'00000'000000'111'00) {
|
||||
default: break;
|
||||
case 0b011110'00000'00000'00000'000000'000'00: return instruction<model, validate_reserved_bits, Operation::rldiclx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'001'00: return instruction<model, validate_reserved_bits, Operation::rldicrx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'010'00: return instruction<model, validate_reserved_bits, Operation::rldicx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'011'00: return instruction<model, validate_reserved_bits, Operation::rldimix>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'000'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldiclx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'001'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldicrx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'010'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldicx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'011'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldimix>(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
// stwcx. and stdcx.
|
||||
switch(opcode & 0b111111'0000'0000'0000'0000'111111111'1) {
|
||||
default: break;
|
||||
case 0b011111'0000'0000'0000'0000'010010110'1: return instruction<model, validate_reserved_bits, Operation::stwcx_>(opcode);
|
||||
case 0b011111'0000'0000'0000'0000'010010110'1:
|
||||
return instruction<model, validate_reserved_bits, Operation::stwcx_>(opcode);
|
||||
case 0b011111'0000'0000'0000'0000'011010110'1:
|
||||
if(is64bit(model)) return instruction<model, validate_reserved_bits, Operation::stdcx_>(opcode);
|
||||
return Instruction(opcode);
|
||||
@ -573,11 +591,16 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
if(is64bit(model)) {
|
||||
switch(opcode & 0b111111'00'00000000'00000000'000000'11) {
|
||||
default: break;
|
||||
case 0b111010'00'00000000'00000000'000000'00: return instruction<model, validate_reserved_bits, Operation::ld>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'01: return instruction<model, validate_reserved_bits, Operation::ldu>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'10: return instruction<model, validate_reserved_bits, Operation::lwa>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'00: return instruction<model, validate_reserved_bits, Operation::std>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'01: return instruction<model, validate_reserved_bits, Operation::stdu>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::ld>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'01:
|
||||
return instruction<model, validate_reserved_bits, Operation::ldu>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'10:
|
||||
return instruction<model, validate_reserved_bits, Operation::lwa>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::std>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'01:
|
||||
return instruction<model, validate_reserved_bits, Operation::stdu>(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,15 +21,15 @@ enum class Model {
|
||||
MPC620,
|
||||
};
|
||||
|
||||
constexpr bool is64bit(Model model) {
|
||||
constexpr bool is64bit(const Model model) {
|
||||
return model == Model::MPC620;
|
||||
}
|
||||
|
||||
constexpr bool is32bit(Model model) {
|
||||
constexpr bool is32bit(const Model model) {
|
||||
return !is64bit(model);
|
||||
}
|
||||
|
||||
constexpr bool is601(Model model) {
|
||||
constexpr bool is601(const Model model) {
|
||||
return model == Model::MPC601;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ constexpr bool is601(Model model) {
|
||||
TODO: determine what specific models of PowerPC do re: reserved bits.
|
||||
*/
|
||||
template <Model model, bool validate_reserved_bits = false> struct Decoder {
|
||||
Instruction decode(uint32_t opcode);
|
||||
Instruction decode(uint32_t);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1363,8 +1363,9 @@ struct Instruction {
|
||||
uint32_t opcode = 0;
|
||||
|
||||
constexpr Instruction() noexcept = default;
|
||||
constexpr Instruction(uint32_t opcode) noexcept : opcode(opcode) {}
|
||||
constexpr Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) noexcept : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
|
||||
constexpr Instruction(const uint32_t opcode) noexcept : opcode(opcode) {}
|
||||
constexpr Instruction(const Operation operation, const uint32_t opcode, const bool is_supervisor = false) noexcept :
|
||||
operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
|
||||
|
||||
// Instruction fields are decoded below; naming is a compromise between
|
||||
// Motorola's documentation and IBM's.
|
||||
|
@ -30,24 +30,24 @@ enum class AccessType {
|
||||
PreauthorisedRead,
|
||||
};
|
||||
|
||||
constexpr bool is_writeable(AccessType type) {
|
||||
constexpr bool is_writeable(const AccessType type) {
|
||||
return type == AccessType::ReadModifyWrite || type == AccessType::Write;
|
||||
}
|
||||
|
||||
template <typename IntT, AccessType type> struct Accessor;
|
||||
|
||||
// Reads: return a value directly.
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = const IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = const IntT; };
|
||||
|
||||
// Writes: return a custom type that can be written but not read.
|
||||
template <typename IntT>
|
||||
class Writeable {
|
||||
public:
|
||||
Writeable(IntT &target) : target_(target) {}
|
||||
IntT operator=(IntT value) { return target_ = value; }
|
||||
private:
|
||||
IntT &target_;
|
||||
public:
|
||||
Writeable(IntT &target) : target_(target) {}
|
||||
IntT operator=(IntT value) { return target_ = value; }
|
||||
private:
|
||||
IntT &target_;
|
||||
};
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Write> { using type = Writeable<IntT>; };
|
||||
|
||||
|
@ -15,7 +15,10 @@
|
||||
using namespace InstructionSet::x86;
|
||||
|
||||
template <Model model>
|
||||
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(const uint8_t *source, std::size_t length) {
|
||||
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(
|
||||
const uint8_t *source,
|
||||
const std::size_t length
|
||||
) {
|
||||
// Instruction length limits:
|
||||
//
|
||||
// 8086/80186: none*
|
||||
@ -1124,6 +1127,6 @@ template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80186>;
|
||||
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80286>;
|
||||
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80386>;
|
||||
|
||||
std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *source, std::size_t length) {
|
||||
std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *const source, const std::size_t length) {
|
||||
return decoder.decode(source, length);
|
||||
}
|
||||
|
@ -22,209 +22,209 @@ namespace InstructionSet::x86 {
|
||||
This is an experimental implementation; it has not yet undergone significant testing.
|
||||
*/
|
||||
template <Model model> class Decoder {
|
||||
public:
|
||||
using InstructionT = Instruction<is_32bit(model)>;
|
||||
public:
|
||||
using InstructionT = Instruction<is_32bit(model)>;
|
||||
|
||||
/*!
|
||||
@returns an @c Instruction plus a size; a positive size indicates successful decoding of
|
||||
an instruction that was that many bytes long in total; a negative size specifies the [negatived]
|
||||
minimum number of further bytes the caller should ideally collect before calling again. The
|
||||
caller is free to call with fewer, but may not get a decoded instruction in response, and the
|
||||
decoder may still not be able to complete decoding even if given that number of bytes.
|
||||
/*!
|
||||
@returns an @c Instruction plus a size; a positive size indicates successful decoding of
|
||||
an instruction that was that many bytes long in total; a negative size specifies the [negatived]
|
||||
minimum number of further bytes the caller should ideally collect before calling again. The
|
||||
caller is free to call with fewer, but may not get a decoded instruction in response, and the
|
||||
decoder may still not be able to complete decoding even if given that number of bytes.
|
||||
|
||||
Successful decoding is defined to mean that all decoding steps are complete. The output
|
||||
may still be an illegal instruction (indicated by Operation::Invalid), if the byte sequence
|
||||
supplied cannot form a valid instruction.
|
||||
Successful decoding is defined to mean that all decoding steps are complete. The output
|
||||
may still be an illegal instruction (indicated by Operation::Invalid), if the byte sequence
|
||||
supplied cannot form a valid instruction.
|
||||
|
||||
@discussion although instructions also contain an indicator of their length, on chips prior
|
||||
to the 80286 there is no limit to potential instruction length.
|
||||
@discussion although instructions also contain an indicator of their length, on chips prior
|
||||
to the 80286 there is no limit to potential instruction length.
|
||||
|
||||
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
|
||||
cannot overflow the field.
|
||||
*/
|
||||
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);
|
||||
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
|
||||
cannot overflow the field.
|
||||
*/
|
||||
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);
|
||||
|
||||
/*!
|
||||
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
|
||||
*/
|
||||
void set_32bit_protected_mode(bool);
|
||||
/*!
|
||||
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
|
||||
*/
|
||||
void set_32bit_protected_mode(bool);
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
/// Captures all prefixes and continues until an instruction byte is encountered.
|
||||
Instruction,
|
||||
/// Having encountered a 0x0f first instruction byte, waits for the next byte fully to determine the instruction.
|
||||
InstructionPageF,
|
||||
/// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately
|
||||
/// or completes decoding of the instruction, as per the instruction format.
|
||||
ModRegRM,
|
||||
/// Awaits n 80386+-style scale-index-base byte ('SIB'), indicating the form of indirect addressing.
|
||||
ScaleIndexBase,
|
||||
/// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured.
|
||||
/// Cf. displacement_size_ and operand_size_.
|
||||
DisplacementOrOperand,
|
||||
/// Forms and returns an Instruction, and resets parsing state.
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
private:
|
||||
enum class Phase {
|
||||
/// Captures all prefixes and continues until an instruction byte is encountered.
|
||||
Instruction,
|
||||
/// Having encountered a 0x0f first instruction byte, waits for the next byte fully to determine the instruction.
|
||||
InstructionPageF,
|
||||
/// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately
|
||||
/// or completes decoding of the instruction, as per the instruction format.
|
||||
ModRegRM,
|
||||
/// Awaits n 80386+-style scale-index-base byte ('SIB'), indicating the form of indirect addressing.
|
||||
ScaleIndexBase,
|
||||
/// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured.
|
||||
/// Cf. displacement_size_ and operand_size_.
|
||||
DisplacementOrOperand,
|
||||
/// Forms and returns an Instruction, and resets parsing state.
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
|
||||
/// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte.
|
||||
///
|
||||
/// During the ReadyToPost phase, format determines how transiently-recorded fields
|
||||
/// are packaged into an Instruction.
|
||||
enum class ModRegRMFormat: uint8_t {
|
||||
// Parse the ModRegRM for mode, register and register/memory fields
|
||||
// and populate the source_ and destination_ fields appropriately.
|
||||
MemReg_Reg,
|
||||
Reg_MemReg,
|
||||
/// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte.
|
||||
///
|
||||
/// During the ReadyToPost phase, format determines how transiently-recorded fields
|
||||
/// are packaged into an Instruction.
|
||||
enum class ModRegRMFormat: uint8_t {
|
||||
// Parse the ModRegRM for mode, register and register/memory fields
|
||||
// and populate the source_ and destination_ fields appropriately.
|
||||
MemReg_Reg,
|
||||
Reg_MemReg,
|
||||
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the single register/memory result.
|
||||
MemRegSingleOperand,
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the single register/memory result.
|
||||
MemRegSingleOperand,
|
||||
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// the destination_ field with the result and setting source_ to Immediate.
|
||||
MemRegMOV,
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// the destination_ field with the result and setting source_ to Immediate.
|
||||
MemRegMOV,
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ field with the result. Fills destination_ with a segment
|
||||
// register based on the reg field.
|
||||
Seg_MemReg,
|
||||
MemReg_Seg,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ field with the result. Fills destination_ with a segment
|
||||
// register based on the reg field.
|
||||
Seg_MemReg,
|
||||
MemReg_Seg,
|
||||
|
||||
//
|
||||
// 'Group 1'
|
||||
//
|
||||
//
|
||||
// 'Group 1'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and
|
||||
// waits for an operand equal to the operation size.
|
||||
MemRegADD_to_CMP,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and
|
||||
// waits for an operand equal to the operation size.
|
||||
MemRegADD_to_CMP,
|
||||
|
||||
// Acts exactly as MemRegADD_to_CMP but the operand is fixed in size
|
||||
// at a single byte, which is sign extended to the operation size.
|
||||
MemRegADD_to_CMP_SignExtend,
|
||||
// Acts exactly as MemRegADD_to_CMP but the operand is fixed in size
|
||||
// at a single byte, which is sign extended to the operation size.
|
||||
MemRegADD_to_CMP_SignExtend,
|
||||
|
||||
//
|
||||
// 'Group 2'
|
||||
//
|
||||
//
|
||||
// 'Group 2'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group.
|
||||
MemRegROL_to_SAR,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group.
|
||||
MemRegROL_to_SAR,
|
||||
|
||||
//
|
||||
// 'Group 3'
|
||||
//
|
||||
//
|
||||
// 'Group 3'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the result. Use the 'register'
|
||||
// field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group.
|
||||
MemRegTEST_to_IDIV,
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the result. Use the 'register'
|
||||
// field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group.
|
||||
MemRegTEST_to_IDIV,
|
||||
|
||||
//
|
||||
// 'Group 4'
|
||||
//
|
||||
//
|
||||
// 'Group 4'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick INC or DEC.
|
||||
MemRegINC_DEC,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick INC or DEC.
|
||||
MemRegINC_DEC,
|
||||
|
||||
//
|
||||
// 'Group 5'
|
||||
//
|
||||
//
|
||||
// 'Group 5'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
|
||||
// the source to ::Immediate and setting an operand size if necessary.
|
||||
MemRegINC_to_PUSH,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
|
||||
// the source to ::Immediate and setting an operand size if necessary.
|
||||
MemRegINC_to_PUSH,
|
||||
|
||||
//
|
||||
// 'Group 6'
|
||||
//
|
||||
//
|
||||
// 'Group 6'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SLDT/STR/LLDT/LTR/VERR/VERW.
|
||||
MemRegSLDT_to_VERW,
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SLDT/STR/LLDT/LTR/VERR/VERW.
|
||||
MemRegSLDT_to_VERW,
|
||||
|
||||
//
|
||||
// 'Group 7'
|
||||
//
|
||||
//
|
||||
// 'Group 7'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SGDT/LGDT/SMSW/LMSW.
|
||||
MemRegSGDT_to_LMSW,
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SGDT/LGDT/SMSW/LMSW.
|
||||
MemRegSGDT_to_LMSW,
|
||||
|
||||
//
|
||||
// 'Group 8'
|
||||
//
|
||||
//
|
||||
// 'Group 8'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory field, populating destination,
|
||||
// and prepare to read a single byte as source.
|
||||
MemRegBT_to_BTC,
|
||||
} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
|
||||
// Parse for mode and register/memory field, populating destination,
|
||||
// and prepare to read a single byte as source.
|
||||
MemRegBT_to_BTC,
|
||||
} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
|
||||
|
||||
// Ephemeral decoding state.
|
||||
Operation operation_ = Operation::Invalid;
|
||||
int consumed_ = 0, operand_bytes_ = 0;
|
||||
// Ephemeral decoding state.
|
||||
Operation operation_ = Operation::Invalid;
|
||||
int consumed_ = 0, operand_bytes_ = 0;
|
||||
|
||||
// Source and destination locations.
|
||||
Source source_ = Source::None;
|
||||
Source destination_ = Source::None;
|
||||
// Source and destination locations.
|
||||
Source source_ = Source::None;
|
||||
Source destination_ = Source::None;
|
||||
|
||||
// Immediate fields.
|
||||
int32_t displacement_ = 0;
|
||||
uint32_t operand_ = 0;
|
||||
uint64_t inward_data_ = 0;
|
||||
int next_inward_data_shift_ = 0;
|
||||
// Immediate fields.
|
||||
int32_t displacement_ = 0;
|
||||
uint32_t operand_ = 0;
|
||||
uint64_t inward_data_ = 0;
|
||||
int next_inward_data_shift_ = 0;
|
||||
|
||||
// Indirection style.
|
||||
ScaleIndexBase sib_;
|
||||
// Indirection style.
|
||||
ScaleIndexBase sib_;
|
||||
|
||||
// Facts about the instruction.
|
||||
DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any.
|
||||
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
|
||||
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.
|
||||
// Facts about the instruction.
|
||||
DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any.
|
||||
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
|
||||
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.
|
||||
|
||||
bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
|
||||
// size; otherwise it'll be zero-padded.
|
||||
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
|
||||
// otherwise it'll be zero-padded.
|
||||
bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
|
||||
// size; otherwise it'll be zero-padded.
|
||||
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
|
||||
// otherwise it'll be zero-padded.
|
||||
|
||||
// Prefix capture fields.
|
||||
Repetition repetition_ = Repetition::None;
|
||||
bool lock_ = false;
|
||||
Source segment_override_ = Source::None;
|
||||
// Prefix capture fields.
|
||||
Repetition repetition_ = Repetition::None;
|
||||
bool lock_ = false;
|
||||
Source segment_override_ = Source::None;
|
||||
|
||||
// 32-bit/16-bit selection.
|
||||
AddressSize default_address_size_ = AddressSize::b16;
|
||||
DataSize default_data_size_ = DataSize::Word;
|
||||
AddressSize address_size_ = AddressSize::b16;
|
||||
DataSize data_size_ = DataSize::Word;
|
||||
// 32-bit/16-bit selection.
|
||||
AddressSize default_address_size_ = AddressSize::b16;
|
||||
DataSize default_data_size_ = DataSize::Word;
|
||||
AddressSize address_size_ = AddressSize::b16;
|
||||
DataSize data_size_ = DataSize::Word;
|
||||
|
||||
/// Resets size capture and all fields with default values.
|
||||
void reset_parsing() {
|
||||
consumed_ = operand_bytes_ = 0;
|
||||
displacement_size_ = operand_size_ = operation_size_ = DataSize::None;
|
||||
displacement_ = operand_ = 0;
|
||||
lock_ = false;
|
||||
address_size_ = default_address_size_;
|
||||
data_size_ = default_data_size_;
|
||||
segment_override_ = Source::None;
|
||||
repetition_ = Repetition::None;
|
||||
phase_ = Phase::Instruction;
|
||||
source_ = destination_ = Source::None;
|
||||
sib_ = ScaleIndexBase();
|
||||
next_inward_data_shift_ = 0;
|
||||
inward_data_ = 0;
|
||||
sign_extend_operand_ = false;
|
||||
sign_extend_displacement_ = true;
|
||||
}
|
||||
/// Resets size capture and all fields with default values.
|
||||
void reset_parsing() {
|
||||
consumed_ = operand_bytes_ = 0;
|
||||
displacement_size_ = operand_size_ = operation_size_ = DataSize::None;
|
||||
displacement_ = operand_ = 0;
|
||||
lock_ = false;
|
||||
address_size_ = default_address_size_;
|
||||
data_size_ = default_data_size_;
|
||||
segment_override_ = Source::None;
|
||||
repetition_ = Repetition::None;
|
||||
phase_ = Phase::Instruction;
|
||||
source_ = destination_ = Source::None;
|
||||
sib_ = ScaleIndexBase();
|
||||
next_inward_data_shift_ = 0;
|
||||
inward_data_ = 0;
|
||||
sign_extend_operand_ = false;
|
||||
sign_extend_displacement_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
// This is a temporary measure; for reasons as-yet unknown, GCC isn't picking up the
|
||||
|
@ -77,154 +77,154 @@ enum class Condition {
|
||||
};
|
||||
|
||||
class Flags {
|
||||
public:
|
||||
using FlagT = uint32_t;
|
||||
public:
|
||||
using FlagT = uint32_t;
|
||||
|
||||
// Flag getters.
|
||||
template <Flag flag_v> bool flag() const {
|
||||
switch(flag_v) {
|
||||
case Flag::Carry: return carry_;
|
||||
case Flag::AuxiliaryCarry: return auxiliary_carry_;
|
||||
case Flag::Sign: return sign_;
|
||||
case Flag::Overflow: return overflow_;
|
||||
case Flag::Trap: return trap_;
|
||||
case Flag::Interrupt: return interrupt_;
|
||||
case Flag::Direction: return direction_ < 0;
|
||||
case Flag::Zero: return !zero_;
|
||||
case Flag::ParityOdd: return not_parity_bit();
|
||||
// Flag getters.
|
||||
template <Flag flag_v> bool flag() const {
|
||||
switch(flag_v) {
|
||||
case Flag::Carry: return carry_;
|
||||
case Flag::AuxiliaryCarry: return auxiliary_carry_;
|
||||
case Flag::Sign: return sign_;
|
||||
case Flag::Overflow: return overflow_;
|
||||
case Flag::Trap: return trap_;
|
||||
case Flag::Interrupt: return interrupt_;
|
||||
case Flag::Direction: return direction_ < 0;
|
||||
case Flag::Zero: return !zero_;
|
||||
case Flag::ParityOdd: return not_parity_bit();
|
||||
}
|
||||
}
|
||||
|
||||
// Condition evaluation.
|
||||
template <Condition test> bool condition() const {
|
||||
switch(test) {
|
||||
case Condition::Overflow: return flag<Flag::Overflow>();
|
||||
case Condition::Below: return flag<Flag::Carry>();
|
||||
case Condition::Zero: return flag<Flag::Zero>();
|
||||
case Condition::BelowOrEqual: return flag<Flag::Zero>() || flag<Flag::Carry>();
|
||||
case Condition::Sign: return flag<Flag::Sign>();
|
||||
case Condition::ParityOdd: return flag<Flag::ParityOdd>();
|
||||
case Condition::Less: return flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
case Condition::LessOrEqual: return flag<Flag::Zero>() || flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience setters.
|
||||
|
||||
/// Sets all of @c flags as a function of @c value:
|
||||
/// • Flag::Zero: sets the zero flag if @c value is zero;
|
||||
/// • Flag::Sign: sets the sign flag if the top bit of @c value is one;
|
||||
/// • Flag::ParityOdd: sets parity based on the low 8 bits of @c value;
|
||||
/// • Flag::Carry: sets carry if @c value is non-zero;
|
||||
/// • Flag::AuxiliaryCarry: sets auxiliary carry if @c value is non-zero;
|
||||
/// • Flag::Overflow: sets overflow if @c value is non-zero;
|
||||
/// • Flag::Interrupt: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Trap: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Direction: sets direction if @c value is non-zero.
|
||||
template <typename IntT, Flag... flags> void set_from(const IntT value) {
|
||||
for(const auto flag: {flags...}) {
|
||||
switch(flag) {
|
||||
default: break;
|
||||
case Flag::Zero: zero_ = value; break;
|
||||
case Flag::Sign: sign_ = value & Numeric::top_bit<IntT>(); break;
|
||||
case Flag::ParityOdd: parity_ = value; break;
|
||||
case Flag::Carry: carry_ = value; break;
|
||||
case Flag::AuxiliaryCarry: auxiliary_carry_ = value; break;
|
||||
case Flag::Overflow: overflow_ = value; break;
|
||||
case Flag::Interrupt: interrupt_ = value; break;
|
||||
case Flag::Trap: trap_ = value; break;
|
||||
case Flag::Direction: direction_ = value ? -1 : 1; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <Flag... flags> void set_from(const FlagT value) {
|
||||
set_from<FlagT, flags...>(value);
|
||||
}
|
||||
|
||||
// Condition evaluation.
|
||||
template <Condition test> bool condition() const {
|
||||
switch(test) {
|
||||
case Condition::Overflow: return flag<Flag::Overflow>();
|
||||
case Condition::Below: return flag<Flag::Carry>();
|
||||
case Condition::Zero: return flag<Flag::Zero>();
|
||||
case Condition::BelowOrEqual: return flag<Flag::Zero>() || flag<Flag::Carry>();
|
||||
case Condition::Sign: return flag<Flag::Sign>();
|
||||
case Condition::ParityOdd: return flag<Flag::ParityOdd>();
|
||||
case Condition::Less: return flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
case Condition::LessOrEqual: return flag<Flag::Zero>() || flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
}
|
||||
}
|
||||
template <typename IntT> IntT carry_bit() const { return carry_ ? 1 : 0; }
|
||||
bool not_parity_bit() const {
|
||||
// x86 parity always considers the lowest 8-bits only.
|
||||
auto result = static_cast<uint8_t>(parity_);
|
||||
result ^= result >> 4;
|
||||
result ^= result >> 2;
|
||||
result ^= result >> 1;
|
||||
return result & 1;
|
||||
}
|
||||
|
||||
// Convenience setters.
|
||||
template <typename IntT> IntT direction() const { return static_cast<IntT>(direction_); }
|
||||
|
||||
/// Sets all of @c flags as a function of @c value:
|
||||
/// • Flag::Zero: sets the zero flag if @c value is zero;
|
||||
/// • Flag::Sign: sets the sign flag if the top bit of @c value is one;
|
||||
/// • Flag::ParityOdd: sets parity based on the low 8 bits of @c value;
|
||||
/// • Flag::Carry: sets carry if @c value is non-zero;
|
||||
/// • Flag::AuxiliaryCarry: sets auxiliary carry if @c value is non-zero;
|
||||
/// • Flag::Overflow: sets overflow if @c value is non-zero;
|
||||
/// • Flag::Interrupt: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Trap: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Direction: sets direction if @c value is non-zero.
|
||||
template <typename IntT, Flag... flags> void set_from(IntT value) {
|
||||
for(const auto flag: {flags...}) {
|
||||
switch(flag) {
|
||||
default: break;
|
||||
case Flag::Zero: zero_ = value; break;
|
||||
case Flag::Sign: sign_ = value & Numeric::top_bit<IntT>(); break;
|
||||
case Flag::ParityOdd: parity_ = value; break;
|
||||
case Flag::Carry: carry_ = value; break;
|
||||
case Flag::AuxiliaryCarry: auxiliary_carry_ = value; break;
|
||||
case Flag::Overflow: overflow_ = value; break;
|
||||
case Flag::Interrupt: interrupt_ = value; break;
|
||||
case Flag::Trap: trap_ = value; break;
|
||||
case Flag::Direction: direction_ = value ? -1 : 1; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <Flag... flags> void set_from(FlagT value) {
|
||||
set_from<FlagT, flags...>(value);
|
||||
}
|
||||
// Complete value get and set.
|
||||
void set(uint16_t value) {
|
||||
set_from<Flag::Carry>(value & FlagValue::Carry);
|
||||
set_from<Flag::AuxiliaryCarry>(value & FlagValue::AuxiliaryCarry);
|
||||
set_from<Flag::Overflow>(value & FlagValue::Overflow);
|
||||
set_from<Flag::Trap>(value & FlagValue::Trap);
|
||||
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
|
||||
set_from<Flag::Direction>(value & FlagValue::Direction);
|
||||
|
||||
template <typename IntT> IntT carry_bit() const { return carry_ ? 1 : 0; }
|
||||
bool not_parity_bit() const {
|
||||
// x86 parity always considers the lowest 8-bits only.
|
||||
auto result = static_cast<uint8_t>(parity_);
|
||||
result ^= result >> 4;
|
||||
result ^= result >> 2;
|
||||
result ^= result >> 1;
|
||||
return result & 1;
|
||||
}
|
||||
set_from<uint8_t, Flag::Sign>(uint8_t(value));
|
||||
|
||||
template <typename IntT> IntT direction() const { return static_cast<IntT>(direction_); }
|
||||
set_from<Flag::Zero>((~value) & FlagValue::Zero);
|
||||
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
|
||||
}
|
||||
|
||||
// Complete value get and set.
|
||||
void set(uint16_t value) {
|
||||
set_from<Flag::Carry>(value & FlagValue::Carry);
|
||||
set_from<Flag::AuxiliaryCarry>(value & FlagValue::AuxiliaryCarry);
|
||||
set_from<Flag::Overflow>(value & FlagValue::Overflow);
|
||||
set_from<Flag::Trap>(value & FlagValue::Trap);
|
||||
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
|
||||
set_from<Flag::Direction>(value & FlagValue::Direction);
|
||||
uint16_t get() const {
|
||||
return
|
||||
0xf002 |
|
||||
|
||||
set_from<uint8_t, Flag::Sign>(uint8_t(value));
|
||||
(flag<Flag::Carry>() ? FlagValue::Carry : 0) |
|
||||
(flag<Flag::AuxiliaryCarry>() ? FlagValue::AuxiliaryCarry : 0) |
|
||||
(flag<Flag::Sign>() ? FlagValue::Sign : 0) |
|
||||
(flag<Flag::Overflow>() ? FlagValue::Overflow : 0) |
|
||||
(flag<Flag::Trap>() ? FlagValue::Trap : 0) |
|
||||
(flag<Flag::Interrupt>() ? FlagValue::Interrupt : 0) |
|
||||
(flag<Flag::Direction>() ? FlagValue::Direction : 0) |
|
||||
(flag<Flag::Zero>() ? FlagValue::Zero : 0) |
|
||||
|
||||
set_from<Flag::Zero>((~value) & FlagValue::Zero);
|
||||
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
|
||||
}
|
||||
(flag<Flag::ParityOdd>() ? 0 : FlagValue::Parity);
|
||||
}
|
||||
|
||||
uint16_t get() const {
|
||||
return
|
||||
0xf002 |
|
||||
std::string to_string() const {
|
||||
std::string result;
|
||||
|
||||
(flag<Flag::Carry>() ? FlagValue::Carry : 0) |
|
||||
(flag<Flag::AuxiliaryCarry>() ? FlagValue::AuxiliaryCarry : 0) |
|
||||
(flag<Flag::Sign>() ? FlagValue::Sign : 0) |
|
||||
(flag<Flag::Overflow>() ? FlagValue::Overflow : 0) |
|
||||
(flag<Flag::Trap>() ? FlagValue::Trap : 0) |
|
||||
(flag<Flag::Interrupt>() ? FlagValue::Interrupt : 0) |
|
||||
(flag<Flag::Direction>() ? FlagValue::Direction : 0) |
|
||||
(flag<Flag::Zero>() ? FlagValue::Zero : 0) |
|
||||
if(flag<Flag::Overflow>()) result += "O"; else result += "-";
|
||||
if(flag<Flag::Direction>()) result += "D"; else result += "-";
|
||||
if(flag<Flag::Interrupt>()) result += "I"; else result += "-";
|
||||
if(flag<Flag::Trap>()) result += "T"; else result += "-";
|
||||
if(flag<Flag::Sign>()) result += "S"; else result += "-";
|
||||
if(flag<Flag::Zero>()) result += "Z"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::AuxiliaryCarry>()) result += "A"; else result += "-";
|
||||
result += "-";
|
||||
if(!flag<Flag::ParityOdd>()) result += "P"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::Carry>()) result += "C"; else result += "-";
|
||||
|
||||
(flag<Flag::ParityOdd>() ? 0 : FlagValue::Parity);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
std::string result;
|
||||
bool operator ==(const Flags &rhs) const {
|
||||
return get() == rhs.get();
|
||||
}
|
||||
|
||||
if(flag<Flag::Overflow>()) result += "O"; else result += "-";
|
||||
if(flag<Flag::Direction>()) result += "D"; else result += "-";
|
||||
if(flag<Flag::Interrupt>()) result += "I"; else result += "-";
|
||||
if(flag<Flag::Trap>()) result += "T"; else result += "-";
|
||||
if(flag<Flag::Sign>()) result += "S"; else result += "-";
|
||||
if(flag<Flag::Zero>()) result += "Z"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::AuxiliaryCarry>()) result += "A"; else result += "-";
|
||||
result += "-";
|
||||
if(!flag<Flag::ParityOdd>()) result += "P"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::Carry>()) result += "C"; else result += "-";
|
||||
private:
|
||||
// Non-zero => set; zero => unset.
|
||||
uint32_t carry_;
|
||||
uint32_t auxiliary_carry_;
|
||||
uint32_t sign_;
|
||||
uint32_t overflow_;
|
||||
uint32_t trap_;
|
||||
uint32_t interrupt_;
|
||||
|
||||
return result;
|
||||
}
|
||||
// +1 = direction flag not set;
|
||||
// -1 = direction flag set.
|
||||
int32_t direction_;
|
||||
|
||||
bool operator ==(const Flags &rhs) const {
|
||||
return get() == rhs.get();
|
||||
}
|
||||
// Zero => set; non-zero => unset.
|
||||
uint32_t zero_;
|
||||
|
||||
private:
|
||||
// Non-zero => set; zero => unset.
|
||||
uint32_t carry_;
|
||||
uint32_t auxiliary_carry_;
|
||||
uint32_t sign_;
|
||||
uint32_t overflow_;
|
||||
uint32_t trap_;
|
||||
uint32_t interrupt_;
|
||||
|
||||
// +1 = direction flag not set;
|
||||
// -1 = direction flag set.
|
||||
int32_t direction_;
|
||||
|
||||
// Zero => set; non-zero => unset.
|
||||
uint32_t zero_;
|
||||
|
||||
// Odd number of bits => set; even => unset.
|
||||
uint32_t parity_;
|
||||
// Odd number of bits => set; even => unset.
|
||||
uint32_t parity_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -240,7 +240,8 @@ void idiv(
|
||||
IF OperandSize = 8 (* word/byte operation *)
|
||||
THEN
|
||||
temp ← AX / SRC; (* signed division *)
|
||||
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than 7FH or a negative result is less than 80H *)
|
||||
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than
|
||||
7FH or a negative result is less than 80H *)
|
||||
THEN #DE; (* divide error *) ;
|
||||
ELSE
|
||||
AL ← temp;
|
||||
@ -250,7 +251,8 @@ void idiv(
|
||||
IF OperandSize = 16 (* doubleword/word operation *)
|
||||
THEN
|
||||
temp ← DX:AX / SRC; (* signed division *)
|
||||
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a negative result is less than 8000H *)
|
||||
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a
|
||||
negative result is less than 8000H *)
|
||||
THEN #DE; (* divide error *) ;
|
||||
ELSE
|
||||
AX ← temp;
|
||||
@ -258,7 +260,8 @@ void idiv(
|
||||
FI;
|
||||
ELSE (* quadword/doubleword operation *)
|
||||
temp ← EDX:EAX / SRC; (* signed division *)
|
||||
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH or a negative result is less than 80000000H *)
|
||||
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH
|
||||
or a negative result is less than 80000000H *)
|
||||
THEN #DE; (* divide error *) ;
|
||||
ELSE
|
||||
EAX ← temp;
|
||||
@ -350,7 +353,11 @@ void neg(
|
||||
The CF flag cleared to 0 if the source operand is 0; otherwise it is set to 1.
|
||||
The OF, SF, ZF, AF, and PF flags are set according to the result.
|
||||
*/
|
||||
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(IntT(0), destination, IntT(-destination)));
|
||||
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(
|
||||
IntT(0),
|
||||
destination,
|
||||
IntT(-destination))
|
||||
);
|
||||
|
||||
destination = -destination;
|
||||
|
||||
|
@ -38,7 +38,7 @@ void aaas(
|
||||
template <typename ContextT>
|
||||
void aad(
|
||||
CPU::RegisterPair16 &ax,
|
||||
uint8_t imm,
|
||||
const uint8_t imm,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -59,7 +59,7 @@ void aad(
|
||||
template <typename ContextT>
|
||||
void aam(
|
||||
CPU::RegisterPair16 &ax,
|
||||
uint8_t imm,
|
||||
const uint8_t imm,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
|
@ -18,8 +18,8 @@ namespace InstructionSet::x86::Primitive {
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
void jump(
|
||||
bool condition,
|
||||
IntT displacement,
|
||||
const bool condition,
|
||||
const IntT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -42,7 +42,7 @@ void jump(
|
||||
template <typename IntT, typename OffsetT, typename ContextT>
|
||||
void loop(
|
||||
modify_t<IntT> counter,
|
||||
OffsetT displacement,
|
||||
const OffsetT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
--counter;
|
||||
@ -54,7 +54,7 @@ void loop(
|
||||
template <typename IntT, typename OffsetT, typename ContextT>
|
||||
void loope(
|
||||
modify_t<IntT> counter,
|
||||
OffsetT displacement,
|
||||
const OffsetT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
--counter;
|
||||
@ -66,7 +66,7 @@ void loope(
|
||||
template <typename IntT, typename OffsetT, typename ContextT>
|
||||
void loopne(
|
||||
modify_t<IntT> counter,
|
||||
OffsetT displacement,
|
||||
const OffsetT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
--counter;
|
||||
@ -125,20 +125,28 @@ void call_far(
|
||||
return;
|
||||
|
||||
case Source::Indirect:
|
||||
source_address = uint16_t(address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::IndirectNoBase:
|
||||
source_address = uint16_t(address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::DirectAddress:
|
||||
source_address = uint16_t(address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto offset =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
source_address += 2;
|
||||
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto segment =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
|
||||
// At least on an 8086, the stack writes occur after the target address read.
|
||||
push<uint16_t, true>(context.registers.cs(), context);
|
||||
@ -157,25 +165,35 @@ void jump_far(
|
||||
const auto pointer = instruction.destination();
|
||||
switch(pointer.source()) {
|
||||
default:
|
||||
case Source::Immediate: context.flow_controller.template jump<uint16_t>(instruction.segment(), instruction.offset()); return;
|
||||
case Source::Immediate:
|
||||
context.flow_controller.template jump<uint16_t>(instruction.segment(), instruction.offset());
|
||||
return;
|
||||
|
||||
case Source::Indirect:
|
||||
source_address = uint16_t(address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::IndirectNoBase:
|
||||
source_address = uint16_t(address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::DirectAddress:
|
||||
source_address = uint16_t(address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const Source source_segment = instruction.data_segment();
|
||||
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||
|
||||
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto offset =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
source_address += 2;
|
||||
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto segment =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
context.flow_controller.template jump<uint16_t>(segment, offset);
|
||||
}
|
||||
|
||||
@ -193,7 +211,7 @@ void iret(
|
||||
|
||||
template <typename InstructionT, typename ContextT>
|
||||
void ret_near(
|
||||
InstructionT instruction,
|
||||
const InstructionT instruction,
|
||||
ContextT &context
|
||||
) {
|
||||
const auto ip = pop<uint16_t, false>(context);
|
||||
@ -203,7 +221,7 @@ void ret_near(
|
||||
|
||||
template <typename InstructionT, typename ContextT>
|
||||
void ret_far(
|
||||
InstructionT instruction,
|
||||
const InstructionT instruction,
|
||||
ContextT &context
|
||||
) {
|
||||
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
|
||||
@ -233,9 +251,11 @@ void bound(
|
||||
|
||||
const auto source_segment = instruction.data_segment();
|
||||
context.memory.preauthorise_read(source_segment, source, 2*sizeof(IntT));
|
||||
const sIntT lower_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
const auto lower_bound =
|
||||
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
source += 2;
|
||||
const sIntT upper_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
const auto upper_bound =
|
||||
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
|
||||
if(sIntT(destination) < lower_bound || sIntT(destination) > upper_bound) {
|
||||
interrupt(Interrupt::BoundRangeExceeded, context);
|
||||
|
@ -14,7 +14,7 @@ namespace InstructionSet::x86::Primitive {
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
void out(
|
||||
uint16_t port,
|
||||
const uint16_t port,
|
||||
read_t<IntT> value,
|
||||
ContextT &context
|
||||
) {
|
||||
@ -23,7 +23,7 @@ void out(
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
void in(
|
||||
uint16_t port,
|
||||
const uint16_t port,
|
||||
write_t<IntT> value,
|
||||
ContextT &context
|
||||
) {
|
||||
|
@ -99,7 +99,7 @@ void cbw(
|
||||
template <typename IntT>
|
||||
void cwd(
|
||||
IntT &dx,
|
||||
IntT ax
|
||||
const IntT ax
|
||||
) {
|
||||
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
|
||||
}
|
||||
|
@ -119,12 +119,12 @@ template <
|
||||
// The two following return the high and low parts of that pair; they also work in Byte mode to return AH:AL,
|
||||
// i.e. AX split into high and low parts.
|
||||
const auto pair_high = [&]() -> IntT& {
|
||||
if constexpr (data_size == DataSize::Byte) return context.registers.ah();
|
||||
if constexpr (data_size == DataSize::Byte) return context.registers.ah();
|
||||
else if constexpr (data_size == DataSize::Word) return context.registers.dx();
|
||||
else if constexpr (data_size == DataSize::DWord) return context.registers.edx();
|
||||
};
|
||||
const auto pair_low = [&]() -> IntT& {
|
||||
if constexpr (data_size == DataSize::Byte) return context.registers.al();
|
||||
if constexpr (data_size == DataSize::Byte) return context.registers.al();
|
||||
else if constexpr (data_size == DataSize::Word) return context.registers.ax();
|
||||
else if constexpr (data_size == DataSize::DWord) return context.registers.eax();
|
||||
};
|
||||
@ -176,8 +176,12 @@ template <
|
||||
case Operation::ESC:
|
||||
case Operation::NOP: return;
|
||||
|
||||
case Operation::AAM: Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context); return;
|
||||
case Operation::AAD: Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context); return;
|
||||
case Operation::AAM:
|
||||
Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context);
|
||||
return;
|
||||
case Operation::AAD:
|
||||
Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context);
|
||||
return;
|
||||
case Operation::AAA: Primitive::aaas<true>(context.registers.axp(), context); return;
|
||||
case Operation::AAS: Primitive::aaas<false>(context.registers.axp(), context); return;
|
||||
case Operation::DAA: Primitive::daas<true>(context.registers.al(), context); return;
|
||||
@ -236,8 +240,8 @@ template <
|
||||
case Operation::CALLrel:
|
||||
Primitive::call_relative<AddressT>(instruction.displacement(), context);
|
||||
return;
|
||||
case Operation::CALLabs: Primitive::call_absolute<IntT, AddressT>(destination_r(), context); return;
|
||||
case Operation::CALLfar: Primitive::call_far<AddressT>(instruction, context); return;
|
||||
case Operation::CALLabs: Primitive::call_absolute<IntT, AddressT>(destination_r(), context); return;
|
||||
case Operation::CALLfar: Primitive::call_far<AddressT>(instruction, context); return;
|
||||
|
||||
case Operation::JMPrel: jcc(true); return;
|
||||
case Operation::JMPabs: Primitive::jump_absolute<IntT>(destination_r(), context); return;
|
||||
@ -417,10 +421,12 @@ template <
|
||||
break;
|
||||
|
||||
case Operation::OUTS:
|
||||
Primitive::outs<IntT, AddressT, Repetition::None>(instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
Primitive::outs<IntT, AddressT, Repetition::None>(
|
||||
instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
return;
|
||||
case Operation::OUTS_REP:
|
||||
Primitive::outs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
Primitive::outs<IntT, AddressT, Repetition::Rep>(
|
||||
instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
return;
|
||||
|
||||
case Operation::INS:
|
||||
@ -453,7 +459,7 @@ template <
|
||||
const InstructionT &instruction,
|
||||
ContextT &context
|
||||
) {
|
||||
auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
|
||||
const auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
|
||||
return int(operation_size) + (int(address_size) << 2);
|
||||
};
|
||||
|
||||
@ -508,7 +514,7 @@ template <
|
||||
template <
|
||||
typename ContextT
|
||||
> void interrupt(
|
||||
int index,
|
||||
const int index,
|
||||
ContextT &context
|
||||
) {
|
||||
const uint32_t address = static_cast<uint32_t>(index) << 2;
|
||||
|
@ -21,9 +21,9 @@ namespace InstructionSet::x86 {
|
||||
/// is copied to @c *immediate and @c immediate is returned.
|
||||
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
||||
typename Accessor<IntT, access>::type resolve(
|
||||
InstructionT &instruction,
|
||||
Source source,
|
||||
DataPointer pointer,
|
||||
const InstructionT &instruction,
|
||||
const Source source,
|
||||
const DataPointer pointer,
|
||||
ContextT &context,
|
||||
IntT *none = nullptr,
|
||||
IntT *immediate = nullptr
|
||||
@ -66,44 +66,45 @@ IntT *register_(ContextT &context) {
|
||||
case Source::eAX:
|
||||
// Slightly contorted if chain here and below:
|
||||
//
|
||||
// (i) does the `constexpr` version of a `switch`; and
|
||||
// (i) ensures .eax() etc aren't called on @c registers for 16-bit processors, so they need not implement 32-bit storage.
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.eax(); }
|
||||
// (i) does the `constexpr` version of a `switch`; and
|
||||
// (i) ensures .eax() etc aren't called on @c registers for 16-bit processors,
|
||||
// so they need not implement 32-bit storage.
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.eax(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.ax(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.al(); }
|
||||
else { return nullptr; }
|
||||
else { return nullptr; }
|
||||
case Source::eCX:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ecx(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ecx(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.cx(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.cl(); }
|
||||
else { return nullptr; }
|
||||
else { return nullptr; }
|
||||
case Source::eDX:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edx(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edx(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.dx(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.dl(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
|
||||
case Source::eBX:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebx(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebx(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.bx(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.bl(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint32_t>) { return nullptr; }
|
||||
case Source::eSPorAH:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esp(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esp(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.sp(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.ah(); }
|
||||
else { return nullptr; }
|
||||
case Source::eBPorCH:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebp(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.ebp(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.bp(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.ch(); }
|
||||
else { return nullptr; }
|
||||
else { return nullptr; }
|
||||
case Source::eSIorDH:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esi(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.esi(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.si(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.dh(); }
|
||||
else { return nullptr; }
|
||||
else { return nullptr; }
|
||||
case Source::eDIorBH:
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edi(); }
|
||||
if constexpr (supports_dword && std::is_same_v<IntT, uint32_t>) { return &context.registers.edi(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint16_t>) { return &context.registers.di(); }
|
||||
else if constexpr (std::is_same_v<IntT, uint8_t>) { return &context.registers.bh(); }
|
||||
else { return nullptr; }
|
||||
@ -150,12 +151,12 @@ uint32_t address(
|
||||
// See forward declaration, above, for details.
|
||||
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
||||
typename Accessor<IntT, access>::type resolve(
|
||||
InstructionT &instruction,
|
||||
Source source,
|
||||
DataPointer pointer,
|
||||
const InstructionT &instruction,
|
||||
const Source source,
|
||||
const DataPointer pointer,
|
||||
ContextT &context,
|
||||
IntT *none,
|
||||
IntT *immediate
|
||||
IntT *const immediate
|
||||
) {
|
||||
// Rules:
|
||||
//
|
||||
|
@ -15,7 +15,7 @@ namespace InstructionSet::x86::Primitive {
|
||||
template <typename IntT, typename ContextT>
|
||||
void rcl(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -76,7 +76,7 @@ void rcl(
|
||||
template <typename IntT, typename ContextT>
|
||||
void rcr(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -123,7 +123,7 @@ void rcr(
|
||||
template <typename IntT, typename ContextT>
|
||||
void rol(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -175,7 +175,7 @@ void rol(
|
||||
template <typename IntT, typename ContextT>
|
||||
void ror(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -283,7 +283,7 @@ void ror(
|
||||
template <typename IntT, typename ContextT>
|
||||
void sal(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
switch(count) {
|
||||
@ -314,7 +314,7 @@ void sal(
|
||||
template <typename IntT, typename ContextT>
|
||||
void sar(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
if(!count) {
|
||||
@ -337,7 +337,7 @@ void sar(
|
||||
template <typename IntT, typename ContextT>
|
||||
void shr(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
if(!count) {
|
||||
|
@ -487,7 +487,7 @@ std::string InstructionSet::x86::to_string(
|
||||
case Operation::OUTS:
|
||||
case Operation::OUTS_REP:
|
||||
switch(instruction.second.data_segment()) {
|
||||
default: break;
|
||||
default: break;
|
||||
case Source::ES: operation += "es "; break;
|
||||
case Source::CS: operation += "cs "; break;
|
||||
case Source::DS: operation += "ds "; break;
|
||||
|
@ -499,10 +499,10 @@ enum class Source: uint8_t {
|
||||
/// getter is used).
|
||||
IndirectNoBase = Indirect - 1,
|
||||
};
|
||||
constexpr bool is_register(Source source) {
|
||||
constexpr bool is_register(const Source source) {
|
||||
return source < Source::None;
|
||||
}
|
||||
constexpr bool is_segment_register(Source source) {
|
||||
constexpr bool is_segment_register(const Source source) {
|
||||
return is_register(source) && source >= Source::ES;
|
||||
}
|
||||
|
||||
@ -698,216 +698,226 @@ class DataPointer {
|
||||
};
|
||||
|
||||
template<bool is_32bit> class Instruction {
|
||||
public:
|
||||
using DisplacementT = typename std::conditional<is_32bit, int32_t, int16_t>::type;
|
||||
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
|
||||
using AddressT = ImmediateT;
|
||||
public:
|
||||
using DisplacementT = typename std::conditional<is_32bit, int32_t, int16_t>::type;
|
||||
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
|
||||
using AddressT = ImmediateT;
|
||||
|
||||
constexpr Instruction() noexcept = default;
|
||||
constexpr Instruction(Operation operation) noexcept :
|
||||
Instruction(operation, Source::None, Source::None, ScaleIndexBase(), false, AddressSize::b16, Source::None, DataSize::None, 0, 0) {}
|
||||
constexpr Instruction(
|
||||
Operation operation,
|
||||
Source source,
|
||||
Source destination,
|
||||
ScaleIndexBase sib,
|
||||
bool lock,
|
||||
AddressSize address_size,
|
||||
Source segment_override,
|
||||
DataSize data_size,
|
||||
DisplacementT displacement,
|
||||
ImmediateT operand) noexcept :
|
||||
operation_(operation),
|
||||
mem_exts_source_(uint8_t(
|
||||
(int(address_size) << 7) |
|
||||
(displacement ? 0x40 : 0x00) |
|
||||
(operand ? 0x20 : 0x00) |
|
||||
int(source) |
|
||||
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)),
|
||||
source_data_dest_sib_(uint16_t(
|
||||
(int(data_size) << 14) |
|
||||
(lock ? (1 << 13) : 0) |
|
||||
((uint8_t(sib) & 0xf8) << 2) |
|
||||
int(destination) |
|
||||
(destination == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)) {
|
||||
// Decisions on whether to include operand, displacement and/or size extension words
|
||||
// have implicitly been made in the int packing above; honour them here.
|
||||
int extension = 0;
|
||||
if(has_operand()) {
|
||||
extensions_[extension] = operand;
|
||||
++extension;
|
||||
}
|
||||
if(has_displacement()) {
|
||||
extensions_[extension] = ImmediateT(displacement);
|
||||
++extension;
|
||||
}
|
||||
|
||||
// Patch in a fully-resolved segment.
|
||||
Source segment = segment_override;
|
||||
if(segment == Source::None) segment = this->source().default_segment();
|
||||
if(segment == Source::None) segment = this->destination().default_segment();
|
||||
if(segment == Source::None) segment = Source::DS;
|
||||
source_data_dest_sib_ |= (int(segment)&7) << 10;
|
||||
constexpr Instruction() noexcept = default;
|
||||
constexpr Instruction(Operation operation) noexcept :
|
||||
Instruction(
|
||||
operation,
|
||||
Source::None,
|
||||
Source::None,
|
||||
ScaleIndexBase(),
|
||||
false,
|
||||
AddressSize::b16,
|
||||
Source::None,
|
||||
DataSize::None,
|
||||
0,
|
||||
0
|
||||
) {}
|
||||
constexpr Instruction(
|
||||
Operation operation,
|
||||
Source source,
|
||||
Source destination,
|
||||
ScaleIndexBase sib,
|
||||
bool lock,
|
||||
AddressSize address_size,
|
||||
Source segment_override,
|
||||
DataSize data_size,
|
||||
DisplacementT displacement,
|
||||
ImmediateT operand) noexcept :
|
||||
operation_(operation),
|
||||
mem_exts_source_(uint8_t(
|
||||
(int(address_size) << 7) |
|
||||
(displacement ? 0x40 : 0x00) |
|
||||
(operand ? 0x20 : 0x00) |
|
||||
int(source) |
|
||||
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)),
|
||||
source_data_dest_sib_(uint16_t(
|
||||
(int(data_size) << 14) |
|
||||
(lock ? (1 << 13) : 0) |
|
||||
((uint8_t(sib) & 0xf8) << 2) |
|
||||
int(destination) |
|
||||
(destination == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)) {
|
||||
// Decisions on whether to include operand, displacement and/or size extension words
|
||||
// have implicitly been made in the int packing above; honour them here.
|
||||
int extension = 0;
|
||||
if(has_operand()) {
|
||||
extensions_[extension] = operand;
|
||||
++extension;
|
||||
}
|
||||
if(has_displacement()) {
|
||||
extensions_[extension] = ImmediateT(displacement);
|
||||
++extension;
|
||||
}
|
||||
|
||||
/// @returns The number of bytes used for meaningful content within this class. A receiver must use at least @c sizeof(Instruction) bytes
|
||||
/// to store an @c Instruction but is permitted to reuse the trailing sizeof(Instruction) - packing_size() for any purpose it likes. Teleologically,
|
||||
/// this allows a denser packing of instructions into containers.
|
||||
constexpr size_t packing_size() const {
|
||||
return
|
||||
offsetof(Instruction<is_32bit>, extensions_) +
|
||||
(has_displacement() + has_operand()) * sizeof(ImmediateT);
|
||||
// Patch in a fully-resolved segment.
|
||||
Source segment = segment_override;
|
||||
if(segment == Source::None) segment = this->source().default_segment();
|
||||
if(segment == Source::None) segment = this->destination().default_segment();
|
||||
if(segment == Source::None) segment = Source::DS;
|
||||
source_data_dest_sib_ |= (int(segment)&7) << 10;
|
||||
}
|
||||
|
||||
/// @returns The number of bytes used for meaningful content within this class. A receiver must use at least @c sizeof(Instruction) bytes
|
||||
/// to store an @c Instruction but is permitted to reuse the trailing sizeof(Instruction) - packing_size() for any purpose it likes. Teleologically,
|
||||
/// this allows a denser packing of instructions into containers.
|
||||
constexpr size_t packing_size() const {
|
||||
return
|
||||
offsetof(Instruction<is_32bit>, extensions_) +
|
||||
(has_displacement() + has_operand()) * sizeof(ImmediateT);
|
||||
}
|
||||
|
||||
/// @returns The @c Operation performed by this instruction.
|
||||
constexpr Operation operation() const {
|
||||
return operation_;
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'destination' of this instruction, conventionally the first operand in Intel-syntax assembly.
|
||||
constexpr DataPointer destination() const {
|
||||
return DataPointer(
|
||||
Source(source_data_dest_sib_ & sib_masks[(source_data_dest_sib_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (source_data_dest_sib_ & 0x07)
|
||||
);
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'source' of this instruction, conventionally the second operand in Intel-syntax assembly.
|
||||
constexpr DataPointer source() const {
|
||||
return DataPointer(
|
||||
Source(mem_exts_source_ & sib_masks[(mem_exts_source_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (mem_exts_source_ & 0x07)
|
||||
);
|
||||
}
|
||||
|
||||
/// @returns @c true if the lock prefix was present on this instruction; @c false otherwise.
|
||||
constexpr bool lock() const {
|
||||
return source_data_dest_sib_ & (1 << 13);
|
||||
}
|
||||
|
||||
/// @returns The address size for this instruction; will always be 16-bit for instructions decoded by a 16-bit decoder but can be 16- or 32-bit for
|
||||
/// instructions decoded by a 32-bit decoder, depending on the program's use of the address size prefix byte.
|
||||
constexpr AddressSize address_size() const {
|
||||
return AddressSize(mem_exts_source_ >> 7);
|
||||
}
|
||||
|
||||
/// @returns The segment that should be used for data fetches if this operation accepts segment overrides.
|
||||
constexpr Source data_segment() const {
|
||||
return Source(
|
||||
int(Source::ES) +
|
||||
((source_data_dest_sib_ >> 10) & 7)
|
||||
);
|
||||
}
|
||||
|
||||
/// @returns The data size of this operation — e.g. `MOV AX, BX` has a data size of `::Word` but `MOV EAX, EBX` has a data size of
|
||||
/// `::DWord`. This value is guaranteed never to be `DataSize::None` even for operations such as `CLI` that don't have operands and operate
|
||||
/// on data that is not a byte, word or double word.
|
||||
constexpr DataSize operation_size() const {
|
||||
return DataSize(source_data_dest_sib_ >> 14);
|
||||
}
|
||||
|
||||
/// @returns The immediate value provided with this instruction, if any. E.g. `ADD AX, 23h` has the operand `23h`.
|
||||
constexpr ImmediateT operand() const {
|
||||
const ImmediateT ops[] = {0, operand_extension()};
|
||||
return ops[has_operand()];
|
||||
}
|
||||
|
||||
/// @returns The nesting level argument supplied to an ENTER.
|
||||
constexpr ImmediateT nesting_level() const {
|
||||
return operand();
|
||||
}
|
||||
|
||||
/// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will
|
||||
/// have a segment value of `1234h`.
|
||||
constexpr uint16_t segment() const {
|
||||
return uint16_t(operand());
|
||||
}
|
||||
|
||||
/// @returns The offset provided with this instruction, if any. E.g. `MOV AX, [es:1998h]` has an offset of `1998h`.
|
||||
constexpr ImmediateT offset() const {
|
||||
const ImmediateT offsets[] = {0, displacement_extension()};
|
||||
return offsets[has_displacement()];
|
||||
}
|
||||
|
||||
/// @returns The displacement provided with this instruction `SUB AX, [SI+BP-23h]` has an offset of `-23h` and `JMP 19h`
|
||||
/// has an offset of `19h`.
|
||||
constexpr DisplacementT displacement() const {
|
||||
return DisplacementT(offset());
|
||||
}
|
||||
|
||||
/// @returns The dynamic storage size argument supplied to an ENTER.
|
||||
constexpr ImmediateT dynamic_storage_size() const {
|
||||
return displacement();
|
||||
}
|
||||
|
||||
// Standard comparison operator.
|
||||
constexpr bool operator ==(const Instruction<is_32bit> &rhs) const {
|
||||
if( operation_ != rhs.operation_ ||
|
||||
mem_exts_source_ != rhs.mem_exts_source_ ||
|
||||
source_data_dest_sib_ != rhs.source_data_dest_sib_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @returns The @c Operation performed by this instruction.
|
||||
constexpr Operation operation() const {
|
||||
return operation_;
|
||||
// Have already established above that this and RHS have the
|
||||
// same extensions, if any.
|
||||
const int extension_count = has_displacement() + has_operand();
|
||||
for(int c = 0; c < extension_count; c++) {
|
||||
if(extensions_[c] != rhs.extensions_[c]) return false;
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'destination' of this instruction, conventionally the first operand in Intel-syntax assembly.
|
||||
constexpr DataPointer destination() const {
|
||||
return DataPointer(
|
||||
Source(source_data_dest_sib_ & sib_masks[(source_data_dest_sib_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (source_data_dest_sib_ & 0x07)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'source' of this instruction, conventionally the second operand in Intel-syntax assembly.
|
||||
constexpr DataPointer source() const {
|
||||
return DataPointer(
|
||||
Source(mem_exts_source_ & sib_masks[(mem_exts_source_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (mem_exts_source_ & 0x07)
|
||||
);
|
||||
}
|
||||
private:
|
||||
Operation operation_ = Operation::Invalid;
|
||||
|
||||
/// @returns @c true if the lock prefix was present on this instruction; @c false otherwise.
|
||||
constexpr bool lock() const {
|
||||
return source_data_dest_sib_ & (1 << 13);
|
||||
}
|
||||
// Packing and encoding of fields is admittedly somewhat convoluted; what this
|
||||
// achieves is that instructions will be sized:
|
||||
//
|
||||
// four bytes + up to two extension words
|
||||
// (extension words being two bytes for 16-bit instructions, four for 32)
|
||||
//
|
||||
// The extension words are used to retain an operand and displacement
|
||||
// if the instruction has those.
|
||||
|
||||
/// @returns The address size for this instruction; will always be 16-bit for instructions decoded by a 16-bit decoder but can be 16- or 32-bit for
|
||||
/// instructions decoded by a 32-bit decoder, depending on the program's use of the address size prefix byte.
|
||||
constexpr AddressSize address_size() const {
|
||||
return AddressSize(mem_exts_source_ >> 7);
|
||||
}
|
||||
// b7: address size;
|
||||
// b6: has displacement;
|
||||
// b5: has operand;
|
||||
// [b4, b0]: source.
|
||||
uint8_t mem_exts_source_ = 0;
|
||||
|
||||
/// @returns The segment that should be used for data fetches if this operation accepts segment overrides.
|
||||
constexpr Source data_segment() const {
|
||||
return Source(
|
||||
int(Source::ES) +
|
||||
((source_data_dest_sib_ >> 10) & 7)
|
||||
);
|
||||
}
|
||||
bool has_displacement() const {
|
||||
return mem_exts_source_ & (1 << 6);
|
||||
}
|
||||
bool has_operand() const {
|
||||
return mem_exts_source_ & (1 << 5);
|
||||
}
|
||||
|
||||
/// @returns The data size of this operation — e.g. `MOV AX, BX` has a data size of `::Word` but `MOV EAX, EBX` has a data size of
|
||||
/// `::DWord`. This value is guaranteed never to be `DataSize::None` even for operations such as `CLI` that don't have operands and operate
|
||||
/// on data that is not a byte, word or double word.
|
||||
constexpr DataSize operation_size() const {
|
||||
return DataSize(source_data_dest_sib_ >> 14);
|
||||
}
|
||||
// [b15, b14]: data size;
|
||||
// [b13]: lock;
|
||||
// [b12, b10]: segment override;
|
||||
// [b9, b5]: top five of SIB;
|
||||
// [b4, b0]: dest.
|
||||
uint16_t source_data_dest_sib_ = 0;
|
||||
|
||||
/// @returns The immediate value provided with this instruction, if any. E.g. `ADD AX, 23h` has the operand `23h`.
|
||||
constexpr ImmediateT operand() const {
|
||||
const ImmediateT ops[] = {0, operand_extension()};
|
||||
return ops[has_operand()];
|
||||
}
|
||||
// {operand}, {displacement}.
|
||||
ImmediateT extensions_[2]{};
|
||||
|
||||
/// @returns The nesting level argument supplied to an ENTER.
|
||||
constexpr ImmediateT nesting_level() const {
|
||||
return operand();
|
||||
}
|
||||
|
||||
/// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will
|
||||
/// have a segment value of `1234h`.
|
||||
constexpr uint16_t segment() const {
|
||||
return uint16_t(operand());
|
||||
}
|
||||
|
||||
/// @returns The offset provided with this instruction, if any. E.g. `MOV AX, [es:1998h]` has an offset of `1998h`.
|
||||
constexpr ImmediateT offset() const {
|
||||
const ImmediateT offsets[] = {0, displacement_extension()};
|
||||
return offsets[has_displacement()];
|
||||
}
|
||||
|
||||
/// @returns The displacement provided with this instruction `SUB AX, [SI+BP-23h]` has an offset of `-23h` and `JMP 19h`
|
||||
/// has an offset of `19h`.
|
||||
constexpr DisplacementT displacement() const {
|
||||
return DisplacementT(offset());
|
||||
}
|
||||
|
||||
/// @returns The dynamic storage size argument supplied to an ENTER.
|
||||
constexpr ImmediateT dynamic_storage_size() const {
|
||||
return displacement();
|
||||
}
|
||||
|
||||
// Standard comparison operator.
|
||||
constexpr bool operator ==(const Instruction<is_32bit> &rhs) const {
|
||||
if( operation_ != rhs.operation_ ||
|
||||
mem_exts_source_ != rhs.mem_exts_source_ ||
|
||||
source_data_dest_sib_ != rhs.source_data_dest_sib_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Have already established above that this and RHS have the
|
||||
// same extensions, if any.
|
||||
const int extension_count = has_displacement() + has_operand();
|
||||
for(int c = 0; c < extension_count; c++) {
|
||||
if(extensions_[c] != rhs.extensions_[c]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Operation operation_ = Operation::Invalid;
|
||||
|
||||
// Packing and encoding of fields is admittedly somewhat convoluted; what this
|
||||
// achieves is that instructions will be sized:
|
||||
//
|
||||
// four bytes + up to two extension words
|
||||
// (extension words being two bytes for 16-bit instructions, four for 32)
|
||||
//
|
||||
// The extension words are used to retain an operand and displacement
|
||||
// if the instruction has those.
|
||||
|
||||
// b7: address size;
|
||||
// b6: has displacement;
|
||||
// b5: has operand;
|
||||
// [b4, b0]: source.
|
||||
uint8_t mem_exts_source_ = 0;
|
||||
|
||||
bool has_displacement() const {
|
||||
return mem_exts_source_ & (1 << 6);
|
||||
}
|
||||
bool has_operand() const {
|
||||
return mem_exts_source_ & (1 << 5);
|
||||
}
|
||||
|
||||
// [b15, b14]: data size;
|
||||
// [b13]: lock;
|
||||
// [b12, b10]: segment override;
|
||||
// [b9, b5]: top five of SIB;
|
||||
// [b4, b0]: dest.
|
||||
uint16_t source_data_dest_sib_ = 0;
|
||||
|
||||
// {operand}, {displacement}.
|
||||
ImmediateT extensions_[2]{};
|
||||
|
||||
ImmediateT operand_extension() const {
|
||||
return extensions_[0];
|
||||
}
|
||||
ImmediateT displacement_extension() const {
|
||||
return extensions_[(mem_exts_source_ >> 5) & 1];
|
||||
}
|
||||
|
||||
// A lookup table to help with stripping parts of the SIB that have been
|
||||
// hidden within the source/destination fields.
|
||||
static constexpr uint8_t sib_masks[] = {
|
||||
0x1f, 0x1f, 0x1f, 0x18
|
||||
};
|
||||
ImmediateT operand_extension() const {
|
||||
return extensions_[0];
|
||||
}
|
||||
ImmediateT displacement_extension() const {
|
||||
return extensions_[(mem_exts_source_ >> 5) & 1];
|
||||
}
|
||||
|
||||
// A lookup table to help with stripping parts of the SIB that have been
|
||||
// hidden within the source/destination fields.
|
||||
static constexpr uint8_t sib_masks[] = {
|
||||
0x1f, 0x1f, 0x1f, 0x18
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(Instruction<true>) <= 16);
|
||||
@ -962,5 +972,4 @@ std::string to_string(
|
||||
Model model,
|
||||
int offset_length = 0,
|
||||
int immediate_length = 0);
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ enum class Model {
|
||||
i80386,
|
||||
};
|
||||
|
||||
static constexpr bool is_32bit(Model model) { return model >= Model::i80386; }
|
||||
static constexpr bool is_32bit(const Model model) { return model >= Model::i80386; }
|
||||
|
||||
template <bool is_32bit> struct AddressT { using type = uint16_t; };
|
||||
template <> struct AddressT<true> { using type = uint32_t; };
|
||||
|
@ -69,7 +69,7 @@ class ConcreteMachine:
|
||||
// This is a 24-cycle window, so at 24Mhz macro_tick() is called at 1Mhz.
|
||||
// Hence, required ticks are:
|
||||
//
|
||||
// * CPU: 24;
|
||||
// * CPU: 24;
|
||||
// * video: 24 / video_divider;
|
||||
// * floppy: 8;
|
||||
// * timers: 2;
|
||||
|
@ -165,7 +165,7 @@ struct InputOutputController: public ClockingHint::Observer {
|
||||
//
|
||||
// fast/1 = FDC
|
||||
// sync/2 = econet
|
||||
// sync/3 = serial line
|
||||
// sync/3 = serial line
|
||||
//
|
||||
// bank 4 = podules
|
||||
//
|
||||
@ -410,7 +410,7 @@ struct InputOutputController: public ClockingHint::Observer {
|
||||
// b0: ?
|
||||
// b1: double/single density; 0 = double.
|
||||
// b2: ?
|
||||
// b3: floppy drive reset; 0 = reset.
|
||||
// b3: floppy drive reset; 0 = reset.
|
||||
// b4: printer strobe
|
||||
// b5: ?
|
||||
// b6: ?
|
||||
@ -474,12 +474,12 @@ struct InputOutputController: public ClockingHint::Observer {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto &sound() { return sound_; }
|
||||
auto &sound() { return sound_; }
|
||||
const auto &sound() const { return sound_; }
|
||||
auto &video() { return video_; }
|
||||
const auto &video() const { return video_; }
|
||||
auto &keyboard() { return keyboard_; }
|
||||
const auto &keyboard() const { return keyboard_; }
|
||||
auto &video() { return video_; }
|
||||
const auto &video() const { return video_; }
|
||||
auto &keyboard() { return keyboard_; }
|
||||
const auto &keyboard() const { return keyboard_; }
|
||||
|
||||
void update_interrupts() {
|
||||
const auto set = [&](Interrupt &target, uint8_t flag, bool set) {
|
||||
|
@ -225,8 +225,8 @@ struct MemoryController {
|
||||
return ioc_.sound().speaker();
|
||||
}
|
||||
|
||||
auto &sound() { return ioc_.sound(); }
|
||||
const auto &sound() const { return ioc_.sound(); }
|
||||
auto &sound() { return ioc_.sound(); }
|
||||
const auto &sound() const { return ioc_.sound(); }
|
||||
auto &video() { return ioc_.video(); }
|
||||
const auto &video() const { return ioc_.video(); }
|
||||
auto &keyboard() { return ioc_.keyboard(); }
|
||||
|
@ -225,7 +225,7 @@ struct Video {
|
||||
return vertical_state_.phase() != Phase::Display;
|
||||
}
|
||||
|
||||
void set_frame_start(uint32_t address) {
|
||||
void set_frame_start(uint32_t address) {
|
||||
frame_start_ = address;
|
||||
++frame_start_sets_;
|
||||
}
|
||||
@ -233,7 +233,7 @@ struct Video {
|
||||
void set_buffer_end(uint32_t address) { buffer_end_ = address; }
|
||||
void set_cursor_start(uint32_t address) { cursor_start_ = address; }
|
||||
|
||||
Outputs::CRT::CRT &crt() { return crt_; }
|
||||
Outputs::CRT::CRT &crt() { return crt_; }
|
||||
const Outputs::CRT::CRT &crt() const { return crt_; }
|
||||
|
||||
int clock_divider() const {
|
||||
|
@ -35,7 +35,7 @@ enum Key: uint16_t {
|
||||
KeyBreak = 0xfffd,
|
||||
};
|
||||
|
||||
constexpr bool is_modifier(Key key) {
|
||||
constexpr bool is_modifier(const Key key) {
|
||||
return (key == KeyShift) || (key == KeyControl) || (key == KeyFunc);
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@ enum class GraphicsMode {
|
||||
/// Fat low res mode is regular low res mode, but clocked out at 7Mhz rather than 14, leading to improper colours.
|
||||
FatLowRes
|
||||
};
|
||||
constexpr bool is_text_mode(GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
|
||||
constexpr bool is_double_mode(GraphicsMode m) { return int(m) & 1; }
|
||||
constexpr bool is_text_mode(const GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
|
||||
constexpr bool is_double_mode(const GraphicsMode m) { return int(m) & 1; }
|
||||
|
||||
template <typename TimeUnit> class VideoSwitches {
|
||||
public:
|
||||
|
@ -86,7 +86,9 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
|
||||
DoubleHighResMono,
|
||||
SuperHighRes
|
||||
};
|
||||
constexpr bool is_colour_ntsc(GraphicsMode m) { return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes; }
|
||||
constexpr bool is_colour_ntsc(const GraphicsMode m) {
|
||||
return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes;
|
||||
}
|
||||
|
||||
GraphicsMode graphics_mode(int row) const {
|
||||
if(new_video_ & 0x80) {
|
||||
|
@ -1039,13 +1039,13 @@ std::unique_ptr<Machine> Machine::MSX(const Analyser::Static::Target *target, co
|
||||
const auto msx_target = dynamic_cast<const Target *>(target);
|
||||
if(msx_target->has_msx_music) {
|
||||
switch(msx_target->model) {
|
||||
default: return nullptr;
|
||||
default: return nullptr;
|
||||
case Target::Model::MSX1: return std::make_unique<ConcreteMachine<Target::Model::MSX1, true>>(*msx_target, rom_fetcher);
|
||||
case Target::Model::MSX2: return std::make_unique<ConcreteMachine<Target::Model::MSX2, true>>(*msx_target, rom_fetcher);
|
||||
}
|
||||
} else {
|
||||
switch(msx_target->model) {
|
||||
default: return nullptr;
|
||||
default: return nullptr;
|
||||
case Target::Model::MSX1: return std::make_unique<ConcreteMachine<Target::Model::MSX1, false>>(*msx_target, rom_fetcher);
|
||||
case Target::Model::MSX2: return std::make_unique<ConcreteMachine<Target::Model::MSX2, false>>(*msx_target, rom_fetcher);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ class MDA {
|
||||
if(count) {
|
||||
switch(output_state) {
|
||||
case OutputState::Sync: crt.output_sync(count); break;
|
||||
case OutputState::Border: crt.output_blank(count); break;
|
||||
case OutputState::Border: crt.output_blank(count); break;
|
||||
case OutputState::Pixels:
|
||||
crt.output_data(count);
|
||||
pixels = pixel_pointer = nullptr;
|
||||
|
@ -362,7 +362,7 @@ class KeyboardController {
|
||||
const auto last_mode = mode_;
|
||||
mode_ = Mode(mode);
|
||||
switch(mode_) {
|
||||
case Mode::NormalOperation: break;
|
||||
case Mode::NormalOperation: break;
|
||||
case Mode::NoIRQsIgnoreInput:
|
||||
pic_.apply_edge<1>(false);
|
||||
break;
|
||||
@ -932,7 +932,7 @@ class ConcreteMachine:
|
||||
void run_for(const Cycles duration) override {
|
||||
switch(speed_) {
|
||||
case Target::Speed::ApproximatelyOriginal: run_for<Target::Speed::ApproximatelyOriginal>(duration); break;
|
||||
case Target::Speed::Fast: run_for<Target::Speed::Fast>(duration); break;
|
||||
case Target::Speed::Fast: run_for<Target::Speed::Fast>(duration); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ namespace {
|
||||
|
||||
// TODO:
|
||||
// Quiksilva sound support:
|
||||
// 7FFFh.W PSG index
|
||||
// 7FFFh.W PSG index
|
||||
// 7FFEh.R/W PSG data
|
||||
|
||||
namespace Sinclair {
|
||||
|
@ -13,8 +13,8 @@
|
||||
namespace Numeric {
|
||||
|
||||
/// @returns @c true if from @c bit there was:
|
||||
/// • carry after calculating @c lhs + @c rhs if @c is_add is true; or
|
||||
/// • borrow after calculating @c lhs - @c rhs if @c is_add is false;
|
||||
/// • carry after calculating @c lhs + @c rhs if @c is_add is true; or
|
||||
/// • borrow after calculating @c lhs - @c rhs if @c is_add is false;
|
||||
/// producing @c result.
|
||||
template <bool is_add, int bit, typename IntT> bool carried_out(IntT lhs, IntT rhs, IntT result) {
|
||||
// Additive:
|
||||
@ -40,8 +40,8 @@ template <bool is_add, int bit, typename IntT> bool carried_out(IntT lhs, IntT r
|
||||
}
|
||||
|
||||
/// @returns @c true if there was carry into @c bit when computing either:
|
||||
/// • @c lhs + @c rhs; or
|
||||
/// • @c lhs - @c rhs;
|
||||
/// • @c lhs + @c rhs; or
|
||||
/// • @c lhs - @c rhs;
|
||||
/// producing @c result.
|
||||
template <int bit, typename IntT> bool carried_in(IntT lhs, IntT rhs, IntT result) {
|
||||
// 0 and 0 or 1 and 1 => did if 1.
|
||||
|
@ -59,7 +59,7 @@ enum class Source {
|
||||
WDFDC,
|
||||
};
|
||||
|
||||
constexpr bool is_enabled(Source source) {
|
||||
constexpr bool is_enabled(const Source source) {
|
||||
#ifdef NDEBUG
|
||||
return false;
|
||||
#endif
|
||||
@ -133,39 +133,39 @@ constexpr const char *prefix(Source source) {
|
||||
|
||||
template <Source source>
|
||||
class Logger {
|
||||
public:
|
||||
static constexpr bool enabled = is_enabled(source);
|
||||
public:
|
||||
static constexpr bool enabled = is_enabled(source);
|
||||
|
||||
struct LogLine {
|
||||
public:
|
||||
LogLine(FILE *stream) : stream_(stream) {
|
||||
if constexpr (!enabled) return;
|
||||
struct LogLine {
|
||||
public:
|
||||
LogLine(FILE *const stream) : stream_(stream) {
|
||||
if constexpr (!enabled) return;
|
||||
|
||||
const auto source_prefix = prefix(source);
|
||||
if(source_prefix) {
|
||||
fprintf(stream_, "[%s] ", source_prefix);
|
||||
}
|
||||
const auto source_prefix = prefix(source);
|
||||
if(source_prefix) {
|
||||
fprintf(stream_, "[%s] ", source_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
~LogLine() {
|
||||
if constexpr (!enabled) return;
|
||||
fprintf(stream_, "\n");
|
||||
}
|
||||
~LogLine() {
|
||||
if constexpr (!enabled) return;
|
||||
fprintf(stream_, "\n");
|
||||
}
|
||||
|
||||
void append(const char *format, ...) {
|
||||
if constexpr (!enabled) return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stream_, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void append(const char *const format, ...) {
|
||||
if constexpr (!enabled) return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stream_, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE *stream_;
|
||||
};
|
||||
private:
|
||||
FILE *stream_;
|
||||
};
|
||||
|
||||
LogLine info() { return LogLine(stdout); }
|
||||
LogLine error() { return LogLine(stderr); }
|
||||
LogLine info() { return LogLine(stdout); }
|
||||
LogLine error() { return LogLine(stderr); }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ enum class DisplayType {
|
||||
CompositeMonochrome
|
||||
};
|
||||
|
||||
constexpr bool is_composite(DisplayType type) {
|
||||
constexpr bool is_composite(const DisplayType type) {
|
||||
return type == DisplayType::CompositeColour || type == DisplayType::CompositeMonochrome;
|
||||
}
|
||||
|
||||
|
@ -38,10 +38,10 @@ enum Personality {
|
||||
PWDC65C02, // like the Rockwell, but with STP and WAI
|
||||
};
|
||||
|
||||
constexpr bool has_decimal_mode(Personality p) { return p >= Personality::P6502; }
|
||||
constexpr bool is_65c02(Personality p) { return p >= Personality::PSynertek65C02; }
|
||||
constexpr bool has_bbrbbsrmbsmb(Personality p) { return p >= Personality::PRockwell65C02; }
|
||||
constexpr bool has_stpwai(Personality p) { return p >= Personality::PWDC65C02; }
|
||||
constexpr bool has_decimal_mode(const Personality p) { return p >= Personality::P6502; }
|
||||
constexpr bool is_65c02(const Personality p) { return p >= Personality::PSynertek65C02; }
|
||||
constexpr bool has_bbrbbsrmbsmb(const Personality p) { return p >= Personality::PRockwell65C02; }
|
||||
constexpr bool has_stpwai(const Personality p) { return p >= Personality::PWDC65C02; }
|
||||
|
||||
/*!
|
||||
An opcode that is guaranteed to cause a 6502 to jam.
|
||||
|
@ -188,7 +188,7 @@ class ProcessorStorage {
|
||||
CycleAddSignedOperandToPC, // sets next_address to PC + (signed)operand. If the high byte of next_address differs from the PC, schedules a throwaway read from the half-updated PC. 65C02 specific: if the top two bytes are the same, proceeds directly to fetch-decode-execute, ignoring any pending interrupts.
|
||||
OperationAddSignedOperandToPC16, // adds (signed)operand into the PC, leaving old PC in next_address_ and skipping a program step if there was no carry from low to high byte
|
||||
|
||||
CycleFetchFromNextAddress, // performs a throwaway fetch from next_address_
|
||||
CycleFetchFromNextAddress, // performs a throwaway fetch from next_address_
|
||||
|
||||
OperationSetFlagsFromOperand, // sets all flags based on operand_
|
||||
OperationSetOperandFromFlagsWithBRKSet, // sets operand_ to the value of all flags, with the break flag set
|
||||
|
@ -48,7 +48,7 @@ template <> class BusHandlerT<Type::TWDC65816>: public BusHandler<uint32_t> {};
|
||||
/*
|
||||
Query for implemented registers.
|
||||
*/
|
||||
constexpr bool has(Type processor_type, Register r) {
|
||||
constexpr bool has(const Type processor_type, const Register r) {
|
||||
switch(r) {
|
||||
case Register::LastOperationAddress:
|
||||
case Register::ProgramCounter:
|
||||
@ -67,7 +67,7 @@ constexpr bool has(Type processor_type, Register r) {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool has_extended_bus_output(Type processor_type) {
|
||||
constexpr bool has_extended_bus_output(const Type processor_type) {
|
||||
return processor_type == Type::TWDC65816;
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ class ProcessorStorage {
|
||||
void *destination = nullptr;
|
||||
PartialMachineCycle machine_cycle{};
|
||||
};
|
||||
static constexpr bool is_terminal(MicroOp::Type type) {
|
||||
static constexpr bool is_terminal(const MicroOp::Type type) {
|
||||
return type == MicroOp::MoveToNextProgram || type == MicroOp::DecodeOperation;
|
||||
}
|
||||
|
||||
|
@ -56,10 +56,10 @@ static constexpr int switch_max = 2048;
|
||||
|
||||
/// Provides glue for a run of calls like:
|
||||
///
|
||||
/// SequencerT.perform<0>(...)
|
||||
/// SequencerT.perform<1>(...)
|
||||
/// SequencerT.perform<2>(...)
|
||||
/// ...etc...
|
||||
/// SequencerT.perform<0>(...)
|
||||
/// SequencerT.perform<1>(...)
|
||||
/// SequencerT.perform<2>(...)
|
||||
/// ...etc...
|
||||
///
|
||||
/// Allowing the caller to execute any subrange of the calls.
|
||||
template <typename SequencerT>
|
||||
|
@ -16,7 +16,7 @@ enum class Density {
|
||||
Single, Double, High
|
||||
};
|
||||
|
||||
constexpr bool is_mfm(Density density) {
|
||||
constexpr bool is_mfm(const Density density) {
|
||||
return density != Density::Single;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user