指令可以采用的不同类型的参数称为寻址模式。这个术语不能与内存中的地址混淆。寻址模式包括寻址存储器和寄存器的方法。寻址模式由中央处理器和汇编程序定义。它们是程序员可以用来寻址操作数的方法。
寄存器寻址模式非常简单。可以使用任何 x86 寄存器。
mov eax, ebx ; EAX and EBX are both registers
add rcx, rdx ; RCX and RDX are 64-bit registers
sub al, bl ; AL and BL are the low 8-bit registers of RAX and RBX
立即或文字寻址模式是文字数字作为指令的参数出现,例如mov eax, 128
,其中128
是文字或立即值。MASM 在几个不同的基础上理解字面数字。
表 5:共同基础
| 基础 | 名字 | 后缀 | 数字 | 例子 | | Two | 二进制的 | b | 0 和 1 | 1001b | | eight | 八进制的 | o | 0 到 7 | 77723o | | Ten | 小数 | d 或无 | 0 到 9 | 1893 或 235d | | Sixteen | 十六进制的 | h | 0 至 F | 783ffh 或 0fch |
| | 注意:用十六进制描述数字时,如果数字以字母数字开头(最左边的数字是 A、B、C、D、E 或 F),那么必须在它前面加一个零;“ffh”必须是“0ffh”。这不会改变操作数的大小。 |
除了使用数字之外,还可以使用数学表达式,只要它们的计算结果是一个常数。数学表达式在运行时不会被中央处理器计算,但是 MASM 会在汇编之前将它们转换成常数值。
mov rax, 29+23 ; This is fine, will become mov rax, 52
mov rcx, 32/(19-4); Evaluates to 2, so MASM will translate to mov rax, 2
mov rdx, rbx*82 ; rbx*82 is not constant, this statement will not work
许多指令操作寄存器或寄存器指向的存储器的某个部分,即使寄存器或存储器地址不作为参数出现。比如字符串指令(MOVSxx
、SCASxx
、LODSxx
等。)参考内存、RAX、RCX、RSI 和 RDI,尽管它们没有参数。这种用法称为隐含寻址模式;参数是由指令本身隐含的,不会出现在代码中。
REP SCASB ; Scan string at [RDI] for AL and scan the number of bytes in RCX
CPUID ; CPUID takes EAX as input and outputs to EAX, EBX, ECX, and EDX
在 MASM,有多种方法可以参考内存。它们本质上都做同样的事情;它们从随机存取存储器中的某个地址读取或写入数据。内存寻址模式的最基本用法是使用在数据段中按名称定义的变量。
.data
xyzVar db ? ; Define some variable in the data segment
.code
SomeFunction proc
mov al, xyzVar ; Move *xyzVar, the value of xyzVar, into AL
.
. Code continues
.
| | 注意:因为数据段中定义的标签实际上是指针,所以有些人倾向于不称它们为变量,而称它们为指针或标签。示例代码中“xyzVar”的用法实际上类似于“mov al,byte ptr [xyzVar]”,其中 xyzVar 是文字地址。 |
经常需要告诉 MASM 内存操作数的大小,以便它知道要生成什么样的机器代码。例如,有许多MOV
指令:有一个移动字节,一个移动字,另一个移动字。它们都使用相同的MOV
助记符,但是它们生成完全不同的机器代码,并且中央处理器为它们中的每一个做不同的事情。
这些前缀可以放在方括号的左边。大小前缀如下。
表 6:指针大小前缀
| 字节大小 | 前缀 | | one | plyash p | | Two | 单词 ptr | | four | S7-1200 可编程控制器 | | eight | qword ptr | | Ten | real10 ptr | | Sixteen | xmmword ptr 函数 | | Thirty-two | 西姆文德 ptr |
| | 注意:这里有符号、无符号或浮点与整数无关。有符号的字有两个字节长,就像无符号的字有两个字节长一样。这些前缀只是告诉 MASM 以字节为单位的数据量;他们不需要更清楚地说明。例如,要移动一个双精度(64 位浮点)字,可以使用 qword ptr,因为 8 字节是一个四字节字,数据恰好是一个实数 8 也没关系。您也可以使用 real8 来移动这些数据。 |
除了使用数据段中定义的简单变量,您还可以使用寄存器作为指针。
mov eax, dword ptr [rcx]; Move 4 bytes starting where RCX is pointing
mov bl, byte ptr [r8] ; Move a byte from *R8 into BL
add dx, word ptr [rax] ; Add the word at *RAX to the value in DX
您也可以在方括号中一起添加两个寄存器。这允许单个基寄存器指向数组的第一个元素,第二个偏移指针遍历数组。你也可以使用一个寄存器,并从中增加或减少一些文字值。
| | 注意:从寄存器中添加或减去的值可以是复杂的表达式,只要它们计算为常数。MASM 将在汇编文件之前计算表达式。 |
sub rbx, qword ptr [rcx+rax] ; Perhaps the base is RCX and RAX is an offset
add dword ptr [r8+68], r9d ; Here we have added a constant to r8
add dword ptr [r8-51], r9d ; Here we have subtracted a constant from r8
| | 注意:每当通过使用文字数字或寄存器将值减或加到地址时,被加或减的量总是代表字节数。汇编不像 C++ 那样有指针算法。程序集中的所有指针一次递增和递减一个字节,而在 C++ 中,整数指针一次自动递增和递减 4 个字节。 |
所有内存寻址模式中最灵活的可能是 SIB(比例、索引、基数)内存寻址模式。这包括一个指向某个数组开头的基寄存器、一个用作数组偏移量的索引寄存器,以及一个可以是 1、2、4 或 8 的比例乘法器,用于乘以索引所保存的值,以正确访问不同大小的元素。
mov bx, byte ptr [rcx+rdx*2] ; RCX is the base, RDX is an offset and we
; are using words so the scale is 2
add qword ptr [rax+rcx*8], r12 ; RAX is the base, RCX is the index
; and we are referencing qwords so the
; scale is 8
这种寻址模式对于以类似于 C++ 的方式遍历数组非常有用。将基寄存器设置为数组的第一个元素,然后递增索引寄存器,并将比例设置为数组元素的数据大小。
mov rax, qword ptr [rcx+rbx*8]; Traverse a qword array at *RCX with RBX
mov cx, word ptr [rax+r8*2] ; Traverse a word array at *RAX with R8