查看http://ref.x86asm.net/coder32.html我发现了两个与语句匹配的操作码
xor eax,eax
1)操作码 31 XOR r/m16/32 r16/32
2)操作码 33 XOR r16/32 r/m16/32
都指操作数 1 和操作数 2 的 32 位寄存器。那么,在异或两个 32 位寄存器的这种特定情况下有什么不同吗?
查看http://ref.x86asm.net/coder32.html我发现了两个与语句匹配的操作码
xor eax,eax
1)操作码 31 XOR r/m16/32 r16/32
2)操作码 33 XOR r16/32 r/m16/32
都指操作数 1 和操作数 2 的 32 位寄存器。那么,在异或两个 32 位寄存器的这种特定情况下有什么不同吗?
x86 有两种冗余方式来编码任何基本 ALU 指令(可追溯到 8086 年)的 2 寄存器实例,使用 r/m 源和 r/m 目标形式。
这种编码冗余reg,reg
是 x86 机器代码如何允许大多数指令的内存目标或内存源的结果:不是花费 ModR/M 字节中的位来对两个操作数进行灵活编码,而是简单地使用两个独立的大多数指令的操作码。
(这就是为什么xor [eax], [ecx]
任何指令都不允许使用 两个显式内存操作数,例如寻址模式。)rep movs
push [mem]
请注意,31
与33
word/dword/qword 大小的xor相比,仅在位 #1 上有所不同。其他指令(如29
vs. 2B
sub )遵循相同的模式。 位 #1 有时称为操作码的“方向”位。(不要与 EFLAGS 中的 DF 混淆,方向标志)。
另请注意,这些指令的 byte 与 word/dword/qword 操作数大小版本仅在低位上有所不同,例如30 XOR r/m8, r8
vs. 31 XOR r/m16, r16
。同样,这种模式出现在可追溯到 8086 的 ALU 指令编码中 。这些操作码的位 #0 有时称为“大小”位。
这些“基本 ALU”指令具有每个方向和大小组合的编码可以追溯到原始 8086;许多后来的指令(如386bsf r, r/m
或186imul r, r/m, imm
)没有可以允许内存目标的形式。或者只有目的地可以是 reg/mem。bt* r/m, r
这也是为什么后来的指令(或它们的新形式,如 imul)通常没有用于字节操作数大小的单独操作码,只允许通过正常前缀机制的 word/dword/qword。8086 占用了大部分编码空间,后来的扩展想为更多的未来扩展留出空间。所以这就是为什么没有imul r, r/m8
.
(dword 和 qword 操作数大小本身就是扩展;8086 没有操作数大小或 REX 前缀。因此,原始 8086 在使用其操作码编码空间方面是相当明智的,并且具有使解码不会一团糟的模式。)
对于 reg,reg 指令,它们在我知道的任何 CPU 上如何解码和执行都没有区别;唯一需要关心汇编器使用哪种编码的时候是当您希望机器代码满足其他一些要求时,例如仅使用表示可打印 ASCII 字符的字节。(例如,用于漏洞利用负载)。
一些汇编程序具有覆盖其默认编码选择的语法,例如GAS 有一个.s
后缀来获取非默认编码。现在已弃用,您应该在助记符之前使用{load}
or{store}
前缀(请参阅文档),如下所示:
{load} xor %eax, %ecx
{store} xor %eax, %ecx
{vex3} vpaddd %xmm0, %xmm1, %xmm1
vpaddd %xmm0, %xmm1, %xmm1 # default is to use 2-byte VEX when possible
gcc -c foo.S && objdump -drwC foo.o
0: 31 c1 xor %eax,%ecx
2: 33 c8 xor %eax,%ecx
4: c4 e1 71 fe c8 vpaddd %xmm0,%xmm1,%xmm1
9: c5 f1 fe c8 vpaddd %xmm0,%xmm1,%xmm1
(相关:在现代 x86 上可以使用哪些方法有效地扩展指令长度?对于 和 的用{vex3}
例{evex}
。{disp32}
)
NASM还具有与 GAS 相同语法的 、 和 前缀{vex2}
,{vex3}
例如. 但我没有看到一种方法来覆盖操作码的vs.选择。{evex}
{vex3} vpaddd xmm1, xmm1, xmm0
op r/m, r
op r, r/m
{load}
(GAS 源中的和{store}
覆盖的前身)。