当提到MODR/M字节时,8086文档站点似乎有点模糊,很难理解它是什么和做什么。
MODR/M字节中使用的所有位是什么,可能的选项是什么?
我找到了一些文档:https://www.scs.stanford.edu/05au-cs240c/lab/i386/s17_02.htm
The ModR/M byte contains three fields of information:
The mod field, which occupies the two most significant bits of the byte, combines with the r/m field to form 32 possible values: eight registers and 24 indexing modes
The reg field, which occupies the next three bits following the mod field, specifies either a register number or three more bits of opcode information. The meaning of the reg field is determined by the first (opcode) byte of the instruction.
The r/m field, which occupies the three least significant bits of the byte, can specify a register as the location of an operand, or can form part of the addressing-mode encoding in combination with the field as described above
什么是索引模式?什么是登记号码?寄存器是如何表示的?等。
英特尔自己的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副本,以防省略只与其他模式相关的内容更有帮助。
从第23页开始的8086入门介绍了操作数的指令编码。这是一本书,而不仅仅是一本技术手册。可以在Stephen Morse的网站(https://stevemorse.org/8086/)上免费获得,他是在英特尔工作时设计的。
但是也许它会帮助描述ModRM目的的基本概述,这样你就知道在这些文档中寻找什么。
ModR/M用途和基础
大多数(但不是全部)x86指令有一个ModRM字节。它可以为两个操作数编码,最多一个是内存,或者两个寄存器。例如:add cx, ax
,或add cx, [bx+si]
。
操作码本身决定r/m和r操作数中哪个是源和/或目的,或者/r
字段是否作为额外的操作码位。(例如,对于移位,这就是为什么他们不能复制和移位,或者使用除CL以外的计数寄存器。)add [bx+si], cx
具有与add cx, [bx+si]
相同的ModRM字节,但操作码不同。
仅限寄存器的操作数由3位/r字段编码。3位可以为x86的8个通用寄存器中的任何一个编码。这是一个"寄存器号",就像在任何具有2^n个寄存器的普通ISA中一样,每个指令代码中有n位的一组用于寄存器操作数。
r/m
操作数也可以是寄存器,但是2位模式是寄存器。字段确定3位r/m字段是寄存器号(mod=0b11)还是内存寻址模式。(加上一个8位或16位的位移,所以编码一个dis0/8/16会占用mode字段的其他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
的某个子集。
参见8086中通用寄存器之间的差异:[bx]工作,[cx]不工作't?/为什么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)
要创建更多示例,请使用汇编程序自己创建机器码。
参见
- 为什么x86 16位寻址模式没有缩放因子,而32位版本有?
- rbp不允许作为SIB基地?-编码意味着[bp]实际上意味着没有基寄存器,只是位移。
- 在Intel x86 Assembly文档中[——][——]是什么意思?(32/64位模式ModR/M和SIB). 当ModRM的
/r
字段被用作额外的操作码位时,如FF /2
CALL r/m16 CALL near, absolute indirect.