1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-13 22:32:03 +00:00

Improve constness, formatting.

This commit is contained in:
Thomas Harte 2024-12-01 17:51:20 -05:00
parent 8b88d1294d
commit 3a0f4a0bfc
34 changed files with 857 additions and 778 deletions

View File

@ -50,7 +50,7 @@ struct Target: public Analyser::Static::Target, public Reflection::StructImpl<Ta
}
};
constexpr bool is_iie(Target::Model model) {
constexpr bool is_iie(const Target::Model model) {
return model == Target::Model::IIe || model == Target::Model::EnhancedIIe;
}

View File

@ -43,7 +43,7 @@ enum class Personality {
EGA, // Extended EGA-style CRTC; uses 16-bit addressing throughout.
};
constexpr bool is_egavga(Personality p) {
constexpr bool is_egavga(const Personality p) {
return p >= Personality::EGA;
}

View File

@ -13,7 +13,7 @@
namespace InstructionSet {
namespace M50740 {
Instruction Decoder::instrucion_for_opcode(uint8_t opcode) {
Instruction Decoder::instrucion_for_opcode(const uint8_t opcode) {
switch(opcode) {
default: return Instruction(opcode);
@ -231,7 +231,7 @@ Instruction Decoder::instrucion_for_opcode(uint8_t opcode) {
}
}
std::pair<int, InstructionSet::M50740::Instruction> Decoder::decode(const uint8_t *source, size_t length) {
std::pair<int, InstructionSet::M50740::Instruction> Decoder::decode(const uint8_t *source, const size_t length) {
const uint8_t *const end = source + length;
if(phase_ == Phase::Instruction && source != end) {

View File

@ -16,19 +16,19 @@
namespace InstructionSet::M50740 {
class Decoder {
public:
std::pair<int, Instruction> decode(const uint8_t *source, size_t length);
Instruction instrucion_for_opcode(uint8_t opcode);
public:
std::pair<int, Instruction> decode(const uint8_t *, size_t length);
Instruction instrucion_for_opcode(uint8_t);
private:
enum class Phase {
Instruction,
AwaitingOperand,
ReadyToPost
} phase_ = Phase::Instruction;
int operand_size_ = 0, operand_bytes_ = 0;
int consumed_ = 0;
Instruction instr_;
private:
enum class Phase {
Instruction,
AwaitingOperand,
ReadyToPost
} phase_ = Phase::Instruction;
int operand_size_ = 0, operand_bytes_ = 0;
int consumed_ = 0;
Instruction instr_;
};
}

View File

@ -49,7 +49,7 @@ void Executor::set_rom(const std::vector<uint8_t> &rom) {
reset();
}
void Executor::run_for(Cycles cycles) {
void Executor::run_for(const Cycles cycles) {
// The incoming clock is divided by four; the local cycles_ count
// ensures that fractional parts are kept track of.
cycles_ += cycles;
@ -61,7 +61,7 @@ void Executor::reset() {
set_program_counter(uint16_t(memory_[0x1ffe] | (memory_[0x1fff] << 8)));
}
void Executor::set_interrupt_line(bool line) {
void Executor::set_interrupt_line(const bool line) {
// Super hack: interrupt now, if permitted. Otherwise do nothing.
// So this will fail to catch enabling of interrupts while the line
// is active, amongst other things.
@ -117,12 +117,12 @@ uint8_t Executor::read(uint16_t address) {
}
}
void Executor::set_port_output(int port) {
void Executor::set_port_output(const int port) {
// Force 'output' to a 1 anywhere that a bit is set as input.
port_handler_.set_port_output(port, port_outputs_[port] | ~port_directions_[port]);
}
void Executor::write(uint16_t address, uint8_t value) {
void Executor::write(uint16_t address, const uint8_t value) {
address &= 0x1fff;
// RAM writes are easy.
@ -182,7 +182,7 @@ void Executor::write(uint16_t address, uint8_t value) {
}
}
void Executor::push(uint8_t value) {
void Executor::push(const uint8_t value) {
write(s_, value);
--s_;
}
@ -192,7 +192,7 @@ uint8_t Executor::pull() {
return read(s_);
}
void Executor::set_flags(uint8_t flags) {
void Executor::set_flags(const uint8_t flags) {
negative_result_ = flags;
overflow_result_ = uint8_t(flags << 1);
index_mode_ = flags & 0x20;
@ -213,7 +213,7 @@ uint8_t Executor::flags() {
carry_flag_;
}
template<bool is_brk> inline void Executor::perform_interrupt(uint16_t vector) {
template<bool is_brk> inline void Executor::perform_interrupt(const uint16_t vector) {
// BRK has an unused operand.
++program_counter_;
push(uint8_t(program_counter_ >> 8));
@ -222,7 +222,7 @@ template<bool is_brk> inline void Executor::perform_interrupt(uint16_t vector) {
set_program_counter(uint16_t(memory_[vector] | (memory_[vector+1] << 8)));
}
void Executor::set_interrupt_request(uint8_t &reg, uint8_t value, uint16_t vector) {
void Executor::set_interrupt_request(uint8_t &reg, const uint8_t value, const uint16_t vector) {
// TODO: this allows interrupts through only if fully enabled at the time they
// signal. Which isn't quite correct, albeit that it seems sufficient for the
// IIgs ADB controller.
@ -797,7 +797,7 @@ template <Operation operation> void Executor::perform(uint8_t *operand [[maybe_u
}
}
inline void Executor::subtract_duration(int duration) {
inline void Executor::subtract_duration(const int duration) {
// Pass along.
CachingExecutor::subtract_duration(duration);
@ -846,7 +846,7 @@ inline void Executor::subtract_duration(int duration) {
}
}
inline int Executor::update_timer(Timer &timer, int count) {
inline int Executor::update_timer(Timer &timer, const int count) {
const int next_value = timer.value - count;
if(next_value < 0) {
// Determine how many reloads were required to get above zero.
@ -859,6 +859,6 @@ inline int Executor::update_timer(Timer &timer, int count) {
return 0;
}
uint8_t Executor::get_output_mask(int port) {
uint8_t Executor::get_output_mask(const int port) {
return port_directions_[port];
}

View File

@ -35,143 +35,142 @@ struct PortHandler {
* timing is correct to whole-opcode boundaries only.
*/
class Executor: public CachingExecutor {
public:
Executor(PortHandler &);
void set_rom(const std::vector<uint8_t> &rom);
public:
Executor(PortHandler &);
void set_rom(const std::vector<uint8_t> &rom);
void reset();
void set_interrupt_line(bool);
void reset();
void set_interrupt_line(bool);
uint8_t get_output_mask(int port);
uint8_t get_output_mask(int port);
/*!
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
*/
void run_for(Cycles cycles);
/*!
Runs, in discrete steps, the minimum number of instructions as it takes to complete at least @c cycles.
*/
void run_for(Cycles);
private:
// MARK: - CachingExecutor-facing interface.
private:
// MARK: - CachingExecutor-facing interface.
friend CachingExecutor;
friend CachingExecutor;
/*!
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
*/
inline PerformerIndex action_for(Instruction instruction) {
// This is a super-simple processor, so the opcode can be used directly to index the performers.
return instruction.opcode;
}
/*!
Maps instructions to performers; called by the CachingExecutor and for this instruction set, extremely trivial.
*/
inline PerformerIndex action_for(const Instruction instruction) {
// This is a super-simple processor, so the opcode can be used directly to index the performers.
return instruction.opcode;
}
/*!
Parses from @c start and no later than @c max_address, using the CachingExecutor as a target.
*/
inline void parse(uint16_t start, uint16_t closing_bound) {
Parser<Executor, false> parser;
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
}
/*!
Parses from @c start and no later than @c max_address, using the CachingExecutor as a target.
*/
inline void parse(const uint16_t start, const uint16_t closing_bound) {
Parser<Executor, false> parser;
parser.parse(*this, &memory_[0], start & 0x1fff, closing_bound);
}
private:
// MARK: - Internal framework for generator performers.
// MARK: - Internal framework for generator performers.
/*!
Provides dynamic lookup of @c perform(Executor*).
*/
class PerformerLookup {
public:
PerformerLookup() {
fill<int(MinOperation)>(performers_);
/*!
Provides dynamic lookup of @c perform(Executor*).
*/
class PerformerLookup {
public:
PerformerLookup() {
fill<int(MinOperation)>(performers_);
}
Performer performer(const Operation operation, const AddressingMode addressing_mode) {
const auto index =
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
(int(addressing_mode) - MinAddressingMode);
return performers_[index];
}
private:
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
fill_operation<operation, addressing_mode+1>(target + 1);
}
}
Performer performer(Operation operation, AddressingMode addressing_mode) {
const auto index =
(int(operation) - MinOperation) * (1 + MaxAddressingMode - MinAddressingMode) +
(int(addressing_mode) - MinAddressingMode);
return performers_[index];
template<int operation> void fill(Performer *target) {
fill_operation<operation, int(MinAddressingMode)>(target);
target += 1 + MaxAddressingMode - MinAddressingMode;
if constexpr (operation+1 <= MaxOperation) {
fill<operation+1>(target);
}
}
};
inline static PerformerLookup performer_lookup_;
private:
Performer performers_[(1 + MaxAddressingMode - MinAddressingMode) * (1 + MaxOperation - MinOperation)];
/*!
Performs @c operation using @c operand as the value fetched from memory, if any.
*/
template <Operation operation> void perform(uint8_t *operand);
template<int operation, int addressing_mode> void fill_operation(Performer *target) {
*target = &Executor::perform<Operation(operation), AddressingMode(addressing_mode)>;
/*!
Performs @c operation in @c addressing_mode.
*/
template <Operation operation, AddressingMode addressing_mode> void perform();
if constexpr (addressing_mode+1 <= MaxAddressingMode) {
fill_operation<operation, addressing_mode+1>(target + 1);
}
}
private:
// MARK: - Instruction set state.
template<int operation> void fill(Performer *target) {
fill_operation<operation, int(MinAddressingMode)>(target);
target += 1 + MaxAddressingMode - MinAddressingMode;
// Memory.
std::array<uint8_t, 0x2000> memory_;
if constexpr (operation+1 <= MaxOperation) {
fill<operation+1>(target);
}
}
};
inline static PerformerLookup performer_lookup_;
// Registers.
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
/*!
Performs @c operation using @c operand as the value fetched from memory, if any.
*/
template <Operation operation> void perform(uint8_t *operand);
uint8_t negative_result_ = 0;
uint8_t zero_result_ = 0;
uint8_t interrupt_disable_ = 0x04;
uint8_t carry_flag_ = 0;
uint8_t overflow_result_ = 0;
bool index_mode_ = false;
bool decimal_mode_ = false;
/*!
Performs @c operation in @c addressing_mode.
*/
template <Operation operation, AddressingMode addressing_mode> void perform();
// IO ports.
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
private:
// MARK: - Instruction set state.
// Timers.
struct Timer {
uint8_t value = 0xff, reload_value = 0xff;
};
int timer_divider_ = 0;
Timer timers_[3], prescalers_[2];
inline int update_timer(Timer &timer, int count);
// Memory.
std::array<uint8_t, 0x2000> memory_;
// Interrupt and timer control.
uint8_t interrupt_control_ = 0, timer_control_ = 0;
bool interrupt_line_ = false;
// Registers.
uint8_t a_ = 0, x_ = 0, y_ = 0, s_ = 0;
// Access helpers.
inline uint8_t read(uint16_t address);
inline void write(uint16_t address, uint8_t value);
inline void push(uint8_t);
inline uint8_t pull();
inline void set_flags(uint8_t);
inline uint8_t flags();
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
inline void set_port_output(int port);
uint8_t negative_result_ = 0;
uint8_t zero_result_ = 0;
uint8_t interrupt_disable_ = 0x04;
uint8_t carry_flag_ = 0;
uint8_t overflow_result_ = 0;
bool index_mode_ = false;
bool decimal_mode_ = false;
void set_interrupt_request(uint8_t &reg, uint8_t value, uint16_t vector);
// IO ports.
uint8_t port_directions_[4] = {0x00, 0x00, 0x00, 0x00};
uint8_t port_outputs_[4] = {0xff, 0xff, 0xff, 0xff};
// MARK: - Execution time
// Timers.
struct Timer {
uint8_t value = 0xff, reload_value = 0xff;
};
int timer_divider_ = 0;
Timer timers_[3], prescalers_[2];
inline int update_timer(Timer &timer, int count);
// Interrupt and timer control.
uint8_t interrupt_control_ = 0, timer_control_ = 0;
bool interrupt_line_ = false;
// Access helpers.
inline uint8_t read(uint16_t address);
inline void write(uint16_t address, uint8_t value);
inline void push(uint8_t value);
inline uint8_t pull();
inline void set_flags(uint8_t);
inline uint8_t flags();
template<bool is_brk> inline void perform_interrupt(uint16_t vector);
inline void set_port_output(int port);
void set_interrupt_request(uint8_t &reg, uint8_t value, uint16_t vector);
// MARK: - Execution time
Cycles cycles_;
Cycles cycles_since_port_handler_;
PortHandler &port_handler_;
inline void subtract_duration(int duration);
Cycles cycles_;
Cycles cycles_since_port_handler_;
PortHandler &port_handler_;
inline void subtract_duration(int);
};
}

View File

@ -31,7 +31,7 @@ enum class AddressingMode {
static constexpr auto MaxAddressingMode = int(AddressingMode::ZeroPageRelative);
static constexpr auto MinAddressingMode = int(AddressingMode::Implied);
constexpr int size(AddressingMode mode) {
constexpr int size(const AddressingMode mode) {
// This is coupled to the AddressingMode list above; be careful!
constexpr int sizes[] = {
0, 0, 1,
@ -92,14 +92,14 @@ enum class Operation: uint8_t {
static constexpr auto MaxOperation = int(Operation::STY);
static constexpr auto MinOperation = int(Operation::BBC0);
constexpr AccessType access_type(Operation operation) {
constexpr AccessType access_type(const Operation operation) {
if(operation < Operation::ADC) return AccessType::None;
if(operation < Operation::ASL) return AccessType::Read;
if(operation < Operation::LDM) return AccessType::ReadModifyWrite;
return AccessType::Write;
}
constexpr bool uses_index_mode(Operation operation) {
constexpr bool uses_index_mode(const Operation operation) {
return
operation == Operation::ADC || operation == Operation::AND ||
operation == Operation::CMP || operation == Operation::EOR ||
@ -110,7 +110,7 @@ constexpr bool uses_index_mode(Operation operation) {
/*!
@returns The name of @c operation.
*/
inline constexpr const char *operation_name(Operation operation) {
inline constexpr const char *operation_name(const Operation operation) {
#define MAP(x) case Operation::x: return #x;
switch(operation) {
default: break;
@ -133,7 +133,7 @@ inline constexpr const char *operation_name(Operation operation) {
return "???";
}
inline std::ostream &operator <<(std::ostream &stream, Operation operation) {
inline std::ostream &operator <<(std::ostream &stream, const Operation operation) {
stream << operation_name(operation);
return stream;
}
@ -141,7 +141,7 @@ inline std::ostream &operator <<(std::ostream &stream, Operation operation) {
/*!
@returns The name of @c addressing_mode.
*/
inline constexpr const char *addressing_mode_name(AddressingMode addressing_mode) {
inline constexpr const char *addressing_mode_name(const AddressingMode addressing_mode) {
switch(addressing_mode) {
default: break;
case AddressingMode::Implied: return "";
@ -167,7 +167,7 @@ inline constexpr const char *addressing_mode_name(AddressingMode addressing_mode
return "???";
}
inline std::ostream &operator <<(std::ostream &stream, AddressingMode mode) {
inline std::ostream &operator <<(std::ostream &stream, const AddressingMode mode) {
stream << addressing_mode_name(mode);
return stream;
}
@ -177,7 +177,11 @@ inline std::ostream &operator <<(std::ostream &stream, AddressingMode mode) {
would appear in an assembler. E.g. '$5a' for that zero page address, or '$5a, x' for zero-page indexed from $5a. This function
may access up to three bytes from @c operation onwards.
*/
inline std::string address(AddressingMode addressing_mode, const uint8_t *operation, uint16_t program_counter) {
inline std::string address(
const AddressingMode addressing_mode,
const uint8_t *operation,
const uint16_t program_counter
) {
std::stringstream output;
output << std::hex;
@ -220,7 +224,8 @@ struct Instruction {
AddressingMode addressing_mode = AddressingMode::Implied;
uint8_t opcode = 0;
Instruction(Operation operation, AddressingMode addressing_mode, uint8_t opcode) : operation(operation), addressing_mode(addressing_mode), opcode(opcode) {}
Instruction(const Operation operation, const AddressingMode addressing_mode, const uint8_t opcode) :
operation(operation), addressing_mode(addressing_mode), opcode(opcode) {}
Instruction(uint8_t opcode) : opcode(opcode) {}
Instruction() = default;
};

View File

@ -15,7 +15,7 @@
namespace InstructionSet::M50740 {
template<typename Target, bool include_entries_and_accesses> struct Parser {
void parse(Target &target, const uint8_t *storage, uint16_t start, uint16_t closing_bound) {
void parse(Target &target, const uint8_t *storage, uint16_t start, const uint16_t closing_bound) {
Decoder decoder;
while(start <= closing_bound) {
@ -97,7 +97,10 @@ template<typename Target, bool include_entries_and_accesses> struct Parser {
// Provide any fixed address accesses.
switch(next.second.addressing_mode) {
case AddressingMode::Absolute:
target.add_access(uint16_t(storage[start + 1] | (storage[start + 2] << 8)), access_type(next.second.operation));
target.add_access(
uint16_t(storage[start + 1] | (storage[start + 2] << 8)),
access_type(next.second.operation)
);
break;
case AddressingMode::ZeroPage: case AddressingMode::ZeroPageRelative:
target.add_access(storage[start + 1], access_type(next.second.operation));

View File

@ -12,7 +12,8 @@ using namespace InstructionSet::PowerPC;
namespace {
template <Model model, bool validate_reserved_bits, Operation operation> Instruction instruction(uint32_t opcode, bool is_supervisor = false) {
template <Model model, bool validate_reserved_bits, Operation operation>
Instruction instruction(const uint32_t opcode, const bool is_supervisor = false) {
// If validation isn't required, there's nothing to do here.
if constexpr (!validate_reserved_bits) {
return Instruction(operation, opcode, is_supervisor);
@ -243,7 +244,7 @@ template <Model model, bool validate_reserved_bits, Operation operation> Instruc
}
template <Model model, bool validate_reserved_bits>
Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
Instruction Decoder<model, validate_reserved_bits>::decode(const uint32_t opcode) {
// Quick bluffer's guide to PowerPC instruction encoding:
//
// There is a six-bit field at the very top of the instruction.
@ -332,23 +333,29 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
default: break;
// 64-bit instructions.
BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux); BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux);
BindConditional(is64bit, SixTen(0b011111, 0b0000001001), mulhdux);
BindConditional(is64bit, SixTen(0b011111, 0b1000001001), mulhdux);
BindConditional(is64bit, SixTen(0b011111, 0b0000010101), ldx);
BindConditional(is64bit, SixTen(0b011111, 0b0000011011), sldx);
BindConditional(is64bit, SixTen(0b011111, 0b0000110101), ldux);
BindConditional(is64bit, SixTen(0b011111, 0b0000111010), cntlzdx);
BindConditional(is64bit, SixTen(0b011111, 0b0001000100), td);
BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx); BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx);
BindConditional(is64bit, SixTen(0b011111, 0b0001001001), mulhdx);
BindConditional(is64bit, SixTen(0b011111, 0b1001001001), mulhdx);
BindConditional(is64bit, SixTen(0b011111, 0b0001010100), ldarx);
BindConditional(is64bit, SixTen(0b011111, 0b0010010101), stdx);
BindConditional(is64bit, SixTen(0b011111, 0b0010110101), stdux);
BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulldx); BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulldx);
BindConditional(is64bit, SixTen(0b011111, 0b0011101001), mulldx);
BindConditional(is64bit, SixTen(0b011111, 0b1011101001), mulldx);
BindConditional(is64bit, SixTen(0b011111, 0b0101010101), lwax);
BindConditional(is64bit, SixTen(0b011111, 0b0101110101), lwaux);
BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix); BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix);
BindConditional(is64bit, SixTen(0b011111, 0b1100111011), sradix);
BindConditional(is64bit, SixTen(0b011111, 0b1100111010), sradix);
BindConditional(is64bit, SixTen(0b011111, 0b0110110010), slbie);
BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux); BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux);
BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx); BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx);
BindConditional(is64bit, SixTen(0b011111, 0b0111001001), divdux);
BindConditional(is64bit, SixTen(0b011111, 0b1111001001), divdux);
BindConditional(is64bit, SixTen(0b011111, 0b0111101001), divdx);
BindConditional(is64bit, SixTen(0b011111, 0b1111101001), divdx);
BindConditional(is64bit, SixTen(0b011111, 0b1000011011), srdx);
BindConditional(is64bit, SixTen(0b011111, 0b1100011010), sradx);
BindConditional(is64bit, SixTen(0b111111, 0b1111011010), extswx);
@ -356,16 +363,22 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
// Power instructions; these are all taken from the MPC601 manual rather than
// the PowerPC Programmer's Reference Guide, hence the decimal encoding of the
// ten-bit field.
BindConditional(is601, SixTen(0b011111, 360), absx); BindConditional(is601, SixTen(0b011111, 512 + 360), absx);
BindConditional(is601, SixTen(0b011111, 360), absx);
BindConditional(is601, SixTen(0b011111, 512 + 360), absx);
BindConditional(is601, SixTen(0b011111, 531), clcs);
BindConditional(is601, SixTen(0b011111, 331), divx); BindConditional(is601, SixTen(0b011111, 512 + 331), divx);
BindConditional(is601, SixTen(0b011111, 363), divsx); BindConditional(is601, SixTen(0b011111, 512 + 363), divsx);
BindConditional(is601, SixTen(0b011111, 264), dozx); BindConditional(is601, SixTen(0b011111, 512 + 264), dozx);
BindConditional(is601, SixTen(0b011111, 331), divx);
BindConditional(is601, SixTen(0b011111, 512 + 331), divx);
BindConditional(is601, SixTen(0b011111, 363), divsx);
BindConditional(is601, SixTen(0b011111, 512 + 363), divsx);
BindConditional(is601, SixTen(0b011111, 264), dozx);
BindConditional(is601, SixTen(0b011111, 512 + 264), dozx);
BindConditional(is601, SixTen(0b011111, 277), lscbxx);
BindConditional(is601, SixTen(0b011111, 29), maskgx);
BindConditional(is601, SixTen(0b011111, 541), maskirx);
BindConditional(is601, SixTen(0b011111, 107), mulx); BindConditional(is601, SixTen(0b011111, 512 + 107), mulx);
BindConditional(is601, SixTen(0b011111, 488), nabsx); BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx);
BindConditional(is601, SixTen(0b011111, 107), mulx);
BindConditional(is601, SixTen(0b011111, 512 + 107), mulx);
BindConditional(is601, SixTen(0b011111, 488), nabsx);
BindConditional(is601, SixTen(0b011111, 512 + 488), nabsx);
BindConditional(is601, SixTen(0b011111, 537), rribx);
BindConditional(is601, SixTen(0b011111, 153), slex);
BindConditional(is601, SixTen(0b011111, 217), sleqx);
@ -553,17 +566,22 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
if(is64bit(model)) {
switch(opcode & 0b111111'00000'00000'00000'000000'111'00) {
default: break;
case 0b011110'00000'00000'00000'000000'000'00: return instruction<model, validate_reserved_bits, Operation::rldiclx>(opcode);
case 0b011110'00000'00000'00000'000000'001'00: return instruction<model, validate_reserved_bits, Operation::rldicrx>(opcode);
case 0b011110'00000'00000'00000'000000'010'00: return instruction<model, validate_reserved_bits, Operation::rldicx>(opcode);
case 0b011110'00000'00000'00000'000000'011'00: return instruction<model, validate_reserved_bits, Operation::rldimix>(opcode);
case 0b011110'00000'00000'00000'000000'000'00:
return instruction<model, validate_reserved_bits, Operation::rldiclx>(opcode);
case 0b011110'00000'00000'00000'000000'001'00:
return instruction<model, validate_reserved_bits, Operation::rldicrx>(opcode);
case 0b011110'00000'00000'00000'000000'010'00:
return instruction<model, validate_reserved_bits, Operation::rldicx>(opcode);
case 0b011110'00000'00000'00000'000000'011'00:
return instruction<model, validate_reserved_bits, Operation::rldimix>(opcode);
}
}
// stwcx. and stdcx.
switch(opcode & 0b111111'0000'0000'0000'0000'111111111'1) {
default: break;
case 0b011111'0000'0000'0000'0000'010010110'1: return instruction<model, validate_reserved_bits, Operation::stwcx_>(opcode);
case 0b011111'0000'0000'0000'0000'010010110'1:
return instruction<model, validate_reserved_bits, Operation::stwcx_>(opcode);
case 0b011111'0000'0000'0000'0000'011010110'1:
if(is64bit(model)) return instruction<model, validate_reserved_bits, Operation::stdcx_>(opcode);
return Instruction(opcode);
@ -573,11 +591,16 @@ Instruction Decoder<model, validate_reserved_bits>::decode(uint32_t opcode) {
if(is64bit(model)) {
switch(opcode & 0b111111'00'00000000'00000000'000000'11) {
default: break;
case 0b111010'00'00000000'00000000'000000'00: return instruction<model, validate_reserved_bits, Operation::ld>(opcode);
case 0b111010'00'00000000'00000000'000000'01: return instruction<model, validate_reserved_bits, Operation::ldu>(opcode);
case 0b111010'00'00000000'00000000'000000'10: return instruction<model, validate_reserved_bits, Operation::lwa>(opcode);
case 0b111110'00'00000000'00000000'000000'00: return instruction<model, validate_reserved_bits, Operation::std>(opcode);
case 0b111110'00'00000000'00000000'000000'01: return instruction<model, validate_reserved_bits, Operation::stdu>(opcode);
case 0b111010'00'00000000'00000000'000000'00:
return instruction<model, validate_reserved_bits, Operation::ld>(opcode);
case 0b111010'00'00000000'00000000'000000'01:
return instruction<model, validate_reserved_bits, Operation::ldu>(opcode);
case 0b111010'00'00000000'00000000'000000'10:
return instruction<model, validate_reserved_bits, Operation::lwa>(opcode);
case 0b111110'00'00000000'00000000'000000'00:
return instruction<model, validate_reserved_bits, Operation::std>(opcode);
case 0b111110'00'00000000'00000000'000000'01:
return instruction<model, validate_reserved_bits, Operation::stdu>(opcode);
}
}

View File

@ -21,15 +21,15 @@ enum class Model {
MPC620,
};
constexpr bool is64bit(Model model) {
constexpr bool is64bit(const Model model) {
return model == Model::MPC620;
}
constexpr bool is32bit(Model model) {
constexpr bool is32bit(const Model model) {
return !is64bit(model);
}
constexpr bool is601(Model model) {
constexpr bool is601(const Model model) {
return model == Model::MPC601;
}
@ -45,7 +45,7 @@ constexpr bool is601(Model model) {
TODO: determine what specific models of PowerPC do re: reserved bits.
*/
template <Model model, bool validate_reserved_bits = false> struct Decoder {
Instruction decode(uint32_t opcode);
Instruction decode(uint32_t);
};
}

View File

@ -1363,8 +1363,9 @@ struct Instruction {
uint32_t opcode = 0;
constexpr Instruction() noexcept = default;
constexpr Instruction(uint32_t opcode) noexcept : opcode(opcode) {}
constexpr Instruction(Operation operation, uint32_t opcode, bool is_supervisor = false) noexcept : operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
constexpr Instruction(const uint32_t opcode) noexcept : opcode(opcode) {}
constexpr Instruction(const Operation operation, const uint32_t opcode, const bool is_supervisor = false) noexcept :
operation(operation), is_supervisor(is_supervisor), opcode(opcode) {}
// Instruction fields are decoded below; naming is a compromise between
// Motorola's documentation and IBM's.

View File

@ -30,24 +30,24 @@ enum class AccessType {
PreauthorisedRead,
};
constexpr bool is_writeable(AccessType type) {
constexpr bool is_writeable(const AccessType type) {
return type == AccessType::ReadModifyWrite || type == AccessType::Write;
}
template <typename IntT, AccessType type> struct Accessor;
// Reads: return a value directly.
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = IntT; };
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = IntT; };
template <typename IntT> struct Accessor<IntT, AccessType::Read> { using type = const IntT; };
template <typename IntT> struct Accessor<IntT, AccessType::PreauthorisedRead> { using type = const IntT; };
// Writes: return a custom type that can be written but not read.
template <typename IntT>
class Writeable {
public:
Writeable(IntT &target) : target_(target) {}
IntT operator=(IntT value) { return target_ = value; }
private:
IntT &target_;
public:
Writeable(IntT &target) : target_(target) {}
IntT operator=(IntT value) { return target_ = value; }
private:
IntT &target_;
};
template <typename IntT> struct Accessor<IntT, AccessType::Write> { using type = Writeable<IntT>; };

View File

@ -15,7 +15,10 @@
using namespace InstructionSet::x86;
template <Model model>
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(const uint8_t *source, std::size_t length) {
std::pair<int, typename Decoder<model>::InstructionT> Decoder<model>::decode(
const uint8_t *source,
const std::size_t length
) {
// Instruction length limits:
//
// 8086/80186: none*
@ -1124,6 +1127,6 @@ template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80186>;
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80286>;
template class InstructionSet::x86::Decoder<InstructionSet::x86::Model::i80386>;
std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *source, std::size_t length) {
std::pair<int, Instruction<false>> Decoder8086::decode(const uint8_t *const source, const std::size_t length) {
return decoder.decode(source, length);
}

View File

@ -22,209 +22,209 @@ namespace InstructionSet::x86 {
This is an experimental implementation; it has not yet undergone significant testing.
*/
template <Model model> class Decoder {
public:
using InstructionT = Instruction<is_32bit(model)>;
public:
using InstructionT = Instruction<is_32bit(model)>;
/*!
@returns an @c Instruction plus a size; a positive size indicates successful decoding of
an instruction that was that many bytes long in total; a negative size specifies the [negatived]
minimum number of further bytes the caller should ideally collect before calling again. The
caller is free to call with fewer, but may not get a decoded instruction in response, and the
decoder may still not be able to complete decoding even if given that number of bytes.
/*!
@returns an @c Instruction plus a size; a positive size indicates successful decoding of
an instruction that was that many bytes long in total; a negative size specifies the [negatived]
minimum number of further bytes the caller should ideally collect before calling again. The
caller is free to call with fewer, but may not get a decoded instruction in response, and the
decoder may still not be able to complete decoding even if given that number of bytes.
Successful decoding is defined to mean that all decoding steps are complete. The output
may still be an illegal instruction (indicated by Operation::Invalid), if the byte sequence
supplied cannot form a valid instruction.
Successful decoding is defined to mean that all decoding steps are complete. The output
may still be an illegal instruction (indicated by Operation::Invalid), if the byte sequence
supplied cannot form a valid instruction.
@discussion although instructions also contain an indicator of their length, on chips prior
to the 80286 there is no limit to potential instruction length.
@discussion although instructions also contain an indicator of their length, on chips prior
to the 80286 there is no limit to potential instruction length.
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
cannot overflow the field.
*/
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
cannot overflow the field.
*/
std::pair<int, InstructionT> decode(const uint8_t *source, std::size_t length);
/*!
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
*/
void set_32bit_protected_mode(bool);
/*!
Enables or disables 32-bit protected mode. Meaningful only if the @c Model supports it.
*/
void set_32bit_protected_mode(bool);
private:
enum class Phase {
/// Captures all prefixes and continues until an instruction byte is encountered.
Instruction,
/// Having encountered a 0x0f first instruction byte, waits for the next byte fully to determine the instruction.
InstructionPageF,
/// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately
/// or completes decoding of the instruction, as per the instruction format.
ModRegRM,
/// Awaits n 80386+-style scale-index-base byte ('SIB'), indicating the form of indirect addressing.
ScaleIndexBase,
/// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured.
/// Cf. displacement_size_ and operand_size_.
DisplacementOrOperand,
/// Forms and returns an Instruction, and resets parsing state.
ReadyToPost
} phase_ = Phase::Instruction;
private:
enum class Phase {
/// Captures all prefixes and continues until an instruction byte is encountered.
Instruction,
/// Having encountered a 0x0f first instruction byte, waits for the next byte fully to determine the instruction.
InstructionPageF,
/// Receives a ModRegRM byte and either populates the source_ and dest_ fields appropriately
/// or completes decoding of the instruction, as per the instruction format.
ModRegRM,
/// Awaits n 80386+-style scale-index-base byte ('SIB'), indicating the form of indirect addressing.
ScaleIndexBase,
/// Waits for sufficiently many bytes to pass for the required displacement and operand to be captured.
/// Cf. displacement_size_ and operand_size_.
DisplacementOrOperand,
/// Forms and returns an Instruction, and resets parsing state.
ReadyToPost
} phase_ = Phase::Instruction;
/// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte.
///
/// During the ReadyToPost phase, format determines how transiently-recorded fields
/// are packaged into an Instruction.
enum class ModRegRMFormat: uint8_t {
// Parse the ModRegRM for mode, register and register/memory fields
// and populate the source_ and destination_ fields appropriately.
MemReg_Reg,
Reg_MemReg,
/// During the ModRegRM phase, format dictates interpretation of the ModRegRM byte.
///
/// During the ReadyToPost phase, format determines how transiently-recorded fields
/// are packaged into an Instruction.
enum class ModRegRMFormat: uint8_t {
// Parse the ModRegRM for mode, register and register/memory fields
// and populate the source_ and destination_ fields appropriately.
MemReg_Reg,
Reg_MemReg,
// Parse for mode and register/memory fields, populating both
// source_ and destination_ fields with the single register/memory result.
MemRegSingleOperand,
// Parse for mode and register/memory fields, populating both
// source_ and destination_ fields with the single register/memory result.
MemRegSingleOperand,
// Parse for mode and register/memory fields, populating both
// the destination_ field with the result and setting source_ to Immediate.
MemRegMOV,
// Parse for mode and register/memory fields, populating both
// the destination_ field with the result and setting source_ to Immediate.
MemRegMOV,
// Parse for mode and register/memory fields, populating the
// source_ field with the result. Fills destination_ with a segment
// register based on the reg field.
Seg_MemReg,
MemReg_Seg,
// Parse for mode and register/memory fields, populating the
// source_ field with the result. Fills destination_ with a segment
// register based on the reg field.
Seg_MemReg,
MemReg_Seg,
//
// 'Group 1'
//
//
// 'Group 1'
//
// Parse for mode and register/memory fields, populating the
// destination_ field with the result. Use the 'register' field
// to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and
// waits for an operand equal to the operation size.
MemRegADD_to_CMP,
// Parse for mode and register/memory fields, populating the
// destination_ field with the result. Use the 'register' field
// to pick an operation from the ADD/OR/ADC/SBB/AND/SUB/XOR/CMP group and
// waits for an operand equal to the operation size.
MemRegADD_to_CMP,
// Acts exactly as MemRegADD_to_CMP but the operand is fixed in size
// at a single byte, which is sign extended to the operation size.
MemRegADD_to_CMP_SignExtend,
// Acts exactly as MemRegADD_to_CMP but the operand is fixed in size
// at a single byte, which is sign extended to the operation size.
MemRegADD_to_CMP_SignExtend,
//
// 'Group 2'
//
//
// 'Group 2'
//
// Parse for mode and register/memory fields, populating the
// destination_ field with the result. Use the 'register' field
// to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group.
MemRegROL_to_SAR,
// Parse for mode and register/memory fields, populating the
// destination_ field with the result. Use the 'register' field
// to pick an operation from the ROL/ROR/RCL/RCR/SAL/SHR/SAR group.
MemRegROL_to_SAR,
//
// 'Group 3'
//
//
// 'Group 3'
//
// Parse for mode and register/memory fields, populating both
// source_ and destination_ fields with the result. Use the 'register'
// field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group.
MemRegTEST_to_IDIV,
// Parse for mode and register/memory fields, populating both
// source_ and destination_ fields with the result. Use the 'register'
// field to pick an operation from the TEST/NOT/NEG/MUL/IMUL/DIV/IDIV group.
MemRegTEST_to_IDIV,
//
// 'Group 4'
//
//
// 'Group 4'
//
// Parse for mode and register/memory fields, populating the
// source_ and destination_ fields with the result. Uses the
// 'register' field to pick INC or DEC.
MemRegINC_DEC,
// Parse for mode and register/memory fields, populating the
// source_ and destination_ fields with the result. Uses the
// 'register' field to pick INC or DEC.
MemRegINC_DEC,
//
// 'Group 5'
//
//
// 'Group 5'
//
// Parse for mode and register/memory fields, populating the
// source_ and destination_ fields with the result. Uses the
// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
// the source to ::Immediate and setting an operand size if necessary.
MemRegINC_to_PUSH,
// Parse for mode and register/memory fields, populating the
// source_ and destination_ fields with the result. Uses the
// 'register' field to pick from INC/DEC/CALL/JMP/PUSH, altering
// the source to ::Immediate and setting an operand size if necessary.
MemRegINC_to_PUSH,
//
// 'Group 6'
//
//
// 'Group 6'
//
// Parse for mode and register/memory field, populating both source_
// and destination_ fields with the result. Uses the 'register' field
// to pick from SLDT/STR/LLDT/LTR/VERR/VERW.
MemRegSLDT_to_VERW,
// Parse for mode and register/memory field, populating both source_
// and destination_ fields with the result. Uses the 'register' field
// to pick from SLDT/STR/LLDT/LTR/VERR/VERW.
MemRegSLDT_to_VERW,
//
// 'Group 7'
//
//
// 'Group 7'
//
// Parse for mode and register/memory field, populating both source_
// and destination_ fields with the result. Uses the 'register' field
// to pick from SGDT/LGDT/SMSW/LMSW.
MemRegSGDT_to_LMSW,
// Parse for mode and register/memory field, populating both source_
// and destination_ fields with the result. Uses the 'register' field
// to pick from SGDT/LGDT/SMSW/LMSW.
MemRegSGDT_to_LMSW,
//
// 'Group 8'
//
//
// 'Group 8'
//
// Parse for mode and register/memory field, populating destination,
// and prepare to read a single byte as source.
MemRegBT_to_BTC,
} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
// Parse for mode and register/memory field, populating destination,
// and prepare to read a single byte as source.
MemRegBT_to_BTC,
} modregrm_format_ = ModRegRMFormat::MemReg_Reg;
// Ephemeral decoding state.
Operation operation_ = Operation::Invalid;
int consumed_ = 0, operand_bytes_ = 0;
// Ephemeral decoding state.
Operation operation_ = Operation::Invalid;
int consumed_ = 0, operand_bytes_ = 0;
// Source and destination locations.
Source source_ = Source::None;
Source destination_ = Source::None;
// Source and destination locations.
Source source_ = Source::None;
Source destination_ = Source::None;
// Immediate fields.
int32_t displacement_ = 0;
uint32_t operand_ = 0;
uint64_t inward_data_ = 0;
int next_inward_data_shift_ = 0;
// Immediate fields.
int32_t displacement_ = 0;
uint32_t operand_ = 0;
uint64_t inward_data_ = 0;
int next_inward_data_shift_ = 0;
// Indirection style.
ScaleIndexBase sib_;
// Indirection style.
ScaleIndexBase sib_;
// Facts about the instruction.
DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any.
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.
// Facts about the instruction.
DataSize displacement_size_ = DataSize::None; // i.e. size of in-stream displacement, if any.
DataSize operand_size_ = DataSize::None; // i.e. size of in-stream operand, if any.
DataSize operation_size_ = DataSize::None; // i.e. size of data manipulated by the operation.
bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
// size; otherwise it'll be zero-padded.
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
// otherwise it'll be zero-padded.
bool sign_extend_displacement_ = true; // If set then sign extend any displacement up to the address
// size; otherwise it'll be zero-padded.
bool sign_extend_operand_ = false; // If set then sign extend the operand up to the operation size;
// otherwise it'll be zero-padded.
// Prefix capture fields.
Repetition repetition_ = Repetition::None;
bool lock_ = false;
Source segment_override_ = Source::None;
// Prefix capture fields.
Repetition repetition_ = Repetition::None;
bool lock_ = false;
Source segment_override_ = Source::None;
// 32-bit/16-bit selection.
AddressSize default_address_size_ = AddressSize::b16;
DataSize default_data_size_ = DataSize::Word;
AddressSize address_size_ = AddressSize::b16;
DataSize data_size_ = DataSize::Word;
// 32-bit/16-bit selection.
AddressSize default_address_size_ = AddressSize::b16;
DataSize default_data_size_ = DataSize::Word;
AddressSize address_size_ = AddressSize::b16;
DataSize data_size_ = DataSize::Word;
/// Resets size capture and all fields with default values.
void reset_parsing() {
consumed_ = operand_bytes_ = 0;
displacement_size_ = operand_size_ = operation_size_ = DataSize::None;
displacement_ = operand_ = 0;
lock_ = false;
address_size_ = default_address_size_;
data_size_ = default_data_size_;
segment_override_ = Source::None;
repetition_ = Repetition::None;
phase_ = Phase::Instruction;
source_ = destination_ = Source::None;
sib_ = ScaleIndexBase();
next_inward_data_shift_ = 0;
inward_data_ = 0;
sign_extend_operand_ = false;
sign_extend_displacement_ = true;
}
/// Resets size capture and all fields with default values.
void reset_parsing() {
consumed_ = operand_bytes_ = 0;
displacement_size_ = operand_size_ = operation_size_ = DataSize::None;
displacement_ = operand_ = 0;
lock_ = false;
address_size_ = default_address_size_;
data_size_ = default_data_size_;
segment_override_ = Source::None;
repetition_ = Repetition::None;
phase_ = Phase::Instruction;
source_ = destination_ = Source::None;
sib_ = ScaleIndexBase();
next_inward_data_shift_ = 0;
inward_data_ = 0;
sign_extend_operand_ = false;
sign_extend_displacement_ = true;
}
};
// This is a temporary measure; for reasons as-yet unknown, GCC isn't picking up the

View File

@ -77,154 +77,154 @@ enum class Condition {
};
class Flags {
public:
using FlagT = uint32_t;
public:
using FlagT = uint32_t;
// Flag getters.
template <Flag flag_v> bool flag() const {
switch(flag_v) {
case Flag::Carry: return carry_;
case Flag::AuxiliaryCarry: return auxiliary_carry_;
case Flag::Sign: return sign_;
case Flag::Overflow: return overflow_;
case Flag::Trap: return trap_;
case Flag::Interrupt: return interrupt_;
case Flag::Direction: return direction_ < 0;
case Flag::Zero: return !zero_;
case Flag::ParityOdd: return not_parity_bit();
// Flag getters.
template <Flag flag_v> bool flag() const {
switch(flag_v) {
case Flag::Carry: return carry_;
case Flag::AuxiliaryCarry: return auxiliary_carry_;
case Flag::Sign: return sign_;
case Flag::Overflow: return overflow_;
case Flag::Trap: return trap_;
case Flag::Interrupt: return interrupt_;
case Flag::Direction: return direction_ < 0;
case Flag::Zero: return !zero_;
case Flag::ParityOdd: return not_parity_bit();
}
}
// Condition evaluation.
template <Condition test> bool condition() const {
switch(test) {
case Condition::Overflow: return flag<Flag::Overflow>();
case Condition::Below: return flag<Flag::Carry>();
case Condition::Zero: return flag<Flag::Zero>();
case Condition::BelowOrEqual: return flag<Flag::Zero>() || flag<Flag::Carry>();
case Condition::Sign: return flag<Flag::Sign>();
case Condition::ParityOdd: return flag<Flag::ParityOdd>();
case Condition::Less: return flag<Flag::Sign>() != flag<Flag::Overflow>();
case Condition::LessOrEqual: return flag<Flag::Zero>() || flag<Flag::Sign>() != flag<Flag::Overflow>();
}
}
// Convenience setters.
/// Sets all of @c flags as a function of @c value:
/// • Flag::Zero: sets the zero flag if @c value is zero;
/// • Flag::Sign: sets the sign flag if the top bit of @c value is one;
/// • Flag::ParityOdd: sets parity based on the low 8 bits of @c value;
/// • Flag::Carry: sets carry if @c value is non-zero;
/// • Flag::AuxiliaryCarry: sets auxiliary carry if @c value is non-zero;
/// • Flag::Overflow: sets overflow if @c value is non-zero;
/// • Flag::Interrupt: sets interrupt if @c value is non-zero;
/// • Flag::Trap: sets interrupt if @c value is non-zero;
/// • Flag::Direction: sets direction if @c value is non-zero.
template <typename IntT, Flag... flags> void set_from(const IntT value) {
for(const auto flag: {flags...}) {
switch(flag) {
default: break;
case Flag::Zero: zero_ = value; break;
case Flag::Sign: sign_ = value & Numeric::top_bit<IntT>(); break;
case Flag::ParityOdd: parity_ = value; break;
case Flag::Carry: carry_ = value; break;
case Flag::AuxiliaryCarry: auxiliary_carry_ = value; break;
case Flag::Overflow: overflow_ = value; break;
case Flag::Interrupt: interrupt_ = value; break;
case Flag::Trap: trap_ = value; break;
case Flag::Direction: direction_ = value ? -1 : 1; break;
}
}
}
template <Flag... flags> void set_from(const FlagT value) {
set_from<FlagT, flags...>(value);
}
// Condition evaluation.
template <Condition test> bool condition() const {
switch(test) {
case Condition::Overflow: return flag<Flag::Overflow>();
case Condition::Below: return flag<Flag::Carry>();
case Condition::Zero: return flag<Flag::Zero>();
case Condition::BelowOrEqual: return flag<Flag::Zero>() || flag<Flag::Carry>();
case Condition::Sign: return flag<Flag::Sign>();
case Condition::ParityOdd: return flag<Flag::ParityOdd>();
case Condition::Less: return flag<Flag::Sign>() != flag<Flag::Overflow>();
case Condition::LessOrEqual: return flag<Flag::Zero>() || flag<Flag::Sign>() != flag<Flag::Overflow>();
}
}
template <typename IntT> IntT carry_bit() const { return carry_ ? 1 : 0; }
bool not_parity_bit() const {
// x86 parity always considers the lowest 8-bits only.
auto result = static_cast<uint8_t>(parity_);
result ^= result >> 4;
result ^= result >> 2;
result ^= result >> 1;
return result & 1;
}
// Convenience setters.
template <typename IntT> IntT direction() const { return static_cast<IntT>(direction_); }
/// Sets all of @c flags as a function of @c value:
/// • Flag::Zero: sets the zero flag if @c value is zero;
/// • Flag::Sign: sets the sign flag if the top bit of @c value is one;
/// • Flag::ParityOdd: sets parity based on the low 8 bits of @c value;
/// • Flag::Carry: sets carry if @c value is non-zero;
/// • Flag::AuxiliaryCarry: sets auxiliary carry if @c value is non-zero;
/// • Flag::Overflow: sets overflow if @c value is non-zero;
/// • Flag::Interrupt: sets interrupt if @c value is non-zero;
/// • Flag::Trap: sets interrupt if @c value is non-zero;
/// • Flag::Direction: sets direction if @c value is non-zero.
template <typename IntT, Flag... flags> void set_from(IntT value) {
for(const auto flag: {flags...}) {
switch(flag) {
default: break;
case Flag::Zero: zero_ = value; break;
case Flag::Sign: sign_ = value & Numeric::top_bit<IntT>(); break;
case Flag::ParityOdd: parity_ = value; break;
case Flag::Carry: carry_ = value; break;
case Flag::AuxiliaryCarry: auxiliary_carry_ = value; break;
case Flag::Overflow: overflow_ = value; break;
case Flag::Interrupt: interrupt_ = value; break;
case Flag::Trap: trap_ = value; break;
case Flag::Direction: direction_ = value ? -1 : 1; break;
}
}
}
template <Flag... flags> void set_from(FlagT value) {
set_from<FlagT, flags...>(value);
}
// Complete value get and set.
void set(uint16_t value) {
set_from<Flag::Carry>(value & FlagValue::Carry);
set_from<Flag::AuxiliaryCarry>(value & FlagValue::AuxiliaryCarry);
set_from<Flag::Overflow>(value & FlagValue::Overflow);
set_from<Flag::Trap>(value & FlagValue::Trap);
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
set_from<Flag::Direction>(value & FlagValue::Direction);
template <typename IntT> IntT carry_bit() const { return carry_ ? 1 : 0; }
bool not_parity_bit() const {
// x86 parity always considers the lowest 8-bits only.
auto result = static_cast<uint8_t>(parity_);
result ^= result >> 4;
result ^= result >> 2;
result ^= result >> 1;
return result & 1;
}
set_from<uint8_t, Flag::Sign>(uint8_t(value));
template <typename IntT> IntT direction() const { return static_cast<IntT>(direction_); }
set_from<Flag::Zero>((~value) & FlagValue::Zero);
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
}
// Complete value get and set.
void set(uint16_t value) {
set_from<Flag::Carry>(value & FlagValue::Carry);
set_from<Flag::AuxiliaryCarry>(value & FlagValue::AuxiliaryCarry);
set_from<Flag::Overflow>(value & FlagValue::Overflow);
set_from<Flag::Trap>(value & FlagValue::Trap);
set_from<Flag::Interrupt>(value & FlagValue::Interrupt);
set_from<Flag::Direction>(value & FlagValue::Direction);
uint16_t get() const {
return
0xf002 |
set_from<uint8_t, Flag::Sign>(uint8_t(value));
(flag<Flag::Carry>() ? FlagValue::Carry : 0) |
(flag<Flag::AuxiliaryCarry>() ? FlagValue::AuxiliaryCarry : 0) |
(flag<Flag::Sign>() ? FlagValue::Sign : 0) |
(flag<Flag::Overflow>() ? FlagValue::Overflow : 0) |
(flag<Flag::Trap>() ? FlagValue::Trap : 0) |
(flag<Flag::Interrupt>() ? FlagValue::Interrupt : 0) |
(flag<Flag::Direction>() ? FlagValue::Direction : 0) |
(flag<Flag::Zero>() ? FlagValue::Zero : 0) |
set_from<Flag::Zero>((~value) & FlagValue::Zero);
set_from<Flag::ParityOdd>((~value) & FlagValue::Parity);
}
(flag<Flag::ParityOdd>() ? 0 : FlagValue::Parity);
}
uint16_t get() const {
return
0xf002 |
std::string to_string() const {
std::string result;
(flag<Flag::Carry>() ? FlagValue::Carry : 0) |
(flag<Flag::AuxiliaryCarry>() ? FlagValue::AuxiliaryCarry : 0) |
(flag<Flag::Sign>() ? FlagValue::Sign : 0) |
(flag<Flag::Overflow>() ? FlagValue::Overflow : 0) |
(flag<Flag::Trap>() ? FlagValue::Trap : 0) |
(flag<Flag::Interrupt>() ? FlagValue::Interrupt : 0) |
(flag<Flag::Direction>() ? FlagValue::Direction : 0) |
(flag<Flag::Zero>() ? FlagValue::Zero : 0) |
if(flag<Flag::Overflow>()) result += "O"; else result += "-";
if(flag<Flag::Direction>()) result += "D"; else result += "-";
if(flag<Flag::Interrupt>()) result += "I"; else result += "-";
if(flag<Flag::Trap>()) result += "T"; else result += "-";
if(flag<Flag::Sign>()) result += "S"; else result += "-";
if(flag<Flag::Zero>()) result += "Z"; else result += "-";
result += "-";
if(flag<Flag::AuxiliaryCarry>()) result += "A"; else result += "-";
result += "-";
if(!flag<Flag::ParityOdd>()) result += "P"; else result += "-";
result += "-";
if(flag<Flag::Carry>()) result += "C"; else result += "-";
(flag<Flag::ParityOdd>() ? 0 : FlagValue::Parity);
}
return result;
}
std::string to_string() const {
std::string result;
bool operator ==(const Flags &rhs) const {
return get() == rhs.get();
}
if(flag<Flag::Overflow>()) result += "O"; else result += "-";
if(flag<Flag::Direction>()) result += "D"; else result += "-";
if(flag<Flag::Interrupt>()) result += "I"; else result += "-";
if(flag<Flag::Trap>()) result += "T"; else result += "-";
if(flag<Flag::Sign>()) result += "S"; else result += "-";
if(flag<Flag::Zero>()) result += "Z"; else result += "-";
result += "-";
if(flag<Flag::AuxiliaryCarry>()) result += "A"; else result += "-";
result += "-";
if(!flag<Flag::ParityOdd>()) result += "P"; else result += "-";
result += "-";
if(flag<Flag::Carry>()) result += "C"; else result += "-";
private:
// Non-zero => set; zero => unset.
uint32_t carry_;
uint32_t auxiliary_carry_;
uint32_t sign_;
uint32_t overflow_;
uint32_t trap_;
uint32_t interrupt_;
return result;
}
// +1 = direction flag not set;
// -1 = direction flag set.
int32_t direction_;
bool operator ==(const Flags &rhs) const {
return get() == rhs.get();
}
// Zero => set; non-zero => unset.
uint32_t zero_;
private:
// Non-zero => set; zero => unset.
uint32_t carry_;
uint32_t auxiliary_carry_;
uint32_t sign_;
uint32_t overflow_;
uint32_t trap_;
uint32_t interrupt_;
// +1 = direction flag not set;
// -1 = direction flag set.
int32_t direction_;
// Zero => set; non-zero => unset.
uint32_t zero_;
// Odd number of bits => set; even => unset.
uint32_t parity_;
// Odd number of bits => set; even => unset.
uint32_t parity_;
};
}

View File

@ -240,7 +240,8 @@ void idiv(
IF OperandSize = 8 (* word/byte operation *)
THEN
temp AX / SRC; (* signed division *)
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than 7FH or a negative result is less than 80H *)
IF (temp > 7FH) OR (temp < 80H) (* if a positive result is greater than
7FH or a negative result is less than 80H *)
THEN #DE; (* divide error *) ;
ELSE
AL temp;
@ -250,7 +251,8 @@ void idiv(
IF OperandSize = 16 (* doubleword/word operation *)
THEN
temp DX:AX / SRC; (* signed division *)
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a negative result is less than 8000H *)
IF (temp > 7FFFH) OR (temp < 8000H) (* if a positive result is greater than 7FFFH or a
negative result is less than 8000H *)
THEN #DE; (* divide error *) ;
ELSE
AX temp;
@ -258,7 +260,8 @@ void idiv(
FI;
ELSE (* quadword/doubleword operation *)
temp EDX:EAX / SRC; (* signed division *)
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH or a negative result is less than 80000000H *)
IF (temp > 7FFFFFFFH) OR (temp < 80000000H) (* if a positive result is greater than 7FFFFFFFH
or a negative result is less than 80000000H *)
THEN #DE; (* divide error *) ;
ELSE
EAX temp;
@ -350,7 +353,11 @@ void neg(
The CF flag cleared to 0 if the source operand is 0; otherwise it is set to 1.
The OF, SF, ZF, AF, and PF flags are set according to the result.
*/
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(IntT(0), destination, IntT(-destination)));
context.flags.template set_from<Flag::AuxiliaryCarry>(Numeric::carried_in<4>(
IntT(0),
destination,
IntT(-destination))
);
destination = -destination;

View File

@ -38,7 +38,7 @@ void aaas(
template <typename ContextT>
void aad(
CPU::RegisterPair16 &ax,
uint8_t imm,
const uint8_t imm,
ContextT &context
) {
/*
@ -59,7 +59,7 @@ void aad(
template <typename ContextT>
void aam(
CPU::RegisterPair16 &ax,
uint8_t imm,
const uint8_t imm,
ContextT &context
) {
/*

View File

@ -18,8 +18,8 @@ namespace InstructionSet::x86::Primitive {
template <typename IntT, typename ContextT>
void jump(
bool condition,
IntT displacement,
const bool condition,
const IntT displacement,
ContextT &context
) {
/*
@ -42,7 +42,7 @@ void jump(
template <typename IntT, typename OffsetT, typename ContextT>
void loop(
modify_t<IntT> counter,
OffsetT displacement,
const OffsetT displacement,
ContextT &context
) {
--counter;
@ -54,7 +54,7 @@ void loop(
template <typename IntT, typename OffsetT, typename ContextT>
void loope(
modify_t<IntT> counter,
OffsetT displacement,
const OffsetT displacement,
ContextT &context
) {
--counter;
@ -66,7 +66,7 @@ void loope(
template <typename IntT, typename OffsetT, typename ContextT>
void loopne(
modify_t<IntT> counter,
OffsetT displacement,
const OffsetT displacement,
ContextT &context
) {
--counter;
@ -125,20 +125,28 @@ void call_far(
return;
case Source::Indirect:
source_address = uint16_t(address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context));
source_address = uint16_t(
address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context)
);
break;
case Source::IndirectNoBase:
source_address = uint16_t(address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context));
source_address = uint16_t(
address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context)
);
break;
case Source::DirectAddress:
source_address = uint16_t(address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context));
source_address = uint16_t(
address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context)
);
break;
}
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
const auto offset =
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
source_address += 2;
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
const auto segment =
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
// At least on an 8086, the stack writes occur after the target address read.
push<uint16_t, true>(context.registers.cs(), context);
@ -157,25 +165,35 @@ void jump_far(
const auto pointer = instruction.destination();
switch(pointer.source()) {
default:
case Source::Immediate: context.flow_controller.template jump<uint16_t>(instruction.segment(), instruction.offset()); return;
case Source::Immediate:
context.flow_controller.template jump<uint16_t>(instruction.segment(), instruction.offset());
return;
case Source::Indirect:
source_address = uint16_t(address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context));
source_address = uint16_t(
address<Source::Indirect, uint16_t, AccessType::Read>(instruction, pointer, context)
);
break;
case Source::IndirectNoBase:
source_address = uint16_t(address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context));
source_address = uint16_t(
address<Source::IndirectNoBase, uint16_t, AccessType::Read>(instruction, pointer, context)
);
break;
case Source::DirectAddress:
source_address = uint16_t(address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context));
source_address = uint16_t(
address<Source::DirectAddress, uint16_t, AccessType::Read>(instruction, pointer, context)
);
break;
}
const Source source_segment = instruction.data_segment();
context.memory.preauthorise_read(source_segment, source_address, sizeof(uint16_t) * 2);
const uint16_t offset = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
const auto offset =
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
source_address += 2;
const uint16_t segment = context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
const auto segment =
context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source_address);
context.flow_controller.template jump<uint16_t>(segment, offset);
}
@ -193,7 +211,7 @@ void iret(
template <typename InstructionT, typename ContextT>
void ret_near(
InstructionT instruction,
const InstructionT instruction,
ContextT &context
) {
const auto ip = pop<uint16_t, false>(context);
@ -203,7 +221,7 @@ void ret_near(
template <typename InstructionT, typename ContextT>
void ret_far(
InstructionT instruction,
const InstructionT instruction,
ContextT &context
) {
context.memory.preauthorise_stack_read(sizeof(uint16_t) * 2);
@ -233,9 +251,11 @@ void bound(
const auto source_segment = instruction.data_segment();
context.memory.preauthorise_read(source_segment, source, 2*sizeof(IntT));
const sIntT lower_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
const auto lower_bound =
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
source += 2;
const sIntT upper_bound = sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
const auto upper_bound =
sIntT(context.memory.template access<uint16_t, AccessType::PreauthorisedRead>(source_segment, source));
if(sIntT(destination) < lower_bound || sIntT(destination) > upper_bound) {
interrupt(Interrupt::BoundRangeExceeded, context);

View File

@ -14,7 +14,7 @@ namespace InstructionSet::x86::Primitive {
template <typename IntT, typename ContextT>
void out(
uint16_t port,
const uint16_t port,
read_t<IntT> value,
ContextT &context
) {
@ -23,7 +23,7 @@ void out(
template <typename IntT, typename ContextT>
void in(
uint16_t port,
const uint16_t port,
write_t<IntT> value,
ContextT &context
) {

View File

@ -99,7 +99,7 @@ void cbw(
template <typename IntT>
void cwd(
IntT &dx,
IntT ax
const IntT ax
) {
dx = ax & Numeric::top_bit<IntT>() ? IntT(~0) : IntT(0);
}

View File

@ -176,8 +176,12 @@ template <
case Operation::ESC:
case Operation::NOP: return;
case Operation::AAM: Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context); return;
case Operation::AAD: Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context); return;
case Operation::AAM:
Primitive::aam(context.registers.axp(), uint8_t(instruction.operand()), context);
return;
case Operation::AAD:
Primitive::aad(context.registers.axp(), uint8_t(instruction.operand()), context);
return;
case Operation::AAA: Primitive::aaas<true>(context.registers.axp(), context); return;
case Operation::AAS: Primitive::aaas<false>(context.registers.axp(), context); return;
case Operation::DAA: Primitive::daas<true>(context.registers.al(), context); return;
@ -236,8 +240,8 @@ template <
case Operation::CALLrel:
Primitive::call_relative<AddressT>(instruction.displacement(), context);
return;
case Operation::CALLabs: Primitive::call_absolute<IntT, AddressT>(destination_r(), context); return;
case Operation::CALLfar: Primitive::call_far<AddressT>(instruction, context); return;
case Operation::CALLabs: Primitive::call_absolute<IntT, AddressT>(destination_r(), context); return;
case Operation::CALLfar: Primitive::call_far<AddressT>(instruction, context); return;
case Operation::JMPrel: jcc(true); return;
case Operation::JMPabs: Primitive::jump_absolute<IntT>(destination_r(), context); return;
@ -417,10 +421,12 @@ template <
break;
case Operation::OUTS:
Primitive::outs<IntT, AddressT, Repetition::None>(instruction, eCX(), context.registers.dx(), eSI(), context);
Primitive::outs<IntT, AddressT, Repetition::None>(
instruction, eCX(), context.registers.dx(), eSI(), context);
return;
case Operation::OUTS_REP:
Primitive::outs<IntT, AddressT, Repetition::Rep>(instruction, eCX(), context.registers.dx(), eSI(), context);
Primitive::outs<IntT, AddressT, Repetition::Rep>(
instruction, eCX(), context.registers.dx(), eSI(), context);
return;
case Operation::INS:
@ -453,7 +459,7 @@ template <
const InstructionT &instruction,
ContextT &context
) {
auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
const auto size = [](DataSize operation_size, AddressSize address_size) constexpr -> int {
return int(operation_size) + (int(address_size) << 2);
};
@ -508,7 +514,7 @@ template <
template <
typename ContextT
> void interrupt(
int index,
const int index,
ContextT &context
) {
const uint32_t address = static_cast<uint32_t>(index) << 2;

View File

@ -21,9 +21,9 @@ namespace InstructionSet::x86 {
/// is copied to @c *immediate and @c immediate is returned.
template <typename IntT, AccessType access, typename InstructionT, typename ContextT>
typename Accessor<IntT, access>::type resolve(
InstructionT &instruction,
Source source,
DataPointer pointer,
const InstructionT &instruction,
const Source source,
const DataPointer pointer,
ContextT &context,
IntT *none = nullptr,
IntT *immediate = nullptr
@ -66,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:
//

View File

@ -15,7 +15,7 @@ namespace InstructionSet::x86::Primitive {
template <typename IntT, typename ContextT>
void rcl(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
/*
@ -76,7 +76,7 @@ void rcl(
template <typename IntT, typename ContextT>
void rcr(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
/*
@ -123,7 +123,7 @@ void rcr(
template <typename IntT, typename ContextT>
void rol(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
/*
@ -175,7 +175,7 @@ void rol(
template <typename IntT, typename ContextT>
void ror(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
/*
@ -283,7 +283,7 @@ void ror(
template <typename IntT, typename ContextT>
void sal(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
switch(count) {
@ -314,7 +314,7 @@ void sal(
template <typename IntT, typename ContextT>
void sar(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
if(!count) {
@ -337,7 +337,7 @@ void sar(
template <typename IntT, typename ContextT>
void shr(
modify_t<IntT> destination,
uint8_t count,
const uint8_t count,
ContextT &context
) {
if(!count) {

View File

@ -499,10 +499,10 @@ enum class Source: uint8_t {
/// getter is used).
IndirectNoBase = Indirect - 1,
};
constexpr bool is_register(Source source) {
constexpr bool is_register(const Source source) {
return source < Source::None;
}
constexpr bool is_segment_register(Source source) {
constexpr bool is_segment_register(const Source source) {
return is_register(source) && source >= Source::ES;
}
@ -698,216 +698,226 @@ class DataPointer {
};
template<bool is_32bit> class Instruction {
public:
using DisplacementT = typename std::conditional<is_32bit, int32_t, int16_t>::type;
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
using AddressT = ImmediateT;
public:
using DisplacementT = typename std::conditional<is_32bit, int32_t, int16_t>::type;
using ImmediateT = typename std::conditional<is_32bit, uint32_t, uint16_t>::type;
using AddressT = ImmediateT;
constexpr Instruction() noexcept = default;
constexpr Instruction(Operation operation) noexcept :
Instruction(operation, Source::None, Source::None, ScaleIndexBase(), false, AddressSize::b16, Source::None, DataSize::None, 0, 0) {}
constexpr Instruction(
Operation operation,
Source source,
Source destination,
ScaleIndexBase sib,
bool lock,
AddressSize address_size,
Source segment_override,
DataSize data_size,
DisplacementT displacement,
ImmediateT operand) noexcept :
operation_(operation),
mem_exts_source_(uint8_t(
(int(address_size) << 7) |
(displacement ? 0x40 : 0x00) |
(operand ? 0x20 : 0x00) |
int(source) |
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
)),
source_data_dest_sib_(uint16_t(
(int(data_size) << 14) |
(lock ? (1 << 13) : 0) |
((uint8_t(sib) & 0xf8) << 2) |
int(destination) |
(destination == Source::Indirect ? (uint8_t(sib) & 7) : 0)
)) {
// Decisions on whether to include operand, displacement and/or size extension words
// have implicitly been made in the int packing above; honour them here.
int extension = 0;
if(has_operand()) {
extensions_[extension] = operand;
++extension;
}
if(has_displacement()) {
extensions_[extension] = ImmediateT(displacement);
++extension;
}
// Patch in a fully-resolved segment.
Source segment = segment_override;
if(segment == Source::None) segment = this->source().default_segment();
if(segment == Source::None) segment = this->destination().default_segment();
if(segment == Source::None) segment = Source::DS;
source_data_dest_sib_ |= (int(segment)&7) << 10;
constexpr Instruction() noexcept = default;
constexpr Instruction(Operation operation) noexcept :
Instruction(
operation,
Source::None,
Source::None,
ScaleIndexBase(),
false,
AddressSize::b16,
Source::None,
DataSize::None,
0,
0
) {}
constexpr Instruction(
Operation operation,
Source source,
Source destination,
ScaleIndexBase sib,
bool lock,
AddressSize address_size,
Source segment_override,
DataSize data_size,
DisplacementT displacement,
ImmediateT operand) noexcept :
operation_(operation),
mem_exts_source_(uint8_t(
(int(address_size) << 7) |
(displacement ? 0x40 : 0x00) |
(operand ? 0x20 : 0x00) |
int(source) |
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
)),
source_data_dest_sib_(uint16_t(
(int(data_size) << 14) |
(lock ? (1 << 13) : 0) |
((uint8_t(sib) & 0xf8) << 2) |
int(destination) |
(destination == Source::Indirect ? (uint8_t(sib) & 7) : 0)
)) {
// Decisions on whether to include operand, displacement and/or size extension words
// have implicitly been made in the int packing above; honour them here.
int extension = 0;
if(has_operand()) {
extensions_[extension] = operand;
++extension;
}
if(has_displacement()) {
extensions_[extension] = ImmediateT(displacement);
++extension;
}
/// @returns The number of bytes used for meaningful content within this class. A receiver must use at least @c sizeof(Instruction) bytes
/// to store an @c Instruction but is permitted to reuse the trailing sizeof(Instruction) - packing_size() for any purpose it likes. Teleologically,
/// this allows a denser packing of instructions into containers.
constexpr size_t packing_size() const {
return
offsetof(Instruction<is_32bit>, extensions_) +
(has_displacement() + has_operand()) * sizeof(ImmediateT);
// Patch in a fully-resolved segment.
Source segment = segment_override;
if(segment == Source::None) segment = this->source().default_segment();
if(segment == Source::None) segment = this->destination().default_segment();
if(segment == Source::None) segment = Source::DS;
source_data_dest_sib_ |= (int(segment)&7) << 10;
}
/// @returns The number of bytes used for meaningful content within this class. A receiver must use at least @c sizeof(Instruction) bytes
/// to store an @c Instruction but is permitted to reuse the trailing sizeof(Instruction) - packing_size() for any purpose it likes. Teleologically,
/// this allows a denser packing of instructions into containers.
constexpr size_t packing_size() const {
return
offsetof(Instruction<is_32bit>, extensions_) +
(has_displacement() + has_operand()) * sizeof(ImmediateT);
}
/// @returns The @c Operation performed by this instruction.
constexpr Operation operation() const {
return operation_;
}
/// @returns A @c DataPointer describing the 'destination' of this instruction, conventionally the first operand in Intel-syntax assembly.
constexpr DataPointer destination() const {
return DataPointer(
Source(source_data_dest_sib_ & sib_masks[(source_data_dest_sib_ >> 3) & 3]),
((source_data_dest_sib_ >> 2) & 0xf8) | (source_data_dest_sib_ & 0x07)
);
}
/// @returns A @c DataPointer describing the 'source' of this instruction, conventionally the second operand in Intel-syntax assembly.
constexpr DataPointer source() const {
return DataPointer(
Source(mem_exts_source_ & sib_masks[(mem_exts_source_ >> 3) & 3]),
((source_data_dest_sib_ >> 2) & 0xf8) | (mem_exts_source_ & 0x07)
);
}
/// @returns @c true if the lock prefix was present on this instruction; @c false otherwise.
constexpr bool lock() const {
return source_data_dest_sib_ & (1 << 13);
}
/// @returns The address size for this instruction; will always be 16-bit for instructions decoded by a 16-bit decoder but can be 16- or 32-bit for
/// instructions decoded by a 32-bit decoder, depending on the program's use of the address size prefix byte.
constexpr AddressSize address_size() const {
return AddressSize(mem_exts_source_ >> 7);
}
/// @returns The segment that should be used for data fetches if this operation accepts segment overrides.
constexpr Source data_segment() const {
return Source(
int(Source::ES) +
((source_data_dest_sib_ >> 10) & 7)
);
}
/// @returns The data size of this operation — e.g. `MOV AX, BX` has a data size of `::Word` but `MOV EAX, EBX` has a data size of
/// `::DWord`. This value is guaranteed never to be `DataSize::None` even for operations such as `CLI` that don't have operands and operate
/// on data that is not a byte, word or double word.
constexpr DataSize operation_size() const {
return DataSize(source_data_dest_sib_ >> 14);
}
/// @returns The immediate value provided with this instruction, if any. E.g. `ADD AX, 23h` has the operand `23h`.
constexpr ImmediateT operand() const {
const ImmediateT ops[] = {0, operand_extension()};
return ops[has_operand()];
}
/// @returns The nesting level argument supplied to an ENTER.
constexpr ImmediateT nesting_level() const {
return operand();
}
/// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will
/// have a segment value of `1234h`.
constexpr uint16_t segment() const {
return uint16_t(operand());
}
/// @returns The offset provided with this instruction, if any. E.g. `MOV AX, [es:1998h]` has an offset of `1998h`.
constexpr ImmediateT offset() const {
const ImmediateT offsets[] = {0, displacement_extension()};
return offsets[has_displacement()];
}
/// @returns The displacement provided with this instruction `SUB AX, [SI+BP-23h]` has an offset of `-23h` and `JMP 19h`
/// has an offset of `19h`.
constexpr DisplacementT displacement() const {
return DisplacementT(offset());
}
/// @returns The dynamic storage size argument supplied to an ENTER.
constexpr ImmediateT dynamic_storage_size() const {
return displacement();
}
// Standard comparison operator.
constexpr bool operator ==(const Instruction<is_32bit> &rhs) const {
if( operation_ != rhs.operation_ ||
mem_exts_source_ != rhs.mem_exts_source_ ||
source_data_dest_sib_ != rhs.source_data_dest_sib_) {
return false;
}
/// @returns The @c Operation performed by this instruction.
constexpr Operation operation() const {
return operation_;
// Have already established above that this and RHS have the
// same extensions, if any.
const int extension_count = has_displacement() + has_operand();
for(int c = 0; c < extension_count; c++) {
if(extensions_[c] != rhs.extensions_[c]) return false;
}
/// @returns A @c DataPointer describing the 'destination' of this instruction, conventionally the first operand in Intel-syntax assembly.
constexpr DataPointer destination() const {
return DataPointer(
Source(source_data_dest_sib_ & sib_masks[(source_data_dest_sib_ >> 3) & 3]),
((source_data_dest_sib_ >> 2) & 0xf8) | (source_data_dest_sib_ & 0x07)
);
}
return true;
}
/// @returns A @c DataPointer describing the 'source' of this instruction, conventionally the second operand in Intel-syntax assembly.
constexpr DataPointer source() const {
return DataPointer(
Source(mem_exts_source_ & sib_masks[(mem_exts_source_ >> 3) & 3]),
((source_data_dest_sib_ >> 2) & 0xf8) | (mem_exts_source_ & 0x07)
);
}
private:
Operation operation_ = Operation::Invalid;
/// @returns @c true if the lock prefix was present on this instruction; @c false otherwise.
constexpr bool lock() const {
return source_data_dest_sib_ & (1 << 13);
}
// Packing and encoding of fields is admittedly somewhat convoluted; what this
// achieves is that instructions will be sized:
//
// four bytes + up to two extension words
// (extension words being two bytes for 16-bit instructions, four for 32)
//
// The extension words are used to retain an operand and displacement
// if the instruction has those.
/// @returns The address size for this instruction; will always be 16-bit for instructions decoded by a 16-bit decoder but can be 16- or 32-bit for
/// instructions decoded by a 32-bit decoder, depending on the program's use of the address size prefix byte.
constexpr AddressSize address_size() const {
return AddressSize(mem_exts_source_ >> 7);
}
// b7: address size;
// b6: has displacement;
// b5: has operand;
// [b4, b0]: source.
uint8_t mem_exts_source_ = 0;
/// @returns The segment that should be used for data fetches if this operation accepts segment overrides.
constexpr Source data_segment() const {
return Source(
int(Source::ES) +
((source_data_dest_sib_ >> 10) & 7)
);
}
bool has_displacement() const {
return mem_exts_source_ & (1 << 6);
}
bool has_operand() const {
return mem_exts_source_ & (1 << 5);
}
/// @returns The data size of this operation — e.g. `MOV AX, BX` has a data size of `::Word` but `MOV EAX, EBX` has a data size of
/// `::DWord`. This value is guaranteed never to be `DataSize::None` even for operations such as `CLI` that don't have operands and operate
/// on data that is not a byte, word or double word.
constexpr DataSize operation_size() const {
return DataSize(source_data_dest_sib_ >> 14);
}
// [b15, b14]: data size;
// [b13]: lock;
// [b12, b10]: segment override;
// [b9, b5]: top five of SIB;
// [b4, b0]: dest.
uint16_t source_data_dest_sib_ = 0;
/// @returns The immediate value provided with this instruction, if any. E.g. `ADD AX, 23h` has the operand `23h`.
constexpr ImmediateT operand() const {
const ImmediateT ops[] = {0, operand_extension()};
return ops[has_operand()];
}
// {operand}, {displacement}.
ImmediateT extensions_[2]{};
/// @returns The nesting level argument supplied to an ENTER.
constexpr ImmediateT nesting_level() const {
return operand();
}
/// @returns The immediate segment value provided with this instruction, if any. Relevant for far calls and jumps; e.g. `JMP 1234h:5678h` will
/// have a segment value of `1234h`.
constexpr uint16_t segment() const {
return uint16_t(operand());
}
/// @returns The offset provided with this instruction, if any. E.g. `MOV AX, [es:1998h]` has an offset of `1998h`.
constexpr ImmediateT offset() const {
const ImmediateT offsets[] = {0, displacement_extension()};
return offsets[has_displacement()];
}
/// @returns The displacement provided with this instruction `SUB AX, [SI+BP-23h]` has an offset of `-23h` and `JMP 19h`
/// has an offset of `19h`.
constexpr DisplacementT displacement() const {
return DisplacementT(offset());
}
/// @returns The dynamic storage size argument supplied to an ENTER.
constexpr ImmediateT dynamic_storage_size() const {
return displacement();
}
// Standard comparison operator.
constexpr bool operator ==(const Instruction<is_32bit> &rhs) const {
if( operation_ != rhs.operation_ ||
mem_exts_source_ != rhs.mem_exts_source_ ||
source_data_dest_sib_ != rhs.source_data_dest_sib_) {
return false;
}
// Have already established above that this and RHS have the
// same extensions, if any.
const int extension_count = has_displacement() + has_operand();
for(int c = 0; c < extension_count; c++) {
if(extensions_[c] != rhs.extensions_[c]) return false;
}
return true;
}
private:
Operation operation_ = Operation::Invalid;
// Packing and encoding of fields is admittedly somewhat convoluted; what this
// achieves is that instructions will be sized:
//
// four bytes + up to two extension words
// (extension words being two bytes for 16-bit instructions, four for 32)
//
// The extension words are used to retain an operand and displacement
// if the instruction has those.
// b7: address size;
// b6: has displacement;
// b5: has operand;
// [b4, b0]: source.
uint8_t mem_exts_source_ = 0;
bool has_displacement() const {
return mem_exts_source_ & (1 << 6);
}
bool has_operand() const {
return mem_exts_source_ & (1 << 5);
}
// [b15, b14]: data size;
// [b13]: lock;
// [b12, b10]: segment override;
// [b9, b5]: top five of SIB;
// [b4, b0]: dest.
uint16_t source_data_dest_sib_ = 0;
// {operand}, {displacement}.
ImmediateT extensions_[2]{};
ImmediateT operand_extension() const {
return extensions_[0];
}
ImmediateT displacement_extension() const {
return extensions_[(mem_exts_source_ >> 5) & 1];
}
// A lookup table to help with stripping parts of the SIB that have been
// hidden within the source/destination fields.
static constexpr uint8_t sib_masks[] = {
0x1f, 0x1f, 0x1f, 0x18
};
ImmediateT operand_extension() const {
return extensions_[0];
}
ImmediateT displacement_extension() const {
return extensions_[(mem_exts_source_ >> 5) & 1];
}
// A lookup table to help with stripping parts of the SIB that have been
// hidden within the source/destination fields.
static constexpr uint8_t sib_masks[] = {
0x1f, 0x1f, 0x1f, 0x18
};
};
static_assert(sizeof(Instruction<true>) <= 16);
@ -962,5 +972,4 @@ std::string to_string(
Model model,
int offset_length = 0,
int immediate_length = 0);
}

View File

@ -19,7 +19,7 @@ enum class Model {
i80386,
};
static constexpr bool is_32bit(Model model) { return model >= Model::i80386; }
static constexpr bool is_32bit(const Model model) { return model >= Model::i80386; }
template <bool is_32bit> struct AddressT { using type = uint16_t; };
template <> struct AddressT<true> { using type = uint32_t; };

View File

@ -35,7 +35,7 @@ enum Key: uint16_t {
KeyBreak = 0xfffd,
};
constexpr bool is_modifier(Key key) {
constexpr bool is_modifier(const Key key) {
return (key == KeyShift) || (key == KeyControl) || (key == KeyFunc);
}

View File

@ -25,8 +25,8 @@ enum class GraphicsMode {
/// Fat low res mode is regular low res mode, but clocked out at 7Mhz rather than 14, leading to improper colours.
FatLowRes
};
constexpr bool is_text_mode(GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
constexpr bool is_double_mode(GraphicsMode m) { return int(m) & 1; }
constexpr bool is_text_mode(const GraphicsMode m) { return m <= GraphicsMode::DoubleText; }
constexpr bool is_double_mode(const GraphicsMode m) { return int(m) & 1; }
template <typename TimeUnit> class VideoSwitches {
public:

View File

@ -86,7 +86,9 @@ class Video: public Apple::II::VideoSwitches<Cycles> {
DoubleHighResMono,
SuperHighRes
};
constexpr bool is_colour_ntsc(GraphicsMode m) { return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes; }
constexpr bool is_colour_ntsc(const GraphicsMode m) {
return m >= GraphicsMode::HighRes && m <= GraphicsMode::FatLowRes;
}
GraphicsMode graphics_mode(int row) const {
if(new_video_ & 0x80) {

View File

@ -59,7 +59,7 @@ enum class Source {
WDFDC,
};
constexpr bool is_enabled(Source source) {
constexpr bool is_enabled(const Source source) {
#ifdef NDEBUG
return false;
#endif
@ -133,39 +133,39 @@ constexpr const char *prefix(Source source) {
template <Source source>
class Logger {
public:
static constexpr bool enabled = is_enabled(source);
public:
static constexpr bool enabled = is_enabled(source);
struct LogLine {
public:
LogLine(FILE *stream) : stream_(stream) {
if constexpr (!enabled) return;
struct LogLine {
public:
LogLine(FILE *const stream) : stream_(stream) {
if constexpr (!enabled) return;
const auto source_prefix = prefix(source);
if(source_prefix) {
fprintf(stream_, "[%s] ", source_prefix);
}
const auto source_prefix = prefix(source);
if(source_prefix) {
fprintf(stream_, "[%s] ", source_prefix);
}
}
~LogLine() {
if constexpr (!enabled) return;
fprintf(stream_, "\n");
}
~LogLine() {
if constexpr (!enabled) return;
fprintf(stream_, "\n");
}
void append(const char *format, ...) {
if constexpr (!enabled) return;
va_list args;
va_start(args, format);
vfprintf(stream_, format, args);
va_end(args);
}
void append(const char *const format, ...) {
if constexpr (!enabled) return;
va_list args;
va_start(args, format);
vfprintf(stream_, format, args);
va_end(args);
}
private:
FILE *stream_;
};
private:
FILE *stream_;
};
LogLine info() { return LogLine(stdout); }
LogLine error() { return LogLine(stderr); }
LogLine info() { return LogLine(stdout); }
LogLine error() { return LogLine(stderr); }
};
}

View File

@ -50,7 +50,7 @@ enum class DisplayType {
CompositeMonochrome
};
constexpr bool is_composite(DisplayType type) {
constexpr bool is_composite(const DisplayType type) {
return type == DisplayType::CompositeColour || type == DisplayType::CompositeMonochrome;
}

View File

@ -38,10 +38,10 @@ enum Personality {
PWDC65C02, // like the Rockwell, but with STP and WAI
};
constexpr bool has_decimal_mode(Personality p) { return p >= Personality::P6502; }
constexpr bool is_65c02(Personality p) { return p >= Personality::PSynertek65C02; }
constexpr bool has_bbrbbsrmbsmb(Personality p) { return p >= Personality::PRockwell65C02; }
constexpr bool has_stpwai(Personality p) { return p >= Personality::PWDC65C02; }
constexpr bool has_decimal_mode(const Personality p) { return p >= Personality::P6502; }
constexpr bool is_65c02(const Personality p) { return p >= Personality::PSynertek65C02; }
constexpr bool has_bbrbbsrmbsmb(const Personality p) { return p >= Personality::PRockwell65C02; }
constexpr bool has_stpwai(const Personality p) { return p >= Personality::PWDC65C02; }
/*!
An opcode that is guaranteed to cause a 6502 to jam.

View File

@ -48,7 +48,7 @@ template <> class BusHandlerT<Type::TWDC65816>: public BusHandler<uint32_t> {};
/*
Query for implemented registers.
*/
constexpr bool has(Type processor_type, Register r) {
constexpr bool has(const Type processor_type, const Register r) {
switch(r) {
case Register::LastOperationAddress:
case Register::ProgramCounter:
@ -67,7 +67,7 @@ constexpr bool has(Type processor_type, Register r) {
}
}
constexpr bool has_extended_bus_output(Type processor_type) {
constexpr bool has_extended_bus_output(const Type processor_type) {
return processor_type == Type::TWDC65816;
}

View File

@ -115,7 +115,7 @@ class ProcessorStorage {
void *destination = nullptr;
PartialMachineCycle machine_cycle{};
};
static constexpr bool is_terminal(MicroOp::Type type) {
static constexpr bool is_terminal(const MicroOp::Type type) {
return type == MicroOp::MoveToNextProgram || type == MicroOp::DecodeOperation;
}

View File

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