位移值对ModRegRm字节的Mod字段有什么影响?

What is the effect of the displacement value on the Mod field of the ModRegRm byte?

我正在编写一个 8086 assembler,它接受指令并生成 8086 机器代码。我用"Intel 8086 User Manual"作为参考。

为了说清楚,我会解释一下情况。假设我想要 assemble 这条指令 mov ax, bx。我会查手册发现当mov的操作数是2个16位寄存器时,mov的操作码是0x89并指定操作数(源和目标),在本例中,mov 后跟一个 ModRegRm 字节,指定源和目标,在本例中为 0xd8。这个二进制字节 = 11011000.

Mod为2位,Reg、Rm各为3位。所以,Mod = 11,Reg = 011,Rm = 000。 这里很简单,但有些东西我不明白,那就是寻址模式和位移。

查看 table 和以下三个指令及其机器代码。

mov [bx+0x6], ax ;894706

mov [bx+0xbf],ax ;8987BF00

mov [bx+0xffff],ax ;8947FF

我假设每条指令中的位移长度分别为8bit、8bit、16bit是不是错了?

我想我是对的,因为很明显,0x60xbf 是 1 个字节,0xffff 是两个字节。

问题来了,为什么第二条指令中的MOD字段是10b or 0x02而不是01b or 0x01?应该是0x01因为位移是8bit的位移对不对?为什么在第三条指令中 MOD 是 0x01 即使位移是 16 位?为什么 assembler 忽略了剩余的位移而只捕获了 1 个字节?

位移的大小取决于 "MOD" 字段(例如,如果 MOD=001b 则为 8 位,如果 MOD=010b 则为 16 位)并且符号扩展到预期尺寸。

这意味着像 mov [bx+6], ax 这样的指令可以编码为 mov [bx+0x0006], ax(具有 MOD=010b 和 16 位位移)或者它可以编码为 mov [bx+0x06], ax(具有 MOD=001b 和 8 位位移)。

同理,mov [bx+65535],ax可以任意编码(8位位移或16位位移);因为 0xFF 可以符号扩展为 0xFFFF。

但是; mov [bx+191],ax不能编码成8位位移,因为191(0xBF)符号扩展后变成0xFFBF,不等于191,必须用16位位移。

本质上;如果完整 16 位位移的最高 9 位都相同(对于值 0x0000 到 0x007F 全部清除,或者对于值 0xFF80 到 0xFFFF 全部设置)它可以被编码为 8 位位移或 16 位位移;否则它必须使用 16 位位移。

当可以选择不同的编码时;一个好的汇编程序会选择最小的可能性(并使用 8 位位移,因为它会使指令缩短 1 个字节)。一个更好的汇编器可能会使用更大的版本,如果它避免了填充的需要(如果下面的指令需要在某个边界上对齐)。例如考虑 .align 2 然后 mov [bx+6], ax 然后 .align 2 然后 clc - 对于较小的(3 字节)mov 你必须插入一个额外的 nop 指令作为 clc 之前的填充,以确保指令在 2 字节边界上对齐(由 align 2 指令请求),并且更大的(4 字节)mov 你不't(所以它少了 1 条指令,但结果代码的字节数相同)。