mov al, [10] ; a0 0a 00
mov ah, [10] ; 8a 26 0a 00
使用 NASM 组装上述 8086 汇编代码后,我注意到生成的机器代码存在长度差异(如上面的注释所示,第一个是 3 个字节,另一个是 4 个字节)。两条指令如此相似,为什么会出现这种差异?
从8086手册来看,第一条指令似乎是使用内存到累加器编码来组装的,第二条指令是使用内存到寄存器编码来编码的。
我是汇编新手,所以我确信我遗漏了一些东西,但是为什么第二条指令需要用 mem 编码到 reg 而不是 mem 到累加器? AH不还是累加器寄存器吗?
AH 不是 8 位版本的“累加器”,而是 AL。
没有 ModRM 字节的特殊情况编码需要操作码来暗示寄存器操作数。 对于方向和操作数大小(加载/存储、8 位和 16 位)的每个组合,只有一个这样的操作码,因此没有剩余位来编码 AH 与 AL。
a0
操作码意味着目标寄存器是AL。 没有暗示 AH 并采用 16 位绝对地址的操作码,因此实际上唯一的选择是 8 位操作数大小的操作码mov r, r/m
。
8位累加器也是如此,立即运算
test al, imm8
和sub al, imm8
为2字节,仅针对AL进行特殊编码。xchg
reg 上的操作码字节与累加器 (AX))
英特尔当前的手册仍然适用于 16 位模式,并且是可搜索的文本,而不仅仅是倾斜的图片:P https://www.felixcloutier.com/x86/mov 是 PDF 的 HTML 抓取SDM 第 2 卷中的 MOV 条目。
令人困惑的是,
moffs8
是带有绝对地址的8位操作数大小;地址大小是该模式的默认大小,而不是 8 位。 所以相关条目是A0 MOV AL, moffs8
在 386 及更高版本上,
66
字节是操作数大小前缀,可与 16 位操作数大小操作码一起使用以使操作数大小为 32 位。 如果模式的默认值是 32 位,则相反。 并且 67
字节使地址大小与模式的默认大小相反,例如如果处于 16 位模式,则为 32 位地址;如果处于 32 位模式,则为 16。 不管怎样,要点是你会看到 MOV AX, moffs16
和 MOV EAX, moffs32
具有相同的 A1
操作码,并且取决于你正在组装的模式(bits 16
与 bits 32
或 bits 64
),汇编器将使用 66h
前缀字节或不使用所需的操作数大小。
知道,英特尔当前的手册应该是可读的,并且它使用特定的寄存器名称,而不仅仅是“累加器”,因为它具有带有确切操作码字节的单独条目,而不是在表中留下通配符
w
位。 这使得阅读起来更加臃肿,并且对于查看模式的帮助较小(例如宽度和方向通常是低 2 位),但确实消除了“累加器”等术语含义的任何歧义。