373 lines
14 KiB
C
373 lines
14 KiB
C
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||
│ Copyright 2020 Justine Alexandra Roberts Tunney │
|
||
│ │
|
||
│ This program is free software; you can redistribute it and/or modify │
|
||
│ it under the terms of the GNU General Public License as published by │
|
||
│ the Free Software Foundation; version 2 of the License. │
|
||
│ │
|
||
│ This program is distributed in the hope that it will be useful, but │
|
||
│ WITHOUT ANY WARRANTY; without even the implied warranty of │
|
||
│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │
|
||
│ General Public License for more details. │
|
||
│ │
|
||
│ You should have received a copy of the GNU General Public License │
|
||
│ along with this program; if not, write to the Free Software │
|
||
│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │
|
||
│ 02110-1301 USA │
|
||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||
#include "libc/bits/safemacros.internal.h"
|
||
#include "libc/fmt/conv.h"
|
||
#include "libc/macros.h"
|
||
#include "libc/runtime/runtime.h"
|
||
#include "libc/stdio/stdio.h"
|
||
#include "libc/str/str.h"
|
||
#include "libc/sysv/consts/ex.h"
|
||
#include "libc/sysv/consts/exit.h"
|
||
#include "third_party/getopt/getopt.h"
|
||
|
||
#define USAGE \
|
||
" [FLAGS] OPERAND..\n\
|
||
Decodes geek operand notation used by ref.x86asm.net\n\
|
||
\n\
|
||
Flags:\n\
|
||
-s succinct mode\n\
|
||
-b BITS sets 16/32/64 bit mode [default 64]\n\
|
||
-? shows this information\n\
|
||
\n\
|
||
Examples:\n\
|
||
o/tool/cpu/help-operand -b64 Evqp\n\
|
||
\n"
|
||
|
||
int bits_;
|
||
bool succinct_;
|
||
|
||
void PrintUsage(int rc, FILE *f) {
|
||
fputs("Usage: ", f);
|
||
fputs(program_invocation_name, f);
|
||
fputs(USAGE, f);
|
||
exit(rc);
|
||
}
|
||
|
||
void GetOpts(int argc, char *argv[]) {
|
||
int opt;
|
||
bits_ = 64;
|
||
while ((opt = getopt(argc, argv, "?hbs")) != -1) {
|
||
switch (opt) {
|
||
case 's':
|
||
succinct_ = true;
|
||
break;
|
||
case 'b':
|
||
bits_ = atoi(optarg);
|
||
break;
|
||
case '?':
|
||
case 'h':
|
||
PrintUsage(EXIT_SUCCESS, stdout);
|
||
default:
|
||
PrintUsage(EX_USAGE, stderr);
|
||
}
|
||
}
|
||
}
|
||
|
||
const struct Descriptors {
|
||
const char prefix[8];
|
||
const char *succinct;
|
||
const char *description;
|
||
} kDescriptors[] = {
|
||
|
||
{"AL", "AL", "AL register"},
|
||
{"CS", "CS", "CS register (code segment)"},
|
||
|
||
{"va", "?",
|
||
"Word or doubleword, according to asz address-size attribute (only REP "
|
||
"and LOOP families)."},
|
||
{"dqa", "?",
|
||
"Doubleword or quadword, according to asz address-size attribute (only "
|
||
"REP and LOOP families)."},
|
||
{"wa", "?",
|
||
"Word, according to asz address-size attribute (only JCXZ instruction)."},
|
||
{"wo", "?",
|
||
"Word, according to current operand size (e. g., MOVSW instruction)."},
|
||
{"ws", "?",
|
||
"Word, according to current stack size (only PUSHF and POPF instructions "
|
||
"in 64-bit mode)."},
|
||
{"da", "?",
|
||
"Doubleword, according to asz address-size attribute (only JECXZ "
|
||
"instruction)."},
|
||
{"do", "?",
|
||
"Doubleword, according to current osz operand size (e. g., MOVSD "
|
||
"instruction)."},
|
||
{"qa", "?",
|
||
"Quadword, according to asz address-size attribute (only JRCXZ "
|
||
"instruction)."},
|
||
{"qs", "64/16",
|
||
"Quadword, according to current stack size via osz operand-size (only "
|
||
"PUSHFQ and POPFQ instructions)."},
|
||
|
||
{"va", "16/32",
|
||
"Word or doubleword sign extended to the size of the stack pointer (for "
|
||
"example, PUSH (68))."},
|
||
|
||
{"vqp", "16/32/64",
|
||
"Word or doubleword, depending on operand-size attribute, or "
|
||
"quadword, promoted by REX.W in 64-bit mode."},
|
||
{"vs", "16/32",
|
||
"Word or doubleword sign extended to the size of the stack pointer (for "
|
||
"example, PUSH (68))."},
|
||
{"vds", "16/32",
|
||
"Word or doubleword, depending on operand-size attribute, or doubleword, "
|
||
"sign-extended to 64 bits for 64-bit operand size."},
|
||
{"vq", "64/16",
|
||
"Quadword (default) or word if operand-size prefix is used (for "
|
||
"example, PUSH (50))."},
|
||
|
||
{"EST", "STi",
|
||
"A ModR/M byte follows the opcode and specifies the x87 FPU stack "
|
||
"register."},
|
||
{"ES", "STi/m",
|
||
"A ModR/M byte follows the opcode and specifies the operand. The "
|
||
"operand is either a x87 FPU stack 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, or a displacement."},
|
||
|
||
{"SC", "",
|
||
"Stack operand, used by instructions which either push an operand to the "
|
||
"stack or pop an operand from the stack. Pop-like instructions are, for "
|
||
"example, POP, RET, IRET, LEAVE. Push-like are, for example, PUSH, CALL, "
|
||
"INT. No Operand type is provided along with this method because it "
|
||
"depends on source/destination operand(s)."},
|
||
|
||
{"BA", "m",
|
||
"Memory addressed by DS:EAX, or by rAX in 64-bit mode (only 0F01C8 "
|
||
"MONITOR)."},
|
||
{"BB", "m",
|
||
"Memory addressed by DS:eBX+AL, or by rBX+AL in 64-bit mode (only XLAT)."},
|
||
{"BD", "m",
|
||
"Memory addressed by DS:eDI or by RDI (only 0FF7 MASKMOVQ and 660FF7 "
|
||
"MASKMOVDQU)"},
|
||
|
||
{"A", "ptr",
|
||
"Direct address. The instruction has no ModR/M byte; the address of the "
|
||
"operand is encoded in the instruction; no base register, index register, "
|
||
"or scaling factor can be applied (for example, far JMP (EA))."},
|
||
{"C", "CRn",
|
||
"The reg field of the ModR/M byte selects a control register (only MOV "
|
||
"(0F20, 0F22))."},
|
||
{"D", "DRn",
|
||
"The reg field of the ModR/M byte selects a debug register (only MOV "
|
||
"(0F21, 0F23))."},
|
||
|
||
{"I", "imm",
|
||
"Immediate data. The operand value is encoded in subsequent bytes of the "
|
||
"instruction."},
|
||
|
||
{"J", "rel",
|
||
"The instruction contains a relative offset to be added to the "
|
||
"instruction pointer register (for example, JMP (E9), LOOP))."},
|
||
|
||
{"E", "r/m",
|
||
"A ModR/M byte follows the opcode and specifies the operand. The "
|
||
"operand is either a general-purpose 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, or a displacement."},
|
||
|
||
{"G", "r",
|
||
"The reg field of the ModR/M byte selects a general register (for "
|
||
"example, AX (000))."},
|
||
|
||
{"H", "r",
|
||
"The r/m field of the ModR/M byte always selects a general register, "
|
||
"regardless of the mod field (for example, MOV (0F20))."},
|
||
|
||
{"M", "rm",
|
||
"The ModR/M byte may refer only to memory: mod != 11bin (BOUND, LEA, "
|
||
"CALLF, JMPF, LES, LDS, LSS, LFS, LGS, CMPXCHG8B, CMPXCHG16B, F20FF0 "
|
||
"LDDQU)."},
|
||
|
||
{"N", "mm",
|
||
"The R/M field of the ModR/M byte selects a packed quadword MMX "
|
||
"technology register."},
|
||
|
||
{"O", "moffs",
|
||
"The instruction has no ModR/M byte; the offset of the operand is coded "
|
||
"as a word, double word or quad word (depending on address size "
|
||
"attribute) in the instruction. No base register, index register, or "
|
||
"scaling factor can be applied (only MOV (A0, A1, A2, A3))."},
|
||
|
||
{"P", "mm",
|
||
"The reg field of the ModR/M byte selects a packed quadword MMX "
|
||
"technology register."},
|
||
|
||
{"Q", "mm/m64",
|
||
"A ModR/M byte follows the opcode and specifies the operand. The "
|
||
"operand is either an MMX technology 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, and a displacement."},
|
||
|
||
{"R", "r",
|
||
"The mod field of the ModR/M byte may refer only to a general "
|
||
"register (only MOV (0F20-0F24, 0F26))."},
|
||
|
||
{"S", "Sreg",
|
||
"The reg field of the ModR/M byte selects a segment register (only MOV "
|
||
"(8C, 8E))."},
|
||
|
||
{"T", "TRn",
|
||
"The reg field of the ModR/M byte selects a test register (only MOV "
|
||
"(0F24, 0F26))."},
|
||
|
||
{"U", "xmm",
|
||
"The R/M field of the ModR/M byte selects a 128-bit XMM register."},
|
||
|
||
{"sr", "32real",
|
||
"Single-real. Only x87 FPU instructions (for example, FADD)."},
|
||
{"dr", "64real",
|
||
"Double-real. Only x87 FPU instructions (for example, FADD)."},
|
||
{"er", "80real",
|
||
"Extended-real. Only x87 FPU instructions (for example, FLD)."},
|
||
{"e", "14/28", "x87 FPU environment (for example, FSTENV)."},
|
||
|
||
{"V", "xmm",
|
||
"The reg field of the ModR/M byte selects a 128-bit XMM register."},
|
||
|
||
{"W", "xmm/m",
|
||
"A ModR/M byte follows the opcode and specifies the operand. The operand "
|
||
"is either a 128-bit XMM 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, "
|
||
"and a displacement"},
|
||
|
||
{"X", "m",
|
||
"Memory addressed by the DS:eSI or by RSI (only MOVS, CMPS, OUTS, "
|
||
"and LODS). In 64-bit mode, only 64-bit (RSI) and 32-bit (ESI) "
|
||
"address sizes are supported. In non-64-bit modes, only 32-bit (ESI) "
|
||
"and 16-bit (SI) address sizes are supported."},
|
||
|
||
{"Y", "m",
|
||
"Memory addressed by the ES:eDI or by RDI (only MOVS, CMPS, INS, "
|
||
"STOS, and SCAS). In 64-bit mode, only 64-bit (RDI) and 32-bit (EDI) "
|
||
"address sizes are supported. In non-64-bit modes, only 32-bit (EDI) "
|
||
"and 16-bit (DI) address sizes are supported. The implicit ES "
|
||
"segment register cannot be overriden by a segment prefix."},
|
||
|
||
{"Z", "r",
|
||
"The instruction has no ModR/M byte; the three least-significant "
|
||
"bits of the opcode byte selects a general-purpose register."},
|
||
|
||
{"F", "-", "rFLAGS register."},
|
||
|
||
{"si", "32real",
|
||
"Doubleword integer register (e. g., eax). (unused even by Intel?)"},
|
||
{"ss", "-",
|
||
"Scalar element of a 128-bit packed single-precision floating data."},
|
||
{"stx", "512", "x87 FPU and SIMD state (FXSAVE and FXRSTOR)."},
|
||
{"st", "94/108", "x87 FPU state (for example, FSAVE)."},
|
||
{"s", "-",
|
||
"6-byte pseudo-descriptor, or 10-byte pseudo-descriptor in 64-bit mode "
|
||
"(for example, SGDT)."},
|
||
|
||
{"bcd", "80dec",
|
||
"Packed-BCD. Only x87 FPU instructions (for example, FBLD)."},
|
||
{"bsq", "", "(Byte, sign-extended to 64 bits.)"},
|
||
{"bss", "8",
|
||
"Byte, sign-extended to the size of the stack pointer (for example, PUSH "
|
||
"(6A))."},
|
||
{"bs", "8", "Byte, sign-extended to the size of the destination operand."},
|
||
|
||
{"a", "16/32&16/32",
|
||
"Two one-word operands in memory or two double-word operands in memory, "
|
||
"depending on operand-size attribute (only BOUND)."},
|
||
{"b", "8", "Byte, regardless of operand-size attribute."},
|
||
{"c", "?",
|
||
"Byte or word, depending on operand-size attribute. (unused even by "
|
||
"Intel?)"},
|
||
{"dqp", "32/64",
|
||
"Doubleword, or quadword, promoted by REX.W in 64-bit mode (for example, "
|
||
"MOVSXD)."},
|
||
{"dq", "128",
|
||
"Double-quadword, regardless of operand-size attribute (for example, "
|
||
"CMPXCHG16B)."},
|
||
{"ds", "32",
|
||
"Doubleword, sign-extended to 64 bits (for example, CALL (E8)."},
|
||
{"di", "32int",
|
||
"Doubleword-integer. Only x87 FPU instructions (for example, FIADD)."},
|
||
{"d", "32", "Doubleword, regardless of operand-size attribute."},
|
||
{"qi", "64int",
|
||
"Qword-integer. Only x87 FPU instructions (for example, FILD)."},
|
||
{"qp", "64", "Quadword, promoted by REX.W (for example, IRETQ)."},
|
||
{"q", "64",
|
||
"Quadword, regardless of operand-size attribute (for example, CALL (FF "
|
||
"/2))."},
|
||
|
||
{"v", "16/32",
|
||
"Word or doubleword, depending on operand-size attribute (for example, "
|
||
"INC (40), PUSH (50))."},
|
||
|
||
{"wi", "16int",
|
||
"Word-integer. Only x87 FPU instructions (for example, FIADD)."},
|
||
{"w", "16",
|
||
"Word, regardless of operand-size attribute (for example, ENTER)."},
|
||
|
||
{"pi", "-", "Quadword MMX technology data."},
|
||
{"psq", "-", "64-bit packed single-precision floating-point data."},
|
||
{"pd", "-", "128-bit packed double-precision floating-point data."},
|
||
{"ps", "-", "128-bit packed single-precision floating-point data."},
|
||
{"ptp", "16:16/32/64",
|
||
"32-bit or 48-bit pointer, depending on operand-size attribute, or 80-bit "
|
||
"far pointer, promoted by REX.W in 64-bit mode (for example, "
|
||
"CALLF (FF /3))."},
|
||
{"p", "16:16/32",
|
||
"32-bit or 48-bit pointer, depending on operand-size attribute (for "
|
||
"example, CALLF (9A)."},
|
||
|
||
};
|
||
|
||
void HandleOperand(const char *op) {
|
||
int i;
|
||
bool found;
|
||
while (*op) {
|
||
found = false;
|
||
for (i = 0; i < ARRAYLEN(kDescriptors); ++i) {
|
||
if (startswith(op, kDescriptors[i].prefix)) {
|
||
found = true;
|
||
op += strlen(kDescriptors[i].prefix);
|
||
if (succinct_) {
|
||
printf("%s ", kDescriptors[i].succinct);
|
||
} else if (!isempty(kDescriptors[i].succinct) &&
|
||
strcmp(kDescriptors[i].succinct, "-") != 0 &&
|
||
strcmp(kDescriptors[i].succinct, "?") != 0) {
|
||
printf("%s (%s): %s\n", kDescriptors[i].prefix,
|
||
kDescriptors[i].succinct, kDescriptors[i].description);
|
||
} else {
|
||
printf("%s: %s\n", kDescriptors[i].prefix,
|
||
kDescriptors[i].description);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
if (!found) {
|
||
printf("%c?", *op);
|
||
if (succinct_) {
|
||
printf(" ");
|
||
} else {
|
||
printf("\n");
|
||
}
|
||
op++;
|
||
}
|
||
}
|
||
printf("\n");
|
||
}
|
||
|
||
int main(int argc, char *argv[]) {
|
||
int i;
|
||
GetOpts(argc, argv);
|
||
for (i = optind; i < argc; ++i) {
|
||
HandleOperand(argv[i]);
|
||
}
|
||
return 0;
|
||
}
|