MOV r/m8,r8 和 MOV r8,r/m8 的区别

Difference between MOV r/m8,r8 and MOV r8,r/m8

通过查看 intel 的指令集,我发现了这个:

1) 88 /r MOV r/m8,r8
2) 8A /r MOV r8,r/m8

当我在 NASM 和 assemble 中用列表选项写下这样的一行时:

mov al, bl

我在列表中得到这个:

88D8 mov al, bl

很明显NASM选择了上面两个指令中的第一个指令,但是第二个指令不是选项二吗?如果是这样,NASM 选择第一个的依据是什么?

这两种编码的存在是因为一个modr/m字节只能编码一个内存操作数。所以要允许 mov r8,m8mov m8,r8,需要两种编码。当然,通过这种方式,我们可以使用任一编码对 mov 的使用进行编码,两个操作数都是寄存器,而 nasm 只是随机选择一个。选择没有特殊原因,我看到汇编程序做出不同的选择。

我还听说过一个汇编程序,它通过以特定方式选择指令编码来为它汇编的二进制文件加水印。这样汇编程序的作者就可以追查并起诉那些不付钱就使用他的汇编程序的人。

正如所指出的:没有理由。
也许作者发现使用正向移动比反向移动更有吸引力,或者也许他们只是选择了第一个操作码。

我查看了 NASM 源代码,发现编码基本上是通过大量查找 table 完成的,所以这真的是一个品味问题。 如果解析没有使用查找 table,则使用其他操作码 (8AC3) 会简化代码(我猜):像 addps 这样的指令是不对称的,并且通过对 [=] 使用 8A /r 15=] 代码可以重复用于为 addps 和类似指令计算 ModR/M 字节。当使用 8A /r 时,addps xmm0, xmm3 使用与 mov al, bl 相同的 ModR/M 字节 (C3)。
请注意,寄存器 A (B) 和 xmm0 (xmm0) 使用相同的数字进行编码。

然而,弄清楚为什么有两种编码仍然很有趣。


Mark Hopkins (re)discovered, the earlier x86 instructions encoding make a lot more sense in octal.
八进制的一个字节有三个数字,我将其称为 G P F(Group、oPeration、Flags)。

G 是八进制组,同一组中的指令倾向于执行类似的任务(例如算术与移动)。
但是,这并不是严格的划分。
P是操作;例如,在算术组中,一个运算是减法,另一个是加法。
F 是用于控制操作行为的位集。每个组和操作都随意使用数字F,甚至可能没有位设置(例如G=2,P=7是mov r16, imm16而F被用来selectr16).

对于从 memory/register 移动到寄存器或 G 为 2 P 为 1 的其他方式的 mov 指令。
F 是一个 3 位字段,语义为:

  2   1   0    bit
+---+---+---+
| s | d | b |
+---+---+---+

s = 1 if moving to/from a segment register
    0 if moving to/from a gp register

d = 1 if moving mem -> reg
    0 if moving mem <- reg

b = 1 if moving a WORD
    0 if moving a BYTE

我们可以开始形成操作码,但我们仍然错过了 select 操作数的方法。

G=2, P=1, F={s=0, d=0, b=0} 210 (88) mov r/m8, r8
G=2, P=1, F={s=0, d=0, b=1} 211 (89) mov r/m16, r16
G=2, P=1, F={s=0, d=1, b=0} 212 (8A) mov r8, r/m8
G=2, P=1, F={s=0, d=1, b=1} 213 (8B) mov r16, r/m16
G=2, P=1, F={s=1, d=0, b=0} 214 (8C) mov r/m16, Sreg
G=2, P=1, F={s=1, d=0, b=1} 215 (8D) Not a move, segment registers are 16-bit
G=2, P=1, F={s=1, d=1, b=0} 216 (8E) mov Sreg, r/m16
G=2, P=1, F={s=1, d=1, b=1} 217 (8F) Not a move, segment registers are 16-bit

操作码后面一定是ModR/M字节,用来select寻址方式和寄存器。

ModR/M字节可以看成八进制的三个字段:X R M.

X和M组合在一起形成寻址方式
R selects 寄存器(例如 0 = A,3 = B)。

其中一种寻址模式(X=3,M=any)让我们寻址寄存器(通过 M)而不是内存。
例如,X=3,R=0,M=3(C3)设置寄存器B为"memory"操作数,寄存器A为寄存器操作数
当 X=3, R=3, M=0 (D8) 将寄存器 A 设置为 "memory" 操作数,将寄存器 B 设置为寄存器操作数。

这里我们可以看出歧义所在:ModR/M 字节让我们对源寄存器和目标寄存器进行编码。同时,操作码让我们对从源到目标或从目标到源的移动进行编码——这让我们可以自由选择哪个寄存器是什么。

例如,假设我们要将 B 移动到 A。

如果我们确定 A 作为寄存器操作数(源),B 作为内存操作数(目标),那么 ModR/M 字节是 X=3,R=0,M=3 (C3)。
要从 B 移动到 A,如您的示例所示,仅使用低 8 位,我们将移动编码为 G=2, P=1, F={s=0,d=1,b=0} (8A) 因为我们移动了 mem->reg (B->A)。 因此最后的指令是 8AC3.

如果我们选择 A 作为内存操作数(目标),B 作为寄存器操作数(源),ModR/M 字节为 X=3,R=3,M=0 (D8)。
移动是 G=2, P=1, F={s=0,d=0,b=0} (88) 因为我们移动 reg->mem (B->A).
最后一条指令是88D8.

如果我们想移动整个 16 位寄存器(这里我们忽略操作数大小前缀)我们只需设置 F 的 b 位:

G=2, P=1, F={s=0,d=1,b=1} 第一种情况,导致 8BC3.
对于第二种情况,G=2,P=1,F={s=0,d=0,b=1},导致 89D8。

您可以通过 ndisasm

查看
00000000  8AC3              mov al,bl
00000002  88D8              mov al,bl
00000004  8BC3              mov ax,bx
00000006  89D8              mov ax,bx

不同编码的有趣副作用:来自 A86 手册。

  1. A86 takes advantage of situations in which more than one set of opcodes can be generated for the same instruction. (For example, MOV AX,BX can be generated using either an 89 or 8B opcode, by reversing fields in the following effective address byte. Both forms are absolutely identical in functionality and execution speed.) A86 adopts an unusual mix of choices in such situations. This creates a code-generation "footprint" that occupies no space in your program file, but will enable me to tell, and to demonstrate in a court of law, if a non-trivial object file has been produced by A86. The specification for this "footprint" is sufficiently obscure and complicated that it would be impossible to duplicate by accident. I claim exclusive rights to the particular "footprint" I have chosen, and prohibit anyone from duplicating it. This has at least two specific implications:

    a. Any assembler that duplicates the "footprint" is mine. If it is not identified as mine and issued under these terms, then those who sell or distribute the assembler will be subject to prosecution.

    b. Any program marked with the "footprint" has been produced by my assembler. It is subject to condition 5 above.