许多用于立即版本指令的操作码,包括使用ModR/M 字节中83
的 3 位/r
字段作为 3 个额外的操作码位。英特尔的第 2 卷手册记录了这一点,我认为附录中的操作码表包括它。
这就是为什么大多数原始 8086 立即数指令and r/m, imm
仍然只允许 2 个操作数,不像shrd eax, edx, 4
或imul edx, [rdi], 12345
其中两个 ModRM 字段都用于编码 dst/src 操作数,以及暗示立即操作数的操作码。
SHRD/SHLD 并添加了 386,并且 imul-immediate 添加了 186 。不幸的是,copy-and-AND ( and eax, edx, 0xf
) 不可编码,但至少 x86 可以使用 LEA 进行非常常见的复制和添加或子操作。
但是,如果每个立即数和单操作数指令(如push
or not
)都需要一个完整的操作码,那么 8086 就会用完 1 字节的操作码。(特别是因为设计者选择在 AL 和 AX 没有 modrm 字节的短格式上花费大量编码空间,比如cmp ax, 12345
在 16 位模式下只有 3 个字节而不是 4 个字节,或者在 32cmp eax, imm32
中只有 5 个字节而不是 6 个字节cmp r/m32, imm32
-bit 模式。对于单字节 xchg-with-ax 和单字节 inc/dec 寄存器。)
示例:解码48 83 C4 38
。 (从一个操作码字节如何根据“寄存器/操作码”字段解码为不同的指令?那是什么?,这个Q的副本)
48
是一个 REX.W 前缀(REX 只设置了 W 位,因此它表示 64 位操作数大小,但没有高位寄存器)。
Opcode83
说它可以是 7 条不同的指令,具体取决于称为“寄存器/操作码字段”的字段
每条指令都有自己的文档,例如add
(vol2 手册的 html 摘录)
REX.W + 83 /0 ib
,显示了 for之类的编码ADD r/m64, imm8
,这就是您所拥有的。
来自 wiki.osdev.org 的 ModRM 位域图
7 0
+---+---+---+---+---+---+---+---+
| mod | reg | rm |
+---+---+---+---+---+---+---+---+
0xc4 = 0b11000100,所以 reg 字段 = 0。因此我们的操作码是83 /0
,在 Intel 的表示法中。
其余的 ModRM 字段是:
所以指令是add rsp, 0x38
ndisasm -b64
同意:
$ cat > foo.asm
db 0x48, 0x83, 0xC4, 0x38
$ nasm foo.asm # create a flat binary with those bytes, not an object file
$ ndisasm -b64 foo
00000000 4883C438 add rsp,byte +0x38