位移值对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是不是错了?
我想我是对的,因为很明显,0x6
和 0xbf
是 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 条指令,但结果代码的字节数相同)。
我正在编写一个 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是不是错了?
我想我是对的,因为很明显,0x6
和 0xbf
是 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 条指令,但结果代码的字节数相同)。