use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Gen {
I8086,
I386,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Bits {
B16,
B32,
B64,
}
impl Bits {
#[must_use]
pub const fn operand(self) -> u8 {
match self {
Bits::B16 => 2,
Bits::B32 | Bits::B64 => 4,
}
}
#[must_use]
pub const fn address(self) -> u8 {
match self {
Bits::B16 => 2,
Bits::B32 => 4,
Bits::B64 => 8,
}
}
#[must_use]
pub const fn operand_alt(self) -> u8 {
match self {
Bits::B16 => 4,
Bits::B32 | Bits::B64 => 2,
}
}
#[must_use]
pub const fn address_alt(self) -> u8 {
match self {
Bits::B16 => 4,
Bits::B32 => 2,
Bits::B64 => 4,
}
}
#[must_use]
pub const fn is_64(self) -> bool {
matches!(self, Bits::B64)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Arg {
None,
Eb,
Ev,
Ew,
Ed,
Gb,
Gv,
Gw,
Sw,
M,
Mp,
Ms,
Rd,
Cd,
Dd,
Td,
Ib,
Iw,
Iv,
Iz,
Ibs,
Jb,
Jv,
Ap,
Ob,
Ov,
Rb,
Rv,
Sr,
One,
Cl,
Dx,
Al,
Ax,
Xb,
Xv,
Yb,
Yv,
Mf32,
Mf64,
Mf80,
Mi16,
Mi32,
Mi64,
Mfenv,
Mfsave,
St0,
Sti,
Vx,
Wx,
Wq,
Wd,
Ux,
Mq,
Mfx,
Ey,
Gy,
}
impl Arg {
#[must_use]
pub const fn width_bytes(self, osz: u8) -> Option<u8> {
match self {
Arg::Eb
| Arg::Gb
| Arg::Ib
| Arg::Ob
| Arg::Rb
| Arg::Al
| Arg::Xb
| Arg::Yb
| Arg::Jb
| Arg::One
| Arg::Cl => Some(1),
Arg::Ew | Arg::Gw | Arg::Iw | Arg::Sw | Arg::Sr => Some(2),
Arg::Cd | Arg::Dd | Arg::Td | Arg::Rd | Arg::Ed => Some(4),
Arg::Ev
| Arg::Gv
| Arg::Iv
| Arg::Iz
| Arg::Ibs
| Arg::Ov
| Arg::Rv
| Arg::Ax
| Arg::Xv
| Arg::Yv
| Arg::Jv => Some(osz),
Arg::None | Arg::M | Arg::Mp | Arg::Ms | Arg::Ap | Arg::Dx => None,
Arg::Mf32
| Arg::Mf64
| Arg::Mf80
| Arg::Mi16
| Arg::Mi32
| Arg::Mi64
| Arg::Mfenv
| Arg::Mfsave
| Arg::St0
| Arg::Sti
| Arg::Vx
| Arg::Wx
| Arg::Wq
| Arg::Wd
| Arg::Ux
| Arg::Mq
| Arg::Mfx
| Arg::Ey
| Arg::Gy => None,
}
}
#[must_use]
pub const fn fp_bytes(self, osz: u8) -> Option<u8> {
match self {
Arg::Mf32 | Arg::Mi32 | Arg::Wd => Some(4),
Arg::Mf64 | Arg::Mi64 | Arg::Wq | Arg::Mq => Some(8),
Arg::Mf80 => Some(10),
Arg::Mi16 => Some(2),
Arg::Wx => Some(16),
Arg::Mfenv => Some(if osz == 2 { 14 } else { 28 }),
Arg::Mfsave => Some(if osz == 2 { 94 } else { 108 }),
_ => None,
}
}
#[must_use]
pub const fn needs_modrm(self) -> bool {
matches!(
self,
Arg::Eb
| Arg::Ev
| Arg::Ew
| Arg::Ed
| Arg::Gb
| Arg::Gv
| Arg::Gw
| Arg::Sw
| Arg::M
| Arg::Mp
| Arg::Ms
| Arg::Rd
| Arg::Cd
| Arg::Dd
| Arg::Td
| Arg::Mf32
| Arg::Mf64
| Arg::Mf80
| Arg::Mi16
| Arg::Mi32
| Arg::Mi64
| Arg::Mfenv
| Arg::Mfsave
| Arg::Sti
| Arg::Vx
| Arg::Wx
| Arg::Wq
| Arg::Wd
| Arg::Ux
| Arg::Mq
| Arg::Mfx
| Arg::Ey
| Arg::Gy
)
}
#[must_use]
pub const fn uses_modrm_memory(self) -> bool {
self.needs_modrm() && !matches!(self, Arg::Rd | Arg::Cd | Arg::Dd | Arg::Td | Arg::Ux)
}
#[must_use]
pub const fn immediate_bytes(self, osz: u8, asz: u8) -> u8 {
match self {
Arg::Ib | Arg::Ibs | Arg::Jb => 1,
Arg::Iw => 2,
Arg::Iv => osz,
Arg::Iz | Arg::Jv => {
if osz > 4 {
4
} else {
osz
}
}
Arg::Ob | Arg::Ov => asz,
Arg::Ap => osz + 2,
_ => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Class {
Documented,
Alias,
Undocumented,
Undefined,
Prefix,
Escape,
}
impl Class {
#[must_use]
pub const fn is_documented(self) -> bool {
matches!(self, Class::Documented)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Grp {
None,
Alu,
Shift,
Pop,
IncDec,
Unary,
Misc,
MovImm,
Grp6,
Grp7,
Grp8,
ModAlt,
Grp9,
Grp15,
X87,
}
macro_rules! define_ops {
($($name:ident = $summary:literal,)*) => {
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Op {
$(
#[doc = $summary]
$name,
)*
}
impl Op {
#[must_use]
pub const fn summary(self) -> &'static str {
match self { $(Op::$name => $summary,)* }
}
pub const ALL: &'static [Op] = &[$(Op::$name,)*];
}
const MNEMONICS: &[&str] = &[$(lower(stringify!($name)),)*];
};
}
const fn lower(name: &'static str) -> &'static str {
let bytes = name.as_bytes();
let mut i = 0;
while i < LOWERCASE.len() {
let cand = LOWERCASE[i].0.as_bytes();
if cand.len() == bytes.len() {
let mut j = 0;
let mut same = true;
while j < bytes.len() {
if cand[j] != bytes[j] {
same = false;
break;
}
j += 1;
}
if same {
return LOWERCASE[i].1;
}
}
i += 1;
}
panic!("mnemonic missing from LOWERCASE");
}
const LOWERCASE: &[(&str, &str)] = &[
("AAA", "aaa"),
("AAD", "aad"),
("AAM", "aam"),
("AAS", "aas"),
("ADC", "adc"),
("ADD", "add"),
("AND", "and"),
("CALL", "call"),
("CALLF", "callf"),
("CBW", "cbw"),
("CLC", "clc"),
("CLD", "cld"),
("CLI", "cli"),
("CMC", "cmc"),
("CMP", "cmp"),
("CMPSB", "cmpsb"),
("CMPSW", "cmpsw"),
("CWD", "cwd"),
("DAA", "daa"),
("DAS", "das"),
("DEC", "dec"),
("DIV", "div"),
("ESC", "esc"),
("HLT", "hlt"),
("IDIV", "idiv"),
("IMUL", "imul"),
("IN", "in"),
("INC", "inc"),
("INT", "int"),
("INT3", "int3"),
("INTO", "into"),
("IRET", "iret"),
("JA", "ja"),
("JB", "jb"),
("JBE", "jbe"),
("JCXZ", "jcxz"),
("JG", "jg"),
("JGE", "jge"),
("JL", "jl"),
("JLE", "jle"),
("JMP", "jmp"),
("JMPF", "jmpf"),
("JNB", "jnb"),
("JNO", "jno"),
("JNP", "jnp"),
("JNS", "jns"),
("JNZ", "jnz"),
("JO", "jo"),
("JP", "jp"),
("JS", "js"),
("JZ", "jz"),
("LAHF", "lahf"),
("LDS", "lds"),
("LEA", "lea"),
("LES", "les"),
("LOCK", "lock"),
("LODSB", "lodsb"),
("LODSW", "lodsw"),
("LOOP", "loop"),
("LOOPE", "loope"),
("LOOPNE", "loopne"),
("MOV", "mov"),
("MOVSB", "movsb"),
("MOVSW", "movsw"),
("MUL", "mul"),
("NEG", "neg"),
("NOP", "nop"),
("NOT", "not"),
("OR", "or"),
("OUT", "out"),
("POP", "pop"),
("POPF", "popf"),
("PUSH", "push"),
("PUSHF", "pushf"),
("RCL", "rcl"),
("RCR", "rcr"),
("REP", "rep"),
("REPNE", "repne"),
("RET", "ret"),
("RETF", "retf"),
("ROL", "rol"),
("ROR", "ror"),
("SAHF", "sahf"),
("SALC", "salc"),
("SAR", "sar"),
("SBB", "sbb"),
("SCASB", "scasb"),
("SCASW", "scasw"),
("SEG", "seg"),
("SETMO", "setmo"),
("SHL", "shl"),
("SHR", "shr"),
("STC", "stc"),
("STD", "std"),
("STI", "sti"),
("STOSB", "stosb"),
("STOSW", "stosw"),
("SUB", "sub"),
("TEST", "test"),
("WAIT", "wait"),
("XCHG", "xchg"),
("XLAT", "xlat"),
("XOR", "xor"),
("ARPL", "arpl"),
("BOUND", "bound"),
("BSF", "bsf"),
("BSR", "bsr"),
("BSWAP", "bswap"),
("BT", "bt"),
("BTC", "btc"),
("BTR", "btr"),
("BTS", "bts"),
("CLTS", "clts"),
("CMPXCHG", "cmpxchg"),
("CPUID", "cpuid"),
("ENTER", "enter"),
("ICEBP", "icebp"),
("INSB", "insb"),
("INSW", "insw"),
("INVD", "invd"),
("INVLPG", "invlpg"),
("LAR", "lar"),
("LEAVE", "leave"),
("LFS", "lfs"),
("LGDT", "lgdt"),
("LGS", "lgs"),
("LIDT", "lidt"),
("LLDT", "lldt"),
("LMSW", "lmsw"),
("LSL", "lsl"),
("LSS", "lss"),
("LTR", "ltr"),
("MOVSX", "movsx"),
("MOVZX", "movzx"),
("OUTSB", "outsb"),
("OUTSW", "outsw"),
("POPA", "popa"),
("PUSHA", "pusha"),
("SETA", "seta"),
("SETB", "setb"),
("SETBE", "setbe"),
("SETG", "setg"),
("SETGE", "setge"),
("SETL", "setl"),
("SETLE", "setle"),
("SETNB", "setnb"),
("SETNO", "setno"),
("SETNP", "setnp"),
("SETNS", "setns"),
("SETNZ", "setnz"),
("SETO", "seto"),
("SETP", "setp"),
("SETS", "sets"),
("SETZ", "setz"),
("SGDT", "sgdt"),
("SHLD", "shld"),
("SHRD", "shrd"),
("SIDT", "sidt"),
("SLDT", "sldt"),
("SMSW", "smsw"),
("STR", "str"),
("UD", "ud"),
("VERR", "verr"),
("VERW", "verw"),
("WBINVD", "wbinvd"),
("XADD", "xadd"),
("CMOVA", "cmova"),
("CMOVB", "cmovb"),
("CMOVBE", "cmovbe"),
("CMOVG", "cmovg"),
("CMOVGE", "cmovge"),
("CMOVL", "cmovl"),
("CMOVLE", "cmovle"),
("CMOVNB", "cmovnb"),
("CMOVNO", "cmovno"),
("CMOVNP", "cmovnp"),
("CMOVNS", "cmovns"),
("CMOVNZ", "cmovnz"),
("CMOVO", "cmovo"),
("CMOVP", "cmovp"),
("CMOVS", "cmovs"),
("CMOVZ", "cmovz"),
("MOVSXD", "movsxd"),
("RDMSR", "rdmsr"),
("RDTSC", "rdtsc"),
("REX", "rex"),
("SWAPGS", "swapgs"),
("SYSCALL", "syscall"),
("SYSRET", "sysret"),
("WRMSR", "wrmsr"),
("CMPXCHG8B", "cmpxchg8b"),
("FABS", "fabs"),
("FADD", "fadd"),
("FADDP", "faddp"),
("FCHS", "fchs"),
("FCMOVB", "fcmovb"),
("FCMOVBE", "fcmovbe"),
("FCMOVE", "fcmove"),
("FCMOVNB", "fcmovnb"),
("FCMOVNBE", "fcmovnbe"),
("FCMOVNE", "fcmovne"),
("FCMOVNU", "fcmovnu"),
("FCMOVU", "fcmovu"),
("FCOM", "fcom"),
("FCOMI", "fcomi"),
("FCOMIP", "fcomip"),
("FCOMP", "fcomp"),
("FCOMPP", "fcompp"),
("FDECSTP", "fdecstp"),
("FDIV", "fdiv"),
("FDIVP", "fdivp"),
("FDIVR", "fdivr"),
("FDIVRP", "fdivrp"),
("FFREE", "ffree"),
("FIADD", "fiadd"),
("FICOM", "ficom"),
("FICOMP", "ficomp"),
("FIDIV", "fidiv"),
("FIDIVR", "fidivr"),
("FILD", "fild"),
("FIMUL", "fimul"),
("FINCSTP", "fincstp"),
("FIST", "fist"),
("FISTP", "fistp"),
("FISUB", "fisub"),
("FISUBR", "fisubr"),
("FLD", "fld"),
("FLD1", "fld1"),
("FLDCW", "fldcw"),
("FLDENV", "fldenv"),
("FLDL2E", "fldl2e"),
("FLDL2T", "fldl2t"),
("FLDLG2", "fldlg2"),
("FLDLN2", "fldln2"),
("FLDPI", "fldpi"),
("FLDZ", "fldz"),
("FMUL", "fmul"),
("FMULP", "fmulp"),
("FNCLEX", "fnclex"),
("FNINIT", "fninit"),
("FNOP", "fnop"),
("FNSAVE", "fnsave"),
("FNSTCW", "fnstcw"),
("FNSTENV", "fnstenv"),
("FNSTSW", "fnstsw"),
("FPREM", "fprem"),
("FPREM1", "fprem1"),
("FRNDINT", "frndint"),
("FRSTOR", "frstor"),
("FSCALE", "fscale"),
("FSQRT", "fsqrt"),
("FST", "fst"),
("FSTP", "fstp"),
("FSUB", "fsub"),
("FSUBP", "fsubp"),
("FSUBR", "fsubr"),
("FSUBRP", "fsubrp"),
("FTST", "ftst"),
("FUCOM", "fucom"),
("FUCOMI", "fucomi"),
("FUCOMIP", "fucomip"),
("FUCOMP", "fucomp"),
("FUCOMPP", "fucompp"),
("FXAM", "fxam"),
("FXCH", "fxch"),
("FXTRACT", "fxtract"),
("ADDPD", "addpd"),
("ADDPS", "addps"),
("ADDSD", "addsd"),
("ADDSS", "addss"),
("ANDNPD", "andnpd"),
("ANDNPS", "andnps"),
("ANDPD", "andpd"),
("ANDPS", "andps"),
("CMPPD", "cmppd"),
("CMPPS", "cmpps"),
("CMPSD", "cmpsd"),
("CMPSS", "cmpss"),
("COMISD", "comisd"),
("COMISS", "comiss"),
("CVTPD2PS", "cvtpd2ps"),
("CVTPS2PD", "cvtps2pd"),
("CVTSD2SI", "cvtsd2si"),
("CVTSD2SS", "cvtsd2ss"),
("CVTSI2SD", "cvtsi2sd"),
("CVTSI2SS", "cvtsi2ss"),
("CVTSS2SD", "cvtss2sd"),
("CVTSS2SI", "cvtss2si"),
("CVTTSD2SI", "cvttsd2si"),
("CVTTSS2SI", "cvttss2si"),
("DIVPD", "divpd"),
("DIVPS", "divps"),
("DIVSD", "divsd"),
("DIVSS", "divss"),
("FXRSTOR", "fxrstor"),
("FXSAVE", "fxsave"),
("LDMXCSR", "ldmxcsr"),
("LFENCE", "lfence"),
("MAXPD", "maxpd"),
("MAXPS", "maxps"),
("MAXSD", "maxsd"),
("MAXSS", "maxss"),
("MFENCE", "mfence"),
("MINPD", "minpd"),
("MINPS", "minps"),
("MINSD", "minsd"),
("MINSS", "minss"),
("MOVAPD", "movapd"),
("MOVAPS", "movaps"),
("MOVD", "movd"),
("MOVDQA", "movdqa"),
("MOVDQU", "movdqu"),
("MOVHLPS", "movhlps"),
("MOVHPD", "movhpd"),
("MOVHPS", "movhps"),
("MOVLHPS", "movlhps"),
("MOVLPD", "movlpd"),
("MOVLPS", "movlps"),
("MOVMSKPD", "movmskpd"),
("MOVMSKPS", "movmskps"),
("MOVQ", "movq"),
("MOVSD", "movsd"),
("MOVSS", "movss"),
("MOVUPD", "movupd"),
("MOVUPS", "movups"),
("MULPD", "mulpd"),
("MULPS", "mulps"),
("MULSD", "mulsd"),
("MULSS", "mulss"),
("ORPD", "orpd"),
("ORPS", "orps"),
("PAND", "pand"),
("PANDN", "pandn"),
("POR", "por"),
("PXOR", "pxor"),
("SFENCE", "sfence"),
("SHUFPD", "shufpd"),
("SHUFPS", "shufps"),
("SQRTPD", "sqrtpd"),
("SQRTPS", "sqrtps"),
("SQRTSD", "sqrtsd"),
("SQRTSS", "sqrtss"),
("STMXCSR", "stmxcsr"),
("SUBPD", "subpd"),
("SUBPS", "subps"),
("SUBSD", "subsd"),
("SUBSS", "subss"),
("UCOMISD", "ucomisd"),
("UCOMISS", "ucomiss"),
("UNPCKHPD", "unpckhpd"),
("UNPCKHPS", "unpckhps"),
("UNPCKLPD", "unpcklpd"),
("UNPCKLPS", "unpcklps"),
("XORPD", "xorpd"),
("XORPS", "xorps"),
];
define_ops! {
AAA = "ASCII adjust AL after addition",
AAD = "ASCII adjust AX before division",
AAM = "ASCII adjust AX after multiplication",
AAS = "ASCII adjust AL after subtraction",
ADC = "add with carry",
ADD = "add",
AND = "bitwise AND",
CALL = "call a near procedure",
CALLF = "call a far procedure",
CBW = "sign-extend AL into AX",
CLC = "clear the carry flag",
CLD = "clear the direction flag",
CLI = "clear the interrupt-enable flag",
CMC = "complement the carry flag",
CMP = "compare by subtracting and discarding the result",
CMPSB = "compare a byte at DS:SI with one at ES:DI",
CMPSW = "compare a word at DS:SI with one at ES:DI",
CWD = "sign-extend AX into DX:AX",
DAA = "decimal adjust AL after addition",
DAS = "decimal adjust AL after subtraction",
DEC = "decrement by one, leaving the carry flag alone",
DIV = "unsigned divide",
ESC = "coprocessor escape: read the operand and ignore it",
HLT = "halt until an interrupt or reset",
IDIV = "signed divide",
IMUL = "signed multiply",
IN = "read a byte or word from an I/O port",
INC = "increment by one, leaving the carry flag alone",
INT = "software interrupt",
INT3 = "breakpoint interrupt (the one-byte encoding of INT 3)",
INTO = "interrupt 4 if the overflow flag is set",
IRET = "return from an interrupt, restoring the flags",
JA = "jump if above (unsigned greater)",
JB = "jump if below (unsigned less; carry set)",
JBE = "jump if below or equal",
JCXZ = "jump if CX is zero",
JG = "jump if greater (signed)",
JGE = "jump if greater or equal (signed)",
JL = "jump if less (signed)",
JLE = "jump if less or equal (signed)",
JMP = "jump",
JMPF = "jump to a far address",
JNB = "jump if not below (carry clear)",
JNO = "jump if the overflow flag is clear",
JNP = "jump if parity odd",
JNS = "jump if the sign flag is clear",
JNZ = "jump if not equal (zero clear)",
JO = "jump if the overflow flag is set",
JP = "jump if parity even",
JS = "jump if the sign flag is set",
JZ = "jump if equal (zero set)",
LAHF = "load the low flags byte into AH",
LDS = "load a far pointer into DS and a register",
LEA = "load the effective address, without accessing memory",
LES = "load a far pointer into ES and a register",
LOCK = "prefix: assert the LOCK pin for the next instruction",
LODSB = "load a byte from DS:SI into AL",
LODSW = "load a word from DS:SI into AX",
LOOP = "decrement CX and jump if it is not zero",
LOOPE = "decrement CX and jump if it is not zero and the zero flag is set",
LOOPNE = "decrement CX and jump if it is not zero and the zero flag is clear",
MOV = "move",
MOVSB = "move a byte from DS:SI to ES:DI",
MOVSW = "move a word from DS:SI to ES:DI",
MUL = "unsigned multiply",
NEG = "two's complement negate",
NOP = "no operation (the XCHG AX,AX encoding)",
NOT = "one's complement",
OR = "bitwise OR",
OUT = "write a byte or word to an I/O port",
POP = "pop a word from the stack",
POPF = "pop the flags register",
PUSH = "push a word onto the stack",
PUSHF = "push the flags register",
RCL = "rotate left through the carry flag",
RCR = "rotate right through the carry flag",
REP = "prefix: repeat a string operation while CX is non-zero (and, for CMPS/SCAS, while equal)",
REPNE = "prefix: repeat a string operation while CX is non-zero and not equal",
RET = "return from a near procedure",
RETF = "return from a far procedure",
ROL = "rotate left",
ROR = "rotate right",
SAHF = "store AH into the low flags byte",
SALC = "undocumented: set AL to 0xff if the carry flag is set, else 0",
SAR = "arithmetic shift right, preserving the sign",
SBB = "subtract with borrow",
SCASB = "compare AL with the byte at ES:DI",
SCASW = "compare AX with the word at ES:DI",
SEG = "prefix: override the default segment",
SETMO = "undocumented: set the operand to all ones",
SHL = "shift left",
SHR = "logical shift right",
STC = "set the carry flag",
STD = "set the direction flag",
STI = "set the interrupt-enable flag",
STOSB = "store AL at ES:DI",
STOSW = "store AX at ES:DI",
SUB = "subtract",
TEST = "AND and set flags, discarding the result",
WAIT = "wait for the coprocessor's TEST pin",
XCHG = "exchange two operands",
XLAT = "load AL from the table at DS:BX indexed by AL",
XOR = "bitwise exclusive OR",
BOUND = "check an array index against a pair of bounds, or raise #BR",
ENTER = "make a stack frame, copying the enclosing frames' pointers",
INSB = "read a byte from the port in DX to ES:DI",
INSW = "read a word or dword from the port in DX to ES:DI",
LEAVE = "unmake the stack frame ENTER made",
OUTSB = "write the byte at DS:SI to the port in DX",
OUTSW = "write the word or dword at DS:SI to the port in DX",
POPA = "pop the eight general registers, discarding the saved SP",
PUSHA = "push the eight general registers",
ARPL = "raise a selector's requested privilege level to the caller's",
CLTS = "clear the task-switched flag in CR0",
LAR = "load a descriptor's access rights, if the selector is visible",
LGDT = "load the global descriptor table register",
LIDT = "load the interrupt descriptor table register",
LLDT = "load the local descriptor table register",
LMSW = "load the low sixteen bits of CR0 (the 286's machine status word)",
LSL = "load a descriptor's segment limit, if the selector is visible",
LTR = "load the task register",
SGDT = "store the global descriptor table register",
SIDT = "store the interrupt descriptor table register",
SLDT = "store the local descriptor table register's selector",
SMSW = "store the low sixteen bits of CR0",
STR = "store the task register's selector",
VERR = "set ZF if a segment is readable from the current privilege level",
VERW = "set ZF if a segment is writable from the current privilege level",
BSF = "scan for the least significant set bit",
BSR = "scan for the most significant set bit",
BT = "copy one bit of the operand into the carry flag",
BTC = "copy one bit into the carry flag and complement it",
BTR = "copy one bit into the carry flag and clear it",
BTS = "copy one bit into the carry flag and set it",
ICEBP = "undocumented: interrupt 1, the in-circuit emulator breakpoint",
LFS = "load a far pointer into FS and a register",
LGS = "load a far pointer into GS and a register",
LSS = "load a far pointer into SS and a register",
MOVSX = "move with sign extension",
MOVZX = "move with zero extension",
SETA = "set the operand to 1 if above (unsigned greater), else 0",
SETB = "set the operand to 1 if below (carry set), else 0",
SETBE = "set the operand to 1 if below or equal, else 0",
SETG = "set the operand to 1 if greater (signed), else 0",
SETGE = "set the operand to 1 if greater or equal (signed), else 0",
SETL = "set the operand to 1 if less (signed), else 0",
SETLE = "set the operand to 1 if less or equal (signed), else 0",
SETNB = "set the operand to 1 if not below (carry clear), else 0",
SETNO = "set the operand to 1 if the overflow flag is clear, else 0",
SETNP = "set the operand to 1 if parity odd, else 0",
SETNS = "set the operand to 1 if the sign flag is clear, else 0",
SETNZ = "set the operand to 1 if not equal, else 0",
SETO = "set the operand to 1 if the overflow flag is set, else 0",
SETP = "set the operand to 1 if parity even, else 0",
SETS = "set the operand to 1 if the sign flag is set, else 0",
SETZ = "set the operand to 1 if equal, else 0",
SHLD = "shift left, filling from a second operand",
SHRD = "shift right, filling from a second operand",
UD = "undefined encoding: raises an invalid-opcode exception",
BSWAP = "reverse the byte order of a 32-bit register",
CMPXCHG = "compare with the accumulator and exchange",
CPUID = "report the processor's identity and features",
INVD = "invalidate the caches without writing them back",
INVLPG = "invalidate one page's translation-lookaside-buffer entry",
WBINVD = "write the caches back and invalidate them",
XADD = "exchange, then add",
CMOVA = "move if above (unsigned greater)",
CMOVB = "move if below (carry set)",
CMOVBE = "move if below or equal",
CMOVG = "move if greater (signed)",
CMOVGE = "move if greater or equal (signed)",
CMOVL = "move if less (signed)",
CMOVLE = "move if less or equal (signed)",
CMOVNB = "move if not below (carry clear)",
CMOVNO = "move if the overflow flag is clear",
CMOVNP = "move if parity odd",
CMOVNS = "move if the sign flag is clear",
CMOVNZ = "move if not equal (zero clear)",
CMOVO = "move if the overflow flag is set",
CMOVP = "move if parity even",
CMOVS = "move if the sign flag is set",
CMOVZ = "move if equal (zero set)",
RDMSR = "read the model-specific register ECX names into EDX:EAX",
RDTSC = "read the time-stamp counter into EDX:EAX",
WRMSR = "write EDX:EAX to the model-specific register ECX names",
MOVSXD = "move a 32-bit source, sign-extended to the operand size",
REX = "prefix: extend the register fields, or widen the operand to 64 bits",
SWAPGS = "exchange the GS base with IA32_KERNEL_GS_BASE",
SYSCALL = "fast call to the kernel entry point in LSTAR",
SYSRET = "fast return from SYSCALL",
CMPXCHG8B = "compare EDX:EAX with a 64-bit memory operand and exchange (CMPXCHG16B with REX.W)",
FABS = "replace ST(0) with its magnitude",
FADD = "floating-point add",
FADDP = "floating-point add and pop",
FCHS = "invert the sign of ST(0)",
FCMOVB = "move ST(i) to ST(0) if below (carry set)",
FCMOVBE = "move ST(i) to ST(0) if below or equal",
FCMOVE = "move ST(i) to ST(0) if equal (zero set)",
FCMOVNB = "move ST(i) to ST(0) if not below (carry clear)",
FCMOVNBE = "move ST(i) to ST(0) if not below or equal",
FCMOVNE = "move ST(i) to ST(0) if not equal (zero clear)",
FCMOVNU = "move ST(i) to ST(0) if not unordered (parity odd)",
FCMOVU = "move ST(i) to ST(0) if unordered (parity even)",
FCOM = "compare ST(0) with the source, setting the condition codes",
FCOMI = "compare ST(0) with ST(i), setting ZF, PF and CF",
FCOMIP = "compare ST(0) with ST(i) into the flags, then pop",
FCOMP = "compare and pop",
FCOMPP = "compare and pop twice",
FDECSTP = "decrement the top-of-stack pointer, leaving the tags alone",
FDIV = "floating-point divide",
FDIVP = "floating-point divide and pop",
FDIVR = "floating-point divide, operands reversed",
FDIVRP = "floating-point reverse divide and pop",
FFREE = "mark ST(i) empty without moving the top-of-stack pointer",
FIADD = "add an integer from memory to ST(0)",
FICOM = "compare ST(0) with an integer from memory",
FICOMP = "compare ST(0) with an integer from memory and pop",
FIDIV = "divide ST(0) by an integer from memory",
FIDIVR = "divide an integer from memory by ST(0)",
FILD = "push an integer from memory, converted",
FIMUL = "multiply ST(0) by an integer from memory",
FINCSTP = "increment the top-of-stack pointer, leaving the tags alone",
FIST = "store ST(0) as an integer",
FISTP = "store ST(0) as an integer and pop",
FISUB = "subtract an integer from memory from ST(0)",
FISUBR = "subtract ST(0) from an integer from memory",
FLD = "push a floating-point value",
FLD1 = "push +1.0",
FLDCW = "load the control word",
FLDENV = "load the environment: the control, status and tag words and the pointers",
FLDL2E = "push log2(e)",
FLDL2T = "push log2(10)",
FLDLG2 = "push log10(2)",
FLDLN2 = "push ln(2)",
FLDPI = "push pi",
FLDZ = "push +0.0",
FMUL = "floating-point multiply",
FMULP = "floating-point multiply and pop",
FNCLEX = "clear the exception flags without checking for a pending one",
FNINIT = "reinitialise the unit without checking for a pending exception",
FNOP = "no operation",
FNSAVE = "store the environment and the eight registers, then reinitialise",
FNSTCW = "store the control word",
FNSTENV = "store the environment",
FNSTSW = "store the status word",
FPREM = "the partial remainder of ST(0) / ST(1), truncated quotient",
FPREM1 = "the IEEE partial remainder of ST(0) / ST(1)",
FRNDINT = "round ST(0) to an integer in the current rounding mode",
FRSTOR = "reload the environment and the eight registers",
FSCALE = "multiply ST(0) by 2 to the truncated ST(1)",
FSQRT = "the square root of ST(0)",
FST = "store ST(0)",
FSTP = "store ST(0) and pop",
FSUB = "floating-point subtract",
FSUBP = "floating-point subtract and pop",
FSUBR = "floating-point subtract, operands reversed",
FSUBRP = "floating-point reverse subtract and pop",
FTST = "compare ST(0) with +0.0",
FUCOM = "compare ST(0) with ST(i), quietly: a quiet NaN is not an invalid operand",
FUCOMI = "compare ST(0) with ST(i) quietly, into the flags",
FUCOMIP = "compare ST(0) with ST(i) quietly into the flags, then pop",
FUCOMP = "unordered compare and pop",
FUCOMPP = "unordered compare and pop twice",
FXAM = "classify ST(0) into the condition codes",
FXCH = "exchange ST(0) with ST(i)",
FXTRACT = "split ST(0) into its exponent and its significand",
ADDPD = "add two packed doubles",
ADDPS = "add four packed singles",
ADDSD = "add the low doubles",
ADDSS = "add the low singles",
ANDNPD = "bitwise AND NOT of packed doubles",
ANDNPS = "bitwise AND NOT of packed singles",
ANDPD = "bitwise AND of packed doubles",
ANDPS = "bitwise AND of packed singles",
CMPPD = "compare packed doubles under an immediate predicate",
CMPPS = "compare packed singles under an immediate predicate",
CMPSD = "compare the low doubles under an immediate predicate",
CMPSS = "compare the low singles under an immediate predicate",
COMISD = "compare the low doubles into the flags, signalling on any NaN",
COMISS = "compare the low singles into the flags, signalling on any NaN",
CVTPD2PS = "convert two packed doubles to two singles",
CVTPS2PD = "convert two packed singles to two doubles",
CVTSD2SI = "convert the low double to an integer, current rounding mode",
CVTSD2SS = "convert the low double to a single",
CVTSI2SD = "convert an integer to the low double",
CVTSI2SS = "convert an integer to the low single",
CVTSS2SD = "convert the low single to a double",
CVTSS2SI = "convert the low single to an integer, current rounding mode",
CVTTSD2SI = "convert the low double to an integer, truncating",
CVTTSS2SI = "convert the low single to an integer, truncating",
DIVPD = "divide packed doubles",
DIVPS = "divide packed singles",
DIVSD = "divide the low doubles",
DIVSS = "divide the low singles",
FXRSTOR = "reload the x87 and SSE state from a 512-byte area",
FXSAVE = "store the x87 and SSE state to a 512-byte area",
LDMXCSR = "load MXCSR",
LFENCE = "serialise loads",
MAXPD = "the larger of packed doubles",
MAXPS = "the larger of packed singles",
MAXSD = "the larger of the low doubles",
MAXSS = "the larger of the low singles",
MFENCE = "serialise loads and stores",
MINPD = "the smaller of packed doubles",
MINPS = "the smaller of packed singles",
MINSD = "the smaller of the low doubles",
MINSS = "the smaller of the low singles",
MOVAPD = "move two aligned packed doubles",
MOVAPS = "move four aligned packed singles",
MOVD = "move a doubleword between a general register and an XMM register",
MOVDQA = "move an aligned double quadword",
MOVDQU = "move an unaligned double quadword",
MOVHLPS = "move the high quadword of the source to the low quadword",
MOVHPD = "move a double to or from the high quadword",
MOVHPS = "move two singles to or from the high quadword",
MOVLHPS = "move the low quadword of the source to the high quadword",
MOVLPD = "move a double to or from the low quadword",
MOVLPS = "move two singles to or from the low quadword",
MOVMSKPD = "gather the sign bits of two packed doubles",
MOVMSKPS = "gather the sign bits of four packed singles",
MOVQ = "move a quadword",
MOVSD = "move the low double, zeroing the rest on a load from memory",
MOVSS = "move the low single, zeroing the rest on a load from memory",
MOVUPD = "move two unaligned packed doubles",
MOVUPS = "move four unaligned packed singles",
MULPD = "multiply packed doubles",
MULPS = "multiply packed singles",
MULSD = "multiply the low doubles",
MULSS = "multiply the low singles",
ORPD = "bitwise OR of packed doubles",
ORPS = "bitwise OR of packed singles",
PAND = "bitwise AND of a double quadword",
PANDN = "bitwise AND NOT of a double quadword",
POR = "bitwise OR of a double quadword",
PXOR = "bitwise exclusive OR of a double quadword",
SFENCE = "serialise stores",
SHUFPD = "select one double from each source under an immediate",
SHUFPS = "select two singles from each source under an immediate",
SQRTPD = "the square roots of packed doubles",
SQRTPS = "the square roots of packed singles",
SQRTSD = "the square root of the low double",
SQRTSS = "the square root of the low single",
STMXCSR = "store MXCSR",
SUBPD = "subtract packed doubles",
SUBPS = "subtract packed singles",
SUBSD = "subtract the low doubles",
SUBSS = "subtract the low singles",
UCOMISD = "compare the low doubles into the flags, quietly",
UCOMISS = "compare the low singles into the flags, quietly",
UNPCKHPD = "interleave the high doubles",
UNPCKHPS = "interleave the high singles",
UNPCKLPD = "interleave the low doubles",
UNPCKLPS = "interleave the low singles",
XORPD = "bitwise exclusive OR of packed doubles",
XORPS = "bitwise exclusive OR of packed singles",
}
impl Op {
#[must_use]
pub const fn mnemonic(self) -> &'static str {
MNEMONICS[self as usize]
}
#[must_use]
pub const fn condition_code(self) -> Option<u8> {
let cc = match self {
Op::JO | Op::SETO | Op::CMOVO => 0,
Op::JNO | Op::SETNO | Op::CMOVNO => 1,
Op::JB | Op::SETB | Op::CMOVB => 2,
Op::JNB | Op::SETNB | Op::CMOVNB => 3,
Op::JZ | Op::SETZ | Op::CMOVZ => 4,
Op::JNZ | Op::SETNZ | Op::CMOVNZ => 5,
Op::JBE | Op::SETBE | Op::CMOVBE => 6,
Op::JA | Op::SETA | Op::CMOVA => 7,
Op::JS | Op::SETS | Op::CMOVS => 8,
Op::JNS | Op::SETNS | Op::CMOVNS => 9,
Op::JP | Op::SETP | Op::CMOVP => 10,
Op::JNP | Op::SETNP | Op::CMOVNP => 11,
Op::JL | Op::SETL | Op::CMOVL => 12,
Op::JGE | Op::SETGE | Op::CMOVGE => 13,
Op::JLE | Op::SETLE | Op::CMOVLE => 14,
Op::JG | Op::SETG | Op::CMOVG => 15,
_ => return None,
};
Some(cc)
}
#[must_use]
pub const fn is_cmov(self) -> bool {
matches!(
self,
Op::CMOVO
| Op::CMOVNO
| Op::CMOVB
| Op::CMOVNB
| Op::CMOVZ
| Op::CMOVNZ
| Op::CMOVBE
| Op::CMOVA
| Op::CMOVS
| Op::CMOVNS
| Op::CMOVP
| Op::CMOVNP
| Op::CMOVL
| Op::CMOVGE
| Op::CMOVLE
| Op::CMOVG
)
}
#[must_use]
pub const fn default_64(self) -> bool {
matches!(
self,
Op::PUSH
| Op::POP
| Op::PUSHF
| Op::POPF
| Op::CALL
| Op::RET
| Op::JMP
| Op::ENTER
| Op::LEAVE
| Op::LOOP
| Op::LOOPE
| Op::LOOPNE
| Op::JCXZ
) || self.is_conditional_jump()
}
#[must_use]
pub const fn is_conditional_jump(self) -> bool {
matches!(
self,
Op::JO
| Op::JNO
| Op::JB
| Op::JNB
| Op::JZ
| Op::JNZ
| Op::JBE
| Op::JA
| Op::JS
| Op::JNS
| Op::JP
| Op::JNP
| Op::JL
| Op::JGE
| Op::JLE
| Op::JG
)
}
#[must_use]
pub const fn is_setcc(self) -> bool {
self.condition_code().is_some() && !self.is_conditional_jump() && !self.is_cmov()
}
#[must_use]
pub const fn is_string(self) -> bool {
matches!(
self,
Op::MOVSB
| Op::MOVSW
| Op::CMPSB
| Op::CMPSW
| Op::STOSB
| Op::STOSW
| Op::LODSB
| Op::LODSW
| Op::SCASB
| Op::SCASW
| Op::INSB
| Op::INSW
| Op::OUTSB
| Op::OUTSW
)
}
#[must_use]
pub const fn repeat_tests_zf(self) -> bool {
matches!(self, Op::CMPSB | Op::CMPSW | Op::SCASB | Op::SCASW)
}
#[must_use]
pub const fn is_x87(self) -> bool {
matches!(
self,
Op::FABS
| Op::FADD
| Op::FADDP
| Op::FCHS
| Op::FCMOVB
| Op::FCMOVBE
| Op::FCMOVE
| Op::FCMOVNB
| Op::FCMOVNBE
| Op::FCMOVNE
| Op::FCMOVNU
| Op::FCMOVU
| Op::FCOM
| Op::FCOMI
| Op::FCOMIP
| Op::FCOMP
| Op::FCOMPP
| Op::FDECSTP
| Op::FDIV
| Op::FDIVP
| Op::FDIVR
| Op::FDIVRP
| Op::FFREE
| Op::FIADD
| Op::FICOM
| Op::FICOMP
| Op::FIDIV
| Op::FIDIVR
| Op::FILD
| Op::FIMUL
| Op::FINCSTP
| Op::FIST
| Op::FISTP
| Op::FISUB
| Op::FISUBR
| Op::FLD
| Op::FLD1
| Op::FLDCW
| Op::FLDENV
| Op::FLDL2E
| Op::FLDL2T
| Op::FLDLG2
| Op::FLDLN2
| Op::FLDPI
| Op::FLDZ
| Op::FMUL
| Op::FMULP
| Op::FNCLEX
| Op::FNINIT
| Op::FNOP
| Op::FNSAVE
| Op::FNSTCW
| Op::FNSTENV
| Op::FNSTSW
| Op::FPREM
| Op::FPREM1
| Op::FRNDINT
| Op::FRSTOR
| Op::FSCALE
| Op::FSQRT
| Op::FST
| Op::FSTP
| Op::FSUB
| Op::FSUBP
| Op::FSUBR
| Op::FSUBRP
| Op::FTST
| Op::FUCOM
| Op::FUCOMI
| Op::FUCOMIP
| Op::FUCOMP
| Op::FUCOMPP
| Op::FXAM
| Op::FXCH
| Op::FXTRACT
)
}
#[must_use]
pub const fn x87_no_wait(self) -> bool {
matches!(
self,
Op::FNCLEX | Op::FNINIT | Op::FNSTCW | Op::FNSTSW | Op::FNSTENV | Op::FNSAVE
)
}
#[must_use]
pub const fn x87_freezes_pointers(self) -> bool {
matches!(
self,
Op::FNINIT
| Op::FNCLEX
| Op::FLDCW
| Op::FNSTCW
| Op::FNSTSW
| Op::FLDENV
| Op::FNSTENV
| Op::FRSTOR
| Op::FNSAVE
)
}
#[must_use]
pub const fn is_sse(self) -> bool {
matches!(
self,
Op::ADDPD
| Op::ADDPS
| Op::ADDSD
| Op::ADDSS
| Op::ANDNPD
| Op::ANDNPS
| Op::ANDPD
| Op::ANDPS
| Op::CMPPD
| Op::CMPPS
| Op::CMPSD
| Op::CMPSS
| Op::COMISD
| Op::COMISS
| Op::CVTPD2PS
| Op::CVTPS2PD
| Op::CVTSD2SI
| Op::CVTSD2SS
| Op::CVTSI2SD
| Op::CVTSI2SS
| Op::CVTSS2SD
| Op::CVTSS2SI
| Op::CVTTSD2SI
| Op::CVTTSS2SI
| Op::DIVPD
| Op::DIVPS
| Op::DIVSD
| Op::DIVSS
| Op::LDMXCSR
| Op::MAXPD
| Op::MAXPS
| Op::MAXSD
| Op::MAXSS
| Op::MINPD
| Op::MINPS
| Op::MINSD
| Op::MINSS
| Op::MOVAPD
| Op::MOVAPS
| Op::MOVD
| Op::MOVDQA
| Op::MOVDQU
| Op::MOVHLPS
| Op::MOVHPD
| Op::MOVHPS
| Op::MOVLHPS
| Op::MOVLPD
| Op::MOVLPS
| Op::MOVMSKPD
| Op::MOVMSKPS
| Op::MOVQ
| Op::MOVSD
| Op::MOVSS
| Op::MOVUPD
| Op::MOVUPS
| Op::MULPD
| Op::MULPS
| Op::MULSD
| Op::MULSS
| Op::ORPD
| Op::ORPS
| Op::PAND
| Op::PANDN
| Op::POR
| Op::PXOR
| Op::SHUFPD
| Op::SHUFPS
| Op::SQRTPD
| Op::SQRTPS
| Op::SQRTSD
| Op::SQRTSS
| Op::STMXCSR
| Op::SUBPD
| Op::SUBPS
| Op::SUBSD
| Op::SUBSS
| Op::UCOMISD
| Op::UCOMISS
| Op::UNPCKHPD
| Op::UNPCKHPS
| Op::UNPCKLPD
| Op::UNPCKLPS
| Op::XORPD
| Op::XORPS
)
}
#[must_use]
pub const fn is_simd(self) -> bool {
self.is_sse()
|| matches!(
self,
Op::FXSAVE | Op::FXRSTOR | Op::LFENCE | Op::MFENCE | Op::SFENCE
)
}
#[must_use]
pub const fn clocks(self) -> u32 {
match self {
Op::MUL => 70,
Op::IMUL => 80,
Op::DIV => 80,
Op::IDIV => 101,
Op::AAM => 83,
Op::AAD => 60,
Op::CALL => 11,
Op::CALLF => 20,
Op::RET => 8,
Op::RETF => 10,
Op::INT | Op::INT3 => 35,
Op::INTO => 37,
Op::IRET => 12,
Op::JMP | Op::JMPF => 7,
Op::LOOP | Op::LOOPE | Op::LOOPNE => 9,
Op::JCXZ => 6,
Op::MOVSB | Op::MOVSW => 9,
Op::CMPSB | Op::CMPSW => 14,
Op::SCASB | Op::SCASW => 11,
Op::LODSB | Op::LODSW => 8,
Op::STOSB | Op::STOSW => 7,
Op::XLAT => 7,
Op::AAA | Op::AAS | Op::DAA | Op::DAS => 4,
Op::PUSH | Op::PUSHF => 7,
Op::POP | Op::POPF => 4,
Op::LES | Op::LDS => 8,
Op::LEA => 2,
Op::IN | Op::OUT => 6,
Op::ESC => 2,
Op::HLT => 2,
Op::PUSHA | Op::POPA => 18,
Op::ENTER => 10,
Op::LEAVE => 4,
Op::BOUND => 10,
Op::ARPL => 20,
Op::LAR | Op::LSL => 15,
Op::VERR | Op::VERW => 10,
Op::LGDT | Op::LIDT | Op::LLDT | Op::LTR => 11,
Op::SGDT | Op::SIDT | Op::SLDT | Op::STR | Op::SMSW => 2,
Op::LMSW => 10,
Op::CLTS => 5,
Op::BSF | Op::BSR => 10,
Op::BT => 3,
Op::BTS | Op::BTR | Op::BTC => 6,
Op::SHLD | Op::SHRD => 3,
Op::MOVZX | Op::MOVSX => 3,
Op::CPUID => 14,
Op::INVD | Op::WBINVD => 4,
Op::INVLPG => 12,
Op::CMPXCHG | Op::XADD => 6,
Op::BSWAP => 1,
Op::INSB | Op::INSW | Op::OUTSB | Op::OUTSW => 9,
Op::FADD | Op::FADDP | Op::FSUB | Op::FSUBP | Op::FSUBR | Op::FSUBRP => 8,
Op::FIADD | Op::FISUB | Op::FISUBR => 20,
Op::FMUL | Op::FMULP => 16,
Op::FIMUL => 23,
Op::FDIV | Op::FDIVP | Op::FDIVR | Op::FDIVRP => 73,
Op::FIDIV | Op::FIDIVR => 85,
Op::FSQRT => 83,
Op::FPREM => 70,
Op::FPREM1 => 72,
Op::FRNDINT => 21,
Op::FSCALE => 30,
Op::FXTRACT => 16,
Op::FLD | Op::FST | Op::FSTP | Op::FXCH => 4,
Op::FILD => 13,
Op::FIST | Op::FISTP => 29,
Op::FCOM | Op::FCOMP | Op::FCOMPP | Op::FUCOM | Op::FUCOMP | Op::FUCOMPP => 4,
Op::FICOM | Op::FICOMP => 16,
Op::FCOMI | Op::FCOMIP | Op::FUCOMI | Op::FUCOMIP => 4,
Op::FLDCW => 4,
Op::FLDENV => 34,
Op::FNSTENV => 67,
Op::FNSAVE => 143,
Op::FRSTOR => 131,
Op::FNINIT => 17,
Op::DIVSS | Op::DIVPS => 18,
Op::DIVSD | Op::DIVPD => 32,
Op::SQRTSS | Op::SQRTPS => 28,
Op::SQRTSD | Op::SQRTPD => 57,
Op::FXSAVE | Op::FXRSTOR => 100,
Op::UD => 0,
_ => 3,
}
}
}
impl fmt::Display for Op {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.mnemonic())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Insn {
pub op: Op,
pub dst: Arg,
pub src: Arg,
pub aux: Arg,
pub group: Grp,
pub class: Class,
pub long: L64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum L64 {
Same,
Gone,
Alt(Op, Arg, Arg),
}
impl Insn {
const fn new(op: Op, dst: Arg, src: Arg, group: Grp, class: Class) -> Insn {
Insn {
op,
dst,
src,
aux: Arg::None,
group,
class,
long: L64::Same,
}
}
const fn with_aux(mut self, aux: Arg) -> Insn {
self.aux = aux;
self
}
const fn with_long(mut self, long: L64) -> Insn {
self.long = long;
self
}
#[must_use]
pub const fn in_long(self) -> Insn {
match self.long {
L64::Same => self,
L64::Gone => UNASSIGNED,
L64::Alt(op, dst, src) => Insn {
op,
dst,
src,
aux: Arg::None,
group: self.group,
class: match op {
Op::REX => Class::Prefix,
_ => self.class,
},
long: L64::Same,
},
}
}
#[must_use]
pub const fn needs_modrm(self) -> bool {
!matches!(self.group, Grp::None)
|| self.dst.needs_modrm()
|| self.src.needs_modrm()
|| self.aux.needs_modrm()
}
#[must_use]
pub const fn width_bytes(self, osz: u8) -> Option<u8> {
match self.dst.width_bytes(osz) {
Some(w) => Some(w),
None => self.src.width_bytes(osz),
}
}
#[must_use]
pub const fn is_prefix(self) -> bool {
matches!(self.class, Class::Prefix)
}
}
const UNASSIGNED: Insn = Insn::new(Op::UD, Arg::None, Arg::None, Grp::None, Class::Undefined);
macro_rules! opmap {
(
base $base:expr;
$($opcode:literal $op:ident $dst:ident $src:ident $(+ $aux:ident)? $group:ident $class:ident
$(=> ($($long:tt)+))? ;)*
) => {{
let mut t: [Insn; 256] = $base;
let mut listed = [false; 256];
$(
t[$opcode as usize] =
Insn::new(Op::$op, Arg::$dst, Arg::$src, Grp::$group, Class::$class)
$(.with_aux(Arg::$aux))?
$(.with_long(long_spec!($($long)+)))?;
listed[$opcode as usize] = true;
)*
(t, listed)
}};
}
macro_rules! long_spec {
(UD) => {
L64::Gone
};
($op:ident $dst:ident $src:ident) => {
L64::Alt(Op::$op, Arg::$dst, Arg::$src)
};
}
const PRIMARY_8086: ([Insn; 256], [bool; 256]) = opmap! {
base [UNASSIGNED; 256];
0x00 ADD Eb Gb None Documented;
0x01 ADD Ev Gv None Documented;
0x02 ADD Gb Eb None Documented;
0x03 ADD Gv Ev None Documented;
0x04 ADD Al Ib None Documented;
0x05 ADD Ax Iz None Documented;
0x06 PUSH Sr None None Documented => (UD);
0x07 POP Sr None None Documented => (UD);
0x08 OR Eb Gb None Documented;
0x09 OR Ev Gv None Documented;
0x0a OR Gb Eb None Documented;
0x0b OR Gv Ev None Documented;
0x0c OR Al Ib None Documented;
0x0d OR Ax Iz None Documented;
0x0e PUSH Sr None None Documented => (UD);
0x0f POP Sr None None Undocumented;
0x10 ADC Eb Gb None Documented;
0x11 ADC Ev Gv None Documented;
0x12 ADC Gb Eb None Documented;
0x13 ADC Gv Ev None Documented;
0x14 ADC Al Ib None Documented;
0x15 ADC Ax Iz None Documented;
0x16 PUSH Sr None None Documented => (UD);
0x17 POP Sr None None Documented => (UD);
0x18 SBB Eb Gb None Documented;
0x19 SBB Ev Gv None Documented;
0x1a SBB Gb Eb None Documented;
0x1b SBB Gv Ev None Documented;
0x1c SBB Al Ib None Documented;
0x1d SBB Ax Iz None Documented;
0x1e PUSH Sr None None Documented => (UD);
0x1f POP Sr None None Documented => (UD);
0x20 AND Eb Gb None Documented;
0x21 AND Ev Gv None Documented;
0x22 AND Gb Eb None Documented;
0x23 AND Gv Ev None Documented;
0x24 AND Al Ib None Documented;
0x25 AND Ax Iz None Documented;
0x26 SEG Sr None None Prefix;
0x27 DAA None None None Documented => (UD);
0x28 SUB Eb Gb None Documented;
0x29 SUB Ev Gv None Documented;
0x2a SUB Gb Eb None Documented;
0x2b SUB Gv Ev None Documented;
0x2c SUB Al Ib None Documented;
0x2d SUB Ax Iz None Documented;
0x2e SEG Sr None None Prefix;
0x2f DAS None None None Documented => (UD);
0x30 XOR Eb Gb None Documented;
0x31 XOR Ev Gv None Documented;
0x32 XOR Gb Eb None Documented;
0x33 XOR Gv Ev None Documented;
0x34 XOR Al Ib None Documented;
0x35 XOR Ax Iz None Documented;
0x36 SEG Sr None None Prefix;
0x37 AAA None None None Documented => (UD);
0x38 CMP Eb Gb None Documented;
0x39 CMP Ev Gv None Documented;
0x3a CMP Gb Eb None Documented;
0x3b CMP Gv Ev None Documented;
0x3c CMP Al Ib None Documented;
0x3d CMP Ax Iz None Documented;
0x3e SEG Sr None None Prefix;
0x3f AAS None None None Documented => (UD);
0x40 INC Rv None None Documented => (REX None None);
0x41 INC Rv None None Documented => (REX None None);
0x42 INC Rv None None Documented => (REX None None);
0x43 INC Rv None None Documented => (REX None None);
0x44 INC Rv None None Documented => (REX None None);
0x45 INC Rv None None Documented => (REX None None);
0x46 INC Rv None None Documented => (REX None None);
0x47 INC Rv None None Documented => (REX None None);
0x48 DEC Rv None None Documented => (REX None None);
0x49 DEC Rv None None Documented => (REX None None);
0x4a DEC Rv None None Documented => (REX None None);
0x4b DEC Rv None None Documented => (REX None None);
0x4c DEC Rv None None Documented => (REX None None);
0x4d DEC Rv None None Documented => (REX None None);
0x4e DEC Rv None None Documented => (REX None None);
0x4f DEC Rv None None Documented => (REX None None);
0x50 PUSH Rv None None Documented;
0x51 PUSH Rv None None Documented;
0x52 PUSH Rv None None Documented;
0x53 PUSH Rv None None Documented;
0x54 PUSH Rv None None Documented;
0x55 PUSH Rv None None Documented;
0x56 PUSH Rv None None Documented;
0x57 PUSH Rv None None Documented;
0x58 POP Rv None None Documented;
0x59 POP Rv None None Documented;
0x5a POP Rv None None Documented;
0x5b POP Rv None None Documented;
0x5c POP Rv None None Documented;
0x5d POP Rv None None Documented;
0x5e POP Rv None None Documented;
0x5f POP Rv None None Documented;
0x60 JO Jb None None Alias;
0x61 JNO Jb None None Alias;
0x62 JB Jb None None Alias;
0x63 JNB Jb None None Alias;
0x64 JZ Jb None None Alias;
0x65 JNZ Jb None None Alias;
0x66 JBE Jb None None Alias;
0x67 JA Jb None None Alias;
0x68 JS Jb None None Alias;
0x69 JNS Jb None None Alias;
0x6a JP Jb None None Alias;
0x6b JNP Jb None None Alias;
0x6c JL Jb None None Alias;
0x6d JGE Jb None None Alias;
0x6e JLE Jb None None Alias;
0x6f JG Jb None None Alias;
0x70 JO Jb None None Documented;
0x71 JNO Jb None None Documented;
0x72 JB Jb None None Documented;
0x73 JNB Jb None None Documented;
0x74 JZ Jb None None Documented;
0x75 JNZ Jb None None Documented;
0x76 JBE Jb None None Documented;
0x77 JA Jb None None Documented;
0x78 JS Jb None None Documented;
0x79 JNS Jb None None Documented;
0x7a JP Jb None None Documented;
0x7b JNP Jb None None Documented;
0x7c JL Jb None None Documented;
0x7d JGE Jb None None Documented;
0x7e JLE Jb None None Documented;
0x7f JG Jb None None Documented;
0x80 ADD Eb Ib Alu Documented;
0x81 ADD Ev Iz Alu Documented;
0x82 ADD Eb Ib Alu Alias => (UD);
0x83 ADD Ev Ibs Alu Documented;
0x84 TEST Eb Gb None Documented;
0x85 TEST Ev Gv None Documented;
0x86 XCHG Eb Gb None Documented;
0x87 XCHG Ev Gv None Documented;
0x88 MOV Eb Gb None Documented;
0x89 MOV Ev Gv None Documented;
0x8a MOV Gb Eb None Documented;
0x8b MOV Gv Ev None Documented;
0x8c MOV Ev Sw None Documented;
0x8d LEA Gv M None Documented;
0x8e MOV Sw Ev None Documented;
0x8f POP Ev None Pop Documented;
0x90 NOP None None None Documented;
0x91 XCHG Ax Rv None Documented;
0x92 XCHG Ax Rv None Documented;
0x93 XCHG Ax Rv None Documented;
0x94 XCHG Ax Rv None Documented;
0x95 XCHG Ax Rv None Documented;
0x96 XCHG Ax Rv None Documented;
0x97 XCHG Ax Rv None Documented;
0x98 CBW None None None Documented;
0x99 CWD None None None Documented;
0x9a CALLF Ap None None Documented => (UD);
0x9b WAIT None None None Documented;
0x9c PUSHF None None None Documented;
0x9d POPF None None None Documented;
0x9e SAHF None None None Documented;
0x9f LAHF None None None Documented;
0xa0 MOV Al Ob None Documented;
0xa1 MOV Ax Ov None Documented;
0xa2 MOV Ob Al None Documented;
0xa3 MOV Ov Ax None Documented;
0xa4 MOVSB Yb Xb None Documented;
0xa5 MOVSW Yv Xv None Documented;
0xa6 CMPSB Xb Yb None Documented;
0xa7 CMPSW Xv Yv None Documented;
0xa8 TEST Al Ib None Documented;
0xa9 TEST Ax Iz None Documented;
0xaa STOSB Yb Al None Documented;
0xab STOSW Yv Ax None Documented;
0xac LODSB Al Xb None Documented;
0xad LODSW Ax Xv None Documented;
0xae SCASB Al Yb None Documented;
0xaf SCASW Ax Yv None Documented;
0xb0 MOV Rb Ib None Documented;
0xb1 MOV Rb Ib None Documented;
0xb2 MOV Rb Ib None Documented;
0xb3 MOV Rb Ib None Documented;
0xb4 MOV Rb Ib None Documented;
0xb5 MOV Rb Ib None Documented;
0xb6 MOV Rb Ib None Documented;
0xb7 MOV Rb Ib None Documented;
0xb8 MOV Rv Iv None Documented;
0xb9 MOV Rv Iv None Documented;
0xba MOV Rv Iv None Documented;
0xbb MOV Rv Iv None Documented;
0xbc MOV Rv Iv None Documented;
0xbd MOV Rv Iv None Documented;
0xbe MOV Rv Iv None Documented;
0xbf MOV Rv Iv None Documented;
0xc0 RET Iw None None Alias;
0xc1 RET None None None Alias;
0xc2 RET Iw None None Documented;
0xc3 RET None None None Documented;
0xc4 LES Gv Mp None Documented => (UD);
0xc5 LDS Gv Mp None Documented => (UD);
0xc6 MOV Eb Ib MovImm Documented;
0xc7 MOV Ev Iz MovImm Documented;
0xc8 RETF Iw None None Alias;
0xc9 RETF None None None Alias;
0xca RETF Iw None None Documented;
0xcb RETF None None None Documented;
0xcc INT3 None None None Documented;
0xcd INT Ib None None Documented;
0xce INTO None None None Documented => (UD);
0xcf IRET None None None Documented;
0xd0 ROL Eb One Shift Documented;
0xd1 ROL Ev One Shift Documented;
0xd2 ROL Eb Cl Shift Documented;
0xd3 ROL Ev Cl Shift Documented;
0xd4 AAM Ib None None Documented => (UD);
0xd5 AAD Ib None None Documented => (UD);
0xd6 SALC None None None Undocumented => (UD);
0xd7 XLAT None None None Documented;
0xd8 ESC Ev None None Escape;
0xd9 ESC Ev None None Escape;
0xda ESC Ev None None Escape;
0xdb ESC Ev None None Escape;
0xdc ESC Ev None None Escape;
0xdd ESC Ev None None Escape;
0xde ESC Ev None None Escape;
0xdf ESC Ev None None Escape;
0xe0 LOOPNE Jb None None Documented;
0xe1 LOOPE Jb None None Documented;
0xe2 LOOP Jb None None Documented;
0xe3 JCXZ Jb None None Documented;
0xe4 IN Al Ib None Documented;
0xe5 IN Ax Ib None Documented;
0xe6 OUT Ib Al None Documented;
0xe7 OUT Ib Ax None Documented;
0xe8 CALL Jv None None Documented;
0xe9 JMP Jv None None Documented;
0xea JMPF Ap None None Documented => (UD);
0xeb JMP Jb None None Documented;
0xec IN Al Dx None Documented;
0xed IN Ax Dx None Documented;
0xee OUT Dx Al None Documented;
0xef OUT Dx Ax None Documented;
0xf0 LOCK None None None Prefix;
0xf1 LOCK None None None Prefix;
0xf2 REPNE None None None Prefix;
0xf3 REP None None None Prefix;
0xf4 HLT None None None Documented;
0xf5 CMC None None None Documented;
0xf6 TEST Eb Ib Unary Documented;
0xf7 TEST Ev Iz Unary Documented;
0xf8 CLC None None None Documented;
0xf9 STC None None None Documented;
0xfa CLI None None None Documented;
0xfb STI None None None Documented;
0xfc CLD None None None Documented;
0xfd STD None None None Documented;
0xfe INC Eb None IncDec Documented;
0xff INC Ev None Misc Documented;
};
pub static TABLE: [Insn; 256] = PRIMARY_8086.0;
pub static LISTED: [bool; 256] = PRIMARY_8086.1;
const PRIMARY_386: ([Insn; 256], [bool; 256]) = opmap! {
base PRIMARY_8086.0;
0x0f ESC None None None Escape;
0x60 PUSHA None None None Documented => (UD);
0x61 POPA None None None Documented => (UD);
0x62 BOUND Gv M None Documented => (UD);
0x63 ARPL Ew Gw None Documented => (MOVSXD Gv Ed);
0x64 SEG None None None Prefix;
0x65 SEG None None None Prefix;
0x66 SEG None None None Prefix;
0x67 SEG None None None Prefix;
0x68 PUSH Iz None None Documented;
0x69 IMUL Gv Ev +Iz None Documented;
0x6a PUSH Ibs None None Documented;
0x6b IMUL Gv Ev +Ibs None Documented;
0x6c INSB Yb Dx None Documented;
0x6d INSW Yv Dx None Documented;
0x6e OUTSB Dx Xb None Documented;
0x6f OUTSW Dx Xv None Documented;
0x8e MOV Sw Ew None Documented;
0xc0 ROL Eb Ib Shift Documented;
0xc1 ROL Ev Ib Shift Documented;
0xc8 ENTER Iw Ib None Documented;
0xc9 LEAVE None None None Documented;
0xd8 ESC Ev None X87 Escape;
0xd9 ESC Ev None X87 Escape;
0xda ESC Ev None X87 Escape;
0xdb ESC Ev None X87 Escape;
0xdc ESC Ev None X87 Escape;
0xdd ESC Ev None X87 Escape;
0xde ESC Ev None X87 Escape;
0xdf ESC Ev None X87 Escape;
0xf1 ICEBP None None None Undocumented;
};
pub static TABLE_386: [Insn; 256] = PRIMARY_386.0;
pub static CHANGED_386: [bool; 256] = PRIMARY_386.1;
const SECONDARY: ([Insn; 256], [bool; 256]) = opmap! {
base [UNASSIGNED; 256];
0x00 SLDT Ew None Grp6 Documented;
0x01 SGDT Ms None Grp7 Documented;
0x02 LAR Gv Ev None Documented;
0x03 LSL Gv Ev None Documented;
0x06 CLTS None None None Documented;
0x08 INVD None None None Documented;
0x09 WBINVD None None None Documented;
0x05 UD None None None Undefined => (SYSCALL None None);
0x07 UD None None None Undefined => (SYSRET None None);
0x10 MOVUPS Vx Wx None Documented;
0x11 MOVUPS Wx Vx None Documented;
0x12 MOVLPS Vx Mq ModAlt Documented;
0x13 MOVLPS Mq Vx ModAlt Documented;
0x14 UNPCKLPS Vx Wx None Documented;
0x15 UNPCKHPS Vx Wx None Documented;
0x16 MOVHPS Vx Mq ModAlt Documented;
0x17 MOVHPS Mq Vx ModAlt Documented;
0x28 MOVAPS Vx Wx None Documented;
0x29 MOVAPS Wx Vx None Documented;
0x2e UCOMISS Vx Wd None Documented;
0x2f COMISS Vx Wd None Documented;
0x50 MOVMSKPS Gy Ux None Documented;
0x51 SQRTPS Vx Wx None Documented;
0x54 ANDPS Vx Wx None Documented;
0x55 ANDNPS Vx Wx None Documented;
0x56 ORPS Vx Wx None Documented;
0x57 XORPS Vx Wx None Documented;
0x58 ADDPS Vx Wx None Documented;
0x59 MULPS Vx Wx None Documented;
0x5a CVTPS2PD Vx Wq None Documented;
0x5c SUBPS Vx Wx None Documented;
0x5d MINPS Vx Wx None Documented;
0x5e DIVPS Vx Wx None Documented;
0x5f MAXPS Vx Wx None Documented;
0x20 MOV Rd Cd None Documented;
0x21 MOV Rd Dd None Documented;
0x22 MOV Cd Rd None Documented;
0x23 MOV Dd Rd None Documented;
0x24 MOV Rd Td None Documented;
0x26 MOV Td Rd None Documented;
0x30 WRMSR None None None Documented;
0x31 RDTSC None None None Documented;
0x32 RDMSR None None None Documented;
0x40 CMOVO Gv Ev None Documented;
0x41 CMOVNO Gv Ev None Documented;
0x42 CMOVB Gv Ev None Documented;
0x43 CMOVNB Gv Ev None Documented;
0x44 CMOVZ Gv Ev None Documented;
0x45 CMOVNZ Gv Ev None Documented;
0x46 CMOVBE Gv Ev None Documented;
0x47 CMOVA Gv Ev None Documented;
0x48 CMOVS Gv Ev None Documented;
0x49 CMOVNS Gv Ev None Documented;
0x4a CMOVP Gv Ev None Documented;
0x4b CMOVNP Gv Ev None Documented;
0x4c CMOVL Gv Ev None Documented;
0x4d CMOVGE Gv Ev None Documented;
0x4e CMOVLE Gv Ev None Documented;
0x4f CMOVG Gv Ev None Documented;
0x80 JO Jv None None Documented;
0x81 JNO Jv None None Documented;
0x82 JB Jv None None Documented;
0x83 JNB Jv None None Documented;
0x84 JZ Jv None None Documented;
0x85 JNZ Jv None None Documented;
0x86 JBE Jv None None Documented;
0x87 JA Jv None None Documented;
0x88 JS Jv None None Documented;
0x89 JNS Jv None None Documented;
0x8a JP Jv None None Documented;
0x8b JNP Jv None None Documented;
0x8c JL Jv None None Documented;
0x8d JGE Jv None None Documented;
0x8e JLE Jv None None Documented;
0x8f JG Jv None None Documented;
0x90 SETO Eb None None Documented;
0x91 SETNO Eb None None Documented;
0x92 SETB Eb None None Documented;
0x93 SETNB Eb None None Documented;
0x94 SETZ Eb None None Documented;
0x95 SETNZ Eb None None Documented;
0x96 SETBE Eb None None Documented;
0x97 SETA Eb None None Documented;
0x98 SETS Eb None None Documented;
0x99 SETNS Eb None None Documented;
0x9a SETP Eb None None Documented;
0x9b SETNP Eb None None Documented;
0x9c SETL Eb None None Documented;
0x9d SETGE Eb None None Documented;
0x9e SETLE Eb None None Documented;
0x9f SETG Eb None None Documented;
0xa0 PUSH Sr None None Documented;
0xa1 POP Sr None None Documented;
0xa2 CPUID None None None Documented;
0xa3 BT Ev Gv None Documented;
0xa4 SHLD Ev Gv +Ib None Documented;
0xa5 SHLD Ev Gv +Cl None Documented;
0xa8 PUSH Sr None None Documented;
0xa9 POP Sr None None Documented;
0xab BTS Ev Gv None Documented;
0xac SHRD Ev Gv +Ib None Documented;
0xad SHRD Ev Gv +Cl None Documented;
0xaf IMUL Gv Ev None Documented;
0xb0 CMPXCHG Eb Gb None Documented;
0xb1 CMPXCHG Ev Gv None Documented;
0xb2 LSS Gv Mp None Documented;
0xb3 BTR Ev Gv None Documented;
0xb4 LFS Gv Mp None Documented;
0xb5 LGS Gv Mp None Documented;
0xb6 MOVZX Gv Eb None Documented;
0xb7 MOVZX Gv Ew None Documented;
0xba BT Ev Ib Grp8 Documented;
0xbb BTC Ev Gv None Documented;
0xbc BSF Gv Ev None Documented;
0xbd BSR Gv Ev None Documented;
0xbe MOVSX Gv Eb None Documented;
0xbf MOVSX Gv Ew None Documented;
0xae FXSAVE Mfx None Grp15 Documented;
0xc0 XADD Eb Gb None Documented;
0xc1 XADD Ev Gv None Documented;
0xc2 CMPPS Vx Wx +Ib None Documented;
0xc6 SHUFPS Vx Wx +Ib None Documented;
0xc7 CMPXCHG8B M None Grp9 Documented;
0xc8 BSWAP Rv None None Documented;
0xc9 BSWAP Rv None None Documented;
0xca BSWAP Rv None None Documented;
0xcb BSWAP Rv None None Documented;
0xcc BSWAP Rv None None Documented;
0xcd BSWAP Rv None None Documented;
0xce BSWAP Rv None None Documented;
0xcf BSWAP Rv None None Documented;
};
pub static TABLE_0F: [Insn; 256] = SECONDARY.0;
pub static LISTED_0F: [bool; 256] = SECONDARY.1;
const SECONDARY_66: ([Insn; 256], [bool; 256]) = opmap! {
base SECONDARY.0;
0x10 MOVUPD Vx Wx None Documented;
0x11 MOVUPD Wx Vx None Documented;
0x12 MOVLPD Vx Mq ModAlt Documented;
0x13 MOVLPD Mq Vx ModAlt Documented;
0x14 UNPCKLPD Vx Wx None Documented;
0x15 UNPCKHPD Vx Wx None Documented;
0x16 MOVHPD Vx Mq ModAlt Documented;
0x17 MOVHPD Mq Vx ModAlt Documented;
0x28 MOVAPD Vx Wx None Documented;
0x29 MOVAPD Wx Vx None Documented;
0x2e UCOMISD Vx Wq None Documented;
0x2f COMISD Vx Wq None Documented;
0x50 MOVMSKPD Gy Ux None Documented;
0x51 SQRTPD Vx Wx None Documented;
0x54 ANDPD Vx Wx None Documented;
0x55 ANDNPD Vx Wx None Documented;
0x56 ORPD Vx Wx None Documented;
0x57 XORPD Vx Wx None Documented;
0x58 ADDPD Vx Wx None Documented;
0x59 MULPD Vx Wx None Documented;
0x5a CVTPD2PS Vx Wx None Documented;
0x5c SUBPD Vx Wx None Documented;
0x5d MINPD Vx Wx None Documented;
0x5e DIVPD Vx Wx None Documented;
0x5f MAXPD Vx Wx None Documented;
0x6e MOVD Vx Ey None Documented;
0x6f MOVDQA Vx Wx None Documented;
0x7e MOVD Ey Vx None Documented;
0x7f MOVDQA Wx Vx None Documented;
0xc2 CMPPD Vx Wx +Ib None Documented;
0xc6 SHUFPD Vx Wx +Ib None Documented;
0xd6 MOVQ Wq Vx None Documented;
0xdb PAND Vx Wx None Documented;
0xdf PANDN Vx Wx None Documented;
0xeb POR Vx Wx None Documented;
0xef PXOR Vx Wx None Documented;
};
pub static TABLE_0F_66: [Insn; 256] = SECONDARY_66.0;
pub static CHANGED_0F_66: [bool; 256] = SECONDARY_66.1;
const SECONDARY_F3: ([Insn; 256], [bool; 256]) = opmap! {
base SECONDARY.0;
0x10 MOVSS Vx Wd None Documented;
0x11 MOVSS Wd Vx None Documented;
0x2a CVTSI2SS Vx Ey None Documented;
0x2c CVTTSS2SI Gy Wd None Documented;
0x2d CVTSS2SI Gy Wd None Documented;
0x51 SQRTSS Vx Wd None Documented;
0x58 ADDSS Vx Wd None Documented;
0x59 MULSS Vx Wd None Documented;
0x5a CVTSS2SD Vx Wd None Documented;
0x5c SUBSS Vx Wd None Documented;
0x5d MINSS Vx Wd None Documented;
0x5e DIVSS Vx Wd None Documented;
0x5f MAXSS Vx Wd None Documented;
0x6f MOVDQU Vx Wx None Documented;
0x7e MOVQ Vx Wq None Documented;
0x7f MOVDQU Wx Vx None Documented;
0xc2 CMPSS Vx Wd +Ib None Documented;
};
pub static TABLE_0F_F3: [Insn; 256] = SECONDARY_F3.0;
pub static CHANGED_0F_F3: [bool; 256] = SECONDARY_F3.1;
const SECONDARY_F2: ([Insn; 256], [bool; 256]) = opmap! {
base SECONDARY.0;
0x10 MOVSD Vx Wq None Documented;
0x11 MOVSD Wq Vx None Documented;
0x2a CVTSI2SD Vx Ey None Documented;
0x2c CVTTSD2SI Gy Wq None Documented;
0x2d CVTSD2SI Gy Wq None Documented;
0x51 SQRTSD Vx Wq None Documented;
0x58 ADDSD Vx Wq None Documented;
0x59 MULSD Vx Wq None Documented;
0x5a CVTSD2SS Vx Wq None Documented;
0x5c SUBSD Vx Wq None Documented;
0x5d MINSD Vx Wq None Documented;
0x5e DIVSD Vx Wq None Documented;
0x5f MAXSD Vx Wq None Documented;
0xc2 CMPSD Vx Wq +Ib None Documented;
};
pub static TABLE_0F_F2: [Insn; 256] = SECONDARY_F2.0;
pub static CHANGED_0F_F2: [bool; 256] = SECONDARY_F2.1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Mandatory {
#[default]
None,
P66,
PF3,
PF2,
}
#[inline]
#[must_use]
pub const fn decode_0f_as(prefix: Mandatory, opcode: u8) -> Insn {
match prefix {
Mandatory::None => TABLE_0F[opcode as usize],
Mandatory::P66 => TABLE_0F_66[opcode as usize],
Mandatory::PF3 => TABLE_0F_F3[opcode as usize],
Mandatory::PF2 => TABLE_0F_F2[opcode as usize],
}
}
pub static GROUP_ALU: [Op; 8] = [
Op::ADD,
Op::OR,
Op::ADC,
Op::SBB,
Op::AND,
Op::SUB,
Op::XOR,
Op::CMP,
];
pub static GROUP_SHIFT: [Op; 8] = [
Op::ROL,
Op::ROR,
Op::RCL,
Op::RCR,
Op::SHL,
Op::SHR,
Op::SETMO,
Op::SAR,
];
const fn unary_group(byte: bool) -> [Insn; 8] {
let (e, i) = if byte {
(Arg::Eb, Arg::Ib)
} else {
(Arg::Ev, Arg::Iz)
};
[
Insn::new(Op::TEST, e, i, Grp::None, Class::Documented),
Insn::new(Op::TEST, e, i, Grp::None, Class::Alias),
Insn::new(Op::NOT, e, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::NEG, e, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::MUL, e, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::IMUL, e, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::DIV, e, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::IDIV, e, Arg::None, Grp::None, Class::Documented),
]
}
pub static GROUP_UNARY8: [Insn; 8] = unary_group(true);
pub static GROUP_UNARY16: [Insn; 8] = unary_group(false);
pub static GROUP_MISC: [Insn; 8] = [
Insn::new(Op::INC, Arg::Ev, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::DEC, Arg::Ev, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::CALL, Arg::Ev, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::CALLF, Arg::Mp, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::JMP, Arg::Ev, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::JMPF, Arg::Mp, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::PUSH, Arg::Ev, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::PUSH, Arg::Ev, Arg::None, Grp::None, Class::Undefined),
];
pub static GROUP_INCDEC: [Insn; 8] = [
Insn::new(Op::INC, Arg::Eb, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::DEC, Arg::Eb, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::CALL, Arg::Ev, Arg::None, Grp::None, Class::Undefined),
Insn::new(Op::CALLF, Arg::Mp, Arg::None, Grp::None, Class::Undefined),
Insn::new(Op::JMP, Arg::Ev, Arg::None, Grp::None, Class::Undefined),
Insn::new(Op::JMPF, Arg::Mp, Arg::None, Grp::None, Class::Undefined),
Insn::new(Op::PUSH, Arg::Ev, Arg::None, Grp::None, Class::Undefined),
Insn::new(Op::PUSH, Arg::Ev, Arg::None, Grp::None, Class::Undefined),
];
static GROUP_MISC_386: [Insn; 8] = {
let mut t = GROUP_MISC;
t[7] = UNASSIGNED;
t
};
static GROUP_INCDEC_386: [Insn; 8] = {
let mut t = [UNASSIGNED; 8];
t[0] = GROUP_INCDEC[0];
t[1] = GROUP_INCDEC[1];
t
};
pub static GROUP6: [Insn; 8] = [
Insn::new(Op::SLDT, Arg::Ew, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::STR, Arg::Ew, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::LLDT, Arg::Ew, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::LTR, Arg::Ew, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::VERR, Arg::Ew, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::VERW, Arg::Ew, Arg::None, Grp::None, Class::Documented),
UNASSIGNED,
UNASSIGNED,
];
pub static GROUP7: [Insn; 8] = [
Insn::new(Op::SGDT, Arg::Ms, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::SIDT, Arg::Ms, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::LGDT, Arg::Ms, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::LIDT, Arg::Ms, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::SMSW, Arg::Ew, Arg::None, Grp::None, Class::Documented),
UNASSIGNED,
Insn::new(Op::LMSW, Arg::Ew, Arg::None, Grp::None, Class::Documented),
Insn::new(Op::INVLPG, Arg::M, Arg::None, Grp::None, Class::Documented),
];
pub static GROUP8: [Insn; 8] = [
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
Insn::new(Op::BT, Arg::Ev, Arg::Ib, Grp::None, Class::Documented),
Insn::new(Op::BTS, Arg::Ev, Arg::Ib, Grp::None, Class::Documented),
Insn::new(Op::BTR, Arg::Ev, Arg::Ib, Grp::None, Class::Documented),
Insn::new(Op::BTC, Arg::Ev, Arg::Ib, Grp::None, Class::Documented),
];
pub static GROUP9: [Insn; 8] = {
let mut t = [UNASSIGNED; 8];
t[1] = Insn::new(
Op::CMPXCHG8B,
Arg::M,
Arg::None,
Grp::None,
Class::Documented,
);
t
};
pub static GROUP15: [Insn; 8] = [
Insn::new(
Op::FXSAVE,
Arg::Mfx,
Arg::None,
Grp::None,
Class::Documented,
),
Insn::new(
Op::FXRSTOR,
Arg::None,
Arg::Mfx,
Grp::None,
Class::Documented,
),
Insn::new(
Op::LDMXCSR,
Arg::None,
Arg::Ed,
Grp::None,
Class::Documented,
),
Insn::new(
Op::STMXCSR,
Arg::Ed,
Arg::None,
Grp::None,
Class::Documented,
),
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
];
pub static GROUP15_REG: [Insn; 8] = [
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
UNASSIGNED,
Insn::new(
Op::LFENCE,
Arg::None,
Arg::None,
Grp::None,
Class::Documented,
),
Insn::new(
Op::MFENCE,
Arg::None,
Arg::None,
Grp::None,
Class::Documented,
),
Insn::new(
Op::SFENCE,
Arg::None,
Arg::None,
Grp::None,
Class::Documented,
),
];
#[must_use]
pub const fn resolve_modrm(map: Gen, insn: Insn, opcode: u8, two_byte: bool, modrm: ModRm) -> Insn {
if matches!(insn.group, Grp::X87) && !two_byte {
return resolve_x87(opcode, modrm);
}
let idx = (modrm.reg & 7) as usize;
match insn.group {
Grp::Grp9 => {
if modrm.md == 3 {
UNASSIGNED
} else {
GROUP9[idx]
}
}
Grp::Grp15 => {
if modrm.md == 3 {
GROUP15_REG[idx]
} else {
GROUP15[idx]
}
}
Grp::ModAlt => {
if modrm.md != 3 {
return insn;
}
match (insn.op, insn.dst) {
(Op::MOVLPS, Arg::Vx) => {
Insn::new(Op::MOVHLPS, Arg::Vx, Arg::Ux, Grp::None, Class::Documented)
}
(Op::MOVHPS, Arg::Vx) => {
Insn::new(Op::MOVLHPS, Arg::Vx, Arg::Ux, Grp::None, Class::Documented)
}
_ => UNASSIGNED,
}
}
_ => resolve_as(map, insn, modrm.reg),
}
}
macro_rules! x87map {
($n:literal; $($start:literal x $count:literal $op:ident $dst:ident $src:ident $class:ident;)*) => {{
let mut t: [Insn; $n] = [UNASSIGNED; $n];
$({
let mut i = 0usize;
while i < $count {
t[$start + i] =
Insn::new(Op::$op, Arg::$dst, Arg::$src, Grp::None, Class::$class);
i += 1;
}
})*
t
}};
}
pub static X87_MEM: [[Insn; 8]; 8] = [
x87map! { 8;
0 x 1 FADD St0 Mf32 Documented;
1 x 1 FMUL St0 Mf32 Documented;
2 x 1 FCOM St0 Mf32 Documented;
3 x 1 FCOMP St0 Mf32 Documented;
4 x 1 FSUB St0 Mf32 Documented;
5 x 1 FSUBR St0 Mf32 Documented;
6 x 1 FDIV St0 Mf32 Documented;
7 x 1 FDIVR St0 Mf32 Documented;
},
x87map! { 8;
0 x 1 FLD None Mf32 Documented;
2 x 1 FST Mf32 None Documented;
3 x 1 FSTP Mf32 None Documented;
4 x 1 FLDENV None Mfenv Documented;
5 x 1 FLDCW None Ew Documented;
6 x 1 FNSTENV Mfenv None Documented;
7 x 1 FNSTCW Ew None Documented;
},
x87map! { 8;
0 x 1 FIADD St0 Mi32 Documented;
1 x 1 FIMUL St0 Mi32 Documented;
2 x 1 FICOM St0 Mi32 Documented;
3 x 1 FICOMP St0 Mi32 Documented;
4 x 1 FISUB St0 Mi32 Documented;
5 x 1 FISUBR St0 Mi32 Documented;
6 x 1 FIDIV St0 Mi32 Documented;
7 x 1 FIDIVR St0 Mi32 Documented;
},
x87map! { 8;
0 x 1 FILD None Mi32 Documented;
2 x 1 FIST Mi32 None Documented;
3 x 1 FISTP Mi32 None Documented;
5 x 1 FLD None Mf80 Documented;
7 x 1 FSTP Mf80 None Documented;
},
x87map! { 8;
0 x 1 FADD St0 Mf64 Documented;
1 x 1 FMUL St0 Mf64 Documented;
2 x 1 FCOM St0 Mf64 Documented;
3 x 1 FCOMP St0 Mf64 Documented;
4 x 1 FSUB St0 Mf64 Documented;
5 x 1 FSUBR St0 Mf64 Documented;
6 x 1 FDIV St0 Mf64 Documented;
7 x 1 FDIVR St0 Mf64 Documented;
},
x87map! { 8;
0 x 1 FLD None Mf64 Documented;
2 x 1 FST Mf64 None Documented;
3 x 1 FSTP Mf64 None Documented;
4 x 1 FRSTOR None Mfsave Documented;
6 x 1 FNSAVE Mfsave None Documented;
7 x 1 FNSTSW Ew None Documented;
},
x87map! { 8;
0 x 1 FIADD St0 Mi16 Documented;
1 x 1 FIMUL St0 Mi16 Documented;
2 x 1 FICOM St0 Mi16 Documented;
3 x 1 FICOMP St0 Mi16 Documented;
4 x 1 FISUB St0 Mi16 Documented;
5 x 1 FISUBR St0 Mi16 Documented;
6 x 1 FIDIV St0 Mi16 Documented;
7 x 1 FIDIVR St0 Mi16 Documented;
},
x87map! { 8;
0 x 1 FILD None Mi16 Documented;
2 x 1 FIST Mi16 None Documented;
3 x 1 FISTP Mi16 None Documented;
5 x 1 FILD None Mi64 Documented;
7 x 1 FISTP Mi64 None Documented;
},
];
pub static X87_REG: [[Insn; 64]; 8] = [
x87map! { 64;
0x00 x 8 FADD St0 Sti Documented;
0x08 x 8 FMUL St0 Sti Documented;
0x10 x 8 FCOM Sti None Documented;
0x18 x 8 FCOMP Sti None Documented;
0x20 x 8 FSUB St0 Sti Documented;
0x28 x 8 FSUBR St0 Sti Documented;
0x30 x 8 FDIV St0 Sti Documented;
0x38 x 8 FDIVR St0 Sti Documented;
},
x87map! { 64;
0x00 x 8 FLD None Sti Documented;
0x08 x 8 FXCH Sti None Documented;
0x10 x 1 FNOP None None Documented;
0x20 x 1 FCHS None None Documented;
0x21 x 1 FABS None None Documented;
0x24 x 1 FTST None None Documented;
0x25 x 1 FXAM None None Documented;
0x28 x 1 FLD1 None None Documented;
0x29 x 1 FLDL2T None None Documented;
0x2a x 1 FLDL2E None None Documented;
0x2b x 1 FLDPI None None Documented;
0x2c x 1 FLDLG2 None None Documented;
0x2d x 1 FLDLN2 None None Documented;
0x2e x 1 FLDZ None None Documented;
0x34 x 1 FXTRACT None None Documented;
0x35 x 1 FPREM1 None None Documented;
0x36 x 1 FDECSTP None None Documented;
0x37 x 1 FINCSTP None None Documented;
0x38 x 1 FPREM None None Documented;
0x3a x 1 FSQRT None None Documented;
0x3c x 1 FRNDINT None None Documented;
0x3d x 1 FSCALE None None Documented;
},
x87map! { 64;
0x00 x 8 FCMOVB St0 Sti Documented;
0x08 x 8 FCMOVE St0 Sti Documented;
0x10 x 8 FCMOVBE St0 Sti Documented;
0x18 x 8 FCMOVU St0 Sti Documented;
0x29 x 1 FUCOMPP None None Documented;
},
x87map! { 64;
0x00 x 8 FCMOVNB St0 Sti Documented;
0x08 x 8 FCMOVNE St0 Sti Documented;
0x10 x 8 FCMOVNBE St0 Sti Documented;
0x18 x 8 FCMOVNU St0 Sti Documented;
0x22 x 1 FNCLEX None None Documented;
0x23 x 1 FNINIT None None Documented;
0x28 x 8 FUCOMI St0 Sti Documented;
0x30 x 8 FCOMI St0 Sti Documented;
},
x87map! { 64;
0x00 x 8 FADD Sti St0 Documented;
0x08 x 8 FMUL Sti St0 Documented;
0x10 x 8 FCOM Sti None Alias;
0x18 x 8 FCOMP Sti None Alias;
0x20 x 8 FSUBR Sti St0 Documented;
0x28 x 8 FSUB Sti St0 Documented;
0x30 x 8 FDIVR Sti St0 Documented;
0x38 x 8 FDIV Sti St0 Documented;
},
x87map! { 64;
0x00 x 8 FFREE Sti None Documented;
0x08 x 8 FXCH Sti None Alias;
0x10 x 8 FST Sti None Documented;
0x18 x 8 FSTP Sti None Documented;
0x20 x 8 FUCOM Sti None Documented;
0x28 x 8 FUCOMP Sti None Documented;
},
x87map! { 64;
0x00 x 8 FADDP Sti St0 Documented;
0x08 x 8 FMULP Sti St0 Documented;
0x10 x 8 FCOMP Sti None Alias;
0x19 x 1 FCOMPP None None Documented;
0x20 x 8 FSUBRP Sti St0 Documented;
0x28 x 8 FSUBP Sti St0 Documented;
0x30 x 8 FDIVRP Sti St0 Documented;
0x38 x 8 FDIVP Sti St0 Documented;
},
x87map! { 64;
0x20 x 1 FNSTSW Ew None Documented;
0x28 x 8 FUCOMIP St0 Sti Documented;
0x30 x 8 FCOMIP St0 Sti Documented;
},
];
#[must_use]
pub const fn resolve_x87(opcode: u8, modrm: ModRm) -> Insn {
let escape = (opcode & 7) as usize;
if modrm.md == 3 {
let byte = (modrm.reg << 3) | modrm.rm;
X87_REG[escape][byte as usize]
} else {
X87_MEM[escape][(modrm.reg & 7) as usize]
}
}
#[inline]
#[must_use]
pub const fn decode(opcode: u8) -> Insn {
TABLE[opcode as usize]
}
#[inline]
#[must_use]
pub const fn decode_as(map: Gen, opcode: u8) -> Insn {
match map {
Gen::I8086 => TABLE[opcode as usize],
Gen::I386 => TABLE_386[opcode as usize],
}
}
#[inline]
#[must_use]
pub const fn decode_0f(opcode: u8) -> Insn {
TABLE_0F[opcode as usize]
}
#[inline]
#[must_use]
pub const fn resolve(insn: Insn, reg: u8) -> Insn {
resolve_as(Gen::I8086, insn, reg)
}
#[inline]
#[must_use]
pub const fn resolve_as(map: Gen, insn: Insn, reg: u8) -> Insn {
let idx = (reg & 7) as usize;
let strict = matches!(map, Gen::I386);
match insn.group {
Grp::None => insn,
Grp::Pop | Grp::MovImm => {
if strict && idx != 0 {
UNASSIGNED
} else {
insn
}
}
Grp::Alu => Insn {
op: GROUP_ALU[idx],
..insn
},
Grp::Shift => Insn {
op: GROUP_SHIFT[idx],
..insn
},
Grp::IncDec => {
if strict {
GROUP_INCDEC_386[idx]
} else {
GROUP_INCDEC[idx]
}
}
Grp::Unary => {
if matches!(insn.dst, Arg::Eb) {
GROUP_UNARY8[idx]
} else {
GROUP_UNARY16[idx]
}
}
Grp::Misc => {
if strict {
GROUP_MISC_386[idx]
} else {
GROUP_MISC[idx]
}
}
Grp::Grp6 => GROUP6[idx],
Grp::Grp7 => GROUP7[idx],
Grp::Grp8 => GROUP8[idx],
Grp::X87 | Grp::Grp9 | Grp::Grp15 | Grp::ModAlt => insn,
}
}
#[must_use]
pub const fn ea_clocks(md: u8, rm: u8, override_seg: bool) -> u32 {
let base = match (md, rm) {
(3, _) => return 0,
(0, 6) => 6,
(0, 4 | 5 | 7) => 5,
(0, 0 | 3) => 7,
(0, 1 | 2) => 8,
(1 | 2, 4..=7) => 9,
(1 | 2, 0 | 3) => 11,
(1 | 2, 1 | 2) => 12,
_ => 5,
};
if override_seg { base + 2 } else { base }
}
pub mod seg {
pub const ES: u8 = 0;
pub const CS: u8 = 1;
pub const SS: u8 = 2;
pub const DS: u8 = 3;
pub const FS: u8 = 4;
pub const GS: u8 = 5;
pub const COUNT: usize = 6;
#[must_use]
pub const fn name(sr: u8) -> &'static str {
match sr {
ES => "es",
CS => "cs",
SS => "ss",
DS => "ds",
FS => "fs",
_ => "gs",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Sib {
pub scale: u8,
pub index: u8,
pub base: u8,
}
impl Sib {
#[inline]
#[must_use]
pub const fn new(byte: u8) -> Sib {
Sib {
scale: byte >> 6,
index: (byte >> 3) & 7,
base: byte & 7,
}
}
#[inline]
#[must_use]
pub const fn has_index(self) -> bool {
self.index != 4
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ModRm {
pub md: u8,
pub reg: u8,
pub rm: u8,
}
impl ModRm {
#[inline]
#[must_use]
pub const fn new(byte: u8) -> ModRm {
ModRm {
md: byte >> 6,
reg: (byte >> 3) & 7,
rm: byte & 7,
}
}
#[inline]
#[must_use]
pub const fn is_register(self) -> bool {
self.md == 3
}
#[must_use]
pub const fn disp_bytes(self) -> u8 {
match (self.md, self.rm) {
(0, 6) => 2,
(1, _) => 1,
(2, _) => 2,
_ => 0,
}
}
#[must_use]
pub const fn default_segment(self) -> u8 {
match (self.md, self.rm) {
(0, 6) => seg::DS,
(_, 2 | 3 | 6) => seg::SS,
_ => seg::DS,
}
}
#[must_use]
pub const fn disp_bytes32(self, sib: Option<Sib>) -> u8 {
match self.md {
1 => 1,
2 => 4,
0 => match (self.rm, sib) {
(5, _) => 4,
(4, Some(s)) if s.base == 5 => 4,
_ => 0,
},
_ => 0,
}
}
#[must_use]
pub const fn default_segment32(self, sib: Option<Sib>) -> u8 {
match (self.md, self.rm, sib) {
(0, 5, _) => seg::DS,
(0, 4, Some(s)) if s.base == 5 => seg::DS,
(_, 4, Some(s)) => {
if s.base == 4 || s.base == 5 {
seg::SS
} else {
seg::DS
}
}
(_, 5, _) => seg::SS,
_ => seg::DS,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Rep {
While,
WhileNot,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Fields {
pub map: Gen,
pub bits: Bits,
pub rex: u8,
pub seg_override: Option<u8>,
pub rep: Option<Rep>,
pub lock: bool,
pub prefix66: bool,
pub mandatory: Mandatory,
pub two_byte: bool,
pub opsize: u8,
pub addrsize: u8,
pub opcode: u8,
pub insn: Insn,
pub modrm: Option<ModRm>,
pub sib: Option<Sib>,
pub disp: i32,
pub mem_seg: u8,
pub rip_relative: bool,
pub imm: u64,
pub imm2: u64,
pub len: u8,
pub truncated: bool,
}
impl Fields {
#[inline]
#[must_use]
pub const fn imm16(&self) -> u16 {
self.imm as u16
}
#[inline]
#[must_use]
pub const fn imm_sized(&self) -> u64 {
match self.opsize {
2 => self.imm & 0xffff,
4 => self.imm & 0xffff_ffff,
_ => self.imm,
}
}
#[inline]
#[must_use]
pub const fn has_rex(&self) -> bool {
self.rex != 0
}
#[inline]
#[must_use]
pub const fn rex_w(&self) -> bool {
self.rex & 0x8 != 0
}
#[inline]
#[must_use]
pub const fn reg_num(&self) -> u8 {
let base = match self.modrm {
Some(m) => m.reg,
None => 0,
};
base | ((self.rex & 0x4) << 1)
}
#[inline]
#[must_use]
pub const fn rm_num(&self) -> u8 {
let base = match self.modrm {
Some(m) => m.rm,
None => 0,
};
base | ((self.rex & 0x1) << 3)
}
#[inline]
#[must_use]
pub const fn base_num(&self) -> u8 {
let base = match self.sib {
Some(s) => s.base,
None => 0,
};
base | ((self.rex & 0x1) << 3)
}
#[inline]
#[must_use]
pub const fn index_num(&self) -> u8 {
let base = match self.sib {
Some(s) => s.index,
None => 0,
};
base | ((self.rex & 0x2) << 2)
}
#[inline]
#[must_use]
pub const fn opcode_reg(&self) -> u8 {
(self.opcode & 7) | ((self.rex & 0x1) << 3)
}
#[inline]
#[must_use]
pub const fn has_index(&self) -> bool {
match self.sib {
Some(s) => s.index != 4 || self.rex & 0x2 != 0,
None => false,
}
}
#[inline]
#[must_use]
pub const fn imm_seg(&self) -> u16 {
self.imm2 as u16
}
#[inline]
#[must_use]
pub const fn segment(&self, default: u8) -> u8 {
match self.seg_override {
Some(sr) => sr,
None => default,
}
}
#[inline]
#[must_use]
pub const fn mem_segment(&self) -> u8 {
self.segment(self.mem_seg)
}
#[inline]
#[must_use]
pub const fn rm_is_register(&self) -> bool {
if matches!(self.insn.dst, Arg::Rd) || matches!(self.insn.src, Arg::Rd) {
return true;
}
match self.modrm {
Some(m) => m.is_register(),
None => false,
}
}
}
pub const MAX_PREFIXES: u8 = 15;
pub fn decode_stream(next: &mut dyn FnMut() -> Option<u8>) -> Fields {
decode_stream_as(Gen::I8086, Bits::B16, next)
}
#[allow(clippy::too_many_lines)]
pub fn decode_stream_as(map: Gen, bits: Bits, next: &mut dyn FnMut() -> Option<u8>) -> Fields {
let bits = match map {
Gen::I8086 => Bits::B16,
Gen::I386 => bits,
};
let mut f = Fields {
map,
bits,
rex: 0,
seg_override: None,
rep: None,
lock: false,
prefix66: false,
mandatory: Mandatory::None,
two_byte: false,
opsize: bits.operand(),
addrsize: bits.address(),
opcode: 0x90,
insn: decode_as(map, 0x90),
modrm: None,
sib: None,
disp: 0,
mem_seg: seg::DS,
rip_relative: false,
imm: 0,
imm2: 0,
len: 0,
truncated: false,
};
let mut take = |f: &mut Fields| -> u8 {
match next() {
Some(b) => {
f.len = f.len.saturating_add(1);
b
}
None => {
f.truncated = true;
0
}
}
};
let mut prefixes = 0u8;
let opcode = loop {
let byte = take(&mut f);
if f.truncated {
return f;
}
let mut row = decode_as(map, byte);
if bits.is_64() {
row = row.in_long();
}
if !row.is_prefix() || prefixes >= MAX_PREFIXES {
break byte;
}
prefixes += 1;
match row.op {
Op::SEG => match byte {
0x26 => f.seg_override = Some(seg::ES),
0x2e => f.seg_override = Some(seg::CS),
0x36 => f.seg_override = Some(seg::SS),
0x3e => f.seg_override = Some(seg::DS),
0x64 => f.seg_override = Some(seg::FS),
0x65 => f.seg_override = Some(seg::GS),
0x66 => {
f.opsize = bits.operand_alt();
f.prefix66 = true;
}
_ => f.addrsize = bits.address_alt(),
},
Op::LOCK => f.lock = true,
Op::REP => f.rep = Some(Rep::While),
Op::REPNE => f.rep = Some(Rep::WhileNot),
Op::REX => {
f.rex = byte;
continue;
}
_ => {}
}
f.rex = 0;
};
let mut insn;
if matches!(map, Gen::I386) && opcode == 0x0f {
f.two_byte = true;
let second = take(&mut f);
if f.truncated {
return f;
}
f.opcode = second;
f.mandatory = match (f.rep, f.prefix66) {
(Some(Rep::While), _) => Mandatory::PF3,
(Some(Rep::WhileNot), _) => Mandatory::PF2,
(None, true) => Mandatory::P66,
(None, false) => Mandatory::None,
};
insn = decode_0f_as(f.mandatory, second);
} else {
f.opcode = opcode;
insn = decode_as(map, opcode);
}
if bits.is_64() {
insn = insn.in_long();
}
if insn.needs_modrm() {
let byte = take(&mut f);
let modrm = ModRm::new(byte);
f.modrm = Some(modrm);
insn = resolve_modrm(map, insn, f.opcode, f.two_byte, modrm);
if bits.is_64() {
insn = insn.in_long();
}
let register_only = matches!(insn.dst, Arg::Rd | Arg::Cd | Arg::Dd | Arg::Td)
|| matches!(insn.src, Arg::Rd);
if register_only {
f.mem_seg = seg::DS;
} else if f.addrsize == 2 {
f.mem_seg = modrm.default_segment();
match modrm.disp_bytes() {
1 => {
let lo = take(&mut f);
f.disp = i32::from(lo as i8);
}
2 => {
let lo = take(&mut f);
let hi = take(&mut f);
let value = u16::from(lo) | (u16::from(hi) << 8);
f.disp = i32::from(value as i16);
}
_ => {}
}
} else {
if !modrm.is_register() && modrm.rm == 4 {
let byte = take(&mut f);
f.sib = Some(Sib::new(byte));
}
f.mem_seg = modrm.default_segment32(f.sib);
f.rip_relative = bits.is_64() && !modrm.is_register() && modrm.md == 0 && modrm.rm == 5;
match modrm.disp_bytes32(f.sib) {
1 => {
let lo = take(&mut f);
f.disp = i32::from(lo as i8);
}
4 => {
let mut value = 0u32;
for i in 0..4 {
let byte = take(&mut f);
value |= u32::from(byte) << (8 * i);
}
f.disp = value as i32;
}
_ => {}
}
}
} else if matches!(insn.dst, Arg::Ob | Arg::Ov) || matches!(insn.src, Arg::Ob | Arg::Ov) {
f.mem_seg = seg::DS;
}
f.insn = insn;
if bits.is_64() {
if f.rex_w() {
f.opsize = 8;
} else if insn.op.default_64() && f.opsize == 4 {
f.opsize = 8;
}
}
let osz = f.opsize;
let asz = f.addrsize;
let mut read = |f: &mut Fields, n: u8| -> u64 {
let mut value = 0u64;
for i in 0..n {
let byte = take(f);
value |= u64::from(byte) << (8 * u32::from(i));
}
value
};
let mut slot = 0u8;
for arg in [insn.dst, insn.src, insn.aux] {
if arg == Arg::Ap {
let offset = read(&mut f, osz);
let selector = read(&mut f, 2);
f.imm = offset;
f.imm2 = selector;
slot = 2;
continue;
}
let n = arg.immediate_bytes(osz, asz);
if n == 0 {
continue;
}
let mut value = read(&mut f, n);
if matches!(arg, Arg::Ibs | Arg::Jb) {
value = ((value as u8 as i8) as i64) as u64;
} else if matches!(arg, Arg::Iz | Arg::Jv) {
value = match (n, osz) {
(2, _) => u64::from(value as u16),
(4, 8) => ((value as u32 as i32) as i64) as u64,
(4, _) => u64::from(value as u32),
_ => value,
};
}
if slot == 0 {
f.imm = value;
slot = 1;
} else {
f.imm2 = value;
slot = 2;
}
}
f
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec::Vec;
fn decode_bytes(bytes: &[u8]) -> Fields {
let mut at = 0usize;
decode_stream(&mut || {
let b = bytes.get(at).copied();
at += 1;
b
})
}
#[test]
fn every_opcode_is_described_exactly_once() {
let missing: Vec<usize> = (0..256).filter(|i| !LISTED[*i]).collect();
assert!(missing.is_empty(), "opcodes not described: {missing:02x?}");
}
#[test]
fn mnemonics_are_all_known() {
assert_eq!(Op::MOV.mnemonic(), "mov");
assert_eq!(Op::CALLF.mnemonic(), "callf");
assert_eq!(Op::SETMO.mnemonic(), "setmo");
for op in Op::ALL {
assert!(!op.mnemonic().is_empty(), "{op:?} has no mnemonic");
assert!(!op.summary().is_empty(), "{op:?} has no summary");
}
}
#[test]
fn every_declared_operation_is_reachable() {
let reachable = |op: Op| {
TABLE.iter().any(|i| i.op == op || i.in_long().op == op)
|| TABLE_386.iter().any(|i| i.op == op || i.in_long().op == op)
|| TABLE_0F.iter().any(|i| i.op == op || i.in_long().op == op)
|| GROUP_ALU.contains(&op)
|| GROUP_SHIFT.contains(&op)
|| GROUP_UNARY8.iter().any(|i| i.op == op)
|| GROUP_MISC.iter().any(|i| i.op == op)
|| GROUP6.iter().any(|i| i.op == op)
|| GROUP7.iter().any(|i| i.op == op)
|| GROUP8.iter().any(|i| i.op == op)
|| GROUP9.iter().any(|i| i.op == op)
|| GROUP15.iter().any(|i| i.op == op)
|| GROUP15_REG.iter().any(|i| i.op == op)
|| TABLE_0F_66.iter().any(|i| i.op == op)
|| TABLE_0F_F3.iter().any(|i| i.op == op)
|| TABLE_0F_F2.iter().any(|i| i.op == op)
|| X87_MEM.iter().flatten().any(|i| i.op == op)
|| X87_REG.iter().flatten().any(|i| i.op == op)
|| matches!(op, Op::MOVHLPS | Op::MOVLHPS)
};
for op in Op::ALL {
if *op == Op::SWAPGS {
continue;
}
assert!(reachable(*op), "{op:?} is declared but unreachable");
}
}
#[test]
fn groups_resolve_to_the_right_operation() {
assert_eq!(resolve(decode(0x80), 5).op, Op::SUB);
assert_eq!(resolve(decode(0x81), 7).op, Op::CMP);
assert_eq!(resolve(decode(0xd1), 4).op, Op::SHL);
assert_eq!(resolve(decode(0xd3), 6).op, Op::SETMO);
assert_eq!(resolve(decode(0xf6), 6).op, Op::DIV);
assert_eq!(resolve(decode(0xf7), 5).op, Op::IMUL);
assert_eq!(resolve(decode(0xff), 3).op, Op::CALLF);
assert_eq!(resolve(decode(0x8f), 5).op, Op::POP);
assert_eq!(resolve(decode(0xc7), 3).op, Op::MOV);
}
#[test]
fn the_unary_group_keeps_its_operand_width() {
assert_eq!(resolve(decode(0xf6), 0).src, Arg::Ib);
assert_eq!(resolve(decode(0xf7), 0).src, Arg::Iz);
assert_eq!(resolve(decode(0xf6), 4).dst, Arg::Eb);
assert_eq!(resolve(decode(0xf7), 4).dst, Arg::Ev);
}
#[test]
fn prefixes_are_marked_as_such() {
for op in [0x26, 0x2e, 0x36, 0x3e, 0xf0, 0xf1, 0xf2, 0xf3] {
assert!(decode(op).is_prefix(), "{op:02x} should be a prefix");
}
assert!(!decode(0x90).is_prefix());
}
#[test]
fn modrm_is_required_exactly_where_the_encoding_needs_one() {
assert!(decode(0x00).needs_modrm()); assert!(decode(0x8d).needs_modrm()); assert!(decode(0xff).needs_modrm()); assert!(!decode(0x40).needs_modrm()); assert!(!decode(0xb8).needs_modrm()); assert!(!decode(0xa0).needs_modrm()); }
#[test]
fn operand_widths_follow_the_encoding() {
assert_eq!(decode(0x00).width_bytes(2), Some(1));
assert_eq!(decode(0x01).width_bytes(2), Some(2));
assert_eq!(decode(0xa4).width_bytes(2), Some(1)); assert_eq!(decode(0xa5).width_bytes(2), Some(2)); assert_eq!(decode(0x83).width_bytes(2), Some(2)); assert_eq!(decode(0xf4).width_bytes(2), None); assert_eq!(decode(0x01).width_bytes(4), Some(4));
assert_eq!(decode(0xa5).width_bytes(4), Some(4));
assert_eq!(decode(0x00).width_bytes(4), Some(1));
}
#[test]
fn the_alias_encodings_are_the_ones_the_hardware_corpus_names() {
let aliases: Vec<u8> = (0..=255u8)
.filter(|o| decode(*o).class == Class::Alias)
.collect();
let mut want: Vec<u8> = (0x60..=0x6f).collect();
want.extend([0x82, 0xc0, 0xc1, 0xc8, 0xc9]);
want.sort_unstable();
assert_eq!(aliases, want);
}
#[test]
fn ea_clocks_match_the_manual() {
assert_eq!(ea_clocks(0, 6, false), 6); assert_eq!(ea_clocks(0, 4, false), 5); assert_eq!(ea_clocks(0, 0, false), 7); assert_eq!(ea_clocks(0, 1, false), 8); assert_eq!(ea_clocks(1, 7, false), 9); assert_eq!(ea_clocks(2, 3, false), 11); assert_eq!(ea_clocks(2, 2, false), 12); assert_eq!(ea_clocks(3, 0, false), 0); assert_eq!(ea_clocks(0, 4, true), 7); }
#[test]
fn the_decoder_measures_instruction_length() {
assert_eq!(decode_bytes(&[0x90]).len, 1); assert_eq!(decode_bytes(&[0xb8, 0x34, 0x12]).len, 3); assert_eq!(decode_bytes(&[0x00, 0x00]).len, 2); assert_eq!(decode_bytes(&[0x00, 0x06, 0x34, 0x12]).len, 4); assert_eq!(decode_bytes(&[0x83, 0x46, 0x10, 0x05]).len, 4); assert_eq!(decode_bytes(&[0xea, 0x00, 0x10, 0x00, 0xf0]).len, 5); assert_eq!(
decode_bytes(&[0x26, 0x81, 0x86, 0x34, 0x12, 0x78, 0x56]).len,
7
);
}
#[test]
fn prefixes_accumulate_and_the_last_override_wins() {
let f = decode_bytes(&[0xf3, 0x26, 0x2e, 0xf0, 0xa4]);
assert_eq!(f.rep, Some(Rep::While));
assert_eq!(f.seg_override, Some(seg::CS));
assert!(f.lock);
assert_eq!(f.insn.op, Op::MOVSB);
assert_eq!(f.len, 5);
}
#[test]
fn a_truncated_stream_is_reported_rather_than_guessed() {
let f = decode_bytes(&[0xb8, 0x34]);
assert!(f.truncated);
assert_eq!(f.insn.op, Op::MOV);
}
#[test]
fn displacements_are_sign_extended_at_decode_time() {
let f = decode_bytes(&[0x00, 0x46, 0xff]);
assert_eq!(f.disp, -1);
let f = decode_bytes(&[0x83, 0xc0, 0xfe]);
assert_eq!(f.imm16(), 0xfffe);
}
#[test]
fn the_direct_address_encoding_is_not_bp_relative() {
let rm = ModRm::new(0b00_000_110);
assert_eq!(rm.disp_bytes(), 2);
assert_eq!(rm.default_segment(), seg::DS);
assert_eq!(ModRm::new(0b01_000_110).default_segment(), seg::SS);
assert_eq!(ModRm::new(0b00_000_010).default_segment(), seg::SS);
assert_eq!(ModRm::new(0b00_000_011).default_segment(), seg::SS);
assert_eq!(ModRm::new(0b00_000_000).default_segment(), seg::DS);
}
#[test]
fn a_far_pointer_immediate_carries_both_halves() {
let f = decode_bytes(&[0x9a, 0x00, 0x10, 0x00, 0xf0]);
assert_eq!(f.insn.op, Op::CALLF);
assert_eq!(f.imm16(), 0x1000);
assert_eq!(f.imm_seg(), 0xf000);
}
}