1
0
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:
Thomas Harte 2024-12-01 20:24:55 -05:00 committed by GitHub
commit 31c878b654
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
62 changed files with 1499 additions and 1349 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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; }

View File

@ -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);

View File

@ -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_;
};
}

View File

@ -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);
}

View File

@ -35,7 +35,7 @@ static constexpr int FlagsStartBit = 20;
using Flags = uint8_t;
template <int position>
constexpr bool flag_bit(uint8_t flags) {
constexpr bool flag_bit(const uint8_t flags) {
static_assert(position >= 20 && position < 28);
return flags & (1 << (position - FlagsStartBit));
}
@ -44,7 +44,7 @@ constexpr bool flag_bit(uint8_t flags) {
// Methods common to data processing and data transfer.
//
struct WithShiftControlBits {
constexpr WithShiftControlBits(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr WithShiftControlBits(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The operand 2 register index if @c operand2_is_immediate() is @c false; meaningless otherwise.
uint32_t operand2() const { return opcode_ & 0xf; }
@ -65,7 +65,7 @@ protected:
// Branch (i.e. B and BL).
//
struct BranchFlags {
constexpr BranchFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr BranchFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
B, /// Add offset to PC; programmer allows for PC being two words ahead.
@ -82,7 +82,7 @@ private:
};
struct Branch {
constexpr Branch(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr Branch(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The 26-bit offset to add to the PC.
uint32_t offset() const { return (opcode_ & 0xff'ffff) << 2; }
@ -113,7 +113,7 @@ enum class DataProcessingOperation {
MVN, /// Rd = NOT Op2.
};
constexpr bool is_logical(DataProcessingOperation operation) {
constexpr bool is_logical(const DataProcessingOperation operation) {
switch(operation) {
case DataProcessingOperation::AND:
case DataProcessingOperation::EOR:
@ -129,7 +129,7 @@ constexpr bool is_logical(DataProcessingOperation operation) {
}
}
constexpr bool is_comparison(DataProcessingOperation operation) {
constexpr bool is_comparison(const DataProcessingOperation operation) {
switch(operation) {
case DataProcessingOperation::TST:
case DataProcessingOperation::TEQ:
@ -142,7 +142,7 @@ constexpr bool is_comparison(DataProcessingOperation operation) {
}
struct DataProcessingFlags {
constexpr DataProcessingFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr DataProcessingFlags(const uint8_t flags) noexcept : flags_(flags) {}
/// @returns The operation to apply.
constexpr DataProcessingOperation operation() const {
@ -183,7 +183,7 @@ struct DataProcessing: public WithShiftControlBits {
// MUL and MLA.
//
struct MultiplyFlags {
constexpr MultiplyFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr MultiplyFlags(const uint8_t flags) noexcept : flags_(flags) {}
/// @c true if the status register should be updated; @c false otherwise.
constexpr bool set_condition_codes() const { return flag_bit<20>(flags_); }
@ -203,7 +203,7 @@ private:
};
struct Multiply {
constexpr Multiply(uint32_t opcode) noexcept : opcode_(opcode) {}
constexpr Multiply(const uint32_t opcode) noexcept : opcode_(opcode) {}
/// The destination register index. i.e. 'Rd'.
uint32_t destination() const { return (opcode_ >> 16) & 0xf; }
@ -225,7 +225,7 @@ private:
// Single data transfer (LDR, STR).
//
struct SingleDataTransferFlags {
constexpr SingleDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr SingleDataTransferFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
LDR, /// Read single byte or word from [base + offset], possibly mutating the base.
@ -266,7 +266,7 @@ struct SingleDataTransfer: public WithShiftControlBits {
// Block data transfer (LDR, STR).
//
struct BlockDataTransferFlags {
constexpr BlockDataTransferFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr BlockDataTransferFlags(const uint8_t flags) noexcept : flags_(flags) {}
enum class Operation {
LDM, /// Read 116 words from [base], possibly mutating it.
@ -311,7 +311,7 @@ struct BlockDataTransfer: public WithShiftControlBits {
// Coprocessor data operation.
//
struct CoprocessorDataOperationFlags {
constexpr CoprocessorDataOperationFlags(uint8_t flags) noexcept : flags_(flags) {}
constexpr CoprocessorDataOperationFlags(const uint8_t flags) noexcept : flags_(flags) {}
constexpr int coprocessor_operation() const { return (flags_ >> (FlagsStartBit - 20)) & 0xf; }
@ -320,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.

View File

@ -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 &reg(bool force_user_mode, uint32_t offset) {
uint32_t &reg(const bool force_user_mode, const uint32_t offset) {
switch(mode_) {
default:
case Mode::User: return active_[offset];

View File

@ -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;
}*/
};
}

View File

@ -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_;
};
}

View File

@ -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) {

View File

@ -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_;
};
}

View File

@ -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 &reg, uint8_t value, uint16_t vector) {
void Executor::set_interrupt_request(uint8_t &reg, 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];
}

View File

@ -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 &reg, 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 &reg, 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);
};
}

View File

@ -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;
};

View File

@ -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));

View File

@ -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
//
// b0b2: a register to shift (the source here, for consistency with the memory operations);
// b8: 0 => b9b11 are a direct count of bits to shift; 1 => b9b11 identify a register containing the shift count;
// b8: 0 => b9b11 are a direct count of bits to shift;
// 1 => b9b11 identify register containing the shift count;
// b9b11: 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();
}

View File

@ -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);
};
}

View File

@ -37,12 +37,12 @@ struct BusHandler {
/// Write @c value of type/size @c IntT to @c address with the processor signalling
/// a FunctionCode of @c function. @c IntT will be one of @c uint8_t, @c uint16_t
/// or @c uint32_t.
template <typename IntT> void write(uint32_t address, IntT value, FunctionCode function);
template <typename IntT> void write(uint32_t address, IntT value, FunctionCode);
/// Read and return a value of type/size @c IntT from @c address with the processor signalling
/// a FunctionCode of @c function. @c IntT will be one of @c uint8_t, @c uint16_t
/// or @c uint32_t.
template <typename IntT> IntT read(uint32_t address, FunctionCode function);
template <typename IntT> IntT read(uint32_t address, FunctionCode);
/// React to the processor programmatically strobing its RESET output.
void reset();
@ -59,106 +59,106 @@ struct BusHandler {
/// As is standard for these executors, no bus- or cache-level fidelity to any real 680x0 is attempted. This is
/// simply an executor of 680x0 code.
template <Model model, typename BusHandler> class Executor {
public:
Executor(BusHandler &);
public:
Executor(BusHandler &);
/// Reset the processor, back to a state as if just externally reset.
void reset();
/// Reset the processor, back to a state as if just externally reset.
void reset();
/// Executes the number of instructions specified;
/// other events — such as initial reset or branching
/// to exceptions — may be zero costed, and interrupts
/// will not necessarily take effect immediately when signalled.
void run_for_instructions(int);
/// Executes the number of instructions specified;
/// other events — such as initial reset or branching
/// to exceptions — may be zero costed, and interrupts
/// will not necessarily take effect immediately when signalled.
void run_for_instructions(int);
/// Call this at any time to interrupt processing with a bus error;
/// the function code and address must be provided. Internally
/// this will raise a C++ exception, and therefore doesn't return.
[[noreturn]] void signal_bus_error(FunctionCode, uint32_t address);
/// Call this at any time to interrupt processing with a bus error;
/// the function code and address must be provided. Internally
/// this will raise a C++ exception, and therefore doesn't return.
[[noreturn]] void signal_bus_error(FunctionCode, uint32_t address);
/// Sets the current input interrupt level.
void set_interrupt_level(int);
/// Sets the current input interrupt level.
void set_interrupt_level(int);
// State for the executor is just the register set.
RegisterSet get_state();
void set_state(const RegisterSet &);
// State for the executor is just the register set.
RegisterSet get_state() const;
void set_state(const RegisterSet &);
private:
class State: public NullFlowController {
public:
State(BusHandler &handler) : bus_handler_(handler) {}
private:
class State: public NullFlowController {
public:
State(BusHandler &handler) : bus_handler_(handler) {}
void run(int &);
bool stopped = false;
void run(int &);
bool stopped = false;
void read(DataSize size, uint32_t address, CPU::SlicedInt32 &value);
void write(DataSize size, uint32_t address, CPU::SlicedInt32 value);
template <typename IntT> IntT read(uint32_t address, bool is_from_pc = false);
template <typename IntT> void write(uint32_t address, IntT value);
void read(DataSize size, uint32_t address, CPU::SlicedInt32 &value);
void write(DataSize size, uint32_t address, CPU::SlicedInt32 value);
template <typename IntT> IntT read(uint32_t address, bool is_from_pc = false);
template <typename IntT> void write(uint32_t address, IntT value);
template <typename IntT> IntT read_pc();
template <typename IntT> IntT read_pc();
// Processor state.
Status status;
CPU::SlicedInt32 program_counter;
CPU::SlicedInt32 registers[16]; // D0D7 followed by A0A7.
CPU::SlicedInt32 stack_pointers[2];
uint32_t instruction_address;
uint16_t instruction_opcode;
// Processor state.
Status status;
CPU::SlicedInt32 program_counter;
CPU::SlicedInt32 registers[16]; // D0D7 followed by A0A7.
CPU::SlicedInt32 stack_pointers[2];
uint32_t instruction_address;
uint16_t instruction_opcode;
// Things that are ephemerally duplicative of Status.
int active_stack_pointer = 0;
Status::FlagT should_trace = 0;
// Things that are ephemerally duplicative of Status.
int active_stack_pointer = 0;
Status::FlagT should_trace = 0;
// Bus state.
int interrupt_input = 0;
// Bus state.
int interrupt_input = 0;
// A lookup table to ensure that A7 is adjusted by 2 rather than 1 in
// postincrement and predecrement mode.
static constexpr uint32_t byte_increments[] = {
1, 1, 1, 1, 1, 1, 1, 2
};
// A lookup table to ensure that A7 is adjusted by 2 rather than 1 in
// postincrement and predecrement mode.
static constexpr uint32_t byte_increments[] = {
1, 1, 1, 1, 1, 1, 1, 2
};
// Flow control; Cf. Perform.hpp.
template <bool use_current_instruction_pc = true> void raise_exception(int);
// Flow control; Cf. Perform.hpp.
template <bool use_current_instruction_pc = true> void raise_exception(int);
void did_update_status();
void did_update_status();
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset);
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset);
void bsr(uint32_t offset);
void jmp(uint32_t);
void jsr(uint32_t offset);
void rtr();
void rts();
void rte();
void stop();
void reset();
template <typename IntT> void complete_bcc(bool matched_condition, IntT offset);
void complete_dbcc(bool matched_condition, bool overflowed, int16_t offset);
void bsr(uint32_t offset);
void jmp(uint32_t);
void jsr(uint32_t offset);
void rtr();
void rts();
void rte();
void stop();
void reset();
void link(Preinstruction instruction, uint32_t offset);
void unlink(uint32_t &address);
void pea(uint32_t address);
void link(Preinstruction instruction, uint32_t offset);
void unlink(uint32_t &address);
void pea(uint32_t address);
void move_to_usp(uint32_t address);
void move_from_usp(uint32_t &address);
void move_to_usp(uint32_t address);
void move_from_usp(uint32_t &address);
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movep(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movem_toM(Preinstruction instruction, uint32_t source, uint32_t dest);
template <typename IntT> void movem_toR(Preinstruction instruction, uint32_t source, uint32_t dest);
void tas(Preinstruction instruction, uint32_t address);
void tas(Preinstruction instruction, uint32_t address);
private:
BusHandler &bus_handler_;
Predecoder<model> decoder_;
private:
BusHandler &bus_handler_;
Predecoder<model> decoder_;
struct EffectiveAddress {
CPU::SlicedInt32 value;
bool requires_fetch;
};
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
uint32_t index_8bitdisplacement(uint32_t);
} state_;
struct EffectiveAddress {
CPU::SlicedInt32 value;
bool requires_fetch;
};
EffectiveAddress calculate_effective_address(Preinstruction instruction, uint16_t opcode, int index);
uint32_t index_8bitdisplacement(uint32_t);
} state_;
};
}

View File

@ -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;

View File

@ -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:

View File

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

View File

@ -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 &);
}

View File

@ -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);
}
}

View File

@ -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);
};
}

View File

@ -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.

View File

@ -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>; };

View File

@ -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);
}

View File

@ -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

View File

@ -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_;
};
}

View File

@ -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;

View File

@ -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
) {
/*

View File

@ -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);

View File

@ -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
) {

View File

@ -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);
}

View File

@ -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;

View File

@ -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:
//

View File

@ -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) {

View File

@ -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;

View File

@ -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);
}

View File

@ -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; };

View File

@ -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;

View File

@ -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) {

View File

@ -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(); }

View File

@ -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 {

View File

@ -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);
}

View File

@ -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:

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;

View File

@ -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;
}
}

View File

@ -39,7 +39,7 @@ namespace {
// TODO:
// Quiksilva sound support:
// 7FFFh.W PSG index
// 7FFFh.W PSG index
// 7FFEh.R/W PSG data
namespace Sinclair {

View File

@ -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.

View File

@ -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); }
};
}

View File

@ -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;
}

View File

@ -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.

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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>

View File

@ -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;
}