前面已经介绍了如何用加减指令实现算术表达式,现在还可以再加上乘法和除法指令。初看上去,实现算术表达式的工作似乎最好是留给编译器的编写者,但是动手研究一下还是能学到不少东西。

读者可以学习编译器怎样优化代码。此外,与典型编译器在乘法操作后检查乘积大小相比,还能实现更好的错误检查。进行 32 位操作数相乘时,绝大多数高级语言编译器都会忽略乘积的高 32 位。而在汇编语言中,可以用进位标志位和溢出标志位来说明乘积是否为 32 位。

有两种简单的方法可以查看 C++ 编译器生成的汇编代码:

一种方法是用 Visual Studio 调试时,在调试窗口中右键点击,选择 Go to Disassembly。

一种方法是,在 Project 菜单中选择 Properties,生成一个列表文件。在 Configuration Properties,选择 Microsoft Macro Assembler,再选择 Listing Fileo 在对话窗口中,将 Generate Preprocessed Source Listing 设置为 Yes,List All Available Information 也设置为 Yes。

【示例 1】使用 32 位无符号整数,用汇编语言实现下述 C++ 语句:

var4 = (var1 + var2) * var3;

这个问题很简单,因为可以从左到右来处理 (先加法再乘法)。执行了第二条指令后,EAX 存放的是 val1 与 var2 之和。第三条指令中,EAX 乘以 var3,乘积存放在 EAX 中:

mov eax, var1

add eax, var2

mul var3 ; EAX = EAX * var3

jc tooBig ;无符号溢出?

mov var4, eax

jmp next

tooBig: ;显示错误消息

如果 MUL 指令产生的乘积大于 32 位,则 JC 指令跳转到有标号指令来处理错误。

【示例 2】使用 32 位无符号整数实现下述 C++ 语句:

var4 = (var1 * 5) / (var2 - 3);

本例有两个用括号括起来的子表达式。左边的子表达式可以分配给 EDX:EAX,因此不必检查溢出。右边的子表达式分配给 EBX,最后用除法完成整个表达式:

mov eax, var1 ;左边的子表达式

mov ebx, 5

mul ebx ;EDX:EAX=乘积

mov ebx, var2 ;右边的子表达式

sub ebx, 3

div ebx ;最后的除法

mov var4, eax

【示例 3】使用 32 位有符号整数实现下述 C++ 语句:

var4 = (varl * -5) / (-var2 % var3);

与之前的例子相比,这个例子需要一些技巧。可以先从右边的表达式开始,并将其保存在 EBX 中。由于操作数是有符号的,因此必须将被除数符号扩展到 EDX,再使用 IDIV 指令:

mov eax,var2 ;开始计算右边的表达式

neg eax

cdq ;符号扩展被除数

idiv var3 ;EDX = 余数

mov ebx,edx ;EBX = 右边表达式的结果

第二步,计算左边的表达式,并将乘积保存在 EDX:EAX 中:

mov eax, -5 ;开始计算左边表达式

imul var1 ;EDX:EAX=左边表达式的结果

最后,左边表达式结果 (EDX:EAX) 除以右边表达式结果 (EBX):

idiv ebx ;最后计算除法

mov var4,eax ;商