# 四、寻址模式

指令可以采用的不同类型的参数称为寻址模式。这个术语不能与内存中的地址混淆。寻址模式包括寻址存储器和寄存器的方法。寻址模式由中央处理器和汇编程序定义。它们是程序员可以用来寻址操作数的方法。

### 寄存器寻址模式

寄存器寻址模式非常简单。可以使用任何 x86 寄存器。

```asm
    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 |

| ![](img/note.png) | 注意:用十六进制描述数字时，如果数字以字母数字开头(最左边的数字是 A、B、C、D、E 或 F)，那么必须在它前面加一个零；“ffh”必须是“0ffh”。这不会改变操作数的大小。 |

除了使用数字之外，还可以使用数学表达式，只要它们的计算结果是一个常数。数学表达式在运行时不会被中央处理器计算，但是 MASM 会在汇编之前将它们转换成常数值。

```asm
    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，尽管它们没有参数。这种用法称为隐含寻址模式；参数是由指令本身隐含的，不会出现在代码中。

```asm
    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，有多种方法可以参考内存。它们本质上都做同样的事情；它们从随机存取存储器中的某个地址读取或写入数据。内存寻址模式的最基本用法是使用在数据段中按名称定义的变量。

```asm
    .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
    .

```

| ![](img/note.png) | 注意:因为数据段中定义的标签实际上是指针，所以有些人倾向于不称它们为变量，而称它们为指针或标签。示例代码中“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 |

| ![](img/note.png) | 注意:这里有符号、无符号或浮点与整数无关。有符号的字有两个字节长，就像无符号的字有两个字节长一样。这些前缀只是告诉 MASM 以字节为单位的数据量；他们不需要更清楚地说明。例如，要移动一个双精度(64 位浮点)字，可以使用 qword ptr，因为 8 字节是一个四字节字，数据恰好是一个实数 8 也没关系。您也可以使用 real8 来移动这些数据。 |

除了使用数据段中定义的简单变量，您还可以使用寄存器作为指针。

```asm
    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

```

您也可以在方括号中一起添加两个寄存器。这允许单个基寄存器指向数组的第一个元素，第二个偏移指针遍历数组。你也可以使用一个寄存器，并从中增加或减少一些文字值。

| ![](img/note.png) | 注意:从寄存器中添加或减去的值可以是复杂的表达式，只要它们计算为常数。MASM 将在汇编文件之前计算表达式。 |

```asm
    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

```

| ![](img/note.png) | 注意:每当通过使用文字数字或寄存器将值减或加到地址时，被加或减的量总是代表字节数。汇编不像 C++ 那样有指针算法。程序集中的所有指针一次递增和递减一个字节，而在 C++ 中，整数指针一次自动递增和递减 4 个字节。 |

所有内存寻址模式中最灵活的可能是 SIB(比例、索引、基数)内存寻址模式。这包括一个指向某个数组开头的基寄存器、一个用作数组偏移量的索引寄存器，以及一个可以是 1、2、4 或 8 的比例乘法器，用于乘以索引所保存的值，以正确访问不同大小的元素。

```asm
    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++ 的方式遍历数组非常有用。将基寄存器设置为数组的第一个元素，然后递增索引寄存器，并将比例设置为数组元素的数据大小。

```asm
    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

```