mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-25 16:31:42 +00:00
Merge pull request #1010 from TomHarte/80386
Expands x86 decoder to recognise 80386 opcodes.
This commit is contained in:
commit
cbf9b345ff
320
InstructionSets/x86/DataPointerResolver.hpp
Normal file
320
InstructionSets/x86/DataPointerResolver.hpp
Normal file
@ -0,0 +1,320 @@
|
||||
//
|
||||
// DataPointerResolver.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 24/02/2022.
|
||||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef DataPointerResolver_hpp
|
||||
#define DataPointerResolver_hpp
|
||||
|
||||
#include "Instruction.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace InstructionSet {
|
||||
namespace x86 {
|
||||
|
||||
/// Unlike source, describes only registers, and breaks
|
||||
/// them down by conventional name — so AL, AH, AX and EAX are all
|
||||
/// listed separately and uniquely, rather than being eAX+size or
|
||||
/// eSPorAH with a size of 1.
|
||||
enum class Register: uint8_t {
|
||||
// 8-bit registers.
|
||||
AL, AH,
|
||||
CL, CH,
|
||||
DL, DH,
|
||||
BL, BH,
|
||||
|
||||
// 16-bit registers.
|
||||
AX, CX, DX, BX,
|
||||
SP, BP, SI, DI,
|
||||
ES, CS, SS, DS,
|
||||
FS, GS,
|
||||
|
||||
// 32-bit registers.
|
||||
EAX, ECX, EDX, EBX,
|
||||
ESP, EBP, ESI, EDI,
|
||||
|
||||
//
|
||||
None
|
||||
};
|
||||
|
||||
/// @returns @c true if @c r is the same size as @c DataT; @c false otherwise.
|
||||
/// @discussion Provided primarily to aid in asserts; if the decoder and resolver are both
|
||||
/// working then it shouldn't be necessary to test this in register files.
|
||||
template <typename DataT> constexpr bool is_sized(Register r) {
|
||||
static_assert(sizeof(DataT) == 4 || sizeof(DataT) == 2 || sizeof(DataT) == 1);
|
||||
|
||||
if constexpr (sizeof(DataT) == 4) {
|
||||
return r >= Register::EAX && r < Register::None;
|
||||
}
|
||||
|
||||
if constexpr (sizeof(DataT) == 2) {
|
||||
return r >= Register::AX && r < Register::EAX;
|
||||
}
|
||||
|
||||
if constexpr (sizeof(DataT) == 1) {
|
||||
return r >= Register::AL && r < Register::AX;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// @returns the proper @c Register given @c source and data of size @c sizeof(DataT),
|
||||
/// or Register::None if no such register exists (e.g. asking for a 32-bit version of CS).
|
||||
template <typename DataT> constexpr Register register_for_source(Source source) {
|
||||
static_assert(sizeof(DataT) == 4 || sizeof(DataT) == 2 || sizeof(DataT) == 1);
|
||||
|
||||
if constexpr (sizeof(DataT) == 4) {
|
||||
switch(source) {
|
||||
case Source::eAX: return Register::EAX;
|
||||
case Source::eCX: return Register::ECX;
|
||||
case Source::eDX: return Register::EDX;
|
||||
case Source::eBX: return Register::EBX;
|
||||
case Source::eSPorAH: return Register::ESP;
|
||||
case Source::eBPorCH: return Register::EBP;
|
||||
case Source::eSIorDH: return Register::ESI;
|
||||
case Source::eDIorBH: return Register::EDI;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (sizeof(DataT) == 2) {
|
||||
switch(source) {
|
||||
case Source::eAX: return Register::AX;
|
||||
case Source::eCX: return Register::CX;
|
||||
case Source::eDX: return Register::DX;
|
||||
case Source::eBX: return Register::BX;
|
||||
case Source::eSPorAH: return Register::SP;
|
||||
case Source::eBPorCH: return Register::BP;
|
||||
case Source::eSIorDH: return Register::SI;
|
||||
case Source::eDIorBH: return Register::DI;
|
||||
case Source::ES: return Register::ES;
|
||||
case Source::CS: return Register::CS;
|
||||
case Source::SS: return Register::SS;
|
||||
case Source::DS: return Register::DS;
|
||||
case Source::FS: return Register::FS;
|
||||
case Source::GS: return Register::GS;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (sizeof(DataT) == 1) {
|
||||
switch(source) {
|
||||
case Source::eAX: return Register::AL;
|
||||
case Source::eCX: return Register::CL;
|
||||
case Source::eDX: return Register::DL;
|
||||
case Source::eBX: return Register::BL;
|
||||
case Source::eSPorAH: return Register::AH;
|
||||
case Source::eBPorCH: return Register::CH;
|
||||
case Source::eSIorDH: return Register::DH;
|
||||
case Source::eDIorBH: return Register::BH;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
return Register::None;
|
||||
}
|
||||
|
||||
/// Reads from or writes to the source or target identified by a DataPointer, relying upon two user-supplied classes:
|
||||
///
|
||||
/// * a register bank; and
|
||||
/// * a memory pool.
|
||||
///
|
||||
/// The register bank should implement `template<typename DataT, Register> DataT read()` and `template<typename DataT, Register> void write(DataT)`.
|
||||
/// Those functions will be called only with registers and data types that are appropriate to the @c model.
|
||||
///
|
||||
/// The memory pool should implement `template<typename DataT> DataT read(Source segment, uint32_t address)` and
|
||||
/// `template<typename DataT> void write(Source segment, uint32_t address, DataT value)`.
|
||||
template <Model model, typename RegistersT, typename MemoryT> class DataPointerResolver {
|
||||
public:
|
||||
public:
|
||||
/// Reads the data pointed to by @c pointer, referencing @c instruction, @c memory and @c registers as necessary.
|
||||
template <typename DataT> static DataT read(
|
||||
RegistersT ®isters,
|
||||
MemoryT &memory,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer);
|
||||
|
||||
/// Writes @c value to the data pointed to by @c pointer, referencing @c instruction, @c memory and @c registers as necessary.
|
||||
template <typename DataT> static void write(
|
||||
RegistersT ®isters,
|
||||
MemoryT &memory,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer,
|
||||
DataT value);
|
||||
|
||||
/// Computes the effective address of @c pointer including any displacement applied by @c instruction.
|
||||
/// @c pointer must be of type Source::Indirect.
|
||||
template <bool obscured_indirectNoBase = true, bool has_base = true>
|
||||
static uint32_t effective_address(
|
||||
RegistersT ®isters,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer);
|
||||
|
||||
private:
|
||||
template <bool is_write, typename DataT> static void access(
|
||||
RegistersT ®isters,
|
||||
MemoryT &memory,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer,
|
||||
DataT &value);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Implementation begins here.
|
||||
//
|
||||
|
||||
template <Model model, typename RegistersT, typename MemoryT>
|
||||
template <typename DataT> DataT DataPointerResolver<model, RegistersT, MemoryT>::read(
|
||||
RegistersT ®isters,
|
||||
MemoryT &memory,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer) {
|
||||
DataT result;
|
||||
access<false>(registers, memory, instruction, pointer, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <Model model, typename RegistersT, typename MemoryT>
|
||||
template <typename DataT> void DataPointerResolver<model, RegistersT, MemoryT>::write(
|
||||
RegistersT ®isters,
|
||||
MemoryT &memory,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer,
|
||||
DataT value) {
|
||||
access<true>(registers, memory, instruction, pointer, value);
|
||||
}
|
||||
|
||||
#define rw(v, r, is_write) \
|
||||
case Source::r: \
|
||||
using VType = typename std::remove_reference<decltype(v)>::type; \
|
||||
if constexpr (is_write) { \
|
||||
registers.template write<VType, register_for_source<VType>(Source::r)>(v); \
|
||||
} else { \
|
||||
v = registers.template read<VType, register_for_source<VType>(Source::r)>(); \
|
||||
} \
|
||||
break;
|
||||
|
||||
#define ALLREGS(v, i) rw(v, eAX, i); rw(v, eCX, i); \
|
||||
rw(v, eDX, i); rw(v, eBX, i); \
|
||||
rw(v, eSPorAH, i); rw(v, eBPorCH, i); \
|
||||
rw(v, eSIorDH, i); rw(v, eDIorBH, i); \
|
||||
rw(v, ES, i); rw(v, CS, i); \
|
||||
rw(v, SS, i); rw(v, DS, i); \
|
||||
rw(v, FS, i); rw(v, GS, i);
|
||||
|
||||
template <Model model, typename RegistersT, typename MemoryT>
|
||||
template <bool obscured_indirectNoBase, bool has_base>
|
||||
uint32_t DataPointerResolver<model, RegistersT, MemoryT>::effective_address(
|
||||
RegistersT ®isters,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer) {
|
||||
using AddressT = typename Instruction<is_32bit(model)>::AddressT;
|
||||
AddressT base = 0, index = 0;
|
||||
|
||||
if constexpr (has_base) {
|
||||
switch(pointer.base<obscured_indirectNoBase>()) {
|
||||
default: break;
|
||||
ALLREGS(base, false);
|
||||
}
|
||||
}
|
||||
|
||||
switch(pointer.index()) {
|
||||
default: break;
|
||||
ALLREGS(index, false);
|
||||
}
|
||||
|
||||
uint32_t address = index;
|
||||
if constexpr (model >= Model::i80386) {
|
||||
address <<= pointer.scale();
|
||||
} else {
|
||||
assert(!pointer.scale());
|
||||
}
|
||||
|
||||
// Always compute address as 32-bit.
|
||||
// TODO: verify use of memory_mask around here.
|
||||
// Also I think possibly an exception is supposed to be generated
|
||||
// if the programmer is in 32-bit mode and has asked for 16-bit
|
||||
// address computation but generated e.g. a 17-bit result. Look into
|
||||
// that when working on execution. For now the goal is merely decoding
|
||||
// and this code exists both to verify the presence of all necessary
|
||||
// fields and to help to explore the best breakdown of storage
|
||||
// within Instruction.
|
||||
constexpr uint32_t memory_masks[] = {0x0000'ffff, 0xffff'ffff};
|
||||
const uint32_t memory_mask = memory_masks[int(instruction.address_size())];
|
||||
address = (address & memory_mask) + (base & memory_mask) + instruction.displacement();
|
||||
return address;
|
||||
}
|
||||
|
||||
template <Model model, typename RegistersT, typename MemoryT>
|
||||
template <bool is_write, typename DataT> void DataPointerResolver<model, RegistersT, MemoryT>::access(
|
||||
RegistersT ®isters,
|
||||
MemoryT &memory,
|
||||
const Instruction<is_32bit(model)> &instruction,
|
||||
DataPointer pointer,
|
||||
DataT &value) {
|
||||
const Source source = pointer.source<false>();
|
||||
|
||||
switch(source) {
|
||||
default:
|
||||
if constexpr (!is_write) {
|
||||
value = 0;
|
||||
}
|
||||
return;
|
||||
|
||||
ALLREGS(value, is_write);
|
||||
|
||||
case Source::DirectAddress:
|
||||
if constexpr(is_write) {
|
||||
memory.template write(instruction.data_segment(), instruction.displacement(), value);
|
||||
} else {
|
||||
value = memory.template read<DataT>(instruction.data_segment(), instruction.displacement());
|
||||
}
|
||||
break;
|
||||
case Source::Immediate:
|
||||
value = DataT(instruction.operand());
|
||||
break;
|
||||
|
||||
#define indirect(has_base) { \
|
||||
const auto address = effective_address<false, has_base> \
|
||||
(registers, instruction, pointer); \
|
||||
\
|
||||
if constexpr (is_write) { \
|
||||
memory.template write( \
|
||||
instruction.data_segment(), \
|
||||
address, \
|
||||
value \
|
||||
); \
|
||||
} else { \
|
||||
value = memory.template read<DataT>( \
|
||||
instruction.data_segment(), \
|
||||
address \
|
||||
); \
|
||||
} \
|
||||
}
|
||||
case Source::IndirectNoBase:
|
||||
indirect(false);
|
||||
break;
|
||||
|
||||
case Source::Indirect:
|
||||
indirect(true);
|
||||
break;
|
||||
#undef indirect
|
||||
|
||||
}
|
||||
}
|
||||
#undef ALLREGS
|
||||
#undef rw
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* DataPointerResolver_hpp */
|
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
||||
#define InstructionSets_x86_Decoder_hpp
|
||||
|
||||
#include "Instruction.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
@ -17,38 +18,54 @@
|
||||
namespace InstructionSet {
|
||||
namespace x86 {
|
||||
|
||||
enum class Model {
|
||||
i8086,
|
||||
};
|
||||
|
||||
/*!
|
||||
Implements Intel x86 instruction decoding.
|
||||
|
||||
This is an experimental implementation; it has not yet undergone significant testing.
|
||||
*/
|
||||
class Decoder {
|
||||
template <Model model> class Decoder {
|
||||
public:
|
||||
Decoder(Model model);
|
||||
using InstructionT = Instruction<is_32bit(model)>;
|
||||
|
||||
/*!
|
||||
@returns an @c Instruction plus a size; a positive size to indicate successful decoding; a
|
||||
negative size specifies the [negatived] 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.
|
||||
|
||||
@discussion although instructions also contain an indicator of their length, on chips prior
|
||||
to the 80286 there is no limit to instruction length and that could in theory overflow the available
|
||||
storage, which can describe instructions only up to 1kb in size.
|
||||
|
||||
The 80286 and 80386 have instruction length limits of 10 and 15 bytes respectively, so
|
||||
cannot overflow the field.
|
||||
*/
|
||||
std::pair<int, Instruction> decode(const uint8_t *source, size_t length);
|
||||
std::pair<int, InstructionT> decode(const uint8_t *source, size_t length);
|
||||
|
||||
/*!
|
||||
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_.
|
||||
AwaitingDisplacementOrOperand,
|
||||
DisplacementOrOperand,
|
||||
/// Forms and returns an Instruction, and resets parsing state.
|
||||
ReadyToPost
|
||||
} phase_ = Phase::Instruction;
|
||||
@ -59,29 +76,27 @@ class Decoder {
|
||||
/// 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 appropriate.
|
||||
// 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 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 check for the POP operation.
|
||||
MemRegPOP,
|
||||
// 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.
|
||||
// Use the 'register' field to check for the MOV operation.
|
||||
MemRegMOV,
|
||||
|
||||
// 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,
|
||||
// source_ field with the result. Fills destination_ with a segment
|
||||
// register based on the reg field.
|
||||
Seg_MemReg,
|
||||
MemReg_Seg,
|
||||
|
||||
//
|
||||
// 'Group 1'
|
||||
//
|
||||
|
||||
// Parse for mode and register/memory fields, populating the
|
||||
// destination_ field with the result. Use the 'register' field
|
||||
@ -89,32 +104,76 @@ class Decoder {
|
||||
// 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,
|
||||
|
||||
//
|
||||
// 'Group 2'
|
||||
//
|
||||
|
||||
// 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.
|
||||
SegReg,
|
||||
// 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'
|
||||
//
|
||||
|
||||
// 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'
|
||||
//
|
||||
|
||||
// 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'
|
||||
//
|
||||
|
||||
// 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 ADD/ADC/SBB/SUB/CMP, altering
|
||||
// the source to ::Immediate and setting an appropriate operand size.
|
||||
MemRegADC_to_CMP,
|
||||
//
|
||||
// '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,
|
||||
|
||||
//
|
||||
// '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,
|
||||
|
||||
//
|
||||
// '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;
|
||||
|
||||
// Ephemeral decoding state.
|
||||
Operation operation_ = Operation::Invalid;
|
||||
uint8_t instr_ = 0x00; // TODO: is this desired, versus loading more context into ModRegRMFormat?
|
||||
int consumed_ = 0, operand_bytes_ = 0;
|
||||
|
||||
// Source and destination locations.
|
||||
@ -122,30 +181,49 @@ class Decoder {
|
||||
Source destination_ = Source::None;
|
||||
|
||||
// Immediate fields.
|
||||
int16_t displacement_ = 0;
|
||||
uint16_t operand_ = 0;
|
||||
int32_t displacement_ = 0;
|
||||
uint32_t operand_ = 0;
|
||||
uint64_t inward_data_ = 0;
|
||||
int next_inward_data_shift_ = 0;
|
||||
|
||||
// Indirection style.
|
||||
ScaleIndexBase sib_;
|
||||
|
||||
// Facts about the instruction.
|
||||
int displacement_size_ = 0; // i.e. size of in-stream displacement, if any.
|
||||
int operand_size_ = 0; // i.e. size of in-stream operand, if any.
|
||||
int operation_size_ = 0; // i.e. size of data manipulated by the operation.
|
||||
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_ = 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;
|
||||
|
||||
// 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_ = 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_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
917
InstructionSets/x86/Documentation/80386 opcode map.html
Normal file
917
InstructionSets/x86/Documentation/80386 opcode map.html
Normal file
@ -0,0 +1,917 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>80386 Opcode Map</title>
|
||||
<style>
|
||||
table, table th, table td {
|
||||
border: 1px solid;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.codetable, .codetable th, .codetable td {
|
||||
border: 0px;
|
||||
border-collapse: collapse;
|
||||
padding-right: 1em;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.optable th, .optable td {
|
||||
width: 5em;
|
||||
}
|
||||
.optable tr:nth-child(even) {
|
||||
border-top: 3px solid;
|
||||
}
|
||||
|
||||
.grouptable, .grouptable th, .grouptable td {
|
||||
border-bottom: 3px solid;
|
||||
}
|
||||
.grouptable th, .grouptable td {
|
||||
width: 4em;
|
||||
}
|
||||
|
||||
.skiprow {
|
||||
background-color: darkgray;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Codes for Addressing Method</h1>
|
||||
|
||||
<table class="codetable">
|
||||
<tr>
|
||||
<td>A</td>
|
||||
<td>Direct address; the instruction has no MODRM field; the address of the operand is encoded in the instruction; no base register, index register, or scaling factor can be applied; e.g., far JMP (EA).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>C</td>
|
||||
<td>The reg field of the MODRM field selects a control register; e.g., MOV (0F20, 0F22).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>D</td>
|
||||
<td>The reg field of the MODRM field selects a debug register; e.g., MOV (0F21, 0F23).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>E</td>
|
||||
<td>A MODRM field follows the opcode and specifies the operand. The operand is either a general register or a memory address. If it is a memory address, the address is computed from a segment register and any of the following values: a base register, an index register, a scaling factor, a displacement.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>F</td>
|
||||
<td>Flags register</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>G</td>
|
||||
<td>The reg field of the MODRM field selects a general register; e.g,. ADD (00).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>I</td>
|
||||
<td>Immediate data. The value of the operand is encoded in subsequent bytes of the instruction.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>J</td>
|
||||
<td>The instruction contains a relative offset to be added to the instruction-pointer register; e.g., JMP short, LOOP.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>M</td>
|
||||
<td>The MODRM field may refer only to memory; e.g., BOUND, LES, LDS, LSS, LFS, LGS.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>O</td>
|
||||
<td>The instruction has no MODRM field; the offset of the operand is coded as a word or dword (depending on address sie attribute) in the instruction. No base register, index register, or scaling factor can be applied; e.g., MOV (A0–A3).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>R</td>
|
||||
<td>The mod field of the MODRM field may refer only to a general register; e.g., MOV(0F20–0F24, 0F26).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>S</td>
|
||||
<td>The reg field of the MODRM field selects a segment register; e.g., MOV (8C, 8E).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>T</td>
|
||||
<td>The reg field of the MODRM field selects a test register; e.g., MOV (0F24, 0F26).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>X</td>
|
||||
<td>Memory addressed by DS:SI; e.g., MOVS, COMPS, OUTS, LODS, SCAS.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Y</td>
|
||||
<td>Memory addressed by ES:DI; e.g., MOVS, CMPS, INS, STOS.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h1>Codes for Operand Type</h1>
|
||||
|
||||
<table class="codetable">
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>Two one-word operands in memory or two dword operands in memory, depending on operand size attribute (used only by BOUND).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>b</td>
|
||||
<td>Byte (regardless of operand size attribute).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>c</td>
|
||||
<td>Byte or word, depending on operand size attribute.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>Dword (regardless of operand size attribute).</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>p</td>
|
||||
<td>32-bit or 48-bit pointer, depending on operand size attribute.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>s</td>
|
||||
<td>Six-byte pesudo-descriptor.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>v</td>
|
||||
<td>Word or dword, depending on operand size attribute.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>w</td>
|
||||
<td>Word (regardless of operand size attribute).</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h1>Register Codes</h1>
|
||||
|
||||
When an operand is a specific register encoded in the opcode, the register is identifed by its name; e.g., AX, CL, or ESI. The name of the register indicates whether the register is 32, 16, or 8 bits wide. A register identifier of the form eXX is used when the width of the register depends on the operand size attribute. For example, eAX indicates that the AX register is used when the operand size attribute is 16, and the EAX register is used when the operand size attribute is 32.
|
||||
|
||||
<h1>One-byte 80386 Opcode Map</h1>
|
||||
<table class="optable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>x0</th>
|
||||
<th>x1</th>
|
||||
<th>x2</th>
|
||||
<th>x3</th>
|
||||
<th>x4</th>
|
||||
<th>x5</th>
|
||||
<th>x6</th>
|
||||
<th>x7</th>
|
||||
<th>x8</th>
|
||||
<th>x9</th>
|
||||
<th>xA</th>
|
||||
<th>xB</th>
|
||||
<th>xC</th>
|
||||
<th>xD</th>
|
||||
<th>xE</th>
|
||||
<th>xF</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>0x</th>
|
||||
|
||||
<td colspan=6>ADD</td>
|
||||
<td rowspan=2>PUSH ES</td>
|
||||
<td rowspan=2>POP ES</td>
|
||||
<td colspan=6>OR</td>
|
||||
<td rowspan=2>PUSH CS</td>
|
||||
<td rowspan=2>2-byte escape codes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- ADD -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
|
||||
<!-- OR -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>1x</th>
|
||||
|
||||
<td colspan=6>ADC</td>
|
||||
<td rowspan=2>PUSH SS</td>
|
||||
<td rowspan=2>POP SS</td>
|
||||
<td colspan=6>SBB</td>
|
||||
<td rowspan=2>PUSH DS</td>
|
||||
<td rowspan=2>POP DS</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- ADC -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
|
||||
<!-- SBB -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>2x</th>
|
||||
|
||||
<td colspan=6>AND</td>
|
||||
<td rowspan=2>SEG =ES</td>
|
||||
<td rowspan=2>POP ES</td>
|
||||
<td colspan=6>SUB</td>
|
||||
<td rowspan=2>SEG =CS</td>
|
||||
<td rowspan=2>DAS</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- AND -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
|
||||
<!-- SUB -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>3x</th>
|
||||
|
||||
<td colspan=6>XOR</td>
|
||||
<td rowspan=2>SEG =SS</td>
|
||||
<td rowspan=2>AAA</td>
|
||||
<td colspan=6>CMP</td>
|
||||
<td rowspan=2>SEG =DS</td>
|
||||
<td rowspan=2>AAS</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- XOR -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
|
||||
<!-- CMP -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>4x</th>
|
||||
|
||||
<td colspan=8>INC general register</td>
|
||||
<td colspan=8>DEC general register</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- INC general register -->
|
||||
<td>eAX</td>
|
||||
<td>eCX</td>
|
||||
<td>eDX</td>
|
||||
<td>eBX</td>
|
||||
<td>eSP</td>
|
||||
<td>eBP</td>
|
||||
<td>eSI</td>
|
||||
<td>eDI</td>
|
||||
|
||||
<!-- DEC general register -->
|
||||
<td>eAX</td>
|
||||
<td>eCX</td>
|
||||
<td>eDX</td>
|
||||
<td>eBX</td>
|
||||
<td>eSP</td>
|
||||
<td>eBP</td>
|
||||
<td>eSI</td>
|
||||
<td>eDI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>5x</th>
|
||||
|
||||
<td colspan=8>PUSH general register</td>
|
||||
<td colspan=8>POP general register</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- PUSH general register -->
|
||||
<td>eAX</td>
|
||||
<td>eCX</td>
|
||||
<td>eDX</td>
|
||||
<td>eBX</td>
|
||||
<td>eSP</td>
|
||||
<td>eBP</td>
|
||||
<td>eSI</td>
|
||||
<td>eDI</td>
|
||||
|
||||
<!-- POP general register -->
|
||||
<td>eAX</td>
|
||||
<td>eCX</td>
|
||||
<td>eDX</td>
|
||||
<td>eBX</td>
|
||||
<td>eSP</td>
|
||||
<td>eBP</td>
|
||||
<td>eSI</td>
|
||||
<td>eDI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>6x</th>
|
||||
|
||||
<td rowspan=2>PUSHA</td>
|
||||
<td rowspan=2>POPA</td>
|
||||
<td rowspan=2>BOUND Gv, Ma</td>
|
||||
<td rowspan=2>ARPL Gv, Ma</td>
|
||||
<td rowspan=2>SEG =FS</td>
|
||||
<td rowspan=2>SEG =GS</td>
|
||||
<td rowspan=2>Operand Size</td>
|
||||
<td rowspan=2>Address Size</td>
|
||||
<td rowspan=2>PUSH Iv</td>
|
||||
<td rowspan=2>IMUL GvEvIv</td>
|
||||
<td rowspan=2>PUSH Ib</td>
|
||||
<td rowspan=2>IMUL GvEvIb</td>
|
||||
<td rowspan=2>INSB Yb, Dx</td>
|
||||
<td rowspan=2>INSW/D Yv, Dx</td>
|
||||
<td rowspan=2>OUTSB Dx, Xb</td>
|
||||
<td rowspan=2>OUTSW/D Dx, Xb</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<th rowspan=2>7x</th>
|
||||
|
||||
<td colspan=16>Short-displacement jump on condition (Jb)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Short-displacement jump on condition (Jb) -->
|
||||
<td>JO</td>
|
||||
<td>JNO</td>
|
||||
<td>JB</td>
|
||||
<td>JNB</td>
|
||||
<td>JZ</td>
|
||||
<td>JNZ</td>
|
||||
<td>JBE</td>
|
||||
<td>JNBE</td>
|
||||
<td>JS</td>
|
||||
<td>JNS</td>
|
||||
<td>JP</td>
|
||||
<td>JNP</td>
|
||||
<td>JL</td>
|
||||
<td>JNL</td>
|
||||
<td>JLE</td>
|
||||
<td>JNLE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>8x</th>
|
||||
|
||||
<td colspan=2>Immediate Grp1</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>Grp1 Ev, Ib</td>
|
||||
<td colspan=2>TEST</td>
|
||||
<td colspan=2>XCHG</td>
|
||||
<td colspan=4>MOV</td>
|
||||
<td rowspan=2>MOV Ew, Sw</td>
|
||||
<td rowspan=2>LEA Gv, M</td>
|
||||
<td rowspan=2>MOV Sw, Ew</td>
|
||||
<td rowspan=2>POP Ev</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Immediate Grp1 -->
|
||||
<td>Eb, Ib</td>
|
||||
<td>Ev, Iv</td>
|
||||
|
||||
<!-- TEST -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
|
||||
<!-- XCHG -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
|
||||
<!-- MOV -->
|
||||
<td>Eb, Gb</td>
|
||||
<td>Ev, Gv</td>
|
||||
<td>Gb, Eb</td>
|
||||
<td>Gv, Ev</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>9x</th>
|
||||
|
||||
<td rowspan=2>NOP</td>
|
||||
<td colspan=7>XCHG word or double-word register with eAX</td>
|
||||
<td rowspan=2>CBW</td>
|
||||
<td rowspan=2>CWD</td>
|
||||
<td rowspan=2>CALL Ap</td>
|
||||
<td rowspan=2>WAIT</td>
|
||||
<td rowspan=2>PUSHF Fv</td>
|
||||
<td rowspan=2>POPF Fv</td>
|
||||
<td rowspan=2>SAHF</td>
|
||||
<td rowspan=2>LAHF</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- XCHG -->
|
||||
<td>eCX</td>
|
||||
<td>eDX</td>
|
||||
<td>eBX</td>
|
||||
<td>eSP</td>
|
||||
<td>eBP</td>
|
||||
<td>eSI</td>
|
||||
<td>eDI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Ax</th>
|
||||
|
||||
<td colspan=4>MOV</td>
|
||||
<td rowspan=2>MOVSB Xb, Yv</td>
|
||||
<td rowspan=2>MOVSW/D Xv, Yv</td>
|
||||
<td rowspan=2>CMPSB Xb, Yb</td>
|
||||
<td rowspan=2>CMPSW/D Xv, Yv</td>
|
||||
<td colspan=2>TEST</td>
|
||||
<td rowspan=2>STOSB Yb, AL</td>
|
||||
<td rowspan=2>STOSW/D Yv, eAX</td>
|
||||
<td rowspan=2>LDSB AL, Xb</td>
|
||||
<td rowspan=2>LDSW/D eAX, Yv</td>
|
||||
<td rowspan=2>SCASB AL, Xb</td>
|
||||
<td rowspan=2>SCASW/D eAX, Xv</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- MOV -->
|
||||
<td>AL, Ob</td>
|
||||
<td>eAX, Ov</td>
|
||||
<td>Ob, AL</td>
|
||||
<td>Ov, eAX</td>
|
||||
|
||||
<!-- TEST -->
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Iv</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Bx</th>
|
||||
|
||||
<td colspan=8>MOV immediate byte into byte register</td>
|
||||
<td colspan=8>MOV immediate word or double into word or double register</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>AL</td>
|
||||
<td>CL</td>
|
||||
<td>DL</td>
|
||||
<td>BL</td>
|
||||
<td>AH</td>
|
||||
<td>CH</td>
|
||||
<td>DH</td>
|
||||
<td>BH</td>
|
||||
|
||||
<td>eAX</td>
|
||||
<td>eCX</td>
|
||||
<td>eDX</td>
|
||||
<td>eBX</td>
|
||||
<td>eSP</td>
|
||||
<td>eBP</td>
|
||||
<td>eSI</td>
|
||||
<td>eDI</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Cx</th>
|
||||
|
||||
<td colspan=2>Shift Grp2</td>
|
||||
<td colspan=2>RET near</td>
|
||||
<td rowspan=2>LES Gv, Mp</td>
|
||||
<td rowspan=2>LDS Gv, Mp</td>
|
||||
<td colspan=2>MOV</td>
|
||||
<td rowspan=2>ENTER</td>
|
||||
<td rowspan=2>LEAVE</td>
|
||||
<td colspan=2>RET far</td>
|
||||
<td rowspan=2>INT 3</td>
|
||||
<td rowspan=2>INT Ib</td>
|
||||
<td rowspan=2>INTO</td>
|
||||
<td rowspan=2>IRET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eb, Ib</td>
|
||||
<td>Ev, Iv</td>
|
||||
<td>Iw</td>
|
||||
<td></td>
|
||||
<td>Eb, Ib</td>
|
||||
<td>Ev, Iv</td>
|
||||
<td>Iw</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Dx</th>
|
||||
|
||||
<td colspan=4>Shift Grp2</td>
|
||||
<td rowspan=2>AAM</td>
|
||||
<td rowspan=2>AAD</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>XLAT</td>
|
||||
<td colspan=8 rowspan=2>ESC (Escape to coprocessor instruction set)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Eb, 1</td>
|
||||
<td>Ev, 1</td>
|
||||
<td>Eb, CL</td>
|
||||
<td>Ev, CL</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Ex</th>
|
||||
|
||||
<td rowspan=2>LOOPNE Jb</td>
|
||||
<td rowspan=2>LOOPE Jb</td>
|
||||
<td rowspan=2>LOOP Jb</td>
|
||||
<td rowspan=2>JCXZ Jb</td>
|
||||
<td colspan=2>IN</td>
|
||||
<td colspan=2>OUT</td>
|
||||
<td rowspan=2>CALL Jv</td>
|
||||
<td colspan=3>JMP</td>
|
||||
<td colspan=2>IN</td>
|
||||
<td colspan=2>OUT</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- IN -->
|
||||
<td>AL, Ib</td>
|
||||
<td>eAX, Ib</td>
|
||||
|
||||
<!-- OUT -->
|
||||
<td>Ib, AL</td>
|
||||
<td>Ib, eAX</td>
|
||||
|
||||
<!-- JMP -->
|
||||
<td>Jv</td>
|
||||
<td>Ap</td>
|
||||
<td>Jb</td>
|
||||
|
||||
<!-- IN -->
|
||||
<td>AL, DX</td>
|
||||
<td>eAX, DX</td>
|
||||
|
||||
<!-- OUT -->
|
||||
<td>DX, AL</td>
|
||||
<td>DX, eAX</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Fx</th>
|
||||
|
||||
<td rowspan=2>LOCK</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>REPNE</td>
|
||||
<td rowspan=2>REP / REPE</td>
|
||||
<td rowspan=2>HLT</td>
|
||||
<td rowspan=2>CMC</td>
|
||||
<td colspan=2>Unary Grp3</td>
|
||||
<td rowspan=2>CLC</td>
|
||||
<td rowspan=2>STC</td>
|
||||
<td rowspan=2>CLI</td>
|
||||
<td rowspan=2>STI</td>
|
||||
<td rowspan=2>CLD</td>
|
||||
<td rowspan=2>STD</td>
|
||||
<td rowspan=2>INC/DEC Grp4</td>
|
||||
<td rowspan=2>Indirect Grp5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Unary Grp3 -->
|
||||
<td>Eb</td>
|
||||
<td>Ev</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h1>Two-Byte 80386 Opcode Map (First byte is 0FH)</h1>
|
||||
<table class="optable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>x0</th>
|
||||
<th>x1</th>
|
||||
<th>x2</th>
|
||||
<th>x3</th>
|
||||
<th>x4</th>
|
||||
<th>x5</th>
|
||||
<th>x6</th>
|
||||
<th>x7</th>
|
||||
<th>x8</th>
|
||||
<th>x9</th>
|
||||
<th>xA</th>
|
||||
<th>xB</th>
|
||||
<th>xC</th>
|
||||
<th>xD</th>
|
||||
<th>xE</th>
|
||||
<th>xF</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>0x</th>
|
||||
|
||||
<td rowspan=2>Grp6</td>
|
||||
<td rowspan=2>Grp7</td>
|
||||
<td rowspan=2>LAR Gv, Ew</td>
|
||||
<td rowspan=2>LSL Gv, Ew</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>CLTS</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<th rowspan=2>1x</th>
|
||||
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<th rowspan=2>2x</th>
|
||||
|
||||
<td rowspan=2>MOV Cd, Rd</td>
|
||||
<td rowspan=2>MOV Dd, Rd</td>
|
||||
<td rowspan=2>MOV Rd, Cd</td>
|
||||
<td rowspan=2>MOV Rd, Dd</td>
|
||||
<td rowspan=2>MOV Td, Rd</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>MOV Rd, Td</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr class="skiprow">
|
||||
<th rowspan=2 colspan=17>≈</th>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<th rowspan=2>8x</th>
|
||||
|
||||
<td colspan=16>Long-displacement jump on condition (Jv)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Long-displacement jump on condition (Jv) -->
|
||||
<td>JO</td>
|
||||
<td>JNO</td>
|
||||
<td>JB</td>
|
||||
<td>JNB</td>
|
||||
<td>JZ</td>
|
||||
<td>JNZ</td>
|
||||
<td>JBE</td>
|
||||
<td>JNBE</td>
|
||||
<td>JS</td>
|
||||
<td>JNS</td>
|
||||
<td>JP</td>
|
||||
<td>JNP</td>
|
||||
<td>JL</td>
|
||||
<td>JNL</td>
|
||||
<td>JLE</td>
|
||||
<td>JNLE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>9x</th>
|
||||
|
||||
<td colspan=16>Byte set on condition (Eb)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- Byte set on condition (Eb) -->
|
||||
<td>SETO</td>
|
||||
<td>SETNO</td>
|
||||
<td>SETB</td>
|
||||
<td>SETNB</td>
|
||||
<td>SETZ</td>
|
||||
<td>SETNZ</td>
|
||||
<td>SETBE</td>
|
||||
<td>SETNBE</td>
|
||||
<td>SETS</td>
|
||||
<td>SETNS</td>
|
||||
<td>SETP</td>
|
||||
<td>SETNP</td>
|
||||
<td>SETL</td>
|
||||
<td>SETNL</td>
|
||||
<td>SETLE</td>
|
||||
<td>SETNLE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowspan=2>Ax</th>
|
||||
|
||||
<td rowspan=2>PUSH FS</td>
|
||||
<td rowspan=2>POP FS</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>BT Ev, Gv</td>
|
||||
<td rowspan=2>SHLD EvGvIb</td>
|
||||
<td rowspan=2>SHLD EvGvCL</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>PUSH GS</td>
|
||||
<td rowspan=2>POP GS</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>BTS Ev, Gv</td>
|
||||
<td rowspan=2>SHRD EvGvIb</td>
|
||||
<td rowspan=2>SHRD EvGvCL</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>IMUL Gv, Ev</td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<th rowspan=2>Bx</th>
|
||||
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>LSS Mp</td>
|
||||
<td rowspan=2>BTR Ev, Gv</td>
|
||||
<td rowspan=2>LFS Mp</td>
|
||||
<td rowspan=2>LGS Mp</td>
|
||||
<td colspan=2>MOVZX</td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2>Grp8 Ev, Ib</td>
|
||||
<td rowspan=2>BTC Ev, Gv</td>
|
||||
<td rowspan=2>BSF Gv, Ev</td>
|
||||
<td rowspan=2>BSR Gv, Ev</td>
|
||||
<td colspan=2>MOVSX</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<!-- MOVZX -->
|
||||
<td>Gv, Eb</td>
|
||||
<td>Gv, Ew</td>
|
||||
|
||||
<!-- MOVSX -->
|
||||
<td>Gv, Eb</td>
|
||||
<td>Gv, Ew</td>
|
||||
</tr>
|
||||
<tr class="skiprow">
|
||||
<th rowspan=2 colspan=17>≈</th>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
<tr>
|
||||
<th rowspan=2>Fx</th>
|
||||
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
<td rowspan=2></td>
|
||||
</tr>
|
||||
<tr></tr>
|
||||
</table>
|
||||
<h1>Opcodes Determined by Bits 5, 4, 3 of MODRM Field</h1>
|
||||
<table>
|
||||
<tr>
|
||||
<td>mod</td>
|
||||
<td>nnn</td>
|
||||
<td>R/M</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br />
|
||||
<table class="grouptable">
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>000</th>
|
||||
<th>001</th>
|
||||
<th>010</th>
|
||||
<th>011</th>
|
||||
<th>100</th>
|
||||
<th>101</th>
|
||||
<th>110</th>
|
||||
<th>111</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 1</th>
|
||||
|
||||
<td>ADD</td>
|
||||
<td>OR</td>
|
||||
<td>ADC</td>
|
||||
<td>SBB</td>
|
||||
<td>AND</td>
|
||||
<td>SUB</td>
|
||||
<td>XOR</td>
|
||||
<td>CMP</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 2</th>
|
||||
|
||||
<td>ROL</td>
|
||||
<td>ROR</td>
|
||||
<td>RCL</td>
|
||||
<td>RCR</td>
|
||||
<td>SHL</td>
|
||||
<td>SHR</td>
|
||||
<td></td>
|
||||
<td>SAR</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 3</th>
|
||||
|
||||
<td>TEST Ib/Iv</td>
|
||||
<td></td>
|
||||
<td>NOT</td>
|
||||
<td>NEG</td>
|
||||
<td>MUL AL/eAX</td>
|
||||
<td>IMUL AL/EAX</td>
|
||||
<td>DIV AL/eAX</td>
|
||||
<td>IDIV AL/eAX</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 4</th>
|
||||
|
||||
<td>INC Eb</td>
|
||||
<td>DEC Eb</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 5</th>
|
||||
|
||||
<td>INC Ev</td>
|
||||
<td>DEC Ev</td>
|
||||
<td>CALL Ev</td>
|
||||
<td>CALL Ep</td>
|
||||
<td>JMP Ev</td>
|
||||
<td>JMP Ep</td>
|
||||
<td>PUSH Ev</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 6</th>
|
||||
|
||||
<td>SLDT Ew</td>
|
||||
<td>STR Ew</td>
|
||||
<td>LLDT Ew</td>
|
||||
<td>LTR Ew</td>
|
||||
<td>VERR Ew</td>
|
||||
<td>VERW Ew</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 7</th>
|
||||
|
||||
<td>SGDT Ms</td>
|
||||
<td>SIDT Ms</td>
|
||||
<td>LGDT Ms</td>
|
||||
<td>LIDT Ms</td>
|
||||
<td>SMSW Ew</td>
|
||||
<td></td>
|
||||
<td>LMSW Ew</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Group 8</th>
|
||||
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>BT</td>
|
||||
<td>BTS</td>
|
||||
<td>BTR</td>
|
||||
<td>BTC</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -9,7 +9,9 @@
|
||||
#ifndef InstructionSets_x86_Instruction_h
|
||||
#define InstructionSets_x86_Instruction_h
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace InstructionSet {
|
||||
namespace x86 {
|
||||
@ -23,6 +25,10 @@ namespace x86 {
|
||||
enum class Operation: uint8_t {
|
||||
Invalid,
|
||||
|
||||
//
|
||||
// 8086 instructions.
|
||||
//
|
||||
|
||||
/// ASCII adjust after addition; source will be AL and destination will be AX.
|
||||
AAA,
|
||||
/// ASCII adjust before division; destination will be AX and source will be a multiplier.
|
||||
@ -36,9 +42,13 @@ enum class Operation: uint8_t {
|
||||
/// Decimal adjust after subtraction; source and destination will be AL.
|
||||
DAS,
|
||||
|
||||
/// Convert byte into word; source will be AL, destination will be AH.
|
||||
/// If data size is word, convert byte into word; source will be AL, destination will be AH.
|
||||
/// If data size is DWord, convert word to dword; AX will be expanded to fill EAX.
|
||||
/// In both cases, conversion will be by sign extension.
|
||||
CBW,
|
||||
/// Convert word to double word; source will be AX and destination will be DX.
|
||||
/// If data size is Word, converts word to double word; source will be AX and destination will be DX.
|
||||
/// If data size is DWord, converts double word to quad word (i.e. CDW); source will be EAX and destination will be EDX:EAX.
|
||||
/// In both cases, conversion will be by sign extension.
|
||||
CWD,
|
||||
|
||||
/// Escape, for a coprocessor; perform the bus cycles necessary to read the source and destination and perform a NOP.
|
||||
@ -59,8 +69,8 @@ enum class Operation: uint8_t {
|
||||
SUB,
|
||||
/// Unsigned multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX.
|
||||
MUL,
|
||||
/// Signed multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX.
|
||||
IMUL,
|
||||
/// Single operand signed multiply; multiplies the source value by AX or AL, storing the result in DX:AX or AX.
|
||||
IMUL_1,
|
||||
/// Unsigned divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH.
|
||||
DIV,
|
||||
/// Signed divide; divide the source value by AX or AL, storing the quotient in AL and the remainder in AH.
|
||||
@ -81,27 +91,27 @@ enum class Operation: uint8_t {
|
||||
JS, JNS, JP, JNP, JL, JNL, JLE, JNLE,
|
||||
|
||||
/// Far call; see the segment() and offset() fields.
|
||||
CALLF,
|
||||
/// Displacement call; followed by a 16-bit operand providing a call offset.
|
||||
CALLD,
|
||||
CALLfar,
|
||||
/// Relative call; see displacement().
|
||||
CALLrel,
|
||||
/// Near call.
|
||||
CALLN,
|
||||
CALLabs,
|
||||
/// Return from interrupt.
|
||||
IRET,
|
||||
/// Near return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack.
|
||||
RETF,
|
||||
RETfar,
|
||||
/// Far return; if source is not ::None then it will be an ::Immediate indicating how many additional bytes to remove from the stack.
|
||||
RETN,
|
||||
/// Near jump; if an operand is not ::None then it gives an absolute destination; otherwise see the displacement.
|
||||
JMPN,
|
||||
RETnear,
|
||||
/// Near jump with an absolute destination.
|
||||
JMPabs,
|
||||
/// Near jump with a relative destination.
|
||||
JMPrel,
|
||||
/// Far jump to the indicated segment and offset.
|
||||
JMPF,
|
||||
JMPfar,
|
||||
/// Relative jump performed only if CX = 0; see the displacement.
|
||||
JPCX,
|
||||
/// Generates a software interrupt of the level stated in the operand.
|
||||
INT,
|
||||
/// Generates a software interrupt of level 3.
|
||||
INT3,
|
||||
/// Generates a software interrupt of level 4 if overflow is set.
|
||||
INTO,
|
||||
|
||||
@ -152,19 +162,19 @@ enum class Operation: uint8_t {
|
||||
PUSH,
|
||||
/// PUSH the flags register to the stack.
|
||||
PUSHF,
|
||||
/// Rotate the destination left through carry the number of bits indicated by source.
|
||||
/// Rotate the destination left through carry the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
RCL,
|
||||
/// Rotate the destination right through carry the number of bits indicated by source.
|
||||
/// Rotate the destination right through carry the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
RCR,
|
||||
/// Rotate the destination left the number of bits indicated by source.
|
||||
/// Rotate the destination left the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
ROL,
|
||||
/// Rotate the destination right the number of bits indicated by source.
|
||||
/// Rotate the destination right the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
ROR,
|
||||
/// Arithmetic shift left the destination by the number of bits indicated by source.
|
||||
/// Arithmetic shift left the destination by the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
SAL,
|
||||
/// Arithmetic shift right the destination by the number of bits indicated by source.
|
||||
/// Arithmetic shift right the destination by the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
SAR,
|
||||
/// Logical shift right the destination by the number of bits indicated by source.
|
||||
/// Logical shift right the destination by the number of bits indicated by source; if the source is a register then implicitly its size is 1.
|
||||
SHR,
|
||||
|
||||
/// Clear carry flag; no source or destination provided.
|
||||
@ -192,110 +202,599 @@ enum class Operation: uint8_t {
|
||||
|
||||
/// Load AL with DS:[AL+BX].
|
||||
XLAT,
|
||||
|
||||
//
|
||||
// 80186 additions.
|
||||
//
|
||||
|
||||
/// Checks whether the signed value in the destination register is within the bounds
|
||||
/// stored at the location indicated by the source register, which will point to two
|
||||
/// 16- or 32-bit words, the first being a signed lower bound and the signed upper.
|
||||
/// Raises a bounds exception if not.
|
||||
BOUND,
|
||||
|
||||
|
||||
/// Create stack frame. See operand() for the nesting level and offset()
|
||||
/// for the dynamic storage size.
|
||||
ENTER,
|
||||
/// Procedure exit; copies BP to SP, then pops a new BP from the stack.
|
||||
LEAVE,
|
||||
|
||||
/// Inputs a byte, word or double word from the port specified by DX, writing it to
|
||||
/// ES:[e]DI and incrementing or decrementing [e]DI as per the
|
||||
/// current EFLAGS DF flag.
|
||||
INS,
|
||||
/// Outputs a byte, word or double word from ES:[e]DI to the port specified by DX,
|
||||
/// incrementing or decrementing [e]DI as per the current EFLAGS DF flag.]
|
||||
OUTS,
|
||||
|
||||
/// Pushes all general purpose registers to the stack, in the order:
|
||||
/// AX, CX, DX, BX, [original] SP, BP, SI, DI.
|
||||
PUSHA,
|
||||
/// Pops all general purpose registers from the stack, in the reverse of
|
||||
/// the PUSHA order, i.e. DI, SI, BP, [final] SP, BX, DX, CX, AX.
|
||||
POPA,
|
||||
|
||||
//
|
||||
// 80286 additions.
|
||||
//
|
||||
|
||||
// TODO: expand detail on all operations below.
|
||||
|
||||
/// Adjusts requested privilege level.
|
||||
ARPL,
|
||||
/// Clears the task-switched flag.
|
||||
CLTS,
|
||||
/// Loads access rights.
|
||||
LAR,
|
||||
|
||||
/// Loads the global descriptor table.
|
||||
LGDT,
|
||||
/// Loads the interrupt descriptor table.
|
||||
LIDT,
|
||||
/// Loads the local descriptor table.
|
||||
LLDT,
|
||||
/// Stores the global descriptor table.
|
||||
SGDT,
|
||||
/// Stores the interrupt descriptor table.
|
||||
SIDT,
|
||||
/// Stores the local descriptor table.
|
||||
SLDT,
|
||||
|
||||
/// Verifies a segment for reading.
|
||||
VERR,
|
||||
/// Verifies a segment for writing.
|
||||
VERW,
|
||||
|
||||
/// Loads the machine status word.
|
||||
LMSW,
|
||||
/// Stores the machine status word.
|
||||
SMSW,
|
||||
/// Loads a segment limit
|
||||
LSL,
|
||||
/// Loads the task register.
|
||||
LTR,
|
||||
/// Stores the task register.
|
||||
STR,
|
||||
|
||||
/// Three-operand form of IMUL; multiply the immediate by the source and write to the destination.
|
||||
IMUL_3,
|
||||
|
||||
/// Undocumented (but used); loads all registers, including internal ones.
|
||||
LOADALL,
|
||||
|
||||
//
|
||||
// 80386 additions.
|
||||
//
|
||||
|
||||
/// Loads a pointer to FS.
|
||||
LFS,
|
||||
/// Loads a pointer to GS.
|
||||
LGS,
|
||||
/// Loads a pointer to SS.
|
||||
LSS,
|
||||
|
||||
/// Shift left double.
|
||||
SHLDimm,
|
||||
SHLDCL,
|
||||
/// Shift right double.
|
||||
SHRDimm,
|
||||
SHRDCL,
|
||||
|
||||
/// Bit scan forwards.
|
||||
BSF,
|
||||
/// Bit scan reverse.
|
||||
BSR,
|
||||
/// Bit test.
|
||||
BT,
|
||||
/// Bit test and complement.
|
||||
BTC,
|
||||
/// Bit test and reset.
|
||||
BTR,
|
||||
/// Bit test and set.
|
||||
BTS,
|
||||
|
||||
/// Move from the source to the destination, extending the source with zeros.
|
||||
/// The instruction data size dictates the size of the source; the destination will
|
||||
/// be either 16- or 32-bit depending on the current processor operating mode.
|
||||
MOVZX,
|
||||
/// Move from the source to the destination, applying a sign extension.
|
||||
/// The instruction data size dictates the size of the source; the destination will
|
||||
/// be either 16- or 32-bit depending on the current processor operating mode.
|
||||
MOVSX,
|
||||
|
||||
/// Two-operand form of IMUL; multiply the source by the destination and write to the destination.
|
||||
IMUL_2,
|
||||
|
||||
// Various conditional sets; each sets the byte at the location given by the operand
|
||||
// to $ff if the condition is met; $00 otherwise.
|
||||
SETO, SETNO, SETB, SETNB, SETZ, SETNZ, SETBE, SETNBE,
|
||||
SETS, SETNS, SETP, SETNP, SETL, SETNL, SETLE, SETNLE,
|
||||
|
||||
// Various special-case moves (i.e. those where it is impractical to extend the
|
||||
// Source enum, so the requirement for special handling is loaded into the operation).
|
||||
// In all cases the Cx, Dx and Tx Source aliases can be used to reinterpret the relevant
|
||||
// source or destination.
|
||||
MOVtoCr, MOVfromCr,
|
||||
MOVtoDr, MOVfromDr,
|
||||
MOVtoTr, MOVfromTr,
|
||||
};
|
||||
|
||||
enum class Size: uint8_t {
|
||||
Implied = 0,
|
||||
Byte = 1,
|
||||
Word = 2,
|
||||
DWord = 4,
|
||||
enum class DataSize: uint8_t {
|
||||
Byte = 0,
|
||||
Word = 1,
|
||||
DWord = 2,
|
||||
None = 3,
|
||||
};
|
||||
|
||||
constexpr int byte_size(DataSize size) {
|
||||
return (1 << int(size)) & 7;
|
||||
}
|
||||
|
||||
constexpr int bit_size(DataSize size) {
|
||||
return (8 << int(size)) & 0x3f;
|
||||
}
|
||||
|
||||
enum class AddressSize: uint8_t {
|
||||
b16 = 0,
|
||||
b32 = 1,
|
||||
};
|
||||
|
||||
constexpr DataSize data_size(AddressSize size) {
|
||||
return DataSize(int(size) + 1);
|
||||
}
|
||||
|
||||
constexpr int byte_size(AddressSize size) {
|
||||
return 2 << int(size);
|
||||
}
|
||||
|
||||
constexpr int bit_size(AddressSize size) {
|
||||
return 16 << int(size);
|
||||
}
|
||||
|
||||
enum class Source: uint8_t {
|
||||
// These are in SIB order; this matters for packing later on.
|
||||
|
||||
/// AL, AX or EAX depending on size.
|
||||
eAX,
|
||||
/// CL, CX or ECX depending on size.
|
||||
eCX,
|
||||
/// DL, DX or EDX depending on size.
|
||||
eDX,
|
||||
/// BL, BX or BDX depending on size.
|
||||
eBX,
|
||||
/// AH if size is 1; SP or ESP otherwise.
|
||||
eSPorAH,
|
||||
/// CH if size is 1; BP or EBP otherwise.
|
||||
eBPorCH,
|
||||
/// DH if size is 1; SI or ESI otherwise.
|
||||
eSIorDH,
|
||||
/// BH if size is 1; DI or EDI otherwise.
|
||||
eDIorBH,
|
||||
|
||||
// Aliases for the dual-purpose enums.
|
||||
eSP = eSPorAH, AH = eSPorAH,
|
||||
eBP = eBPorCH, CH = eBPorCH,
|
||||
eSI = eSIorDH, DH = eSIorDH,
|
||||
eDI = eDIorBH, BH = eDIorBH,
|
||||
|
||||
// Aliases for control, test and debug registers.
|
||||
C0 = 0, C1 = 1, C2 = 2, C3 = 3, C4 = 4, C5 = 5, C6 = 6, C7 = 7,
|
||||
T0 = 0, T1 = 1, T2 = 2, T3 = 3, T4 = 4, T5 = 5, T6 = 6, T7 = 7,
|
||||
D0 = 0, D1 = 1, D2 = 2, D3 = 3, D4 = 4, D5 = 5, D6 = 6, D7 = 7,
|
||||
|
||||
// Selectors.
|
||||
ES, CS, SS, DS, FS, GS,
|
||||
|
||||
/// @c None can be treated as a source that produces 0 when encountered;
|
||||
/// it is semantically valid to receive it with that meaning in some contexts —
|
||||
/// e.g. to indicate no index in indirect addressing.
|
||||
/// It's listed here in order to allow an [optional] segment override to fit into three bits.
|
||||
None,
|
||||
CS, DS, ES, SS,
|
||||
|
||||
AL, AH, AX,
|
||||
BL, BH, BX,
|
||||
CL, CH, CX,
|
||||
DL, DH, DX,
|
||||
|
||||
SI, DI,
|
||||
BP, SP,
|
||||
|
||||
IndBXPlusSI,
|
||||
IndBXPlusDI,
|
||||
IndBPPlusSI,
|
||||
IndBPPlusDI,
|
||||
IndSI,
|
||||
IndDI,
|
||||
/// The address included within this instruction should be used as the source.
|
||||
DirectAddress,
|
||||
IndBP,
|
||||
IndBX,
|
||||
|
||||
Immediate
|
||||
/// The immediate value included within this instruction should be used as the source.
|
||||
Immediate,
|
||||
|
||||
/// The ScaleIndexBase associated with this source should be used.
|
||||
Indirect = 0b11000,
|
||||
// Elsewhere, as an implementation detail, the low three bits of an indirect source
|
||||
// are reused; (Indirect-1) is also used as a sentinel value but is not a valid member
|
||||
// of the enum and isn't exposed externally.
|
||||
|
||||
/// The ScaleIndexBase associated with this source should be used, but
|
||||
/// its base should be ignored (and is guaranteed to be zero if the default
|
||||
/// getter is used).
|
||||
IndirectNoBase = Indirect - 1,
|
||||
};
|
||||
|
||||
enum class Repetition: uint8_t {
|
||||
None, RepE, RepNE
|
||||
};
|
||||
|
||||
class Instruction {
|
||||
/// Provides a 32-bit-style scale, index and base; to produce the address this represents,
|
||||
/// calcluate base() + (index() << scale()).
|
||||
///
|
||||
/// This form of indirect addressing is used to describe both 16- and 32-bit indirect addresses,
|
||||
/// even though it is a superset of that supported prior to the 80386.
|
||||
///
|
||||
/// This class can represent only exactly what a SIB byte can — a scale of 0 to 3, a base
|
||||
/// that is any one of the eight general purpose registers, and an index that is one of the seven
|
||||
/// general purpose registers excluding eSP or is ::None.
|
||||
///
|
||||
/// It cannot natively describe a base of ::None.
|
||||
class ScaleIndexBase {
|
||||
public:
|
||||
Operation operation = Operation::Invalid;
|
||||
constexpr ScaleIndexBase() noexcept {}
|
||||
constexpr ScaleIndexBase(uint8_t sib) noexcept : sib_(sib) {}
|
||||
constexpr ScaleIndexBase(int scale, Source index, Source base) noexcept :
|
||||
sib_(uint8_t(
|
||||
scale << 6 |
|
||||
(int(index != Source::None ? index : Source::eSI) << 3) |
|
||||
int(base)
|
||||
)) {}
|
||||
constexpr ScaleIndexBase(Source index, Source base) noexcept : ScaleIndexBase(0, index, base) {}
|
||||
constexpr explicit ScaleIndexBase(Source base) noexcept : ScaleIndexBase(0, Source::None, base) {}
|
||||
|
||||
bool operator ==(const Instruction &rhs) const {
|
||||
/// @returns the power of two by which to multiply @c index() before adding it to @c base().
|
||||
constexpr int scale() const {
|
||||
return sib_ >> 6;
|
||||
}
|
||||
|
||||
/// @returns the @c index for this address; this is guaranteed to be one of eAX, eBX, eCX, eDX, None, eBP, eSI or eDI.
|
||||
constexpr Source index() const {
|
||||
constexpr Source sources[] = {
|
||||
Source::eAX, Source::eCX, Source::eDX, Source::eBX, Source::None, Source::eBP, Source::eSI, Source::eDI,
|
||||
};
|
||||
static_assert(sizeof(sources) == 8);
|
||||
return sources[(sib_ >> 3) & 0x7];
|
||||
}
|
||||
|
||||
/// @returns the @c base for this address; this is guaranteed to be one of eAX, eBX, eCX, eDX, eSP, eBP, eSI or eDI.
|
||||
constexpr Source base() const {
|
||||
return Source(sib_ & 0x7);
|
||||
}
|
||||
|
||||
constexpr uint8_t without_base() const {
|
||||
return sib_ & ~0x3;
|
||||
}
|
||||
|
||||
bool operator ==(const ScaleIndexBase &rhs) const {
|
||||
// Permit either exact equality or index and base being equal
|
||||
// but transposed with a scale of 1.
|
||||
return
|
||||
repetition_size_ == rhs.repetition_size_ &&
|
||||
sources_ == rhs.sources_ &&
|
||||
displacement_ == rhs.displacement_ &&
|
||||
operand_ == rhs.operand_;
|
||||
(sib_ == rhs.sib_) ||
|
||||
(
|
||||
!scale() && !rhs.scale() &&
|
||||
rhs.index() == base() &&
|
||||
rhs.base() == index()
|
||||
);
|
||||
}
|
||||
|
||||
operator uint8_t() const {
|
||||
return sib_;
|
||||
}
|
||||
|
||||
private:
|
||||
// b0, b1: a Repetition;
|
||||
// b2+: operation size.
|
||||
uint8_t repetition_size_ = 0;
|
||||
// Data is stored directly as an 80386 SIB byte.
|
||||
uint8_t sib_ = 0;
|
||||
};
|
||||
static_assert(sizeof(ScaleIndexBase) == 1);
|
||||
static_assert(alignof(ScaleIndexBase) == 1);
|
||||
|
||||
// b0–b5: source;
|
||||
// b6–b11: destination;
|
||||
// b12–b14: segment override;
|
||||
// b15: lock.
|
||||
uint16_t sources_ = 0;
|
||||
/// Provides the location of an operand's source or destination.
|
||||
///
|
||||
/// Callers should use .source() as a first point of entry. If it directly nominates a register
|
||||
/// then use the register contents directly. If it indicates ::DirectAddress or ::Immediate
|
||||
/// then ask the instruction for the address or immediate value that was provided in
|
||||
/// the instruction.
|
||||
///
|
||||
/// If .source() indicates ::Indirect then use base(), index() and scale() to construct an address.
|
||||
///
|
||||
/// In all cases, the applicable segment is indicated by the instruction.
|
||||
class DataPointer {
|
||||
public:
|
||||
/// Constricts a DataPointer referring to the given source; it shouldn't be ::Indirect.
|
||||
constexpr DataPointer(Source source) noexcept : source_(source) {}
|
||||
|
||||
// Unpackable fields.
|
||||
int16_t displacement_ = 0;
|
||||
uint16_t operand_ = 0; // ... or used to store a segment for far operations.
|
||||
/// Constricts a DataPointer with a source of ::Indirect and the specified sib.
|
||||
constexpr DataPointer(ScaleIndexBase sib) noexcept : sib_(sib) {}
|
||||
|
||||
/// Constructs a DataPointer with a source and SIB; use the source to indicate
|
||||
/// whether the base field of the SIB is effective.
|
||||
constexpr DataPointer(Source source, ScaleIndexBase sib) noexcept : source_(source), sib_(sib) {}
|
||||
|
||||
/// Constructs an indirect DataPointer referencing the given base, index and scale.
|
||||
/// Automatically maps Source::Indirect to Source::IndirectNoBase if base is Source::None.
|
||||
constexpr DataPointer(Source base, Source index, int scale) noexcept :
|
||||
source_(base != Source::None ? Source::Indirect : Source::IndirectNoBase),
|
||||
sib_(scale, index, base) {}
|
||||
|
||||
constexpr bool operator ==(const DataPointer &rhs) const {
|
||||
// Require a SIB match only if source_ is ::Indirect or ::IndirectNoBase.
|
||||
return
|
||||
source_ == rhs.source_ && (
|
||||
source_ < Source::IndirectNoBase ||
|
||||
(source_ == Source::Indirect && sib_ == rhs.sib_) ||
|
||||
(source_ == Source::IndirectNoBase && sib_.without_base() == rhs.sib_.without_base())
|
||||
);
|
||||
}
|
||||
|
||||
template <bool obscure_indirectNoBase = false> constexpr Source source() const {
|
||||
if constexpr (obscure_indirectNoBase) {
|
||||
return (source_ >= Source::IndirectNoBase) ? Source::Indirect : source_;
|
||||
}
|
||||
return source_;
|
||||
}
|
||||
|
||||
constexpr int scale() const {
|
||||
return sib_.scale();
|
||||
}
|
||||
|
||||
constexpr Source index() const {
|
||||
return sib_.index();
|
||||
}
|
||||
|
||||
template <bool obscure_indirectNoBase = false> constexpr Source base() const {
|
||||
if constexpr (obscure_indirectNoBase) {
|
||||
return (source_ <= Source::IndirectNoBase) ? Source::None : sib_.base();
|
||||
}
|
||||
return sib_.base();
|
||||
}
|
||||
|
||||
private:
|
||||
Source source_ = Source::Indirect;
|
||||
ScaleIndexBase sib_;
|
||||
};
|
||||
|
||||
template<bool is_32bit> class Instruction {
|
||||
public:
|
||||
Operation operation = Operation::Invalid;
|
||||
|
||||
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_length_extension() + has_displacement() + has_operand();
|
||||
for(int c = 0; c < extension_count; c++) {
|
||||
if(extensions_[c] != rhs.extensions_[c]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private:
|
||||
// Packing and encoding of fields is admittedly somewhat convoluted; what this
|
||||
// achieves is that instructions will be sized:
|
||||
//
|
||||
// four bytes + up to three extension words
|
||||
// (two bytes for 16-bit instructions, four for 32)
|
||||
//
|
||||
// Two of the extension words are used to retain an operand and displacement
|
||||
// if the instruction has those. The other can store sizes greater than 15
|
||||
// bytes (for earlier processors), plus any repetition, segment override or
|
||||
// repetition prefixes.
|
||||
|
||||
// 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, b10]: source length (0 => has length extension);
|
||||
// [b9, b5]: top five of SIB;
|
||||
// [b4, b0]: dest.
|
||||
uint16_t source_data_dest_sib_ = 1 << 10; // So that ::Invalid doesn't seem to have a length extension.
|
||||
|
||||
bool has_length_extension() const {
|
||||
return !((source_data_dest_sib_ >> 10) & 15);
|
||||
}
|
||||
|
||||
// {operand}, {displacement}, {length extension}.
|
||||
//
|
||||
// If length extension is present then:
|
||||
//
|
||||
// [b15, b6]: source length;
|
||||
// [b5, b4]: repetition;
|
||||
// [b3, b1]: segment override;
|
||||
// b0: lock.
|
||||
ImmediateT extensions_[3]{};
|
||||
|
||||
ImmediateT operand_extension() const {
|
||||
return extensions_[0];
|
||||
}
|
||||
ImmediateT displacement_extension() const {
|
||||
return extensions_[(mem_exts_source_ >> 5) & 1];
|
||||
}
|
||||
ImmediateT length_extension() const {
|
||||
return extensions_[((mem_exts_source_ >> 5) & 1) + ((mem_exts_source_ >> 6) & 1)];
|
||||
}
|
||||
|
||||
public:
|
||||
Source source() const { return Source(sources_ & 0x3f); }
|
||||
Source destination() const { return Source((sources_ >> 6) & 0x3f); }
|
||||
bool lock() const { return sources_ & 0x8000; }
|
||||
Source segment_override() const { return Source((sources_ >> 12) & 7); }
|
||||
/// @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.
|
||||
size_t packing_size() const {
|
||||
return
|
||||
offsetof(Instruction<is_32bit>, extensions) +
|
||||
(has_displacement() + has_operand() + has_length_extension()) * sizeof(ImmediateT);
|
||||
|
||||
Repetition repetition() const { return Repetition(repetition_size_ & 3); }
|
||||
Size operation_size() const { return Size(repetition_size_ >> 2); }
|
||||
// To consider in the future: the length extension is always the last one,
|
||||
// and uses only 8 bits of content within 32-bit instructions, so it'd be
|
||||
// possible further to trim the packing size on little endian machines.
|
||||
//
|
||||
// ... but is that a speed improvement? How much space does it save, and
|
||||
// is it enough to undo the costs of unaligned data?
|
||||
}
|
||||
|
||||
uint16_t segment() const { return uint16_t(operand_); }
|
||||
uint16_t offset() const { return uint16_t(displacement_); }
|
||||
private:
|
||||
// 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
|
||||
};
|
||||
|
||||
int16_t displacement() const { return displacement_; }
|
||||
uint16_t operand() const { return operand_; }
|
||||
public:
|
||||
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)
|
||||
);
|
||||
}
|
||||
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)
|
||||
);
|
||||
}
|
||||
bool lock() const {
|
||||
return has_length_extension() && length_extension()&1;
|
||||
}
|
||||
|
||||
Instruction() noexcept {}
|
||||
Instruction(
|
||||
AddressSize address_size() const {
|
||||
return AddressSize(mem_exts_source_ >> 7);
|
||||
}
|
||||
|
||||
/// @returns @c Source::DS if no segment override was found; the overridden segment otherwise.
|
||||
/// On x86 a segment override cannot modify the segment used as a destination in string instructions,
|
||||
/// or that used by stack instructions, but this function does not spend the time necessary to provide
|
||||
/// the correct default for those.
|
||||
Source data_segment() const {
|
||||
if(!has_length_extension()) return Source::DS;
|
||||
return Source(
|
||||
int(Source::ES) +
|
||||
((length_extension() >> 1) & 7)
|
||||
);
|
||||
}
|
||||
|
||||
Repetition repetition() const {
|
||||
if(!has_length_extension()) return Repetition::None;
|
||||
return Repetition((length_extension() >> 4) & 3);
|
||||
}
|
||||
DataSize operation_size() const {
|
||||
return DataSize(source_data_dest_sib_ >> 14);
|
||||
}
|
||||
|
||||
int length() const {
|
||||
const int short_length = (source_data_dest_sib_ >> 10) & 15;
|
||||
if(short_length) return short_length;
|
||||
return length_extension() >> 6;
|
||||
}
|
||||
|
||||
ImmediateT operand() const {
|
||||
const ImmediateT ops[] = {0, operand_extension()};
|
||||
return ops[has_operand()];
|
||||
}
|
||||
DisplacementT displacement() const {
|
||||
return DisplacementT(offset());
|
||||
}
|
||||
|
||||
uint16_t segment() const {
|
||||
return uint16_t(operand());
|
||||
}
|
||||
ImmediateT offset() const {
|
||||
const ImmediateT offsets[] = {0, displacement_extension()};
|
||||
return offsets[has_displacement()];
|
||||
}
|
||||
|
||||
constexpr Instruction() noexcept {}
|
||||
constexpr Instruction(Operation operation, int length) noexcept :
|
||||
Instruction(operation, Source::None, Source::None, ScaleIndexBase(), false, AddressSize::b16, Source::None, Repetition::None, DataSize::None, 0, 0, length) {}
|
||||
constexpr Instruction(
|
||||
Operation operation,
|
||||
Source source,
|
||||
Source destination,
|
||||
ScaleIndexBase sib,
|
||||
bool lock,
|
||||
AddressSize address_size,
|
||||
Source segment_override,
|
||||
Repetition repetition,
|
||||
Size operation_size,
|
||||
int16_t displacement,
|
||||
uint16_t operand) noexcept :
|
||||
DataSize data_size,
|
||||
DisplacementT displacement,
|
||||
ImmediateT operand,
|
||||
int length) noexcept :
|
||||
operation(operation),
|
||||
repetition_size_(uint8_t((int(operation_size) << 2) | int(repetition))),
|
||||
sources_(uint16_t(
|
||||
mem_exts_source_(uint8_t(
|
||||
(int(address_size) << 7) |
|
||||
(displacement ? 0x40 : 0x00) |
|
||||
(operand ? 0x20 : 0x00) |
|
||||
int(source) |
|
||||
(int(destination) << 6) |
|
||||
(int(segment_override) << 12) |
|
||||
(int(lock) << 15)
|
||||
(source == Source::Indirect ? (uint8_t(sib) & 7) : 0)
|
||||
)),
|
||||
displacement_(displacement),
|
||||
operand_(operand) {}
|
||||
source_data_dest_sib_(uint16_t(
|
||||
(int(data_size) << 14) |
|
||||
((
|
||||
(lock || (segment_override != Source::None) || (length > 15) || (repetition != Repetition::None))
|
||||
) ? 0 : (length << 10)) |
|
||||
((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;
|
||||
}
|
||||
if(has_length_extension()) {
|
||||
// As per the rule stated for segment(), this class provides ::DS for any instruction
|
||||
// that doesn't have a segment override.
|
||||
if(segment_override == Source::None) segment_override = Source::DS;
|
||||
extensions_[extension] = ImmediateT(
|
||||
(length << 6) | (int(repetition) << 4) | ((int(segment_override) & 7) << 1) | int(lock)
|
||||
);
|
||||
++extension;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(Instruction) <= 8);
|
||||
static_assert(sizeof(Instruction<true>) <= 16);
|
||||
static_assert(sizeof(Instruction<false>) <= 10);
|
||||
|
||||
}
|
||||
}
|
||||
|
27
InstructionSets/x86/Model.hpp
Normal file
27
InstructionSets/x86/Model.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Model.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 27/02/2022.
|
||||
// Copyright © 2022 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef Model_h
|
||||
#define Model_h
|
||||
|
||||
namespace InstructionSet {
|
||||
namespace x86 {
|
||||
|
||||
enum class Model {
|
||||
i8086,
|
||||
i80186,
|
||||
i80286,
|
||||
i80386,
|
||||
};
|
||||
|
||||
static constexpr bool is_32bit(Model model) { return model >= Model::i80386; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* Model_h */
|
@ -981,6 +981,7 @@
|
||||
4BE21219253FCE9C00435408 /* AppleIIgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE21214253FCE9C00435408 /* AppleIIgs.cpp */; };
|
||||
4BE2121A253FCE9C00435408 /* AppleIIgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE21214253FCE9C00435408 /* AppleIIgs.cpp */; };
|
||||
4BE34438238389E10058E78F /* AtariSTVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE34437238389E10058E78F /* AtariSTVideoTests.mm */; };
|
||||
4BE3C69727CC32DC000EAD28 /* x86DataPointerTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE3C69627CC32DC000EAD28 /* x86DataPointerTests.mm */; };
|
||||
4BE76CF922641ED400ACD6FA /* QLTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE76CF822641ED300ACD6FA /* QLTests.mm */; };
|
||||
4BE8EB6625C750B50040BC40 /* DAT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE8EB6425C750B50040BC40 /* DAT.cpp */; };
|
||||
4BE90FFD22D5864800FB464D /* MacintoshVideoTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */; };
|
||||
@ -2074,10 +2075,12 @@
|
||||
4BE3231520532AA7006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE3231620532BED006EF799 /* Target.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Target.hpp; sourceTree = "<group>"; };
|
||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AtariSTVideoTests.mm; sourceTree = "<group>"; };
|
||||
4BE3C69327C793EF000EAD28 /* DataPointerResolver.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DataPointerResolver.hpp; sourceTree = "<group>"; };
|
||||
4BE3C69527CBC540000EAD28 /* Model.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Model.hpp; sourceTree = "<group>"; };
|
||||
4BE3C69627CC32DC000EAD28 /* x86DataPointerTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = x86DataPointerTests.mm; sourceTree = "<group>"; };
|
||||
4BE76CF822641ED300ACD6FA /* QLTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = QLTests.mm; sourceTree = "<group>"; };
|
||||
4BE845201F2FF7F100A5EA22 /* CRTC6845.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CRTC6845.hpp; sourceTree = "<group>"; };
|
||||
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Disassembler.hpp; sourceTree = "<group>"; };
|
||||
4BE8EB5525C0EA490040BC40 /* Sizes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Sizes.hpp; sourceTree = "<group>"; };
|
||||
4BE8EB6425C750B50040BC40 /* DAT.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = DAT.cpp; sourceTree = "<group>"; };
|
||||
4BE8EB6525C750B50040BC40 /* DAT.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DAT.hpp; sourceTree = "<group>"; };
|
||||
4BE90FFC22D5864800FB464D /* MacintoshVideoTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MacintoshVideoTests.mm; sourceTree = "<group>"; };
|
||||
@ -4120,7 +4123,6 @@
|
||||
children = (
|
||||
4B85322922778E4200F26553 /* Comparative68000.hpp */,
|
||||
4B90467222C6FA31000E2074 /* TestRunner68000.hpp */,
|
||||
4BF7019F26FFD32300996424 /* AmigaBlitterTests.mm */,
|
||||
4B90467522C6FD6E000E2074 /* 68000ArithmeticTests.mm */,
|
||||
4B9D0C4A22C7D70900DE1AD3 /* 68000BCDTests.mm */,
|
||||
4B90467322C6FADD000E2074 /* 68000BitwiseTests.mm */,
|
||||
@ -4129,6 +4131,7 @@
|
||||
4BC5C3DF22C994CC00795658 /* 68000MoveTests.mm */,
|
||||
4B9D0C4E22C7E0CF00DE1AD3 /* 68000RollShiftTests.mm */,
|
||||
4BD388872239E198002D14B5 /* 68000Tests.mm */,
|
||||
4BF7019F26FFD32300996424 /* AmigaBlitterTests.mm */,
|
||||
4B924E981E74D22700B76AF1 /* AtariStaticAnalyserTests.mm */,
|
||||
4BE34437238389E10058E78F /* AtariSTVideoTests.mm */,
|
||||
4BB2A9AE1E13367E001A5C23 /* CRCTests.mm */,
|
||||
@ -4149,6 +4152,7 @@
|
||||
4B8DD3672633B2D400B3C866 /* SpectrumVideoContentionTests.mm */,
|
||||
4B2AF8681E513FC20027EE29 /* TIATests.mm */,
|
||||
4B1D08051E0F7A1100763741 /* TimeTests.mm */,
|
||||
4BE3C69627CC32DC000EAD28 /* x86DataPointerTests.mm */,
|
||||
4BEE4BD325A26E2B00011BD2 /* x86DecoderTests.mm */,
|
||||
4BDA8234261E8E000021AA19 /* Z80ContentionTests.mm */,
|
||||
4BB73EB81B587A5100552FC2 /* Info.plist */,
|
||||
@ -4707,7 +4711,6 @@
|
||||
4BEDA42925B3C26B000C2DBD /* AccessType.hpp */,
|
||||
4BEDA45425B5ECAB000C2DBD /* CachingExecutor.hpp */,
|
||||
4BE8EB5425C0E9D40040BC40 /* Disassembler.hpp */,
|
||||
4BE8EB5525C0EA490040BC40 /* Sizes.hpp */,
|
||||
4BEDA3B625B25563000C2DBD /* README.md */,
|
||||
4BEDA40925B2844B000C2DBD /* M50740 */,
|
||||
4BEDA3B325B25563000C2DBD /* PowerPC */,
|
||||
@ -4733,6 +4736,8 @@
|
||||
4BEDA3B925B25563000C2DBD /* Decoder.cpp */,
|
||||
4BEDA3B825B25563000C2DBD /* Decoder.hpp */,
|
||||
4BEDA3DB25B2588F000C2DBD /* Instruction.hpp */,
|
||||
4BE3C69327C793EF000EAD28 /* DataPointerResolver.hpp */,
|
||||
4BE3C69527CBC540000EAD28 /* Model.hpp */,
|
||||
);
|
||||
path = x86;
|
||||
sourceTree = "<group>";
|
||||
@ -5917,6 +5922,7 @@
|
||||
4B778F5C23A5F3070000D260 /* MSX.cpp in Sources */,
|
||||
4B778F0323A5EBB00000D260 /* FAT12.cpp in Sources */,
|
||||
4B778F4023A5F1910000D260 /* z8530.cpp in Sources */,
|
||||
4BE3C69727CC32DC000EAD28 /* x86DataPointerTests.mm in Sources */,
|
||||
4B778EFD23A5EB8E0000D260 /* AppleDSK.cpp in Sources */,
|
||||
4B778EFB23A5EB7E0000D260 /* HFE.cpp in Sources */,
|
||||
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */,
|
||||
@ -6118,9 +6124,7 @@
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
@ -6143,9 +6147,7 @@
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Frameworks",
|
||||
@ -6190,9 +6192,9 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
@ -6249,9 +6251,9 @@
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
@ -6286,9 +6288,7 @@
|
||||
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@ -6318,7 +6318,6 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-Signal";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
@ -6337,9 +6336,7 @@
|
||||
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = DV3346VVUN;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
@ -6371,7 +6368,6 @@
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-Signal";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Clock Signal/ClockSignal-Bridging-Header.h";
|
||||
};
|
||||
name = Release;
|
||||
@ -6382,10 +6378,7 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
INFOPLIST_FILE = "Clock SignalTests/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -6396,7 +6389,6 @@
|
||||
MACOSX_DEPLOYMENT_TARGET = 11.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Bridges/Clock SignalTests-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
@ -6410,10 +6402,7 @@
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "Clock Signal/Clock Signal.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
ENABLE_HARDENED_RUNTIME = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 2;
|
||||
INFOPLIST_FILE = "Clock SignalTests/Info.plist";
|
||||
@ -6426,7 +6415,6 @@
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "TH.Clock-SignalTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Clock SignalTests/Bridges/Clock SignalTests-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Clock Signal.app/Contents/MacOS/Clock Signal";
|
||||
|
103
OSBindings/Mac/Clock SignalTests/x86DataPointerTests.mm
Normal file
103
OSBindings/Mac/Clock SignalTests/x86DataPointerTests.mm
Normal file
@ -0,0 +1,103 @@
|
||||
//
|
||||
// x86DataPointerTests.m
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 27/02/2022.
|
||||
// Copyright 2022 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include "../../../InstructionSets/x86/DataPointerResolver.hpp"
|
||||
#include <map>
|
||||
|
||||
using namespace InstructionSet::x86;
|
||||
|
||||
@interface x86DataPointerTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation x86DataPointerTests
|
||||
|
||||
- (void)test16bitSize1 {
|
||||
const DataPointer indirectPointer(
|
||||
Source::eAX, Source::eDI, 0
|
||||
);
|
||||
const DataPointer registerPointer(
|
||||
Source::eBX
|
||||
);
|
||||
|
||||
struct Registers {
|
||||
uint16_t ax = 0x1234, di = 0x00ee;
|
||||
uint8_t bl = 0xaa;
|
||||
|
||||
template <typename DataT, Register r> DataT read() {
|
||||
assert(is_sized<DataT>(r));
|
||||
switch(r) {
|
||||
case Register::AX: return ax;
|
||||
case Register::BL: return bl;
|
||||
case Register::DI: return di;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
template <typename DataT, Register r> void write(DataT value) {
|
||||
assert(is_sized<DataT>(r));
|
||||
switch(r) {
|
||||
case Register::BL: bl = value; break;
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
} registers;
|
||||
|
||||
struct Memory {
|
||||
std::map<uint32_t, uint8_t> data;
|
||||
|
||||
template<typename DataT> DataT read(Source, uint32_t address) {
|
||||
if(address == 0x1234 + 0x00ee) return 0xff;
|
||||
return 0;
|
||||
}
|
||||
template<typename DataT> void write(Source, uint32_t address, DataT value) {
|
||||
data[address] = value;
|
||||
}
|
||||
} memory;
|
||||
|
||||
// TODO: construct this more formally; the code below just assumes size = 1, which is not a contractual guarantee.
|
||||
const auto instruction = Instruction<false>();
|
||||
|
||||
using Resolver = DataPointerResolver<Model::i8086, Registers, Memory>;
|
||||
const uint8_t memoryValue = Resolver::read<uint8_t>(
|
||||
registers,
|
||||
memory,
|
||||
instruction,
|
||||
indirectPointer
|
||||
);
|
||||
registers.ax = 0x0100;
|
||||
Resolver::write<uint8_t>(
|
||||
registers,
|
||||
memory,
|
||||
instruction,
|
||||
indirectPointer,
|
||||
0xef
|
||||
);
|
||||
|
||||
XCTAssertEqual(memoryValue, 0xff);
|
||||
XCTAssertEqual(memory.data[0x01ee], 0xef);
|
||||
|
||||
const uint8_t registerValue = Resolver::read<uint8_t>(
|
||||
registers,
|
||||
memory,
|
||||
instruction,
|
||||
registerPointer
|
||||
);
|
||||
Resolver::write<uint8_t>(
|
||||
registers,
|
||||
memory,
|
||||
instruction,
|
||||
registerPointer,
|
||||
0x93
|
||||
);
|
||||
|
||||
XCTAssertEqual(registerValue, 0xaa);
|
||||
XCTAssertEqual(registers.bl, 0x93);
|
||||
}
|
||||
|
||||
@end
|
@ -9,91 +9,54 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include "../../../InstructionSets/x86/Decoder.hpp"
|
||||
#include "../../../InstructionSets/x86/DataPointerResolver.hpp"
|
||||
|
||||
using namespace InstructionSet::x86;
|
||||
|
||||
namespace {
|
||||
using Operation = InstructionSet::x86::Operation;
|
||||
using Instruction = InstructionSet::x86::Instruction;
|
||||
using Source = InstructionSet::x86::Source;
|
||||
using Size = InstructionSet::x86::Size;
|
||||
}
|
||||
|
||||
@interface x86DecoderTests : XCTestCase
|
||||
@end
|
||||
|
||||
/*!
|
||||
Tests 8086 decoding by throwing a bunch of randomly-generated
|
||||
word streams and checking that the result matches what I got from a
|
||||
disassembler elsewhere.
|
||||
*/
|
||||
@implementation x86DecoderTests {
|
||||
std::vector<Instruction> instructions;
|
||||
}
|
||||
|
||||
// MARK: - Specific instruction asserts.
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation {
|
||||
template <typename InstructionT> void test(const InstructionT &instruction, DataSize size, Operation operation) {
|
||||
XCTAssertEqual(instruction.operation_size(), InstructionSet::x86::DataSize(size));
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size {
|
||||
template <typename InstructionT> void test(
|
||||
const InstructionT &instruction,
|
||||
DataSize size,
|
||||
Operation operation,
|
||||
std::optional<InstructionSet::x86::DataPointer> source,
|
||||
std::optional<InstructionSet::x86::DataPointer> destination = std::nullopt,
|
||||
std::optional<typename InstructionT::ImmediateT> operand = std::nullopt,
|
||||
std::optional<typename InstructionT::DisplacementT> displacement = std::nullopt) {
|
||||
|
||||
XCTAssertEqual(instruction.operation_size(), InstructionSet::x86::DataSize(size));
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
XCTAssertEqual(instruction.operation_size(), InstructionSet::x86::Size(size));
|
||||
if(source) XCTAssert(instruction.source() == *source);
|
||||
if(destination) XCTAssert(instruction.destination() == *destination);
|
||||
if(operand) XCTAssertEqual(instruction.operand(), *operand);
|
||||
if(displacement) XCTAssertEqual(instruction.displacement(), *displacement);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination displacement:(int16_t)displacement {
|
||||
template <typename InstructionT> void test(
|
||||
const InstructionT &instruction,
|
||||
Operation operation,
|
||||
std::optional<typename InstructionT::ImmediateT> operand = std::nullopt,
|
||||
std::optional<typename InstructionT::DisplacementT> displacement = std::nullopt) {
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
XCTAssertEqual(instruction.operation_size(), InstructionSet::x86::Size(size));
|
||||
XCTAssertEqual(instruction.source(), source);
|
||||
XCTAssertEqual(instruction.destination(), destination);
|
||||
XCTAssertEqual(instruction.displacement(), displacement);
|
||||
if(operand) XCTAssertEqual(instruction.operand(), *operand);
|
||||
if(displacement) XCTAssertEqual(instruction.displacement(), *displacement);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination displacement:(int16_t)displacement operand:(uint16_t)operand {
|
||||
[self assert:instruction operation:operation size:size source:source destination:destination displacement:displacement];
|
||||
XCTAssertEqual(instruction.operand(), operand);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination operand:(uint16_t)operand {
|
||||
[self assert:instruction operation:operation size:size source:source destination:destination displacement:0 operand:operand];
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source destination:(Source)destination {
|
||||
[self assert:instruction operation:operation size:size source:source destination:destination displacement:0];
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size source:(Source)source {
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
XCTAssertEqual(instruction.operation_size(), InstructionSet::x86::Size(size));
|
||||
XCTAssertEqual(instruction.source(), source);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size destination:(Source)destination {
|
||||
[self assert:instruction operation:operation size:size];
|
||||
XCTAssertEqual(instruction.destination(), destination);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation size:(int)size operand:(uint16_t)operand destination:(Source)destination {
|
||||
[self assert:instruction operation:operation size:size];
|
||||
XCTAssertEqual(instruction.destination(), destination);
|
||||
XCTAssertEqual(instruction.source(), Source::Immediate);
|
||||
XCTAssertEqual(instruction.operand(), operand);
|
||||
XCTAssertEqual(instruction.displacement(), 0);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation displacement:(int16_t)displacement {
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
XCTAssertEqual(instruction.displacement(), displacement);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation operand:(uint16_t)operand {
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
XCTAssertEqual(instruction.operand(), operand);
|
||||
XCTAssertEqual(instruction.displacement(), 0);
|
||||
}
|
||||
|
||||
- (void)assert:(Instruction &)instruction operation:(Operation)operation segment:(uint16_t)segment offset:(uint16_t)offset {
|
||||
template <typename InstructionT> void test_far(
|
||||
const InstructionT &instruction,
|
||||
Operation operation,
|
||||
uint16_t segment,
|
||||
typename InstructionT::DisplacementT offset) {
|
||||
XCTAssertEqual(instruction.operation, operation);
|
||||
XCTAssertEqual(instruction.segment(), segment);
|
||||
XCTAssertEqual(instruction.offset(), offset);
|
||||
@ -101,9 +64,32 @@ namespace {
|
||||
|
||||
// MARK: - Decoder
|
||||
|
||||
- (void)decode:(const std::initializer_list<uint8_t> &)stream {
|
||||
template <Model model, typename CollectionT>
|
||||
std::vector<typename InstructionSet::x86::Decoder<model>::InstructionT>
|
||||
decode(const CollectionT &stream, bool set_32_bit = false) {
|
||||
// Build instructions list with a byte-by-byte decoding.
|
||||
std::vector<typename InstructionSet::x86::Decoder<model>::InstructionT> instructions;
|
||||
|
||||
InstructionSet::x86::Decoder<model> decoder;
|
||||
decoder.set_32bit_protected_mode(set_32_bit);
|
||||
|
||||
for(uint8_t item: stream) {
|
||||
const auto [size, next] = decoder.decode(&item, 1);
|
||||
if(size > 0) {
|
||||
instructions.push_back(next);
|
||||
}
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
template <Model model>
|
||||
std::vector<typename InstructionSet::x86::Decoder<model>::InstructionT>
|
||||
decode(const std::initializer_list<uint8_t> &stream, bool set_32_bit = false) {
|
||||
// Decode by offering up all data at once.
|
||||
InstructionSet::x86::Decoder decoder(InstructionSet::x86::Model::i8086);
|
||||
std::vector<typename InstructionSet::x86::Decoder<model>::InstructionT> instructions;
|
||||
InstructionSet::x86::Decoder<model> decoder;
|
||||
decoder.set_32bit_protected_mode(set_32_bit);
|
||||
instructions.clear();
|
||||
const uint8_t *byte = stream.begin();
|
||||
while(byte != stream.end()) {
|
||||
@ -114,23 +100,35 @@ namespace {
|
||||
}
|
||||
|
||||
// Grab a byte-at-a-time decoding and check that it matches the previous.
|
||||
{
|
||||
InstructionSet::x86::Decoder decoder(InstructionSet::x86::Model::i8086);
|
||||
const auto byte_instructions = decode<model>(std::vector<uint8_t>{stream}, set_32_bit);
|
||||
|
||||
XCTAssertEqual(byte_instructions.size(), instructions.size());
|
||||
|
||||
auto previous_instruction = instructions.begin();
|
||||
for(auto item: stream) {
|
||||
const auto [size, next] = decoder.decode(&item, 1);
|
||||
if(size > 0) {
|
||||
XCTAssert(next == *previous_instruction);
|
||||
auto byte_instruction = byte_instructions.begin();
|
||||
while(previous_instruction != instructions.end()) {
|
||||
XCTAssert(*previous_instruction == *byte_instruction);
|
||||
|
||||
++previous_instruction;
|
||||
++byte_instruction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instructions;
|
||||
}
|
||||
|
||||
// MARK: - Tests
|
||||
}
|
||||
|
||||
- (void)testSequence1 {
|
||||
@interface x86DecoderTests : XCTestCase
|
||||
@end
|
||||
|
||||
/*!
|
||||
Tests 8086 decoding by throwing a bunch of randomly-generated
|
||||
word streams and checking that the result matches what I got from a
|
||||
disassembler elsewhere.
|
||||
*/
|
||||
@implementation x86DecoderTests
|
||||
|
||||
- (void)test16BitSequence {
|
||||
// Sequences the Online Disassembler believes to exist but The 8086 Book does not:
|
||||
//
|
||||
// 0x6a 0x65 push $65
|
||||
@ -139,7 +137,7 @@ namespace {
|
||||
// 0x6c insb (%dx), %es:(%di)
|
||||
// 0xc9 leave
|
||||
//
|
||||
[self decode:{
|
||||
const auto instructions = decode<Model::i8086>({
|
||||
0x2d, 0x77, 0xea, 0x72, 0xfc, 0x4b, 0xb5, 0x28, 0xc3, 0xca, 0x26, 0x48, /* 0x65, 0x6d, */ 0x7b, 0x9f,
|
||||
0xc2, 0x65, 0x42, 0x4e, 0xef, 0x70, 0x20, 0x94, 0xc4, 0xd4, 0x93, 0x43, 0x3c, 0x8e, /* 0x6a, 0x65, */
|
||||
0x1a, 0x78, 0x45, 0x10, 0x7f, 0x3c, 0x19, 0x5a, 0x16, 0x31, 0x64, 0x2c, 0xe7, 0xc6, 0x7d, 0xb0,
|
||||
@ -148,7 +146,7 @@ namespace {
|
||||
0xbd, 0xa1, 0x12, 0xc5, 0x29, /* 0xc9, */ 0x9e, 0xd8, 0xf3, 0xcf, 0x92, 0x39, 0x5d, 0x90, 0x15, 0xc3,
|
||||
0xb8, 0xad, 0xe8, 0xc8, 0x16, 0x4a, 0xb0, 0x9e, 0xf9, 0xbf, 0x56, 0xea, 0x4e, 0xfd, 0xe4, 0x5a,
|
||||
0x23, 0xaa, 0x2c, 0x5b, 0x2a, 0xd2, 0xf7, 0x5f, 0x18, 0x86, 0x90, 0x25, 0x64, 0xb7, 0xc3
|
||||
}];
|
||||
});
|
||||
|
||||
// 63 instructions are expected.
|
||||
XCTAssertEqual(instructions.size(), 63);
|
||||
@ -157,29 +155,29 @@ namespace {
|
||||
// jb 0x00000001
|
||||
// dec %bx
|
||||
// mov $0x28,%ch
|
||||
[self assert:instructions[0] operation:Operation::SUB size:2 operand:0xea77 destination:Source::AX];
|
||||
[self assert:instructions[1] operation:Operation::JB displacement:0xfffc];
|
||||
[self assert:instructions[2] operation:Operation::DEC size:2 source:Source::BX destination:Source::BX];
|
||||
[self assert:instructions[3] operation:Operation::MOV size:1 operand:0x28 destination:Source::CH];
|
||||
test(instructions[0], DataSize::Word, Operation::SUB, Source::Immediate, Source::eAX, 0xea77);
|
||||
test(instructions[1], Operation::JB, std::nullopt, 0xfffc);
|
||||
test(instructions[2], DataSize::Word, Operation::DEC, Source::eBX, Source::eBX);
|
||||
test(instructions[3], DataSize::Byte, Operation::MOV, Source::Immediate, Source::CH, 0x28);
|
||||
|
||||
// ret
|
||||
// lret $0x4826
|
||||
// [[ omitted: gs insw (%dx),%es:(%di) ]]
|
||||
// jnp 0xffffffaf
|
||||
// ret $0x4265
|
||||
[self assert:instructions[4] operation:Operation::RETN];
|
||||
[self assert:instructions[5] operation:Operation::RETF operand:0x4826];
|
||||
[self assert:instructions[6] operation:Operation::JNP displacement:0xff9f];
|
||||
[self assert:instructions[7] operation:Operation::RETN operand:0x4265];
|
||||
test(instructions[4], Operation::RETnear);
|
||||
test(instructions[5], Operation::RETfar, 0x4826);
|
||||
test(instructions[6], Operation::JNP, std::nullopt, 0xff9f);
|
||||
test(instructions[7], Operation::RETnear, 0x4265);
|
||||
|
||||
// dec %si
|
||||
// out %ax,(%dx)
|
||||
// jo 0x00000037
|
||||
// xchg %ax,%sp
|
||||
[self assert:instructions[8] operation:Operation::DEC size:2 source:Source::SI destination:Source::SI];
|
||||
[self assert:instructions[9] operation:Operation::OUT size:2 source:Source::AX destination:Source::DX];
|
||||
[self assert:instructions[10] operation:Operation::JO displacement:0x20];
|
||||
[self assert:instructions[11] operation:Operation::XCHG size:2 source:Source::AX destination:Source::SP];
|
||||
test(instructions[8], DataSize::Word, Operation::DEC, Source::eSI, Source::eSI);
|
||||
test(instructions[9], DataSize::Word, Operation::OUT, Source::eAX, Source::eDX);
|
||||
test(instructions[10], Operation::JO, std::nullopt, 0x20);
|
||||
test(instructions[11], DataSize::Word, Operation::XCHG, Source::eAX, Source::eSP);
|
||||
|
||||
// ODA has:
|
||||
// c4 (bad)
|
||||
@ -190,145 +188,496 @@ namespace {
|
||||
//
|
||||
// c4 d4 (bad)
|
||||
// 93 XCHG AX, BX
|
||||
[self assert:instructions[12] operation:Operation::Invalid];
|
||||
[self assert:instructions[13] operation:Operation::XCHG size:2 source:Source::AX destination:Source::BX];
|
||||
test(instructions[12], Operation::Invalid);
|
||||
test(instructions[13], DataSize::Word, Operation::XCHG, Source::eAX, Source::eBX);
|
||||
|
||||
// inc %bx
|
||||
// cmp $0x8e,%al
|
||||
// [[ omitted: push $0x65 ]]
|
||||
// sbb 0x45(%bx,%si),%bh
|
||||
// adc %bh,0x3c(%bx)
|
||||
[self assert:instructions[14] operation:Operation::INC size:2 source:Source::BX destination:Source::BX];
|
||||
[self assert:instructions[15] operation:Operation::CMP size:1 operand:0x8e destination:Source::AL];
|
||||
[self assert:instructions[16] operation:Operation::SBB size:1 source:Source::IndBXPlusSI destination:Source::BH displacement:0x45];
|
||||
[self assert:instructions[17] operation:Operation::ADC size:1 source:Source::BH destination:Source::IndBX displacement:0x3c];
|
||||
test(instructions[14], DataSize::Word, Operation::INC, Source::eBX, Source::eBX);
|
||||
test(instructions[15], DataSize::Byte, Operation::CMP, Source::Immediate, Source::eAX, 0x8e);
|
||||
test(instructions[16], DataSize::Byte, Operation::SBB, ScaleIndexBase(Source::eBX, Source::eSI), Source::BH, std::nullopt, 0x45);
|
||||
test(instructions[17], DataSize::Byte, Operation::ADC, Source::BH, ScaleIndexBase(Source::eBX), std::nullopt, 0x3c);
|
||||
|
||||
// sbb %bx,0x16(%bp,%si)
|
||||
// xor %sp,0x2c(%si)
|
||||
// out %ax,$0xc6
|
||||
// jge 0xffffffe0
|
||||
[self assert:instructions[18] operation:Operation::SBB size:2 source:Source::BX destination:Source::IndBPPlusSI displacement:0x16];
|
||||
[self assert:instructions[19] operation:Operation::XOR size:2 source:Source::SP destination:Source::IndSI displacement:0x2c];
|
||||
[self assert:instructions[20] operation:Operation::OUT size:2 source:Source::AX destination:Source::DirectAddress operand:0xc6];
|
||||
[self assert:instructions[21] operation:Operation::JNL displacement:0xffb0];
|
||||
test(instructions[18], DataSize::Word, Operation::SBB, Source::eBX, ScaleIndexBase(Source::eBP, Source::eSI), std::nullopt, 0x16);
|
||||
test(instructions[19], DataSize::Word, Operation::XOR, Source::eSP, ScaleIndexBase(Source::eSI), std::nullopt, 0x2c);
|
||||
test(instructions[20], DataSize::Word, Operation::OUT, Source::eAX, Source::DirectAddress, 0xc6);
|
||||
test(instructions[21], Operation::JNL, std::nullopt, 0xffb0);
|
||||
|
||||
// mov $0x49,%ch
|
||||
// [[ omitted: addr32 popa ]]
|
||||
// mov $0xcbc0,%dx
|
||||
// adc $0x7e,%al
|
||||
// jno 0x0000000b
|
||||
[self assert:instructions[22] operation:Operation::MOV size:1 operand:0x49 destination:Source::CH];
|
||||
[self assert:instructions[23] operation:Operation::MOV size:2 operand:0xcbc0 destination:Source::DX];
|
||||
[self assert:instructions[24] operation:Operation::ADC size:1 operand:0x7e destination:Source::AL];
|
||||
[self assert:instructions[25] operation:Operation::JNO displacement:0xffd0];
|
||||
test(instructions[22], DataSize::Byte, Operation::MOV, Source::Immediate, Source::CH, 0x49);
|
||||
test(instructions[23], DataSize::Word, Operation::MOV, Source::Immediate, Source::eDX, 0xcbc0);
|
||||
test(instructions[24], DataSize::Byte, Operation::ADC, Source::Immediate, Source::eAX, 0x7e);
|
||||
test(instructions[25], Operation::JNO, std::nullopt, 0xffd0);
|
||||
|
||||
// push %ax
|
||||
// js 0x0000007b
|
||||
// add (%di),%bx
|
||||
// in $0xc9,%ax
|
||||
[self assert:instructions[26] operation:Operation::PUSH size:2 source:Source::AX];
|
||||
[self assert:instructions[27] operation:Operation::JS displacement:0x3d];
|
||||
[self assert:instructions[28] operation:Operation::ADD size:2 source:Source::IndDI destination:Source::BX];
|
||||
[self assert:instructions[29] operation:Operation::IN size:2 source:Source::DirectAddress destination:Source::AX operand:0xc9];
|
||||
test(instructions[26], DataSize::Word, Operation::PUSH, Source::eAX);
|
||||
test(instructions[27], Operation::JS, std::nullopt, 0x3d);
|
||||
test(instructions[28], DataSize::Word, Operation::ADD, ScaleIndexBase(Source::eDI), Source::eBX);
|
||||
test(instructions[29], DataSize::Word, Operation::IN, Source::DirectAddress, Source::eAX, 0xc9);
|
||||
|
||||
// xchg %ax,%di
|
||||
// ret
|
||||
// fwait
|
||||
// out %al,$0xd3
|
||||
[self assert:instructions[30] operation:Operation::XCHG size:2 source:Source::AX destination:Source::DI];
|
||||
[self assert:instructions[31] operation:Operation::RETN];
|
||||
[self assert:instructions[32] operation:Operation::WAIT];
|
||||
[self assert:instructions[33] operation:Operation::OUT size:1 source:Source::AL destination:Source::DirectAddress operand:0xd3];
|
||||
test(instructions[30], DataSize::Word, Operation::XCHG, Source::eAX, Source::eDI);
|
||||
test(instructions[31], Operation::RETnear);
|
||||
test(instructions[32], Operation::WAIT);
|
||||
test(instructions[33], DataSize::Byte, Operation::OUT, Source::eAX, Source::DirectAddress, 0xd3);
|
||||
|
||||
// [[ omitted: insb (%dx),%es:(%di) ]]
|
||||
// pop %ax
|
||||
// dec %bp
|
||||
// jbe 0xffffffcc
|
||||
// inc %sp
|
||||
[self assert:instructions[34] operation:Operation::POP size:2 destination:Source::AX];
|
||||
[self assert:instructions[35] operation:Operation::DEC size:2 source:Source::BP destination:Source::BP];
|
||||
[self assert:instructions[36] operation:Operation::JBE displacement:0xff80];
|
||||
[self assert:instructions[37] operation:Operation::INC size:2 source:Source::SP destination:Source::SP];
|
||||
test(instructions[34], DataSize::Word, Operation::POP, Source::eAX, Source::eAX);
|
||||
test(instructions[35], DataSize::Word, Operation::DEC, Source::eBP, Source::eBP);
|
||||
test(instructions[36], Operation::JBE, std::nullopt, 0xff80);
|
||||
test(instructions[37], DataSize::Word, Operation::INC, Source::eSP, Source::eSP);
|
||||
|
||||
// (bad)
|
||||
// lahf
|
||||
// movsw %ds:(%si),%es:(%di)
|
||||
// mov $0x12a1,%bp
|
||||
[self assert:instructions[38] operation:Operation::Invalid];
|
||||
[self assert:instructions[39] operation:Operation::LAHF];
|
||||
[self assert:instructions[40] operation:Operation::MOVS size:2];
|
||||
[self assert:instructions[41] operation:Operation::MOV size:2 operand:0x12a1 destination:Source::BP];
|
||||
test(instructions[38], Operation::Invalid);
|
||||
test(instructions[39], Operation::LAHF);
|
||||
test(instructions[40], DataSize::Word, Operation::MOVS); // Arguments are implicit.
|
||||
test(instructions[41], DataSize::Word, Operation::MOV, Source::Immediate, Source::eBP, 0x12a1);
|
||||
|
||||
// lds (%bx,%di),%bp
|
||||
// [[ omitted: leave ]]
|
||||
// sahf
|
||||
// fdiv %st(3),%st
|
||||
// iret
|
||||
[self assert:instructions[42] operation:Operation::LDS size:2];
|
||||
[self assert:instructions[43] operation:Operation::SAHF];
|
||||
[self assert:instructions[44] operation:Operation::ESC];
|
||||
[self assert:instructions[45] operation:Operation::IRET];
|
||||
test(instructions[42], DataSize::Word, Operation::LDS);
|
||||
test(instructions[43], Operation::SAHF);
|
||||
test(instructions[44], Operation::ESC);
|
||||
test(instructions[45], Operation::IRET);
|
||||
|
||||
// xchg %ax,%dx
|
||||
// cmp %bx,-0x70(%di)
|
||||
// adc $0xb8c3,%ax
|
||||
// lods %ds:(%si),%ax
|
||||
[self assert:instructions[46] operation:Operation::XCHG size:2 source:Source::AX destination:Source::DX];
|
||||
[self assert:instructions[47] operation:Operation::CMP size:2 source:Source::BX destination:Source::IndDI displacement:0xff90];
|
||||
[self assert:instructions[48] operation:Operation::ADC size:2 operand:0xb8c3 destination:Source::AX];
|
||||
[self assert:instructions[49] operation:Operation::LODS size:2];
|
||||
test(instructions[46], DataSize::Word, Operation::XCHG, Source::eAX, Source::eDX);
|
||||
test(instructions[47], DataSize::Word, Operation::CMP, Source::eBX, ScaleIndexBase(Source::eDI), std::nullopt, 0xff90);
|
||||
test(instructions[48], DataSize::Word, Operation::ADC, Source::Immediate, Source::eAX, 0xb8c3);
|
||||
test(instructions[49], DataSize::Word, Operation::LODS);
|
||||
|
||||
// call 0x0000172d
|
||||
// dec %dx
|
||||
// mov $0x9e,%al
|
||||
// stc
|
||||
[self assert:instructions[50] operation:Operation::CALLD operand:0x16c8];
|
||||
[self assert:instructions[51] operation:Operation::DEC size:2 source:Source::DX destination:Source::DX];
|
||||
[self assert:instructions[52] operation:Operation::MOV size:1 operand:0x9e destination:Source::AL];
|
||||
[self assert:instructions[53] operation:Operation::STC];
|
||||
test(instructions[50], Operation::CALLrel, 0, 0x16c8);
|
||||
test(instructions[51], DataSize::Word, Operation::DEC, Source::eDX, Source::eDX);
|
||||
test(instructions[52], DataSize::Byte, Operation::MOV, Source::Immediate, Source::eAX, 0x9e);
|
||||
test(instructions[53], Operation::STC);
|
||||
|
||||
// mov $0xea56,%di
|
||||
// dec %si
|
||||
// std
|
||||
// in $0x5a,%al
|
||||
[self assert:instructions[54] operation:Operation::MOV size:2 operand:0xea56 destination:Source::DI];
|
||||
[self assert:instructions[55] operation:Operation::DEC size:2 source:Source::SI destination:Source::SI];
|
||||
[self assert:instructions[56] operation:Operation::STD];
|
||||
[self assert:instructions[57] operation:Operation::IN size:1 source:Source::DirectAddress destination:Source::AL operand:0x5a];
|
||||
test(instructions[54], DataSize::Word, Operation::MOV, Source::Immediate, Source::eDI, 0xea56);
|
||||
test(instructions[55], DataSize::Word, Operation::DEC, Source::eSI, Source::eSI);
|
||||
test(instructions[56], Operation::STD);
|
||||
test(instructions[57], DataSize::Byte, Operation::IN, Source::DirectAddress, Source::eAX, 0x5a);
|
||||
|
||||
// and 0x5b2c(%bp,%si),%bp
|
||||
// sub %dl,%dl
|
||||
// negw 0x18(%bx)
|
||||
// xchg %dl,0x6425(%bx,%si)
|
||||
[self assert:instructions[58] operation:Operation::AND size:2 source:Source::IndBPPlusSI destination:Source::BP displacement:0x5b2c];
|
||||
[self assert:instructions[59] operation:Operation::SUB size:1 source:Source::DL destination:Source::DL];
|
||||
[self assert:instructions[60] operation:Operation::NEG size:2 source:Source::IndBX destination:Source::IndBX displacement:0x18];
|
||||
[self assert:instructions[61] operation:Operation::XCHG size:1 source:Source::IndBXPlusSI destination:Source::DL displacement:0x6425];
|
||||
test(instructions[58], DataSize::Word, Operation::AND, ScaleIndexBase(Source::eBP, Source::eSI), Source::eBP, std::nullopt, 0x5b2c);
|
||||
test(instructions[59], DataSize::Byte, Operation::SUB, Source::eDX, Source::eDX);
|
||||
test(instructions[60], DataSize::Word, Operation::NEG, ScaleIndexBase(Source::eBX), ScaleIndexBase(Source::eBX), std::nullopt, 0x18);
|
||||
test(instructions[61], DataSize::Byte, Operation::XCHG, ScaleIndexBase(Source::eBX, Source::eSI), Source::eDX, std::nullopt, 0x6425);
|
||||
|
||||
// mov $0xc3,%bh
|
||||
[self assert:instructions[62] operation:Operation::MOV size:1 operand:0xc3 destination:Source::BH];
|
||||
test(instructions[62], DataSize::Byte, Operation::MOV, Source::Immediate, Source::BH, 0xc3);
|
||||
}
|
||||
|
||||
- (void)test83 {
|
||||
[self decode:{
|
||||
const auto instructions = decode<Model::i8086>({
|
||||
0x83, 0x10, 0x80, // adcw $0xff80,(%bx,%si)
|
||||
0x83, 0x3b, 0x04, // cmpw $0x4,(%bp,%di)
|
||||
0x83, 0x2f, 0x09, // subw $0x9,(%bx)
|
||||
}];
|
||||
});
|
||||
|
||||
XCTAssertEqual(instructions.size(), 3);
|
||||
[self assert:instructions[0] operation:Operation::ADC size:2 source:Source::Immediate destination:Source::IndBXPlusSI operand:0xff80];
|
||||
[self assert:instructions[1] operation:Operation::CMP size:2 source:Source::Immediate destination:Source::IndBPPlusDI operand:0x4];
|
||||
[self assert:instructions[2] operation:Operation::SUB size:2 source:Source::Immediate destination:Source::IndBX operand:0x9];
|
||||
test(instructions[0], DataSize::Word, Operation::ADC, Source::Immediate, ScaleIndexBase(Source::eBX, Source::eSI), 0xff80);
|
||||
test(instructions[1], DataSize::Word, Operation::CMP, Source::Immediate, ScaleIndexBase(Source::eBP, Source::eDI), 0x4);
|
||||
test(instructions[2], DataSize::Word, Operation::SUB, Source::Immediate, ScaleIndexBase(Source::eBX), 0x9);
|
||||
}
|
||||
|
||||
- (void)testFar {
|
||||
[self decode:{
|
||||
const auto instructions = decode<Model::i8086>({
|
||||
0x9a, 0x12, 0x34, 0x56, 0x78, // lcall 0x7856, 0x3412
|
||||
}];
|
||||
});
|
||||
|
||||
XCTAssertEqual(instructions.size(), 1);
|
||||
[self assert:instructions[0] operation:Operation::CALLF segment:0x7856 offset:0x3412];
|
||||
test_far(instructions[0], Operation::CALLfar, 0x7856, 0x3412);
|
||||
}
|
||||
|
||||
- (void)testLDSLESEtc {
|
||||
auto run_test = [](bool is_32, DataSize size) {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
0xc5, 0x33, // 16-bit: lds si, (bp, di); 32-bit: lds esi, (ebx)
|
||||
0xc4, 0x17, // 16-bit: les dx, (bx); 32-bit: les edx, (edi)
|
||||
0x0f, 0xb2, 0x17, // 16-bit: lss dx, (bx); 32-bit: lss edx, (edi)
|
||||
}, is_32);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 3);
|
||||
if(is_32) {
|
||||
test(instructions[0], size, Operation::LDS, ScaleIndexBase(Source::eBX), Source::eSI);
|
||||
test(instructions[1], size, Operation::LES, ScaleIndexBase(Source::eDI), Source::eDX);
|
||||
test(instructions[2], size, Operation::LSS, ScaleIndexBase(Source::eDI), Source::eDX);
|
||||
} else {
|
||||
test(instructions[0], size, Operation::LDS, ScaleIndexBase(Source::eBP, Source::eDI), Source::eSI);
|
||||
test(instructions[1], size, Operation::LES, ScaleIndexBase(Source::eBX), Source::eDX);
|
||||
test(instructions[2], size, Operation::LSS, ScaleIndexBase(Source::eBX), Source::eDX);
|
||||
}
|
||||
};
|
||||
|
||||
run_test(false, DataSize::Word);
|
||||
run_test(true, DataSize::DWord);
|
||||
}
|
||||
|
||||
- (void)testSIB {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
// add edx, -0x7d(ebp + eax*2)
|
||||
0x01, 0x54, 0x45, 0x83,
|
||||
|
||||
// add edx, -0x80(si)
|
||||
0x67, 0x01, 0x54, 0x80,
|
||||
}, true);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 2);
|
||||
test(instructions[0], DataSize::DWord, Operation::ADD, Source::eDX, ScaleIndexBase(1, Source::eAX, Source::eBP), 0x00, -125);
|
||||
test(instructions[1], DataSize::DWord, Operation::ADD, Source::eDX, ScaleIndexBase(Source::eSI), 0x00, -128);
|
||||
XCTAssertEqual(instructions[1].address_size(), AddressSize::b16);
|
||||
}
|
||||
|
||||
- (void)testJMP {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
// JMP +0x00efcdab
|
||||
0xe9, 0xab, 0xcd, 0xef, 0x00,
|
||||
// JMP 0xc389:0x67452301
|
||||
0xea, 0x01, 0x23, 0x45, 0x67, 0x89, 0xc3,
|
||||
// JMP -79
|
||||
0xeb, 0xb1,
|
||||
// JMP DWORD (edx)
|
||||
0xff, 0x22,
|
||||
// JMP FWORD (eax)
|
||||
0xff, 0x28,
|
||||
}, true);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 5);
|
||||
test(instructions[0], Operation::JMPrel, 0, 0xefcdab);
|
||||
test_far(instructions[1], Operation::JMPfar, 0xc389, 0x67452301);
|
||||
test(instructions[2], Operation::JMPrel, 0, -79);
|
||||
test(instructions[3], DataSize::DWord, Operation::JMPabs, ScaleIndexBase(Source::eDX));
|
||||
test(instructions[4], DataSize::DWord, Operation::JMPfar, ScaleIndexBase(Source::eAX));
|
||||
}
|
||||
|
||||
- (void)test32bitSequence {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
0x2e, 0x42, 0x0c, 0x09, 0x81, 0x47, 0xbe, 0xa9, 0x3a, 0x68, 0x9f, 0xf0, 0x7a, 0xe2, 0x3e, 0xb4,
|
||||
0xc1, 0x1f, 0xaa, 0x60, 0xb4, 0xe1, 0x91, 0xdc, 0xf6, 0x62, 0x90, 0x90, 0xdf, 0xcd, 0xf9, 0x0f,
|
||||
0xbb, 0x71, 0x4b, 0x58, 0x55, 0x38, 0x2c, 0xf9, 0x50, 0xfe, 0xce, 0xe0, 0xc1, 0xda, 0x83, 0x8c,
|
||||
0x19, 0x0c, 0x9b, 0x89, 0x13, 0x34, 0x45, 0xc5, 0x11, 0xa2, 0xd3, 0xa6, 0xdb, 0xe4, 0x1f, 0xa5,
|
||||
0x79, 0xf3, 0x7d, 0x1c, 0xb8, 0xda, 0x6b, 0x76, 0x8a, 0x79, 0x28, 0x52, 0xcd, 0xc4, 0xe9, 0xba,
|
||||
0x11, 0xcf, 0x29, 0x09, 0x46, 0x1a, 0xc0, 0x5d, 0x88, 0x34, 0xa5, 0x83, 0xe2, 0xd0, 0xf5, 0x44,
|
||||
0x9d, 0xa5, 0xc1, 0x5e, 0x4f, 0x07, 0x51, 0xd4, 0xed, 0xb0, 0x69, 0xd7, 0x00, 0xc5, 0x51, 0xfb,
|
||||
0x68, 0x85, 0x3a, 0x8b, 0x69, 0x28, 0x0c, 0xec, 0xb1, 0xb7, 0x3b, 0x8d, 0x5f, 0x44, 0x87, 0x2c,
|
||||
0xe3, 0x02, 0x9e, 0x74, 0x6e, 0x1b, 0x8f, 0x4d, 0xc5, 0x33, 0x04, 0x9f, 0xac, 0xc0, 0xc9, 0x60,
|
||||
0x9a, 0x8a, 0xf5, 0xd0, 0x97, 0x1b, 0xe2, 0x64, 0x60, 0xb0, 0xcf, 0xe3, 0x37,
|
||||
}, true);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 64);
|
||||
|
||||
// cs inc edx
|
||||
// or al,0x9
|
||||
// add DWORD PTR [edi-0x42],0x9f683aa9
|
||||
// lock jp 0xfffffff0 (from 0000000e)
|
||||
test(instructions[0], DataSize::DWord, Operation::INC, Source::eDX);
|
||||
XCTAssertEqual(instructions[0].data_segment(), Source::CS);
|
||||
test(instructions[1], DataSize::Byte, Operation::OR, Source::Immediate, Source::eAX, 0x9);
|
||||
test(instructions[2], DataSize::DWord, Operation::ADD, Source::Immediate, ScaleIndexBase(Source::eDI), 0x9f683aa9, -0x42);
|
||||
test(instructions[3], Operation::JP, 0, -30);
|
||||
XCTAssert(instructions[3].lock());
|
||||
|
||||
// ds mov ah,0xc1
|
||||
// pop ds
|
||||
// stos BYTE PTR es:[edi],al
|
||||
// pusha
|
||||
test(instructions[4], DataSize::Byte, Operation::MOV, Source::Immediate, Source::AH, 0xc1);
|
||||
XCTAssertEqual(instructions[4].data_segment(), Source::DS);
|
||||
test(instructions[5], DataSize::Word, Operation::POP, Source::None, Source::DS);
|
||||
test(instructions[6], DataSize::Byte, Operation::STOS);
|
||||
test(instructions[7], Operation::PUSHA);
|
||||
|
||||
// mov ah,0xe1
|
||||
// xchg ecx,eax
|
||||
// fdivr st(6),st
|
||||
// bound edx,QWORD PTR [eax-0x6322070]
|
||||
test(instructions[8], DataSize::Byte, Operation::MOV, Source::Immediate, Source::AH, 0xe1);
|
||||
test(instructions[9], DataSize::DWord, Operation::XCHG, Source::eAX, Source::eCX);
|
||||
test(instructions[10], DataSize::None, Operation::ESC);
|
||||
test(instructions[11], DataSize::DWord, Operation::BOUND, ScaleIndexBase(Source::eAX), Source::eDX, 0, -0x6322070);
|
||||
|
||||
// btc DWORD PTR [ecx+0x4b],esi
|
||||
// pop eax
|
||||
// push ebp
|
||||
// cmp BYTE PTR [ecx+edi*8],ch
|
||||
test(instructions[12], DataSize::DWord, Operation::BTC, Source::eSI, ScaleIndexBase(Source::eCX), 0, 0x4b);
|
||||
test(instructions[13], DataSize::DWord, Operation::POP, Source::eAX, Source::eAX);
|
||||
test(instructions[14], DataSize::DWord, Operation::PUSH, Source::eBP);
|
||||
test(instructions[15], DataSize::Byte, Operation::CMP, Source::CH, ScaleIndexBase(3, Source::eDI, Source::eCX));
|
||||
|
||||
// Possibly TODO: pick a lane on whether PUSH/POP duplicate source and destination.
|
||||
// It doesn't really matter outside of these tests though.
|
||||
|
||||
// push eax
|
||||
// dec dh
|
||||
// loopne 0xffffffee (from 0x2d)
|
||||
// fiadd DWORD PTR [ebx-0x64f3e674]
|
||||
test(instructions[16], DataSize::DWord, Operation::PUSH, Source::eAX);
|
||||
test(instructions[17], DataSize::Byte, Operation::DEC, Source::DH);
|
||||
test(instructions[18], Operation::LOOPNE, 0, -63);
|
||||
test(instructions[19], Operation::ESC);
|
||||
|
||||
// mov DWORD PTR [ebx],edx
|
||||
// xor al,0x45
|
||||
// lds edx,FWORD PTR [ecx]
|
||||
// mov ds:0xe4dba6d3,al
|
||||
test(instructions[20], DataSize::DWord, Operation::MOV, Source::eDX, ScaleIndexBase(Source::eBX));
|
||||
test(instructions[21], DataSize::Byte, Operation::XOR, Source::Immediate, Source::eAX, 0x45);
|
||||
test(instructions[22], DataSize::DWord, Operation::LDS, ScaleIndexBase(Source::eCX), Source::eDX);
|
||||
test(instructions[23], DataSize::Byte, Operation::MOV, Source::eAX, Source::DirectAddress, 0xe4dba6d3);
|
||||
XCTAssertEqual(instructions[23].data_segment(), Source::DS);
|
||||
|
||||
// pop ds
|
||||
// movs DWORD PTR es:[edi],DWORD PTR ds:[esi]
|
||||
// jns 0x00000035 (from 0x42)
|
||||
// jge 0x00000060 (from 0x44)
|
||||
test(instructions[24], DataSize::Word, Operation::POP, Source::None, Source::DS);
|
||||
test(instructions[25], DataSize::DWord, Operation::MOVS);
|
||||
test(instructions[26], Operation::JNS, 0, -0xd);
|
||||
test(instructions[27], Operation::JNL, 0, 0x1c);
|
||||
|
||||
// mov eax,0x8a766bda
|
||||
// jns 0x00000073 (from 0x4b)
|
||||
// push edx
|
||||
// int 0xc4
|
||||
test(instructions[28], DataSize::DWord, Operation::MOV, Source::Immediate, Source::eAX, 0x8a766bda);
|
||||
test(instructions[29], Operation::JNS, 0, 0x28);
|
||||
test(instructions[30], DataSize::DWord, Operation::PUSH, Source::eDX);
|
||||
test(instructions[31], Operation::INT, 0xc4);
|
||||
|
||||
// jmp 0x29cf120d (from 0x53)
|
||||
// or DWORD PTR [esi+0x1a],eax
|
||||
// rcr BYTE PTR [ebp-0x78],0x34
|
||||
// movs DWORD PTR es:[edi],DWORD PTR ds:[esi]
|
||||
test(instructions[32], Operation::JMPrel, 0, 0x29cf120d - 0x53);
|
||||
test(instructions[33], DataSize::DWord, Operation::OR, Source::eAX, ScaleIndexBase(Source::eSI), 0, 0x1a);
|
||||
test(instructions[34], DataSize::Byte, Operation::RCR, Source::Immediate, ScaleIndexBase(Source::eBP), 0x34, -0x78);
|
||||
test(instructions[35], DataSize::DWord, Operation::MOVS);
|
||||
|
||||
// and edx,0xffffffd0
|
||||
// cmc
|
||||
// inc esp
|
||||
// popf
|
||||
test(instructions[36], DataSize::DWord, Operation::AND, Source::Immediate, Source::eDX);
|
||||
test(instructions[37], DataSize::None, Operation::CMC);
|
||||
test(instructions[38], DataSize::DWord, Operation::INC, Source::eSP);
|
||||
test(instructions[39], DataSize::DWord, Operation::POPF);
|
||||
|
||||
// movs DWORD PTR es:[edi],DWORD PTR ds:[esi]
|
||||
// rcr DWORD PTR [esi+0x4f],0x7
|
||||
// push ecx
|
||||
// aam 0xed
|
||||
test(instructions[40], DataSize::DWord, Operation::MOVS);
|
||||
test(instructions[41], DataSize::DWord, Operation::RCR, Source::Immediate, ScaleIndexBase(Source::eSI), 0x07, 0x4f);
|
||||
test(instructions[42], DataSize::DWord, Operation::PUSH, Source::eCX);
|
||||
test(instructions[43], Operation::AAM, 0xed);
|
||||
|
||||
// mov al,0x69
|
||||
// xlat BYTE PTR ds:[ebx]
|
||||
// add ch,al
|
||||
// push ecx
|
||||
test(instructions[44], DataSize::Byte, Operation::MOV, Source::Immediate, Source::eAX, 0x69);
|
||||
test(instructions[45], Operation::XLAT);
|
||||
test(instructions[46], DataSize::Byte, Operation::ADD, Source::eAX, Source::CH);
|
||||
test(instructions[47], DataSize::DWord, Operation::PUSH, Source::eCX);
|
||||
|
||||
// sti
|
||||
// push 0x698b3a85
|
||||
// sub BYTE PTR [esp+ebp*8],cl
|
||||
// mov cl,0xb7
|
||||
test(instructions[48], Operation::STI);
|
||||
test(instructions[49], DataSize::DWord, Operation::PUSH, Source::Immediate, Source::None, 0x698b3a85);
|
||||
test(instructions[50], DataSize::Byte, Operation::SUB, Source::eCX, ScaleIndexBase(3, Source::eBP, Source::eSP));
|
||||
test(instructions[51], DataSize::Byte, Operation::MOV, Source::Immediate, Source::eCX, 0xb7);
|
||||
|
||||
// cmp ecx,DWORD PTR [ebp+0x2c87445f]
|
||||
// jecxz 0x00000084 (from 0x82)
|
||||
// sahf
|
||||
// je 0x000000f3 (from 0x85)
|
||||
test(instructions[52], DataSize::DWord, Operation::CMP, ScaleIndexBase(Source::eBP), Source::eCX, 0, 0x2c87445f);
|
||||
test(instructions[53], Operation::JPCX, 0, 0x02);
|
||||
test(instructions[54], Operation::SAHF);
|
||||
test(instructions[55], Operation::JE, 0, 0x6e);
|
||||
|
||||
// sbb ecx,DWORD PTR [edi+0x433c54d]
|
||||
// lahf
|
||||
// lods al,BYTE PTR ds:[esi]
|
||||
// ror cl,0x60
|
||||
test(instructions[56], DataSize::DWord, Operation::SBB, ScaleIndexBase(Source::eDI), Source::eCX, 0, 0x433c54d);
|
||||
test(instructions[57], Operation::LAHF);
|
||||
test(instructions[58], Operation::LODS);
|
||||
test(instructions[59], DataSize::Byte, Operation::ROR, Source::Immediate, Source::eCX, 0x60);
|
||||
|
||||
// call 0xe21b:0x97d0f58a
|
||||
// fs pusha
|
||||
// mov al,0xcf
|
||||
// jecxz 0x000000d4 (from 0x9d)
|
||||
test_far(instructions[60], Operation::CALLfar, 0xe21b, 0x97d0f58a);
|
||||
test(instructions[61], Operation::PUSHA);
|
||||
test(instructions[62], DataSize::Byte, Operation::MOV, Source::Immediate, Source::eAX, 0xcf);
|
||||
test(instructions[63], Operation::JPCX, 0, 0xd4 - 0x9d);
|
||||
}
|
||||
|
||||
- (void)testSourceModRegRM1 {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
0x62, 0x90, 0x90, 0xdf, 0xcd, 0xf9
|
||||
}, true);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 1);
|
||||
test(instructions[0], DataSize::DWord, Operation::BOUND, ScaleIndexBase(Source::eAX), Source::eDX, 0, -0x6322070);
|
||||
}
|
||||
|
||||
- (void)testSourceModRegRM2 {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
0x81, 0x47, 0xbe, 0xa9, 0x3a, 0x68, 0x9f
|
||||
}, true);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 1);
|
||||
test(instructions[0], DataSize::DWord, Operation::ADD, Source::Immediate, ScaleIndexBase(Source::eDI), 0x9f683aa9, -0x42);
|
||||
}
|
||||
|
||||
- (void)test8086LengthLimit {
|
||||
const std::vector<uint8_t> all_prefix(65536, 0x26);
|
||||
const auto instructions = decode<Model::i8086>(all_prefix);
|
||||
XCTAssertEqual(instructions.size(), 1);
|
||||
test(instructions[0], Operation::NOP);
|
||||
}
|
||||
|
||||
- (void)test286LengthLimit {
|
||||
const auto instructions = decode<Model::i80286>({
|
||||
0x90,
|
||||
0x26, 0x90,
|
||||
0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
});
|
||||
|
||||
XCTAssertEqual(instructions.size(), 12);
|
||||
test(instructions[0], Operation::NOP);
|
||||
test(instructions[1], Operation::NOP);
|
||||
test(instructions[2], Operation::NOP);
|
||||
test(instructions[3], Operation::NOP);
|
||||
test(instructions[4], Operation::NOP);
|
||||
test(instructions[5], Operation::NOP);
|
||||
test(instructions[6], Operation::NOP);
|
||||
test(instructions[7], Operation::NOP);
|
||||
test(instructions[8], Operation::NOP);
|
||||
test(instructions[9], Operation::NOP);
|
||||
test(instructions[10], Operation::Invalid);
|
||||
test(instructions[11], Operation::NOP);
|
||||
}
|
||||
|
||||
- (void)test386LengthLimit {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
0x90,
|
||||
0x26, 0x90,
|
||||
0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x90,
|
||||
});
|
||||
|
||||
XCTAssertEqual(instructions.size(), 17);
|
||||
test(instructions[0], Operation::NOP);
|
||||
test(instructions[1], Operation::NOP);
|
||||
test(instructions[2], Operation::NOP);
|
||||
test(instructions[3], Operation::NOP);
|
||||
test(instructions[4], Operation::NOP);
|
||||
test(instructions[5], Operation::NOP);
|
||||
test(instructions[6], Operation::NOP);
|
||||
test(instructions[7], Operation::NOP);
|
||||
test(instructions[8], Operation::NOP);
|
||||
test(instructions[9], Operation::NOP);
|
||||
test(instructions[10], Operation::NOP);
|
||||
test(instructions[11], Operation::NOP);
|
||||
test(instructions[12], Operation::NOP);
|
||||
test(instructions[13], Operation::NOP);
|
||||
test(instructions[14], Operation::NOP);
|
||||
test(instructions[15], Operation::Invalid);
|
||||
test(instructions[16], Operation::NOP);
|
||||
}
|
||||
|
||||
- (void)testAddressSizeModifier {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
0x67, 0xf3, 0x5d, 0x67, 0x3f, 0x67, 0x5a, 0x67, 0xea, 0x17, 0xa2, 0x38, 0x0b, 0xeb, 0xbc, 0x67,
|
||||
0x4c, 0x67, 0x3a, 0x1f, 0x67, 0x00, 0x8d, 0xf9, 0x43, 0x67, 0xb1, 0x7c, 0x67, 0x88, 0xd1, 0x67,
|
||||
0x31, 0xed, 0x67, 0x22, 0x00, 0x67, 0x79, 0xa7, 0x67, 0x87, 0x3c, 0x67, 0xd4, 0xa2, 0x67, 0x57,
|
||||
0x67, 0x02, 0x21, 0x67, 0x48, 0x67, 0x33, 0x5d, 0xd7, 0x67, 0x3c, 0xe1, 0x67, 0x91, 0x67, 0x1b,
|
||||
0x84, 0x43, 0x7f, 0x67, 0x15, 0xf6, 0x06, 0x2b, 0x6d
|
||||
}, true);
|
||||
|
||||
// Lazy: just check that the right number of operations came out.
|
||||
// Since the potential issue is reading the wrong size of address, that'll do.
|
||||
XCTAssertEqual(instructions.size(), 22);
|
||||
}
|
||||
|
||||
- (void)testAddressSizeModifierSIB {
|
||||
const auto instructions = decode<Model::i80386>({
|
||||
// add dword ptr [bx + si + 256], eax
|
||||
0x67, 0x01, 0x80, 0x00, 0x01,
|
||||
// add [eax + 256], eax
|
||||
0x01, 0x80, 0x00, 0x01, 0x00, 0x00
|
||||
}, true);
|
||||
|
||||
XCTAssertEqual(instructions.size(), 2);
|
||||
test(instructions[0], DataSize::DWord, Operation::ADD, Source::eAX, ScaleIndexBase(Source::eBX, Source::eSI), 0, 0x100);
|
||||
test(instructions[1], DataSize::DWord, Operation::ADD, Source::eAX, ScaleIndexBase(Source::eAX), 0, 0x100);
|
||||
}
|
||||
|
||||
@end
|
||||
|
Loading…
Reference in New Issue
Block a user