英特尔自己的 PDF 手册详细记录了这一点;看SDM 第 2 卷,特别是每条指令条目之前的介绍章节。
各个网站上也有详细的说明,例如https://wiki.osdev.org/X86-64_Instruction_Encoding#ModR.2FM_and_SIB_bytes(涵盖 16 位 ModRM,因此它不仅仅是谈论 x86-64 长模式。)现代 x86 使用与 8086 相同的指令编码(在 16 位实模式下);向后兼容是 x86 的全部要点,也是它如此令人讨厌的原因。
当然,您可以找到实际 8086 手册本身的 PDF 副本,以防省略仅与其他模式相关的内容更有帮助。
8086 入门书从第 23 页开始介绍操作数的指令编码。它被写成一本书,而不仅仅是一本技术手册。它可以在 Stephen Morse 的网站上免费获得(https://stevemorse.org/8086/),他在英特尔时设计了它。
但也许它会有助于描述 ModRM 用途的基本概述,以便您知道在这些文档中寻找什么。
ModR/M 目的和基础知识
大多数(但不是全部)x86 指令都有一个 ModRM 字节。它可以对 2 个操作数进行编码,最多其中之一是内存,或者两个操作数都是寄存器。例如add cx, ax
, or add cx, [bx+si]
.
操作码本身确定 r/m 和 r 操作数中哪一个是源和/或目标,或者是否是/r
字段充当额外的操作码位。 (例如,对于移位,这就是为什么它们不能复制和移位,或使用 CL 以外的计数寄存器。)add [bx+si], cx
具有相同的 ModRM 字节add cx, [bx+si]
但操作码不同。
仅寄存器操作数由 3 位 /r 字段编码。 3 位可以为 x86 的 8 个通用寄存器中的任何一个进行编码。这是一个“寄存器号”,就像任何具有 2^n 个寄存器的普通 ISA 一样,每个指令代码中的 n 位组用于寄存器操作数。
The r/m
操作数也可以是寄存器,但2位“模式”字段决定3位r/m字段是寄存器号(mod=0b11)还是存储器寻址模式。 (再加上 8 位或 16 位位移,因此 disp0/8/16 的编码会占用模式字段的其他 3 种编码。)
https://wiki.osdev.org/X86-64_Instruction_Encoding#ModR.2FM_and_SIB_bytes显示 16 位地址大小的字段和解释,包括寄存器编号。
因此只有 3 位来指定一个寄存器或寄存器组合的内存地址。 386 为 SIB 字节添加了转义码,允许完全选择寻址模式,例如[eax + ecx*4]
,但 8086(以及任何 CPU 上的 16 位地址大小)必须是[BX|BP] + [SI|DI] + disp0/8/16
.
See 8086 中通用寄存器之间的差异:[bx] 有效,[cx] 无效? / 为什么 x86 16 位寻址模式没有比例因子,而 32 位版本有?
组装示例foo.asm
进而ndisasm -b16 foo
,或者要求 NASM 本身列出清单nasm -l/dev/stdout foo.asm
。然后进行编辑以简化输出字段。
00 00 add [bx+si],al ; opcode=0x00 (add byte, mem dst) mod=00 r=000 r/m=000
01 C0 add ax, ax ; add r/m, r mod=11 (register) r=000 (AX) r/m=0 (AX)
01 08 add [bx+si], cx ; add r/m, r
03 08 add cx, [bx+si] ; mod=0, r=001 (CX) r/m=000 ([bx+si])
03 0F add cx, [bx] ; mod=00 r=001 (CX) r/m=111 ([BX])
03 4F 04 add cx, [bx + 4] ; mod=01 r=001 (CX) r/m=111 disp8=4
01 F2 add dx, si ; mod=11 r=110 (SI) r/m=010 (DX)
要创建更多示例,请使用汇编器自行创建机器代码。
See also
- 为什么 x86 16 位寻址模式没有比例因子,而 32 位版本有?
-
rbp 不允许作为 SIB 基础?- 意味着 [bp] 的编码实际上意味着没有基址寄存器,只是位移。
-
Intel x86 Assembly 文档中的 [--][--] 是什么意思?(32/64 位模式 ModR/M 和 SIB)。
-
如何阅读英特尔操作码符号当。。。的时候
/r
ModRM 的字段用作额外的操作码位,例如FF /2
CALL r/m16 调用近端,绝对间接。