汇编语言USES运算符:保存和恢复寄存器


在上一节《整数数组求和》的 ArraySum 示例中,ECX 和 ESI 在过程开始时被压入堆栈,在过程结束时被弹出堆栈。这是大多数过程修改寄存器的典型操作。总是保存和恢复被过程修改的寄存器,将使得调用程序确保自己的寄存器值不会被覆盖。但是对用于返回数值的寄存器应该例外,通常是指 EAX,不要将它们压入和弹出堆栈。

USES 运算符

USES 运算符与 PROC 伪指令一起使用,让程序员列出在该过程中修改的所有寄存器名。USES 告诉汇编器做两件事情:第一,在过程开始时生成 PUSH 指令,将寄存器保存到堆栈;第二,在过程结束时生成 POP 指令,从堆栈恢复寄存器的值。

USES 运算符紧跟在 PROC 之后,其后是位于同一行上的寄存器列表,表项之间用空格符或制表符(不是逗号)分隔。

在 ArraySum 过程使用 PUSH 和 POP 指令来保存和恢复 ESI 和 ECX。 USES 运算符能够更加容易地实现同样的功能:
ArraySum PROC USES esi ecx
  mov eax, 0                   ;置和数为0
L1:
  add eax,[esi]                ;将每个整数与和数相加
  add esi, TYPE DWORD          ;指向下个整数
  loop L1                      ;按照数组大小重复
  ret                          ;和数在 EAX 中
ArraySum ENDP
汇编器生成的相应代码展示了使用 USES 的效果:
ArraySum PROC
  push esi
  push ecx
  mov eax, 0                      ;置和数为0
L1:
  add eax, [esi]                  ;将每个整数与和数相加
  add esi, TYPE DWORD             ;指向下一个整数
  loop L1                         ;按照数组大小重复
  pop ecx
  pop esi
  ret
ArraySum ENDP

调试提示:使用 Microsoft Visual Studio 调试器可以查看由 MASM 高级运算符和伪指令生成的隐藏机器指令。在调试窗口中右键点击,选择 Go To Disassembly。该窗口显示程序源代码,以及由汇编器生成的隐藏机器指令。

当过程利用寄存器(通常用 EAX)返回数值时,保存使用寄存器的惯例就岀现了一个重要的例外。在这种情况下,返回寄存器不能被压入和弹出堆栈。例如下述 SumOf 过程把 EAX 压入、弹出堆栈,就会丢失过程的返回值:
SumOf PROC                             ;三个整数之和
  push eax                             ;保存EAX
  add eax, ebx
  add eax, ecx                         ;计算EAX、EBX和ECX之和
  pop eax                              ;和数丢失!
  ret
SumOf ENDP