mirror of
https://github.com/TomHarte/CLK.git
synced 2025-01-13 22:32:03 +00:00
Improve const
ness, formatting.
This commit is contained in:
parent
8b88d1294d
commit
3a0f4a0bfc
@ -50,7 +50,7 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
|
||||
}
|
||||
};
|
||||
|
||||
constexpr bool is_iie(Target::Model model) {
|
||||
constexpr bool is_iie(const Target::Model model) {
|
||||
return model == Target::Model::IIe || model == Target::Model::EnhancedIIe;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ enum class Personality {
|
||||
EGA, // Extended EGA-style CRTC; uses 16-bit addressing throughout.
|
||||
};
|
||||
|
||||
constexpr bool is_egavga(Personality p) {
|
||||
constexpr bool is_egavga(const Personality p) {
|
||||
return p >= Personality::EGA;
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
namespace InstructionSet {
|
||||
namespace M50740 {
|
||||
|
||||
Instruction Decoder::instrucion_for_opcode(uint8_t opcode) {
|
||||
Instruction Decoder::instrucion_for_opcode(const uint8_t opcode) {
|
||||
switch(opcode) {
|
||||
default: return Instruction(opcode);
|
||||
|
||||
@ -231,7 +231,7 @@ Instruction Decoder::instrucion_for_opcode(uint8_t opcode) {
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, InstructionSet::M50740::Instruction> Decoder::decode(const uint8_t *source, size_t length) {
|
||||
std::pair<int, InstructionSet::M50740::Instruction> Decoder::decode(const uint8_t *source, const size_t length) {
|
||||
const uint8_t *const end = source + length;
|
||||
|
||||
if(phase_ == Phase::Instruction && source != end) {
|
||||
|
@ -16,19 +16,19 @@
|
||||
namespace InstructionSet::M50740 {
|
||||
|
||||
class Decoder {
|
||||
public:
|
||||
std::pair<int, Instruction> decode(const uint8_t *source, size_t length);
|
||||
Instruction instrucion_for_opcode(uint8_t opcode);
|
||||
public:
|
||||
std::pair<int, Instruction> decode(const uint8_t *, size_t length);
|
||||
Instruction instrucion_for_opcode(uint8_t);
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
Instruction,
|
||||
AwaitingOperand,
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
int operand_size_ = 0, operand_bytes_ = 0;
|
||||
int consumed_ = 0;
|
||||
Instruction instr_;
|
||||
private:
|
||||
enum class Phase {
|
||||
Instruction,
|
||||
AwaitingOperand,
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
int operand_size_ = 0, operand_bytes_ = 0;
|
||||
int consumed_ = 0;
|
||||
Instruction instr_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ void Executor::set_rom(const std::vector<uint8_t> &rom) {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Executor::run_for(Cycles cycles) {
|
||||
void Executor::run_for(const Cycles cycles) {
|
||||
// The incoming clock is divided by four; the local cycles_ count
|
||||
// ensures that fractional parts are kept track of.
|
||||
cycles_ += cycles;
|
||||
@ -61,7 +61,7 @@ void Executor::reset() {
|
||||
set_program_counter(uint16_t(memory_[0x1ffe] | (memory_[0x1fff] << 8)));
|
||||
}
|
||||
|
||||
void Executor::set_interrupt_line(bool line) {
|
||||
void Executor::set_interrupt_line(const bool line) {
|
||||
// Super hack: interrupt now, if permitted. Otherwise do nothing.
|
||||
// So this will fail to catch enabling of interrupts while the line
|
||||
// is active, amongst other things.
|
||||
@ -117,12 +117,12 @@ uint8_t Executor::read(uint16_t address) {
|
||||
}
|
||||
}
|
||||
|
||||
void Executor::set_port_output(int port) {
|
||||
void Executor::set_port_output(const int port) {
|
||||
// Force 'output' to a 1 anywhere that a bit is set as input.
|
||||
port_handler_.set_port_output(port, port_outputs_[port] | ~port_directions_[port]);
|
||||
}
|
||||
|
||||
void Executor::write(uint16_t address, uint8_t value) {
|
||||
void Executor::write(uint16_t address, const uint8_t value) {
|
||||
address &= 0x1fff;
|
||||
|
||||
// RAM writes are easy.
|
||||
@ -182,7 +182,7 @@ void Executor::write(uint16_t address, uint8_t value) {
|
||||
}
|
||||
}
|
||||
|
||||
void Executor::push(uint8_t value) {
|
||||
void Executor::push(const uint8_t value) {
|
||||
write(s_, value);
|
||||
--s_;
|
||||
}
|
||||
@ -192,7 +192,7 @@ uint8_t Executor::pull() {
|
||||
return read(s_);
|
||||
}
|
||||
|
||||
void Executor::set_flags(uint8_t flags) {
|
||||
void Executor::set_flags(const uint8_t flags) {
|
||||
negative_result_ = flags;
|
||||
overflow_result_ = uint8_t(flags << 1);
|
||||
index_mode_ = flags & 0x20;
|
||||
@ -213,7 +213,7 @@ uint8_t Executor::flags() {
|
||||
carry_flag_;
|
||||
}
|
||||
|
||||
template<bool is_brk> inline void Executor::perform_interrupt(uint16_t vector) {
|
||||
template<bool is_brk> inline void Executor::perform_interrupt(const uint16_t vector) {
|
||||
// BRK has an unused operand.
|
||||
++program_counter_;
|
||||
push(uint8_t(program_counter_ >> 8));
|
||||
@ -222,7 +222,7 @@ template<bool is_brk> inline void Executor::perform_interrupt(uint16_t vector) {
|
||||
set_program_counter(uint16_t(memory_[vector] | (memory_[vector+1] << 8)));
|
||||
}
|
||||
|
||||
void Executor::set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector) {
|
||||
void Executor::set_interrupt_request(uint8_t ®, const uint8_t value, const uint16_t vector) {
|
||||
// TODO: this allows interrupts through only if fully enabled at the time they
|
||||
// signal. Which isn't quite correct, albeit that it seems sufficient for the
|
||||
// IIgs ADB controller.
|
||||
@ -797,7 +797,7 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
|
||||
}
|
||||
}
|
||||
|
||||
inline void Executor::subtract_duration(int duration) {
|
||||
inline void Executor::subtract_duration(const int duration) {
|
||||
// Pass along.
|
||||
CachingExecutor::subtract_duration(duration);
|
||||
|
||||
@ -846,7 +846,7 @@ inline void Executor::subtract_duration(int duration) {
|
||||
}
|
||||
}
|
||||
|
||||
inline int Executor::update_timer(Timer &timer, int count) {
|
||||
inline int Executor::update_timer(Timer &timer, const int count) {
|
||||
const int next_value = timer.value - count;
|
||||
if(next_value < 0) {
|
||||
// Determine how many reloads were required to get above zero.
|
||||
@ -859,6 +859,6 @@ inline int Executor::update_timer(Timer &timer, int count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t Executor::get_output_mask(int port) {
|
||||
uint8_t Executor::get_output_mask(const int port) {
|
||||
return port_directions_[port];
|
||||
}
|
||||
|
@ -35,143 +35,142 @@ struct PortHandler {
|
||||
* timing is correct to whole-opcode boundaries only.
|
||||
*/
|
||||
class Executor: public CachingExecutor {
|
||||
public:
|
||||
Executor(PortHandler &);
|
||||
void set_rom(const std::vector<uint8_t> &rom);
|
||||
public:
|
||||
Executor(PortHandler &);
|
||||
void set_rom(const std::vector<uint8_t> &rom);
|
||||
|
||||
void reset();
|
||||
void set_interrupt_line(bool);
|
||||
void reset();
|
||||
void set_interrupt_line(bool);
|
||||
|
||||
uint8_t get_output_mask(int port);
|
||||
uint8_t get_output_mask(int port);
|
||||
|
||||
/*!
|
||||
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
|
||||
*/
|
||||
void run_for(Cycles cycles);
|
||||
/*!
|
||||
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
|
||||
*/
|
||||
void run_for(Cycles);
|
||||
|
||||
private:
|
||||
// MARK: - CachingExecutor-facing interface.
|
||||
private:
|
||||
// MARK: - CachingExecutor-facing interface.
|
||||
|
||||
friend CachingExecutor;
|
||||
friend CachingExecutor;
|
||||
|
||||
/*!
|
||||
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
|
||||
*/
|
||||
inline PerformerIndex action_for(Instruction instruction) {
|
||||
// This is a super-simple processor, so the opcode can be used directly to index the performers.
|
||||
return instruction.opcode;
|
||||
}
|
||||
/*!
|
||||
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
|
||||
*/
|
||||
inline PerformerIndex action_for(const Instruction instruction) {
|
||||
// This is a super-simple processor, so the opcode can be used directly to index the performers.
|
||||
return instruction.opcode;
|
||||
}
|
||||
|
||||
/*!
|
||||
Parses from @c start and no later than @c max_address, using the CachingExecutor as a target.
|
||||
*/
|
||||
inline void parse(uint16_t start, uint16_t closing_bound) {
|
||||
Parser<Executor, false> parser;
|
||||
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
|
||||
}
|
||||
/*!
|
||||
Parses from @c start and no later than @c max_address, using the CachingExecutor as a target.
|
||||
*/
|
||||
inline void parse(const uint16_t start, const uint16_t closing_bound) {
|
||||
Parser<Executor, false> parser;
|
||||
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
|
||||
}
|
||||
|
||||
private:
|
||||
// MARK: - Internal framework for generator performers.
|
||||
// MARK: - Internal framework for generator performers.
|
||||
|
||||
/*!
|
||||
Provides dynamic lookup of @c perform(Executor*).
|
||||
*/
|
||||
class PerformerLookup {
|
||||
public:
|
||||
PerformerLookup() {
|
||||
fill<int(MinOperation)>(performers_);
|
||||
/*!
|
||||
Provides dynamic lookup of @c perform(Executor*).
|
||||
*/
|
||||
class PerformerLookup {
|
||||
public:
|
||||
PerformerLookup() {
|
||||
fill<int(MinOperation)>(performers_);
|
||||
}
|
||||
|
||||
Performer performer(const Operation operation, const AddressingMode addressing_mode) {
|
||||
const auto index =
|
||||
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
|
||||
(int(addressing_mode) - MinAddressingMode);
|
||||
return performers_[index];
|
||||
}
|
||||
|
||||
private:
|
||||
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
|
||||
|
||||
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
|
||||
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
|
||||
|
||||
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
|
||||
fill_operation<operation, addressing_mode+1>(target + 1);
|
||||
}
|
||||
}
|
||||
|
||||
Performer performer(Operation operation, AddressingMode addressing_mode) {
|
||||
const auto index =
|
||||
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
|
||||
(int(addressing_mode) - MinAddressingMode);
|
||||
return performers_[index];
|
||||
template<int operation> void fill(Performer *target) {
|
||||
fill_operation<operation, int(MinAddressingMode)>(target);
|
||||
target += 1 + MaxAddressingMode - MinAddressingMode;
|
||||
|
||||
if constexpr (operation+1 <= MaxOperation) {
|
||||
fill<operation+1>(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
inline static PerformerLookup performer_lookup_;
|
||||
|
||||
private:
|
||||
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
|
||||
/*!
|
||||
Performs @c operation using @c operand as the value fetched from memory, if any.
|
||||
*/
|
||||
template <Operation operation> void perform(uint8_t *operand);
|
||||
|
||||
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
|
||||
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
|
||||
/*!
|
||||
Performs @c operation in @c addressing_mode.
|
||||
*/
|
||||
template <Operation operation, AddressingMode addressing_mode> void perform();
|
||||
|
||||
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
|
||||
fill_operation<operation, addressing_mode+1>(target + 1);
|
||||
}
|
||||
}
|
||||
private:
|
||||
// MARK: - Instruction set state.
|
||||
|
||||
template<int operation> void fill(Performer *target) {
|
||||
fill_operation<operation, int(MinAddressingMode)>(target);
|
||||
target += 1 + MaxAddressingMode - MinAddressingMode;
|
||||
// Memory.
|
||||
std::array<uint8_t, 0x2000> memory_;
|
||||
|
||||
if constexpr (operation+1 <= MaxOperation) {
|
||||
fill<operation+1>(target);
|
||||
}
|
||||
}
|
||||
};
|
||||
inline static PerformerLookup performer_lookup_;
|
||||
// Registers.
|
||||
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
|
||||
|
||||
/*!
|
||||
Performs @c operation using @c operand as the value fetched from memory, if any.
|
||||
*/
|
||||
template <Operation operation> void perform(uint8_t *operand);
|
||||
uint8_t negative_result_ = 0;
|
||||
uint8_t zero_result_ = 0;
|
||||
uint8_t interrupt_disable_ = 0x04;
|
||||
uint8_t carry_flag_ = 0;
|
||||
uint8_t overflow_result_ = 0;
|
||||
bool index_mode_ = false;
|
||||
bool decimal_mode_ = false;
|
||||
|
||||
/*!
|
||||
Performs @c operation in @c addressing_mode.
|
||||
*/
|
||||
template <Operation operation, AddressingMode addressing_mode> void perform();
|
||||
// IO ports.
|
||||
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
|
||||
|
||||
private:
|
||||
// MARK: - Instruction set state.
|
||||
// Timers.
|
||||
struct Timer {
|
||||
uint8_t value = 0xff, reload_value = 0xff;
|
||||
};
|
||||
int timer_divider_ = 0;
|
||||
Timer timers_[3], prescalers_[2];
|
||||
inline int update_timer(Timer &timer, int count);
|
||||
|
||||
// Memory.
|
||||
std::array<uint8_t, 0x2000> memory_;
|
||||
// Interrupt and timer control.
|
||||
uint8_t interrupt_control_ = 0, timer_control_ = 0;
|
||||
bool interrupt_line_ = false;
|
||||
|
||||
// Registers.
|
||||
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
|
||||
// Access helpers.
|
||||
inline uint8_t read(uint16_t address);
|
||||
inline void write(uint16_t address, uint8_t value);
|
||||
inline void push(uint8_t);
|
||||
inline uint8_t pull();
|
||||
inline void set_flags(uint8_t);
|
||||
inline uint8_t flags();
|
||||
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
|
||||
inline void set_port_output(int port);
|
||||
|
||||
uint8_t negative_result_ = 0;
|
||||
uint8_t zero_result_ = 0;
|
||||
uint8_t interrupt_disable_ = 0x04;
|
||||
uint8_t carry_flag_ = 0;
|
||||
uint8_t overflow_result_ = 0;
|
||||
bool index_mode_ = false;
|
||||
bool decimal_mode_ = false;
|
||||
void set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector);
|
||||
|
||||
// IO ports.
|
||||
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
|
||||
// MARK: - Execution time
|
||||
|
||||
// Timers.
|
||||
struct Timer {
|
||||
uint8_t value = 0xff, reload_value = 0xff;
|
||||
};
|
||||
int timer_divider_ = 0;
|
||||
Timer timers_[3], prescalers_[2];
|
||||
inline int update_timer(Timer &timer, int count);
|
||||
|
||||
// Interrupt and timer control.
|
||||
uint8_t interrupt_control_ = 0, timer_control_ = 0;
|
||||
bool interrupt_line_ = false;
|
||||
|
||||
// Access helpers.
|
||||
inline uint8_t read(uint16_t address);
|
||||
inline void write(uint16_t address, uint8_t value);
|
||||
inline void push(uint8_t value);
|
||||
inline uint8_t pull();
|
||||
inline void set_flags(uint8_t);
|
||||
inline uint8_t flags();
|
||||
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
|
||||
inline void set_port_output(int port);
|
||||
|
||||
void set_interrupt_request(uint8_t ®, uint8_t value, uint16_t vector);
|
||||
|
||||
// MARK: - Execution time
|
||||
|
||||
Cycles cycles_;
|
||||
Cycles cycles_since_port_handler_;
|
||||
PortHandler &port_handler_;
|
||||
inline void subtract_duration(int duration);
|
||||
Cycles cycles_;
|
||||
Cycles cycles_since_port_handler_;
|
||||
PortHandler &port_handler_;
|
||||
inline void subtract_duration(int);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ enum class AddressingMode {
|
||||
static constexpr auto MaxAddressingMode = int(AddressingMode::ZeroPageRelative);
|
||||
static constexpr auto MinAddressingMode = int(AddressingMode::Implied);
|
||||
|
||||
constexpr int size(AddressingMode mode) {
|
||||
constexpr int size(const AddressingMode mode) {
|
||||
// This is coupled to the AddressingMode list above; be careful!
|
||||
constexpr int sizes[] = {
|
||||
0, 0, 1,
|
||||
@ -92,14 +92,14 @@ enum class Operation: uint8_t {
|
||||
static constexpr auto MaxOperation = int(Operation::STY);
|
||||
static constexpr auto MinOperation = int(Operation::BBC0);
|
||||
|
||||
constexpr AccessType access_type(Operation operation) {
|
||||
constexpr AccessType access_type(const Operation operation) {
|
||||
if(operation < Operation::ADC) return AccessType::None;
|
||||
if(operation < Operation::ASL) return AccessType::Read;
|
||||
if(operation < Operation::LDM) return AccessType::ReadModifyWrite;
|
||||
return AccessType::Write;
|
||||
}
|
||||
|
||||
constexpr bool uses_index_mode(Operation operation) {
|
||||
constexpr bool uses_index_mode(const Operation operation) {
|
||||
return
|
||||
operation == Operation::ADC || operation == Operation::AND ||
|
||||
operation == Operation::CMP || operation == Operation::EOR ||
|
||||
@ -110,7 +110,7 @@ constexpr bool uses_index_mode(Operation operation) {
|
||||
/*!
|
||||
@returns The name of @c operation.
|
||||
*/
|
||||
inline constexpr const char *operation_name(Operation operation) {
|
||||
inline constexpr const char *operation_name(const Operation operation) {
|
||||
#define MAP(x) case Operation::x: return #x;
|
||||
switch(operation) {
|
||||
default: break;
|
||||
@ -133,7 +133,7 @@ inline constexpr const char *operation_name(Operation operation) {
|
||||
return "???";
|
||||
}
|
||||
|
||||
inline std::ostream &operator <<(std::ostream &stream, Operation operation) {
|
||||
inline std::ostream &operator <<(std::ostream &stream, const Operation operation) {
|
||||
stream << operation_name(operation);
|
||||
return stream;
|
||||
}
|
||||
@ -141,7 +141,7 @@ inline std::ostream &operator <<(std::ostream &stream, Operation operation) {
|
||||
/*!
|
||||
@returns The name of @c addressing_mode.
|
||||
*/
|
||||
inline constexpr const char *addressing_mode_name(AddressingMode addressing_mode) {
|
||||
inline constexpr const char *addressing_mode_name(const AddressingMode addressing_mode) {
|
||||
switch(addressing_mode) {
|
||||
default: break;
|
||||
case AddressingMode::Implied: return "";
|
||||
@ -167,7 +167,7 @@ inline constexpr const char *addressing_mode_name(AddressingMode addressing_mode
|
||||
return "???";
|
||||
}
|
||||
|
||||
inline std::ostream &operator <<(std::ostream &stream, AddressingMode mode) {
|
||||
inline std::ostream &operator <<(std::ostream &stream, const AddressingMode mode) {
|
||||
stream << addressing_mode_name(mode);
|
||||
return stream;
|
||||
}
|
||||
@ -177,7 +177,11 @@ inline std::ostream &operator <<(std::ostream &stream, AddressingMode mode) {
|
||||
would appear in an assembler. E.g. '$5a' for that zero page address, or '$5a, x' for zero-page indexed from $5a. This function
|
||||
may access up to three bytes from @c operation onwards.
|
||||
*/
|
||||
inline std::string address(AddressingMode addressing_mode, const uint8_t *operation, uint16_t program_counter) {
|
||||
inline std::string address(
|
||||
const AddressingMode addressing_mode,
|
||||
const uint8_t *operation,
|
||||
const uint16_t program_counter
|
||||
) {
|
||||
std::stringstream output;
|
||||
output << std::hex;
|
||||
|
||||
@ -220,7 +224,8 @@ struct Instruction {
|
||||
AddressingMode addressing_mode = AddressingMode::Implied;
|
||||
uint8_t opcode = 0;
|
||||
|
||||
Instruction(Operation operation, AddressingMode addressing_mode, uint8_t opcode) : operation(operation), addressing_mode(addressing_mode), opcode(opcode) {}
|
||||
Instruction(const Operation operation, const AddressingMode addressing_mode, const uint8_t opcode) :
|
||||
operation(operation), addressing_mode(addressing_mode), opcode(opcode) {}
|
||||
Instruction(uint8_t opcode) : opcode(opcode) {}
|
||||
Instruction() = default;
|
||||
};
|
||||
|
@ -15,7 +15,7 @@
|
||||
namespace InstructionSet::M50740 {
|
||||
|
||||
template<typename Target, bool include_entries_and_accesses> struct Parser {
|
||||
void parse(Target &target, const uint8_t *storage, uint16_t start, uint16_t closing_bound) {
|
||||
void parse(Target &target, const uint8_t *storage, uint16_t start, const uint16_t closing_bound) {
|
||||
Decoder decoder;
|
||||
|
||||
while(start <= closing_bound) {
|
||||
@ -97,7 +97,10 @@ template<typename Target, bool include_entries_and_accesses> struct Parser {
|
||||
// Provide any fixed address accesses.
|
||||
switch(next.second.addressing_mode) {
|
||||
case AddressingMode::Absolute:
|
||||
target.add_access(uint16_t(storage[start + 1] | (storage[start + 2] << 8)), access_type(next.second.operation));
|
||||
target.add_access(
|
||||
uint16_t(storage[start + 1] | (storage[start + 2] << 8)),
|
||||
access_type(next.second.operation)
|
||||
);
|
||||
break;
|
||||
case AddressingMode::ZeroPage: case AddressingMode::ZeroPageRelative:
|
||||
target.add_access(storage[start + 1], access_type(next.second.operation));
|
||||
|
@ -12,7 +12,8 @@ using namespace InstructionSet::PowerPC;
|
||||
|
||||
namespace {
|
||||
|
||||
template <Model model, bool validate_reserved_bits, Operation operation> Instruction instruction(uint32_t opcode, bool is_supervisor = false) {
|
||||
template <Model model, bool validate_reserved_bits, Operation operation>
|
||||
Instruction instruction(const uint32_t opcode, const bool is_supervisor = false) {
|
||||
// If validation isn't required, there's nothing to do here.
|
||||
if constexpr (!validate_reserved_bits) {
|
||||
return Instruction(operation, opcode, is_supervisor);
|
||||
@ -243,7 +244,7 @@ template <Model model, bool validate_reserved_bits, Operation operation> Instruc
|
||||
}
|
||||
|
||||
template <Model model, bool validate_reserved_bits>
|
||||
Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
Instruction Decoder<model, validate_reserved_bits>::decode(const uint32_t opcode) {
|
||||
// Quick bluffer's guide to PowerPC instruction encoding:
|
||||
//
|
||||
// There is a six-bit field at the very top of the instruction.
|
||||
@ -332,23 +333,29 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
default: break;
|
||||
|
||||
// 64-bit instructions.
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000110101), ldux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0000111010), cntlzdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001000100), td);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0001010100), ldarx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0010010101), stdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0010110101), stdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulldx); BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulldx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0101010101), lwax);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0101110101), lwaux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1000011011), srdx);
|
||||
BindConditional(is64bit, SixTen(0b011111, 0b1100011010), sradx);
|
||||
BindConditional(is64bit, SixTen(0b111111, 0b1111011010), extswx);
|
||||
@ -356,16 +363,22 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
// Power instructions; these are all taken from the MPC601 manual rather than
|
||||
// the PowerPC Programmer's Reference Guide, hence the decimal encoding of the
|
||||
// ten-bit field.
|
||||
BindConditional(is601, SixTen(0b011111, 360), absx); BindConditional(is601, SixTen(0b011111, 512 + 360), absx);
|
||||
BindConditional(is601, SixTen(0b011111, 360), absx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 360), absx);
|
||||
BindConditional(is601, SixTen(0b011111, 531), clcs);
|
||||
BindConditional(is601, SixTen(0b011111, 331), divx); BindConditional(is601, SixTen(0b011111, 512 + 331), divx);
|
||||
BindConditional(is601, SixTen(0b011111, 363), divsx); BindConditional(is601, SixTen(0b011111, 512 + 363), divsx);
|
||||
BindConditional(is601, SixTen(0b011111, 264), dozx); BindConditional(is601, SixTen(0b011111, 512 + 264), dozx);
|
||||
BindConditional(is601, SixTen(0b011111, 331), divx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 331), divx);
|
||||
BindConditional(is601, SixTen(0b011111, 363), divsx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 363), divsx);
|
||||
BindConditional(is601, SixTen(0b011111, 264), dozx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 264), dozx);
|
||||
BindConditional(is601, SixTen(0b011111, 277), lscbxx);
|
||||
BindConditional(is601, SixTen(0b011111, 29), maskgx);
|
||||
BindConditional(is601, SixTen(0b011111, 541), maskirx);
|
||||
BindConditional(is601, SixTen(0b011111, 107), mulx); BindConditional(is601, SixTen(0b011111, 512 + 107), mulx);
|
||||
BindConditional(is601, SixTen(0b011111, 488), nabsx); BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx);
|
||||
BindConditional(is601, SixTen(0b011111, 107), mulx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 107), mulx);
|
||||
BindConditional(is601, SixTen(0b011111, 488), nabsx);
|
||||
BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx);
|
||||
BindConditional(is601, SixTen(0b011111, 537), rribx);
|
||||
BindConditional(is601, SixTen(0b011111, 153), slex);
|
||||
BindConditional(is601, SixTen(0b011111, 217), sleqx);
|
||||
@ -553,17 +566,22 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
if(is64bit(model)) {
|
||||
switch(opcode & 0b111111'00000'00000'00000'000000'111'00) {
|
||||
default: break;
|
||||
case 0b011110'00000'00000'00000'000000'000'00: return instruction<model, validate_reserved_bits, Operation::rldiclx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'001'00: return instruction<model, validate_reserved_bits, Operation::rldicrx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'010'00: return instruction<model, validate_reserved_bits, Operation::rldicx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'011'00: return instruction<model, validate_reserved_bits, Operation::rldimix>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'000'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldiclx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'001'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldicrx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'010'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldicx>(opcode);
|
||||
case 0b011110'00000'00000'00000'000000'011'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::rldimix>(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
// stwcx. and stdcx.
|
||||
switch(opcode & 0b111111'0000'0000'0000'0000'111111111'1) {
|
||||
default: break;
|
||||
case 0b011111'0000'0000'0000'0000'010010110'1: return instruction<model, validate_reserved_bits, Operation::stwcx_>(opcode);
|
||||
case 0b011111'0000'0000'0000'0000'010010110'1:
|
||||
return instruction<model, validate_reserved_bits, Operation::stwcx_>(opcode);
|
||||
case 0b011111'0000'0000'0000'0000'011010110'1:
|
||||
if(is64bit(model)) return instruction<model, validate_reserved_bits, Operation::stdcx_>(opcode);
|
||||
return Instruction(opcode);
|
||||
@ -573,11 +591,16 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
|
||||
if(is64bit(model)) {
|
||||
switch(opcode & 0b111111'00'00000000'00000000'000000'11) {
|
||||
default: break;
|
||||
case 0b111010'00'00000000'00000000'000000'00: return instruction<model, validate_reserved_bits, Operation::ld>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'01: return instruction<model, validate_reserved_bits, Operation::ldu>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'10: return instruction<model, validate_reserved_bits, Operation::lwa>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'00: return instruction<model, validate_reserved_bits, Operation::std>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'01: return instruction<model, validate_reserved_bits, Operation::stdu>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::ld>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'01:
|
||||
return instruction<model, validate_reserved_bits, Operation::ldu>(opcode);
|
||||
case 0b111010'00'00000000'00000000'000000'10:
|
||||
return instruction<model, validate_reserved_bits, Operation::lwa>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'00:
|
||||
return instruction<model, validate_reserved_bits, Operation::std>(opcode);
|
||||
case 0b111110'00'00000000'00000000'000000'01:
|
||||
return instruction<model, validate_reserved_bits, Operation::stdu>(opcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,15 +21,15 @@ enum class Model {
|
||||
MPC620,
|
||||
};
|
||||
|
||||
constexpr bool is64bit(Model model) {
|
||||
constexpr bool is64bit(const Model model) {
|
||||
return model == Model::MPC620;
|
||||
}
|
||||
|
||||
constexpr bool is32bit(Model model) {
|
||||
constexpr bool is32bit(const Model model) {
|
||||
return !is64bit(model);
|
||||
}
|
||||
|
||||
constexpr bool is601(Model model) {
|
||||
constexpr bool is601(const Model model) {
|
||||
return model == Model::MPC601;
|
||||
}
|
||||
|
||||
@ -45,7 +45,7 @@ constexpr bool is601(Model model) {
|
||||
TODO: determine what specific models of PowerPC do re: reserved bits.
|
||||
*/
|
||||
template <Model model, bool validate_reserved_bits = false> struct Decoder {
|
||||
Instruction decode(uint32_t opcode);
|
||||
Instruction decode(uint32_t);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1363,8 +1363,9 @@ struct Instruction {
|
||||
uint32_t opcode = 0;
|
||||
|
||||
constexpr Instruction() noexcept = default;
|
||||
constexpr Instruction(uint32_t opcode) noexcept : opcode(opcode) {}
|
||||
constexpr Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) noexcept : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
|
||||
constexpr Instruction(const uint32_t opcode) noexcept : opcode(opcode) {}
|
||||
constexpr Instruction(const Operation operation, const uint32_t opcode, const bool is_supervisor = false) noexcept :
|
||||
operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
|
||||
|
||||
// Instruction fields are decoded below; naming is a compromise between
|
||||
// Motorola's documentation and IBM's.
|
||||
|
@ -30,24 +30,24 @@ enum class AccessType {
|
||||
PreauthorisedRead,
|
||||
};
|
||||
|
||||
constexpr bool is_writeable(AccessType type) {
|
||||
constexpr bool is_writeable(const AccessType type) {
|
||||
return type == AccessType::ReadModifyWrite || type == AccessType::Write;
|
||||
}
|
||||
|
||||
template <typename IntT, AccessType type> struct Accessor;
|
||||
|
||||
// Reads: return a value directly.
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = const IntT; };
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = const IntT; };
|
||||
|
||||
// Writes: return a custom type that can be written but not read.
|
||||
template <typename IntT>
|
||||
class Writeable {
|
||||
public:
|
||||
Writeable(IntT &target) : target_(target) {}
|
||||
IntT operator=(IntT value) { return target_ = value; }
|
||||
private:
|
||||
IntT &target_;
|
||||
public:
|
||||
Writeable(IntT &target) : target_(target) {}
|
||||
IntT operator=(IntT value) { return target_ = value; }
|
||||
private:
|
||||
IntT &target_;
|
||||
};
|
||||
template <typename IntT> struct Accessor<IntT, AccessType::Write> { using type = Writeable<IntT>; };
|
||||
|
||||
|
@ -15,7 +15,10 @@
|
||||
using namespace InstructionSet::x86;
|
||||
|
||||
template <Model model>
|
||||
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(const uint8_t *source, std::size_t length) {
|
||||
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(
|
||||
const uint8_t *source,
|
||||
const std::size_t length
|
||||
) {
|
||||
// Instruction length limits:
|
||||
//
|
||||
// 8086/80186: none*
|
||||
@ -1124,6 +1127,6 @@ template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80186>;
|
||||
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80286>;
|
||||
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80386>;
|
||||
|
||||
std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *source, std::size_t length) {
|
||||
std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *const source, const std::size_t length) {
|
||||
return decoder.decode(source, length);
|
||||
}
|
||||
|
@ -22,209 +22,209 @@ namespace InstructionSet::x86 {
|
||||
This is an experimental implementation; it has not yet undergone significant testing.
|
||||
*/
|
||||
template <Model model> class Decoder {
|
||||
public:
|
||||
using InstructionT = Instruction<is_32bit(model)>;
|
||||
public:
|
||||
using InstructionT = Instruction<is_32bit(model)>;
|
||||
|
||||
/*!
|
||||
@returns an @c Instruction plus a size; a positive size indicates successful decoding of
|
||||
an instruction that was that many bytes long in total; a negative size specifies the [negatived]
|
||||
minimum number of further bytes the caller should ideally collect before calling again. The
|
||||
caller is free to call with fewer, but may not get a decoded instruction in response, and the
|
||||
decoder may still not be able to complete decoding even if given that number of bytes.
|
||||
/*!
|
||||
@returns an @c Instruction plus a size; a positive size indicates successful decoding of
|
||||
an instruction that was that many bytes long in total; a negative size specifies the [negatived]
|
||||
minimum number of further bytes the caller should ideally collect before calling again. The
|
||||
caller is free to call with fewer, but may not get a decoded instruction in response, and the
|
||||
decoder may still not be able to complete decoding even if given that number of bytes.
|
||||
|
||||
Successful decoding is defined to mean that all decoding steps are complete. The output
|
||||
may still be an illegal instruction (indicated by Operation::Invalid), if the byte sequence
|
||||
supplied cannot form a valid instruction.
|
||||
Successful decoding is defined to mean that all decoding steps are complete. The output
|
||||
may still be an illegal instruction (indicated by Operation::Invalid), if the byte sequence
|
||||
supplied cannot form a valid instruction.
|
||||
|
||||
@discussion although instructions also contain an indicator of their length, on chips prior
|
||||
to the 80286 there is no limit to potential instruction length.
|
||||
@discussion although instructions also contain an indicator of their length, on chips prior
|
||||
to the 80286 there is no limit to potential instruction length.
|
||||
|
||||
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
|
||||
cannot overflow the field.
|
||||
*/
|
||||
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);
|
||||
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
|
||||
cannot overflow the field.
|
||||
*/
|
||||
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);
|
||||
|
||||
/*!
|
||||
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
|
||||
*/
|
||||
void set_32bit_protected_mode(bool);
|
||||
/*!
|
||||
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
|
||||
*/
|
||||
void set_32bit_protected_mode(bool);
|
||||
|
||||
private:
|
||||
enum class Phase {
|
||||
/// Captures all prefixes and continues until an instruction byte is encountered.
|
||||
Instruction,
|
||||
/// Having encountered a 0x0f first instruction byte, waits for the next byte fully to determine the instruction.
|
||||
InstructionPageF,
|
||||
/// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately
|
||||
/// or completes decoding of the instruction, as per the instruction format.
|
||||
ModRegRM,
|
||||
/// Awaits n 80386+-style scale-index-base byte ('SIB'), indicating the form of indirect addressing.
|
||||
ScaleIndexBase,
|
||||
/// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured.
|
||||
/// Cf. displacement_size_ and operand_size_.
|
||||
DisplacementOrOperand,
|
||||
/// Forms and returns an Instruction, and resets parsing state.
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
private:
|
||||
enum class Phase {
|
||||
/// Captures all prefixes and continues until an instruction byte is encountered.
|
||||
Instruction,
|
||||
/// Having encountered a 0x0f first instruction byte, waits for the next byte fully to determine the instruction.
|
||||
InstructionPageF,
|
||||
/// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately
|
||||
/// or completes decoding of the instruction, as per the instruction format.
|
||||
ModRegRM,
|
||||
/// Awaits n 80386+-style scale-index-base byte ('SIB'), indicating the form of indirect addressing.
|
||||
ScaleIndexBase,
|
||||
/// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured.
|
||||
/// Cf. displacement_size_ and operand_size_.
|
||||
DisplacementOrOperand,
|
||||
/// Forms and returns an Instruction, and resets parsing state.
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
|
||||
/// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte.
|
||||
///
|
||||
/// During the ReadyToPost phase, format determines how transiently-recorded fields
|
||||
/// are packaged into an Instruction.
|
||||
enum class ModRegRMFormat: uint8_t {
|
||||
// Parse the ModRegRM for mode, register and register/memory fields
|
||||
// and populate the source_ and destination_ fields appropriately.
|
||||
MemReg_Reg,
|
||||
Reg_MemReg,
|
||||
/// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte.
|
||||
///
|
||||
/// During the ReadyToPost phase, format determines how transiently-recorded fields
|
||||
/// are packaged into an Instruction.
|
||||
enum class ModRegRMFormat: uint8_t {
|
||||
// Parse the ModRegRM for mode, register and register/memory fields
|
||||
// and populate the source_ and destination_ fields appropriately.
|
||||
MemReg_Reg,
|
||||
Reg_MemReg,
|
||||
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the single register/memory result.
|
||||
MemRegSingleOperand,
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the single register/memory result.
|
||||
MemRegSingleOperand,
|
||||
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// the destination_ field with the result and setting source_ to Immediate.
|
||||
MemRegMOV,
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// the destination_ field with the result and setting source_ to Immediate.
|
||||
MemRegMOV,
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ field with the result. Fills destination_ with a segment
|
||||
// register based on the reg field.
|
||||
Seg_MemReg,
|
||||
MemReg_Seg,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ field with the result. Fills destination_ with a segment
|
||||
// register based on the reg field.
|
||||
Seg_MemReg,
|
||||
MemReg_Seg,
|
||||
|
||||
//
|
||||
// 'Group 1'
|
||||
//
|
||||
//
|
||||
// 'Group 1'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and
|
||||
// waits for an operand equal to the operation size.
|
||||
MemRegADD_to_CMP,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and
|
||||
// waits for an operand equal to the operation size.
|
||||
MemRegADD_to_CMP,
|
||||
|
||||
// Acts exactly as MemRegADD_to_CMP but the operand is fixed in size
|
||||
// at a single byte, which is sign extended to the operation size.
|
||||
MemRegADD_to_CMP_SignExtend,
|
||||
// Acts exactly as MemRegADD_to_CMP but the operand is fixed in size
|
||||
// at a single byte, which is sign extended to the operation size.
|
||||
MemRegADD_to_CMP_SignExtend,
|
||||
|
||||
//
|
||||
// 'Group 2'
|
||||
//
|
||||
//
|
||||
// 'Group 2'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group.
|
||||
MemRegROL_to_SAR,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
// to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group.
|
||||
MemRegROL_to_SAR,
|
||||
|
||||
//
|
||||
// 'Group 3'
|
||||
//
|
||||
//
|
||||
// 'Group 3'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the result. Use the 'register'
|
||||
// field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group.
|
||||
MemRegTEST_to_IDIV,
|
||||
// Parse for mode and register/memory fields, populating both
|
||||
// source_ and destination_ fields with the result. Use the 'register'
|
||||
// field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group.
|
||||
MemRegTEST_to_IDIV,
|
||||
|
||||
//
|
||||
// 'Group 4'
|
||||
//
|
||||
//
|
||||
// 'Group 4'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick INC or DEC.
|
||||
MemRegINC_DEC,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick INC or DEC.
|
||||
MemRegINC_DEC,
|
||||
|
||||
//
|
||||
// 'Group 5'
|
||||
//
|
||||
//
|
||||
// 'Group 5'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
|
||||
// the source to ::Immediate and setting an operand size if necessary.
|
||||
MemRegINC_to_PUSH,
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// source_ and destination_ fields with the result. Uses the
|
||||
// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
|
||||
// the source to ::Immediate and setting an operand size if necessary.
|
||||
MemRegINC_to_PUSH,
|
||||
|
||||
//
|
||||
// 'Group 6'
|
||||
//
|
||||
//
|
||||
// 'Group 6'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SLDT/STR/LLDT/LTR/VERR/VERW.
|
||||
MemRegSLDT_to_VERW,
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SLDT/STR/LLDT/LTR/VERR/VERW.
|
||||
MemRegSLDT_to_VERW,
|
||||
|
||||
//
|
||||
// 'Group 7'
|
||||
//
|
||||
//
|
||||
// 'Group 7'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SGDT/LGDT/SMSW/LMSW.
|
||||
MemRegSGDT_to_LMSW,
|
||||
// Parse for mode and register/memory field, populating both source_
|
||||
// and destination_ fields with the result. Uses the 'register' field
|
||||
// to pick from SGDT/LGDT/SMSW/LMSW.
|
||||
MemRegSGDT_to_LMSW,
|
||||
|
||||
//
|
||||
// 'Group 8'
|
||||
//
|
||||
//
|
||||
// 'Group 8'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory field, populating destination,
|
||||
// and prepare to read a single byte as source.
|
||||
MemRegBT_to_BTC,
|
||||
} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
|
||||
// Parse for mode and register/memory field, populating destination,
|
||||
// and prepare to read a single byte as source.
|
||||
MemRegBT_to_BTC,
|
||||
} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
|
||||
|
||||
// Ephemeral decoding state.
|
||||
Operation operation_ = Operation::Invalid;
|
||||
int consumed_ = 0, operand_bytes_ = 0;
|
||||
// Ephemeral decoding state.
|
||||
Operation operation_ = Operation::Invalid;
|
||||
int consumed_ = 0, operand_bytes_ = 0;
|
||||
|
||||
// Source and destination locations.
|
||||
Source source_ = Source::None;
|
||||
Source destination_ = Source::None;
|
||||
// Source and destination locations.
|
||||
Source source_ = Source::None;
|
||||
Source destination_ = Source::None;
|
||||
|
||||
// Immediate fields.
|
||||
int32_t displacement_ = 0;
|
||||
uint32_t operand_ = 0;
|
||||
uint64_t inward_data_ = 0;
|
||||
int next_inward_data_shift_ = 0;
|
||||
// Immediate fields.
|
||||
int32_t displacement_ = 0;
|
||||
uint32_t operand_ = 0;
|
||||
uint64_t inward_data_ = 0;
|
||||
int next_inward_data_shift_ = 0;
|
||||
|
||||
// Indirection style.
|
||||
ScaleIndexBase sib_;
|
||||
// Indirection style.
|
||||
ScaleIndexBase sib_;
|
||||
|
||||
// Facts about the instruction.
|
||||
DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any.
|
||||
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
|
||||
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.
|
||||
// Facts about the instruction.
|
||||
DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any.
|
||||
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
|
||||
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.
|
||||
|
||||
bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
|
||||
// size; otherwise it'll be zero-padded.
|
||||
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
|
||||
// otherwise it'll be zero-padded.
|
||||
bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
|
||||
// size; otherwise it'll be zero-padded.
|
||||
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
|
||||
// otherwise it'll be zero-padded.
|
||||
|
||||
// Prefix capture fields.
|
||||
Repetition repetition_ = Repetition::None;
|
||||
bool lock_ = false;
|
||||
Source segment_override_ = Source::None;
|
||||
// Prefix capture fields.
|
||||
Repetition repetition_ = Repetition::None;
|
||||
bool lock_ = false;
|
||||
Source segment_override_ = Source::None;
|
||||
|
||||
// 32-bit/16-bit selection.
|
||||
AddressSize default_address_size_ = AddressSize::b16;
|
||||
DataSize default_data_size_ = DataSize::Word;
|
||||
AddressSize address_size_ = AddressSize::b16;
|
||||
DataSize data_size_ = DataSize::Word;
|
||||
// 32-bit/16-bit selection.
|
||||
AddressSize default_address_size_ = AddressSize::b16;
|
||||
DataSize default_data_size_ = DataSize::Word;
|
||||
AddressSize address_size_ = AddressSize::b16;
|
||||
DataSize data_size_ = DataSize::Word;
|
||||
|
||||
/// Resets size capture and all fields with default values.
|
||||
void reset_parsing() {
|
||||
consumed_ = operand_bytes_ = 0;
|
||||
displacement_size_ = operand_size_ = operation_size_ = DataSize::None;
|
||||
displacement_ = operand_ = 0;
|
||||
lock_ = false;
|
||||
address_size_ = default_address_size_;
|
||||
data_size_ = default_data_size_;
|
||||
segment_override_ = Source::None;
|
||||
repetition_ = Repetition::None;
|
||||
phase_ = Phase::Instruction;
|
||||
source_ = destination_ = Source::None;
|
||||
sib_ = ScaleIndexBase();
|
||||
next_inward_data_shift_ = 0;
|
||||
inward_data_ = 0;
|
||||
sign_extend_operand_ = false;
|
||||
sign_extend_displacement_ = true;
|
||||
}
|
||||
/// Resets size capture and all fields with default values.
|
||||
void reset_parsing() {
|
||||
consumed_ = operand_bytes_ = 0;
|
||||
displacement_size_ = operand_size_ = operation_size_ = DataSize::None;
|
||||
displacement_ = operand_ = 0;
|
||||
lock_ = false;
|
||||
address_size_ = default_address_size_;
|
||||
data_size_ = default_data_size_;
|
||||
segment_override_ = Source::None;
|
||||
repetition_ = Repetition::None;
|
||||
phase_ = Phase::Instruction;
|
||||
source_ = destination_ = Source::None;
|
||||
sib_ = ScaleIndexBase();
|
||||
next_inward_data_shift_ = 0;
|
||||
inward_data_ = 0;
|
||||
sign_extend_operand_ = false;
|
||||
sign_extend_displacement_ = true;
|
||||
}
|
||||
};
|
||||
|
||||
// This is a temporary measure; for reasons as-yet unknown, GCC isn't picking up the
|
||||
|
@ -77,154 +77,154 @@ enum class Condition {
|
||||
};
|
||||
|
||||
class Flags {
|
||||
public:
|
||||
using FlagT = uint32_t;
|
||||
public:
|
||||
using FlagT = uint32_t;
|
||||
|
||||
// Flag getters.
|
||||
template <Flag flag_v> bool flag() const {
|
||||
switch(flag_v) {
|
||||
case Flag::Carry: return carry_;
|
||||
case Flag::AuxiliaryCarry: return auxiliary_carry_;
|
||||
case Flag::Sign: return sign_;
|
||||
case Flag::Overflow: return overflow_;
|
||||
case Flag::Trap: return trap_;
|
||||
case Flag::Interrupt: return interrupt_;
|
||||
case Flag::Direction: return direction_ < 0;
|
||||
case Flag::Zero: return !zero_;
|
||||
case Flag::ParityOdd: return not_parity_bit();
|
||||
// Flag getters.
|
||||
template <Flag flag_v> bool flag() const {
|
||||
switch(flag_v) {
|
||||
case Flag::Carry: return carry_;
|
||||
case Flag::AuxiliaryCarry: return auxiliary_carry_;
|
||||
case Flag::Sign: return sign_;
|
||||
case Flag::Overflow: return overflow_;
|
||||
case Flag::Trap: return trap_;
|
||||
case Flag::Interrupt: return interrupt_;
|
||||
case Flag::Direction: return direction_ < 0;
|
||||
case Flag::Zero: return !zero_;
|
||||
case Flag::ParityOdd: return not_parity_bit();
|
||||
}
|
||||
}
|
||||
|
||||
// Condition evaluation.
|
||||
template <Condition test> bool condition() const {
|
||||
switch(test) {
|
||||
case Condition::Overflow: return flag<Flag::Overflow>();
|
||||
case Condition::Below: return flag<Flag::Carry>();
|
||||
case Condition::Zero: return flag<Flag::Zero>();
|
||||
case Condition::BelowOrEqual: return flag<Flag::Zero>() || flag<Flag::Carry>();
|
||||
case Condition::Sign: return flag<Flag::Sign>();
|
||||
case Condition::ParityOdd: return flag<Flag::ParityOdd>();
|
||||
case Condition::Less: return flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
case Condition::LessOrEqual: return flag<Flag::Zero>() || flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience setters.
|
||||
|
||||
/// Sets all of @c flags as a function of @c value:
|
||||
/// • Flag::Zero: sets the zero flag if @c value is zero;
|
||||
/// • Flag::Sign: sets the sign flag if the top bit of @c value is one;
|
||||
/// • Flag::ParityOdd: sets parity based on the low 8 bits of @c value;
|
||||
/// • Flag::Carry: sets carry if @c value is non-zero;
|
||||
/// • Flag::AuxiliaryCarry: sets auxiliary carry if @c value is non-zero;
|
||||
/// • Flag::Overflow: sets overflow if @c value is non-zero;
|
||||
/// • Flag::Interrupt: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Trap: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Direction: sets direction if @c value is non-zero.
|
||||
template <typename IntT, Flag... flags> void set_from(const IntT value) {
|
||||
for(const auto flag: {flags...}) {
|
||||
switch(flag) {
|
||||
default: break;
|
||||
case Flag::Zero: zero_ = value; break;
|
||||
case Flag::Sign: sign_ = value & Numeric::top_bit<IntT>(); break;
|
||||
case Flag::ParityOdd: parity_ = value; break;
|
||||
case Flag::Carry: carry_ = value; break;
|
||||
case Flag::AuxiliaryCarry: auxiliary_carry_ = value; break;
|
||||
case Flag::Overflow: overflow_ = value; break;
|
||||
case Flag::Interrupt: interrupt_ = value; break;
|
||||
case Flag::Trap: trap_ = value; break;
|
||||
case Flag::Direction: direction_ = value ? -1 : 1; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <Flag... flags> void set_from(const FlagT value) {
|
||||
set_from<FlagT, flags...>(value);
|
||||
}
|
||||
|
||||
// Condition evaluation.
|
||||
template <Condition test> bool condition() const {
|
||||
switch(test) {
|
||||
case Condition::Overflow: return flag<Flag::Overflow>();
|
||||
case Condition::Below: return flag<Flag::Carry>();
|
||||
case Condition::Zero: return flag<Flag::Zero>();
|
||||
case Condition::BelowOrEqual: return flag<Flag::Zero>() || flag<Flag::Carry>();
|
||||
case Condition::Sign: return flag<Flag::Sign>();
|
||||
case Condition::ParityOdd: return flag<Flag::ParityOdd>();
|
||||
case Condition::Less: return flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
case Condition::LessOrEqual: return flag<Flag::Zero>() || flag<Flag::Sign>() != flag<Flag::Overflow>();
|
||||
}
|
||||
}
|
||||
template <typename IntT> IntT carry_bit() const { return carry_ ? 1 : 0; }
|
||||
bool not_parity_bit() const {
|
||||
// x86 parity always considers the lowest 8-bits only.
|
||||
auto result = static_cast<uint8_t>(parity_);
|
||||
result ^= result >> 4;
|
||||
result ^= result >> 2;
|
||||
result ^= result >> 1;
|
||||
return result & 1;
|
||||
}
|
||||
|
||||
// Convenience setters.
|
||||
template <typename IntT> IntT direction() const { return static_cast<IntT>(direction_); }
|
||||
|
||||
/// Sets all of @c flags as a function of @c value:
|
||||
/// • Flag::Zero: sets the zero flag if @c value is zero;
|
||||
/// • Flag::Sign: sets the sign flag if the top bit of @c value is one;
|
||||
/// • Flag::ParityOdd: sets parity based on the low 8 bits of @c value;
|
||||
/// • Flag::Carry: sets carry if @c value is non-zero;
|
||||
/// • Flag::AuxiliaryCarry: sets auxiliary carry if @c value is non-zero;
|
||||
/// • Flag::Overflow: sets overflow if @c value is non-zero;
|
||||
/// • Flag::Interrupt: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Trap: sets interrupt if @c value is non-zero;
|
||||
/// • Flag::Direction: sets direction if @c value is non-zero.
|
||||
template <typename IntT, Flag... flags> void set_from(IntT value) {
|
||||
for(const auto flag: {flags...}) {
|
||||
switch(flag) {
|
||||
default: break;
|
||||
case Flag::Zero: zero_ = value; break;
|
||||
case Flag::Sign: sign_ = value & Numeric::top_bit<IntT>(); break;
|
||||
case Flag::ParityOdd: parity_ = value; break;
|
||||
case Flag::Carry: carry_ = value; break;
|
||||
case Flag::AuxiliaryCarry: auxiliary_carry_ = value; break;
|
||||
case Flag::Overflow: overflow_ = value; break;
|
||||
case Flag::Interrupt: interrupt_ = value; break;
|
||||
case Flag::Trap: trap_ = value; break;
|
||||
case Flag::Direction: direction_ = value ? -1 : 1; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <Flag... flags> void set_from(FlagT value) {
|
||||
set_from<FlagT, flags...>(value);
|
||||
}
|
||||
// Complete value get and set.
|
||||
void set(uint16_t value) {
|
||||
set_from<Flag::Carry>(value & FlagValue::Carry);
|
||||
set_from<Flag::AuxiliaryCarry>(value & FlagValue::AuxiliaryCarry);
|
||||
set_from<Flag::Overflow>(value & FlagValue::Overflow);
|
||||
set_from<Flag::Trap>(value & FlagValue::Trap);
|
||||
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
|
||||
set_from<Flag::Direction>(value & FlagValue::Direction);
|
||||
|
||||
template <typename IntT> IntT carry_bit() const { return carry_ ? 1 : 0; }
|
||||
bool not_parity_bit() const {
|
||||
// x86 parity always considers the lowest 8-bits only.
|
||||
auto result = static_cast<uint8_t>(parity_);
|
||||
result ^= result >> 4;
|
||||
result ^= result >> 2;
|
||||
result ^= result >> 1;
|
||||
return result & 1;
|
||||
}
|
||||
set_from<uint8_t, Flag::Sign>(uint8_t(value));
|
||||
|
||||
template <typename IntT> IntT direction() const { return static_cast<IntT>(direction_); }
|
||||
set_from<Flag::Zero>((~value) & FlagValue::Zero);
|
||||
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
|
||||
}
|
||||
|
||||
// Complete value get and set.
|
||||
void set(uint16_t value) {
|
||||
set_from<Flag::Carry>(value & FlagValue::Carry);
|
||||
set_from<Flag::AuxiliaryCarry>(value & FlagValue::AuxiliaryCarry);
|
||||
set_from<Flag::Overflow>(value & FlagValue::Overflow);
|
||||
set_from<Flag::Trap>(value & FlagValue::Trap);
|
||||
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
|
||||
set_from<Flag::Direction>(value & FlagValue::Direction);
|
||||
uint16_t get() const {
|
||||
return
|
||||
0xf002 |
|
||||
|
||||
set_from<uint8_t, Flag::Sign>(uint8_t(value));
|
||||
(flag<Flag::Carry>() ? FlagValue::Carry : 0) |
|
||||
(flag<Flag::AuxiliaryCarry>() ? FlagValue::AuxiliaryCarry : 0) |
|
||||
(flag<Flag::Sign>() ? FlagValue::Sign : 0) |
|
||||
(flag<Flag::Overflow>() ? FlagValue::Overflow : 0) |
|
||||
(flag<Flag::Trap>() ? FlagValue::Trap : 0) |
|
||||
(flag<Flag::Interrupt>() ? FlagValue::Interrupt : 0) |
|
||||
(flag<Flag::Direction>() ? FlagValue::Direction : 0) |
|
||||
(flag<Flag::Zero>() ? FlagValue::Zero : 0) |
|
||||
|
||||
set_from<Flag::Zero>((~value) & FlagValue::Zero);
|
||||
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
|
||||
}
|
||||
(flag<Flag::ParityOdd>() ? 0 : FlagValue::Parity);
|
||||
}
|
||||
|
||||
uint16_t get() const {
|
||||
return
|
||||
0xf002 |
|
||||
std::string to_string() const {
|
||||
std::string result;
|
||||
|
||||
(flag<Flag::Carry>() ? FlagValue::Carry : 0) |
|
||||
(flag<Flag::AuxiliaryCarry>() ? FlagValue::AuxiliaryCarry : 0) |
|
||||
(flag<Flag::Sign>() ? FlagValue::Sign : 0) |
|
||||
(flag<Flag::Overflow>() ? FlagValue::Overflow : 0) |
|
||||
(flag<Flag::Trap>() ? FlagValue::Trap : 0) |
|
||||
(flag<Flag::Interrupt>() ? FlagValue::Interrupt : 0) |
|
||||
(flag<Flag::Direction>() ? FlagValue::Direction : 0) |
|
||||
(flag<Flag::Zero>() ? FlagValue::Zero : 0) |
|
||||
if(flag<Flag::Overflow>()) result += "O"; else result += "-";
|
||||
if(flag<Flag::Direction>()) result += "D"; else result += "-";
|
||||
if(flag<Flag::Interrupt>()) result += "I"; else result += "-";
|
||||
if(flag<Flag::Trap>()) result += "T"; else result += "-";
|
||||
if(flag<Flag::Sign>()) result += "S"; else result += "-";
|
||||
if(flag<Flag::Zero>()) result += "Z"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::AuxiliaryCarry>()) result += "A"; else result += "-";
|
||||
result += "-";
|
||||
if(!flag<Flag::ParityOdd>()) result += "P"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::Carry>()) result += "C"; else result += "-";
|
||||
|
||||
(flag<Flag::ParityOdd>() ? 0 : FlagValue::Parity);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string to_string() const {
|
||||
std::string result;
|
||||
bool operator ==(const Flags &rhs) const {
|
||||
return get() == rhs.get();
|
||||
}
|
||||
|
||||
if(flag<Flag::Overflow>()) result += "O"; else result += "-";
|
||||
if(flag<Flag::Direction>()) result += "D"; else result += "-";
|
||||
if(flag<Flag::Interrupt>()) result += "I"; else result += "-";
|
||||
if(flag<Flag::Trap>()) result += "T"; else result += "-";
|
||||
if(flag<Flag::Sign>()) result += "S"; else result += "-";
|
||||
if(flag<Flag::Zero>()) result += "Z"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::AuxiliaryCarry>()) result += "A"; else result += "-";
|
||||
result += "-";
|
||||
if(!flag<Flag::ParityOdd>()) result += "P"; else result += "-";
|
||||
result += "-";
|
||||
if(flag<Flag::Carry>()) result += "C"; else result += "-";
|
||||
private:
|
||||
// Non-zero => set; zero => unset.
|
||||
uint32_t carry_;
|
||||
uint32_t auxiliary_carry_;
|
||||
uint32_t sign_;
|
||||
uint32_t overflow_;
|
||||
uint32_t trap_;
|
||||
uint32_t interrupt_;
|
||||
|
||||
return result;
|
||||
}
|
||||
// +1 = direction flag not set;
|
||||
// -1 = direction flag set.
|
||||
int32_t direction_;
|
||||
|
||||
bool operator ==(const Flags &rhs) const {
|
||||
return get() == rhs.get();
|
||||
}
|
||||
// Zero => set; non-zero => unset.
|
||||
uint32_t zero_;
|
||||
|
||||
private:
|
||||
// Non-zero => set; zero => unset.
|
||||
uint32_t carry_;
|
||||
uint32_t auxiliary_carry_;
|
||||
uint32_t sign_;
|
||||
uint32_t overflow_;
|
||||
uint32_t trap_;
|
||||
uint32_t interrupt_;
|
||||
|
||||
// +1 = direction flag not set;
|
||||
// -1 = direction flag set.
|
||||
int32_t direction_;
|
||||
|
||||
// Zero => set; non-zero => unset.
|
||||
uint32_t zero_;
|
||||
|
||||
// Odd number of bits => set; even => unset.
|
||||
uint32_t parity_;
|
||||
// Odd number of bits => set; even => unset.
|
||||
uint32_t parity_;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -240,7 +240,8 @@ void idiv(
|
||||
IF OperandSize = 8 (* word/byte operation *)
|
||||
THEN
|
||||
temp ← AX / SRC; (* signed division *)
|
||||
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than 7FH or a negative result is less than 80H *)
|
||||
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than
|
||||
7FH or a negative result is less than 80H *)
|
||||
THEN #DE; (* divide error *) ;
|
||||
ELSE
|
||||
AL ← temp;
|
||||
@ -250,7 +251,8 @@ void idiv(
|
||||
IF OperandSize = 16 (* doubleword/word operation *)
|
||||
THEN
|
||||
temp ← DX:AX / SRC; (* signed division *)
|
||||
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a negative result is less than 8000H *)
|
||||
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a
|
||||
negative result is less than 8000H *)
|
||||
THEN #DE; (* divide error *) ;
|
||||
ELSE
|
||||
AX ← temp;
|
||||
@ -258,7 +260,8 @@ void idiv(
|
||||
FI;
|
||||
ELSE (* quadword/doubleword operation *)
|
||||
temp ← EDX:EAX / SRC; (* signed division *)
|
||||
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH or a negative result is less than 80000000H *)
|
||||
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH
|
||||
or a negative result is less than 80000000H *)
|
||||
THEN #DE; (* divide error *) ;
|
||||
ELSE
|
||||
EAX ← temp;
|
||||
@ -350,7 +353,11 @@ void neg(
|
||||
The CF flag cleared to 0 if the source operand is 0; otherwise it is set to 1.
|
||||
The OF, SF, ZF, AF, and PF flags are set according to the result.
|
||||
*/
|
||||
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(IntT(0), destination, IntT(-destination)));
|
||||
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(
|
||||
IntT(0),
|
||||
destination,
|
||||
IntT(-destination))
|
||||
);
|
||||
|
||||
destination = -destination;
|
||||
|
||||
|
@ -38,7 +38,7 @@ void aaas(
|
||||
template <typename ContextT>
|
||||
void aad(
|
||||
CPU::RegisterPair16 &ax,
|
||||
uint8_t imm,
|
||||
const uint8_t imm,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -59,7 +59,7 @@ void aad(
|
||||
template <typename ContextT>
|
||||
void aam(
|
||||
CPU::RegisterPair16 &ax,
|
||||
uint8_t imm,
|
||||
const uint8_t imm,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
|
@ -18,8 +18,8 @@ namespace InstructionSet::x86::Primitive {
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
void jump(
|
||||
bool condition,
|
||||
IntT displacement,
|
||||
const bool condition,
|
||||
const IntT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -42,7 +42,7 @@ void jump(
|
||||
template <typename IntT, typename OffsetT, typename ContextT>
|
||||
void loop(
|
||||
modify_t<IntT> counter,
|
||||
OffsetT displacement,
|
||||
const OffsetT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
--counter;
|
||||
@ -54,7 +54,7 @@ void loop(
|
||||
template <typename IntT, typename OffsetT, typename ContextT>
|
||||
void loope(
|
||||
modify_t<IntT> counter,
|
||||
OffsetT displacement,
|
||||
const OffsetT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
--counter;
|
||||
@ -66,7 +66,7 @@ void loope(
|
||||
template <typename IntT, typename OffsetT, typename ContextT>
|
||||
void loopne(
|
||||
modify_t<IntT> counter,
|
||||
OffsetT displacement,
|
||||
const OffsetT displacement,
|
||||
ContextT &context
|
||||
) {
|
||||
--counter;
|
||||
@ -125,20 +125,28 @@ void call_far(
|
||||
return;
|
||||
|
||||
case Source::Indirect:
|
||||
source_address = uint16_t(address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::IndirectNoBase:
|
||||
source_address = uint16_t(address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::DirectAddress:
|
||||
source_address = uint16_t(address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto offset =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
source_address += 2;
|
||||
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto segment =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
|
||||
// At least on an 8086, the stack writes occur after the target address read.
|
||||
push<uint16_t, true>(context.registers.cs(), context);
|
||||
@ -157,25 +165,35 @@ void jump_far(
|
||||
const auto pointer = instruction.destination();
|
||||
switch(pointer.source()) {
|
||||
default:
|
||||
case Source::Immediate: context.flow_controller.template jump<uint16_t>(instruction.segment(), instruction.offset()); return;
|
||||
case Source::Immediate:
|
||||
context.flow_controller.template jump<uint16_t>(instruction.segment(), instruction.offset());
|
||||
return;
|
||||
|
||||
case Source::Indirect:
|
||||
source_address = uint16_t(address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::IndirectNoBase:
|
||||
source_address = uint16_t(address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
case Source::DirectAddress:
|
||||
source_address = uint16_t(address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context));
|
||||
source_address = uint16_t(
|
||||
address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context)
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
const Source source_segment = instruction.data_segment();
|
||||
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
|
||||
|
||||
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto offset =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
source_address += 2;
|
||||
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
const auto segment =
|
||||
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
|
||||
context.flow_controller.template jump<uint16_t>(segment, offset);
|
||||
}
|
||||
|
||||
@ -193,7 +211,7 @@ void iret(
|
||||
|
||||
template <typename InstructionT, typename ContextT>
|
||||
void ret_near(
|
||||
InstructionT instruction,
|
||||
const InstructionT instruction,
|
||||
ContextT &context
|
||||
) {
|
||||
const auto ip = pop<uint16_t, false>(context);
|
||||
@ -203,7 +221,7 @@ void ret_near(
|
||||
|
||||
template <typename InstructionT, typename ContextT>
|
||||
void ret_far(
|
||||
InstructionT instruction,
|
||||
const InstructionT instruction,
|
||||
ContextT &context
|
||||
) {
|
||||
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
|
||||
@ -233,9 +251,11 @@ void bound(
|
||||
|
||||
const auto source_segment = instruction.data_segment();
|
||||
context.memory.preauthorise_read(source_segment, source, 2*sizeof(IntT));
|
||||
const sIntT lower_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
const auto lower_bound =
|
||||
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
source += 2;
|
||||
const sIntT upper_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
const auto upper_bound =
|
||||
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
|
||||
|
||||
if(sIntT(destination) < lower_bound || sIntT(destination) > upper_bound) {
|
||||
interrupt(Interrupt::BoundRangeExceeded, context);
|
||||
|
@ -14,7 +14,7 @@ namespace InstructionSet::x86::Primitive {
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
void out(
|
||||
uint16_t port,
|
||||
const uint16_t port,
|
||||
read_t<IntT> value,
|
||||
ContextT &context
|
||||
) {
|
||||
@ -23,7 +23,7 @@ void out(
|
||||
|
||||
template <typename IntT, typename ContextT>
|
||||
void in(
|
||||
uint16_t port,
|
||||
const uint16_t port,
|
||||
write_t<IntT> value,
|
||||
ContextT &context
|
||||
) {
|
||||
|
@ -99,7 +99,7 @@ void cbw(
|
||||
template <typename IntT>
|
||||
void cwd(
|
||||
IntT &dx,
|
||||
IntT ax
|
||||
const IntT ax
|
||||
) {
|
||||
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
|
||||
}
|
||||
|
@ -176,8 +176,12 @@ template <
|
||||
case Operation::ESC:
|
||||
case Operation::NOP: return;
|
||||
|
||||
case Operation::AAM: Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context); return;
|
||||
case Operation::AAD: Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context); return;
|
||||
case Operation::AAM:
|
||||
Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context);
|
||||
return;
|
||||
case Operation::AAD:
|
||||
Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context);
|
||||
return;
|
||||
case Operation::AAA: Primitive::aaas<true>(context.registers.axp(), context); return;
|
||||
case Operation::AAS: Primitive::aaas<false>(context.registers.axp(), context); return;
|
||||
case Operation::DAA: Primitive::daas<true>(context.registers.al(), context); return;
|
||||
@ -236,8 +240,8 @@ template <
|
||||
case Operation::CALLrel:
|
||||
Primitive::call_relative<AddressT>(instruction.displacement(), context);
|
||||
return;
|
||||
case Operation::CALLabs: Primitive::call_absolute<IntT, AddressT>(destination_r(), context); return;
|
||||
case Operation::CALLfar: Primitive::call_far<AddressT>(instruction, context); return;
|
||||
case Operation::CALLabs: Primitive::call_absolute<IntT, AddressT>(destination_r(), context); return;
|
||||
case Operation::CALLfar: Primitive::call_far<AddressT>(instruction, context); return;
|
||||
|
||||
case Operation::JMPrel: jcc(true); return;
|
||||
case Operation::JMPabs: Primitive::jump_absolute<IntT>(destination_r(), context); return;
|
||||
@ -417,10 +421,12 @@ template <
|
||||
break;
|
||||
|
||||
case Operation::OUTS:
|
||||
Primitive::outs<IntT, AddressT, Repetition::None>(instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
Primitive::outs<IntT, AddressT, Repetition::None>(
|
||||
instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
return;
|
||||
case Operation::OUTS_REP:
|
||||
Primitive::outs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
Primitive::outs<IntT, AddressT, Repetition::Rep>(
|
||||
instruction, eCX(), context.registers.dx(), eSI(), context);
|
||||
return;
|
||||
|
||||
case Operation::INS:
|
||||
@ -453,7 +459,7 @@ template <
|
||||
const InstructionT &instruction,
|
||||
ContextT &context
|
||||
) {
|
||||
auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
|
||||
const auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
|
||||
return int(operation_size) + (int(address_size) << 2);
|
||||
};
|
||||
|
||||
@ -508,7 +514,7 @@ template <
|
||||
template <
|
||||
typename ContextT
|
||||
> void interrupt(
|
||||
int index,
|
||||
const int index,
|
||||
ContextT &context
|
||||
) {
|
||||
const uint32_t address = static_cast<uint32_t>(index) << 2;
|
||||
|
@ -21,9 +21,9 @@ namespace InstructionSet::x86 {
|
||||
/// is copied to @c *immediate and @c immediate is returned.
|
||||
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
||||
typename Accessor<IntT, access>::type resolve(
|
||||
InstructionT &instruction,
|
||||
Source source,
|
||||
DataPointer pointer,
|
||||
const InstructionT &instruction,
|
||||
const Source source,
|
||||
const DataPointer pointer,
|
||||
ContextT &context,
|
||||
IntT *none = nullptr,
|
||||
IntT *immediate = nullptr
|
||||
@ -66,8 +66,9 @@ 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.
|
||||
// (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(); }
|
||||
@ -150,12 +151,12 @@ uint32_t address(
|
||||
// See forward declaration, above, for details.
|
||||
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
|
||||
typename Accessor<IntT, access>::type resolve(
|
||||
InstructionT &instruction,
|
||||
Source source,
|
||||
DataPointer pointer,
|
||||
const InstructionT &instruction,
|
||||
const Source source,
|
||||
const DataPointer pointer,
|
||||
ContextT &context,
|
||||
IntT *none,
|
||||
IntT *immediate
|
||||
IntT *const immediate
|
||||
) {
|
||||
// Rules:
|
||||
//
|
||||
|
@ -15,7 +15,7 @@ namespace InstructionSet::x86::Primitive {
|
||||
template <typename IntT, typename ContextT>
|
||||
void rcl(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -76,7 +76,7 @@ void rcl(
|
||||
template <typename IntT, typename ContextT>
|
||||
void rcr(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -123,7 +123,7 @@ void rcr(
|
||||
template <typename IntT, typename ContextT>
|
||||
void rol(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -175,7 +175,7 @@ void rol(
|
||||
template <typename IntT, typename ContextT>
|
||||
void ror(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
/*
|
||||
@ -283,7 +283,7 @@ void ror(
|
||||
template <typename IntT, typename ContextT>
|
||||
void sal(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
switch(count) {
|
||||
@ -314,7 +314,7 @@ void sal(
|
||||
template <typename IntT, typename ContextT>
|
||||
void sar(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
if(!count) {
|
||||
@ -337,7 +337,7 @@ void sar(
|
||||
template <typename IntT, typename ContextT>
|
||||
void shr(
|
||||
modify_t<IntT> destination,
|
||||
uint8_t count,
|
||||
const uint8_t count,
|
||||
ContextT &context
|
||||
) {
|
||||
if(!count) {
|
||||
|
@ -499,10 +499,10 @@ enum class Source: uint8_t {
|
||||
/// getter is used).
|
||||
IndirectNoBase = Indirect - 1,
|
||||
};
|
||||
constexpr bool is_register(Source source) {
|
||||
constexpr bool is_register(const Source source) {
|
||||
return source < Source::None;
|
||||
}
|
||||
constexpr bool is_segment_register(Source source) {
|
||||
constexpr bool is_segment_register(const Source source) {
|
||||
return is_register(source) && source >= Source::ES;
|
||||
}
|
||||
|
||||
@ -698,216 +698,226 @@ class DataPointer {
|
||||
};
|
||||
|
||||
template<bool is_32bit> class Instruction {
|
||||
public:
|
||||
using DisplacementT = typename std::conditional<is_32bit, int32_t, int16_t>::type;
|
||||
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
|
||||
using AddressT = ImmediateT;
|
||||
public:
|
||||
using DisplacementT = typename std::conditional<is_32bit, int32_t, int16_t>::type;
|
||||
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
|
||||
using AddressT = ImmediateT;
|
||||
|
||||
constexpr Instruction() noexcept = default;
|
||||
constexpr Instruction(Operation operation) noexcept :
|
||||
Instruction(operation, Source::None, Source::None, ScaleIndexBase(), false, AddressSize::b16, Source::None, DataSize::None, 0, 0) {}
|
||||
constexpr Instruction(
|
||||
Operation operation,
|
||||
Source source,
|
||||
Source destination,
|
||||
ScaleIndexBase sib,
|
||||
bool lock,
|
||||
AddressSize address_size,
|
||||
Source segment_override,
|
||||
DataSize data_size,
|
||||
DisplacementT displacement,
|
||||
ImmediateT operand) noexcept :
|
||||
operation_(operation),
|
||||
mem_exts_source_(uint8_t(
|
||||
(int(address_size) << 7) |
|
||||
(displacement ? 0x40 : 0x00) |
|
||||
(operand ? 0x20 : 0x00) |
|
||||
int(source) |
|
||||
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)),
|
||||
source_data_dest_sib_(uint16_t(
|
||||
(int(data_size) << 14) |
|
||||
(lock ? (1 << 13) : 0) |
|
||||
((uint8_t(sib) & 0xf8) << 2) |
|
||||
int(destination) |
|
||||
(destination == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)) {
|
||||
// Decisions on whether to include operand, displacement and/or size extension words
|
||||
// have implicitly been made in the int packing above; honour them here.
|
||||
int extension = 0;
|
||||
if(has_operand()) {
|
||||
extensions_[extension] = operand;
|
||||
++extension;
|
||||
}
|
||||
if(has_displacement()) {
|
||||
extensions_[extension] = ImmediateT(displacement);
|
||||
++extension;
|
||||
}
|
||||
|
||||
// Patch in a fully-resolved segment.
|
||||
Source segment = segment_override;
|
||||
if(segment == Source::None) segment = this->source().default_segment();
|
||||
if(segment == Source::None) segment = this->destination().default_segment();
|
||||
if(segment == Source::None) segment = Source::DS;
|
||||
source_data_dest_sib_ |= (int(segment)&7) << 10;
|
||||
constexpr Instruction() noexcept = default;
|
||||
constexpr Instruction(Operation operation) noexcept :
|
||||
Instruction(
|
||||
operation,
|
||||
Source::None,
|
||||
Source::None,
|
||||
ScaleIndexBase(),
|
||||
false,
|
||||
AddressSize::b16,
|
||||
Source::None,
|
||||
DataSize::None,
|
||||
0,
|
||||
0
|
||||
) {}
|
||||
constexpr Instruction(
|
||||
Operation operation,
|
||||
Source source,
|
||||
Source destination,
|
||||
ScaleIndexBase sib,
|
||||
bool lock,
|
||||
AddressSize address_size,
|
||||
Source segment_override,
|
||||
DataSize data_size,
|
||||
DisplacementT displacement,
|
||||
ImmediateT operand) noexcept :
|
||||
operation_(operation),
|
||||
mem_exts_source_(uint8_t(
|
||||
(int(address_size) << 7) |
|
||||
(displacement ? 0x40 : 0x00) |
|
||||
(operand ? 0x20 : 0x00) |
|
||||
int(source) |
|
||||
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)),
|
||||
source_data_dest_sib_(uint16_t(
|
||||
(int(data_size) << 14) |
|
||||
(lock ? (1 << 13) : 0) |
|
||||
((uint8_t(sib) & 0xf8) << 2) |
|
||||
int(destination) |
|
||||
(destination == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)) {
|
||||
// Decisions on whether to include operand, displacement and/or size extension words
|
||||
// have implicitly been made in the int packing above; honour them here.
|
||||
int extension = 0;
|
||||
if(has_operand()) {
|
||||
extensions_[extension] = operand;
|
||||
++extension;
|
||||
}
|
||||
if(has_displacement()) {
|
||||
extensions_[extension] = ImmediateT(displacement);
|
||||
++extension;
|
||||
}
|
||||
|
||||
/// @returns The number of bytes used for meaningful content within this class. A receiver must use at least @c sizeof(Instruction) bytes
|
||||
/// to store an @c Instruction but is permitted to reuse the trailing sizeof(Instruction) - packing_size() for any purpose it likes. Teleologically,
|
||||
/// this allows a denser packing of instructions into containers.
|
||||
constexpr size_t packing_size() const {
|
||||
return
|
||||
offsetof(Instruction<is_32bit>, extensions_) +
|
||||
(has_displacement() + has_operand()) * sizeof(ImmediateT);
|
||||
// Patch in a fully-resolved segment.
|
||||
Source segment = segment_override;
|
||||
if(segment == Source::None) segment = this->source().default_segment();
|
||||
if(segment == Source::None) segment = this->destination().default_segment();
|
||||
if(segment == Source::None) segment = Source::DS;
|
||||
source_data_dest_sib_ |= (int(segment)&7) << 10;
|
||||
}
|
||||
|
||||
/// @returns The number of bytes used for meaningful content within this class. A receiver must use at least @c sizeof(Instruction) bytes
|
||||
/// to store an @c Instruction but is permitted to reuse the trailing sizeof(Instruction) - packing_size() for any purpose it likes. Teleologically,
|
||||
/// this allows a denser packing of instructions into containers.
|
||||
constexpr size_t packing_size() const {
|
||||
return
|
||||
offsetof(Instruction<is_32bit>, extensions_) +
|
||||
(has_displacement() + has_operand()) * sizeof(ImmediateT);
|
||||
}
|
||||
|
||||
/// @returns The @c Operation performed by this instruction.
|
||||
constexpr Operation operation() const {
|
||||
return operation_;
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'destination' of this instruction, conventionally the first operand in Intel-syntax assembly.
|
||||
constexpr DataPointer destination() const {
|
||||
return DataPointer(
|
||||
Source(source_data_dest_sib_ & sib_masks[(source_data_dest_sib_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (source_data_dest_sib_ & 0x07)
|
||||
);
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'source' of this instruction, conventionally the second operand in Intel-syntax assembly.
|
||||
constexpr DataPointer source() const {
|
||||
return DataPointer(
|
||||
Source(mem_exts_source_ & sib_masks[(mem_exts_source_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (mem_exts_source_ & 0x07)
|
||||
);
|
||||
}
|
||||
|
||||
/// @returns @c true if the lock prefix was present on this instruction; @c false otherwise.
|
||||
constexpr bool lock() const {
|
||||
return source_data_dest_sib_ & (1 << 13);
|
||||
}
|
||||
|
||||
/// @returns The address size for this instruction; will always be 16-bit for instructions decoded by a 16-bit decoder but can be 16- or 32-bit for
|
||||
/// instructions decoded by a 32-bit decoder, depending on the program's use of the address size prefix byte.
|
||||
constexpr AddressSize address_size() const {
|
||||
return AddressSize(mem_exts_source_ >> 7);
|
||||
}
|
||||
|
||||
/// @returns The segment that should be used for data fetches if this operation accepts segment overrides.
|
||||
constexpr Source data_segment() const {
|
||||
return Source(
|
||||
int(Source::ES) +
|
||||
((source_data_dest_sib_ >> 10) & 7)
|
||||
);
|
||||
}
|
||||
|
||||
/// @returns The data size of this operation — e.g. `MOV AX, BX` has a data size of `::Word` but `MOV EAX, EBX` has a data size of
|
||||
/// `::DWord`. This value is guaranteed never to be `DataSize::None` even for operations such as `CLI` that don't have operands and operate
|
||||
/// on data that is not a byte, word or double word.
|
||||
constexpr DataSize operation_size() const {
|
||||
return DataSize(source_data_dest_sib_ >> 14);
|
||||
}
|
||||
|
||||
/// @returns The immediate value provided with this instruction, if any. E.g. `ADD AX, 23h` has the operand `23h`.
|
||||
constexpr ImmediateT operand() const {
|
||||
const ImmediateT ops[] = {0, operand_extension()};
|
||||
return ops[has_operand()];
|
||||
}
|
||||
|
||||
/// @returns The nesting level argument supplied to an ENTER.
|
||||
constexpr ImmediateT nesting_level() const {
|
||||
return operand();
|
||||
}
|
||||
|
||||
/// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will
|
||||
/// have a segment value of `1234h`.
|
||||
constexpr uint16_t segment() const {
|
||||
return uint16_t(operand());
|
||||
}
|
||||
|
||||
/// @returns The offset provided with this instruction, if any. E.g. `MOV AX, [es:1998h]` has an offset of `1998h`.
|
||||
constexpr ImmediateT offset() const {
|
||||
const ImmediateT offsets[] = {0, displacement_extension()};
|
||||
return offsets[has_displacement()];
|
||||
}
|
||||
|
||||
/// @returns The displacement provided with this instruction `SUB AX, [SI+BP-23h]` has an offset of `-23h` and `JMP 19h`
|
||||
/// has an offset of `19h`.
|
||||
constexpr DisplacementT displacement() const {
|
||||
return DisplacementT(offset());
|
||||
}
|
||||
|
||||
/// @returns The dynamic storage size argument supplied to an ENTER.
|
||||
constexpr ImmediateT dynamic_storage_size() const {
|
||||
return displacement();
|
||||
}
|
||||
|
||||
// Standard comparison operator.
|
||||
constexpr bool operator ==(const Instruction<is_32bit> &rhs) const {
|
||||
if( operation_ != rhs.operation_ ||
|
||||
mem_exts_source_ != rhs.mem_exts_source_ ||
|
||||
source_data_dest_sib_ != rhs.source_data_dest_sib_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @returns The @c Operation performed by this instruction.
|
||||
constexpr Operation operation() const {
|
||||
return operation_;
|
||||
// Have already established above that this and RHS have the
|
||||
// same extensions, if any.
|
||||
const int extension_count = has_displacement() + has_operand();
|
||||
for(int c = 0; c < extension_count; c++) {
|
||||
if(extensions_[c] != rhs.extensions_[c]) return false;
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'destination' of this instruction, conventionally the first operand in Intel-syntax assembly.
|
||||
constexpr DataPointer destination() const {
|
||||
return DataPointer(
|
||||
Source(source_data_dest_sib_ & sib_masks[(source_data_dest_sib_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (source_data_dest_sib_ & 0x07)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// @returns A @c DataPointer describing the 'source' of this instruction, conventionally the second operand in Intel-syntax assembly.
|
||||
constexpr DataPointer source() const {
|
||||
return DataPointer(
|
||||
Source(mem_exts_source_ & sib_masks[(mem_exts_source_ >> 3) & 3]),
|
||||
((source_data_dest_sib_ >> 2) & 0xf8) | (mem_exts_source_ & 0x07)
|
||||
);
|
||||
}
|
||||
private:
|
||||
Operation operation_ = Operation::Invalid;
|
||||
|
||||
/// @returns @c true if the lock prefix was present on this instruction; @c false otherwise.
|
||||
constexpr bool lock() const {
|
||||
return source_data_dest_sib_ & (1 << 13);
|
||||
}
|
||||
// Packing and encoding of fields is admittedly somewhat convoluted; what this
|
||||
// achieves is that instructions will be sized:
|
||||
//
|
||||
// four bytes + up to two extension words
|
||||
// (extension words being two bytes for 16-bit instructions, four for 32)
|
||||
//
|
||||
// The extension words are used to retain an operand and displacement
|
||||
// if the instruction has those.
|
||||
|
||||
/// @returns The address size for this instruction; will always be 16-bit for instructions decoded by a 16-bit decoder but can be 16- or 32-bit for
|
||||
/// instructions decoded by a 32-bit decoder, depending on the program's use of the address size prefix byte.
|
||||
constexpr AddressSize address_size() const {
|
||||
return AddressSize(mem_exts_source_ >> 7);
|
||||
}
|
||||
// b7: address size;
|
||||
// b6: has displacement;
|
||||
// b5: has operand;
|
||||
// [b4, b0]: source.
|
||||
uint8_t mem_exts_source_ = 0;
|
||||
|
||||
/// @returns The segment that should be used for data fetches if this operation accepts segment overrides.
|
||||
constexpr Source data_segment() const {
|
||||
return Source(
|
||||
int(Source::ES) +
|
||||
((source_data_dest_sib_ >> 10) & 7)
|
||||
);
|
||||
}
|
||||
bool has_displacement() const {
|
||||
return mem_exts_source_ & (1 << 6);
|
||||
}
|
||||
bool has_operand() const {
|
||||
return mem_exts_source_ & (1 << 5);
|
||||
}
|
||||
|
||||
/// @returns The data size of this operation — e.g. `MOV AX, BX` has a data size of `::Word` but `MOV EAX, EBX` has a data size of
|
||||
/// `::DWord`. This value is guaranteed never to be `DataSize::None` even for operations such as `CLI` that don't have operands and operate
|
||||
/// on data that is not a byte, word or double word.
|
||||
constexpr DataSize operation_size() const {
|
||||
return DataSize(source_data_dest_sib_ >> 14);
|
||||
}
|
||||
// [b15, b14]: data size;
|
||||
// [b13]: lock;
|
||||
// [b12, b10]: segment override;
|
||||
// [b9, b5]: top five of SIB;
|
||||
// [b4, b0]: dest.
|
||||
uint16_t source_data_dest_sib_ = 0;
|
||||
|
||||
/// @returns The immediate value provided with this instruction, if any. E.g. `ADD AX, 23h` has the operand `23h`.
|
||||
constexpr ImmediateT operand() const {
|
||||
const ImmediateT ops[] = {0, operand_extension()};
|
||||
return ops[has_operand()];
|
||||
}
|
||||
// {operand}, {displacement}.
|
||||
ImmediateT extensions_[2]{};
|
||||
|
||||
/// @returns The nesting level argument supplied to an ENTER.
|
||||
constexpr ImmediateT nesting_level() const {
|
||||
return operand();
|
||||
}
|
||||
|
||||
/// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will
|
||||
/// have a segment value of `1234h`.
|
||||
constexpr uint16_t segment() const {
|
||||
return uint16_t(operand());
|
||||
}
|
||||
|
||||
/// @returns The offset provided with this instruction, if any. E.g. `MOV AX, [es:1998h]` has an offset of `1998h`.
|
||||
constexpr ImmediateT offset() const {
|
||||
const ImmediateT offsets[] = {0, displacement_extension()};
|
||||
return offsets[has_displacement()];
|
||||
}
|
||||
|
||||
/// @returns The displacement provided with this instruction `SUB AX, [SI+BP-23h]` has an offset of `-23h` and `JMP 19h`
|
||||
/// has an offset of `19h`.
|
||||
constexpr DisplacementT displacement() const {
|
||||
return DisplacementT(offset());
|
||||
}
|
||||
|
||||
/// @returns The dynamic storage size argument supplied to an ENTER.
|
||||
constexpr ImmediateT dynamic_storage_size() const {
|
||||
return displacement();
|
||||
}
|
||||
|
||||
// Standard comparison operator.
|
||||
constexpr bool operator ==(const Instruction<is_32bit> &rhs) const {
|
||||
if( operation_ != rhs.operation_ ||
|
||||
mem_exts_source_ != rhs.mem_exts_source_ ||
|
||||
source_data_dest_sib_ != rhs.source_data_dest_sib_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Have already established above that this and RHS have the
|
||||
// same extensions, if any.
|
||||
const int extension_count = has_displacement() + has_operand();
|
||||
for(int c = 0; c < extension_count; c++) {
|
||||
if(extensions_[c] != rhs.extensions_[c]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Operation operation_ = Operation::Invalid;
|
||||
|
||||
// Packing and encoding of fields is admittedly somewhat convoluted; what this
|
||||
// achieves is that instructions will be sized:
|
||||
//
|
||||
// four bytes + up to two extension words
|
||||
// (extension words being two bytes for 16-bit instructions, four for 32)
|
||||
//
|
||||
// The extension words are used to retain an operand and displacement
|
||||
// if the instruction has those.
|
||||
|
||||
// b7: address size;
|
||||
// b6: has displacement;
|
||||
// b5: has operand;
|
||||
// [b4, b0]: source.
|
||||
uint8_t mem_exts_source_ = 0;
|
||||
|
||||
bool has_displacement() const {
|
||||
return mem_exts_source_ & (1 << 6);
|
||||
}
|
||||
bool has_operand() const {
|
||||
return mem_exts_source_ & (1 << 5);
|
||||
}
|
||||
|
||||
// [b15, b14]: data size;
|
||||
// [b13]: lock;
|
||||
// [b12, b10]: segment override;
|
||||
// [b9, b5]: top five of SIB;
|
||||
// [b4, b0]: dest.
|
||||
uint16_t source_data_dest_sib_ = 0;
|
||||
|
||||
// {operand}, {displacement}.
|
||||
ImmediateT extensions_[2]{};
|
||||
|
||||
ImmediateT operand_extension() const {
|
||||
return extensions_[0];
|
||||
}
|
||||
ImmediateT displacement_extension() const {
|
||||
return extensions_[(mem_exts_source_ >> 5) & 1];
|
||||
}
|
||||
|
||||
// A lookup table to help with stripping parts of the SIB that have been
|
||||
// hidden within the source/destination fields.
|
||||
static constexpr uint8_t sib_masks[] = {
|
||||
0x1f, 0x1f, 0x1f, 0x18
|
||||
};
|
||||
ImmediateT operand_extension() const {
|
||||
return extensions_[0];
|
||||
}
|
||||
ImmediateT displacement_extension() const {
|
||||
return extensions_[(mem_exts_source_ >> 5) & 1];
|
||||
}
|
||||
|
||||
// A lookup table to help with stripping parts of the SIB that have been
|
||||
// hidden within the source/destination fields.
|
||||
static constexpr uint8_t sib_masks[] = {
|
||||
0x1f, 0x1f, 0x1f, 0x18
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(Instruction<true>) <= 16);
|
||||
@ -962,5 +972,4 @@ std::string to_string(
|
||||
Model model,
|
||||
int offset_length = 0,
|
||||
int immediate_length = 0);
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ enum class Model {
|
||||
i80386,
|
||||
};
|
||||
|
||||
static constexpr bool is_32bit(Model model) { return model >= Model::i80386; }
|
||||
static constexpr bool is_32bit(const Model model) { return model >= Model::i80386; }
|
||||
|
||||
template <bool is_32bit> struct AddressT { using type = uint16_t; };
|
||||
template <> struct AddressT<true> { using type = uint32_t; };
|
||||
|
@ -35,7 +35,7 @@ enum Key: uint16_t {
|
||||
KeyBreak = 0xfffd,
|
||||
};
|
||||
|
||||
constexpr bool is_modifier(Key key) {
|
||||
constexpr bool is_modifier(const Key key) {
|
||||
return (key == KeyShift) || (key == KeyControl) || (key == KeyFunc);
|
||||
}
|
||||
|
||||
|
@ -25,8 +25,8 @@ enum class GraphicsMode {
|
||||
/// Fat low res mode is regular low res mode, but clocked out at 7Mhz rather than 14, leading to improper colours.
|
||||
FatLowRes
|
||||
};
|
||||
constexpr bool is_text_mode(GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
|
||||
constexpr bool is_double_mode(GraphicsMode m) { return int(m) & 1; }
|
||||
constexpr bool is_text_mode(const GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
|
||||
constexpr bool is_double_mode(const GraphicsMode m) { return int(m) & 1; }
|
||||
|
||||
template <typename TimeUnit> class VideoSwitches {
|
||||
public:
|
||||
|
@ -86,7 +86,9 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
|
||||
DoubleHighResMono,
|
||||
SuperHighRes
|
||||
};
|
||||
constexpr bool is_colour_ntsc(GraphicsMode m) { return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes; }
|
||||
constexpr bool is_colour_ntsc(const GraphicsMode m) {
|
||||
return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes;
|
||||
}
|
||||
|
||||
GraphicsMode graphics_mode(int row) const {
|
||||
if(new_video_ & 0x80) {
|
||||
|
@ -59,7 +59,7 @@ enum class Source {
|
||||
WDFDC,
|
||||
};
|
||||
|
||||
constexpr bool is_enabled(Source source) {
|
||||
constexpr bool is_enabled(const Source source) {
|
||||
#ifdef NDEBUG
|
||||
return false;
|
||||
#endif
|
||||
@ -133,39 +133,39 @@ constexpr const char *prefix(Source source) {
|
||||
|
||||
template <Source source>
|
||||
class Logger {
|
||||
public:
|
||||
static constexpr bool enabled = is_enabled(source);
|
||||
public:
|
||||
static constexpr bool enabled = is_enabled(source);
|
||||
|
||||
struct LogLine {
|
||||
public:
|
||||
LogLine(FILE *stream) : stream_(stream) {
|
||||
if constexpr (!enabled) return;
|
||||
struct LogLine {
|
||||
public:
|
||||
LogLine(FILE *const stream) : stream_(stream) {
|
||||
if constexpr (!enabled) return;
|
||||
|
||||
const auto source_prefix = prefix(source);
|
||||
if(source_prefix) {
|
||||
fprintf(stream_, "[%s] ", source_prefix);
|
||||
}
|
||||
const auto source_prefix = prefix(source);
|
||||
if(source_prefix) {
|
||||
fprintf(stream_, "[%s] ", source_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
~LogLine() {
|
||||
if constexpr (!enabled) return;
|
||||
fprintf(stream_, "\n");
|
||||
}
|
||||
~LogLine() {
|
||||
if constexpr (!enabled) return;
|
||||
fprintf(stream_, "\n");
|
||||
}
|
||||
|
||||
void append(const char *format, ...) {
|
||||
if constexpr (!enabled) return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stream_, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
void append(const char *const format, ...) {
|
||||
if constexpr (!enabled) return;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stream_, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE *stream_;
|
||||
};
|
||||
private:
|
||||
FILE *stream_;
|
||||
};
|
||||
|
||||
LogLine info() { return LogLine(stdout); }
|
||||
LogLine error() { return LogLine(stderr); }
|
||||
LogLine info() { return LogLine(stdout); }
|
||||
LogLine error() { return LogLine(stderr); }
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ enum class DisplayType {
|
||||
CompositeMonochrome
|
||||
};
|
||||
|
||||
constexpr bool is_composite(DisplayType type) {
|
||||
constexpr bool is_composite(const DisplayType type) {
|
||||
return type == DisplayType::CompositeColour || type == DisplayType::CompositeMonochrome;
|
||||
}
|
||||
|
||||
|
@ -38,10 +38,10 @@ enum Personality {
|
||||
PWDC65C02, // like the Rockwell, but with STP and WAI
|
||||
};
|
||||
|
||||
constexpr bool has_decimal_mode(Personality p) { return p >= Personality::P6502; }
|
||||
constexpr bool is_65c02(Personality p) { return p >= Personality::PSynertek65C02; }
|
||||
constexpr bool has_bbrbbsrmbsmb(Personality p) { return p >= Personality::PRockwell65C02; }
|
||||
constexpr bool has_stpwai(Personality p) { return p >= Personality::PWDC65C02; }
|
||||
constexpr bool has_decimal_mode(const Personality p) { return p >= Personality::P6502; }
|
||||
constexpr bool is_65c02(const Personality p) { return p >= Personality::PSynertek65C02; }
|
||||
constexpr bool has_bbrbbsrmbsmb(const Personality p) { return p >= Personality::PRockwell65C02; }
|
||||
constexpr bool has_stpwai(const Personality p) { return p >= Personality::PWDC65C02; }
|
||||
|
||||
/*!
|
||||
An opcode that is guaranteed to cause a 6502 to jam.
|
||||
|
@ -48,7 +48,7 @@ template <> class BusHandlerT<Type::TWDC65816>: public BusHandler<uint32_t> {};
|
||||
/*
|
||||
Query for implemented registers.
|
||||
*/
|
||||
constexpr bool has(Type processor_type, Register r) {
|
||||
constexpr bool has(const Type processor_type, const Register r) {
|
||||
switch(r) {
|
||||
case Register::LastOperationAddress:
|
||||
case Register::ProgramCounter:
|
||||
@ -67,7 +67,7 @@ constexpr bool has(Type processor_type, Register r) {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool has_extended_bus_output(Type processor_type) {
|
||||
constexpr bool has_extended_bus_output(const Type processor_type) {
|
||||
return processor_type == Type::TWDC65816;
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ class ProcessorStorage {
|
||||
void *destination = nullptr;
|
||||
PartialMachineCycle machine_cycle{};
|
||||
};
|
||||
static constexpr bool is_terminal(MicroOp::Type type) {
|
||||
static constexpr bool is_terminal(const MicroOp::Type type) {
|
||||
return type == MicroOp::MoveToNextProgram || type == MicroOp::DecodeOperation;
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ enum class Density {
|
||||
Single, Double, High
|
||||
};
|
||||
|
||||
constexpr bool is_mfm(Density density) {
|
||||
constexpr bool is_mfm(const Density density) {
|
||||
return density != Density::Single;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user