查看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
这两种操作码都适用于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
这两种操作码都适用于32位寄存器作为操作数一和操作数二。那么,在这种特定情况下,对两个32位寄存器进行异或运算有什么区别吗?
注意,字/双字/四字大小的xor指令中,31
与33
之间的区别仅在于第一位。其他指令,如29
与2B
之间的sub也遵循同样的模式。 第一位有时被称为操作码的“方向”位。(不要与EFLAGS中的DF方向标志混淆)。
还要注意的是,这些指令的字节版本与字/双字/四字操作数版本之间的区别仅在于低位,例如:30 XOR r/m8, r8
与31 XOR r/m16, r16
。同样,这种模式出现在从8086年开始就存在的ALU指令编码中。这些操作码的第0位有时被称为“大小”位。
bsf r,r/m
或186 imul r,r/m,imm
没有一个形式,可以允许内存目标。或者对于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}
或{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}
和 {evex}
前缀,例如:{vex3} vpaddd xmm1, xmm1, xmm0
。但我没有看到一种方法来覆盖 op r/m, r
vs. op r, r/m
的选择。
{load}
和{store}
覆盖的前身)。