前面已经介绍了如何用加减指令实现算术表达式,现在还可以再加上乘法和除法指令。初看上去,实现算术表达式的工作似乎最好是留给编译器的编写者,但是动手研究一下还是能学到不少东西。
读者可以学习编译器怎样优化代码。此外,与典型编译器在乘法操作后检查乘积大小相比,还能实现更好的错误检查。进行 32 位操作数相乘时,绝大多数高级语言编译器都会忽略乘积的高 32 位。而在汇编语言中,可以用进位标志位和溢出标志位来说明乘积是否为 32 位。
有两种简单的方法可以查看 C++ 编译器生成的汇编代码:
【示例 1】使用 32 位无符号整数,用汇编语言实现下述 C++ 语句:
这个问题很简单,因为可以从左到右来处理 (先加法再乘法)。执行了第二条指令后,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++ 语句:
本例有两个用括号括起来的子表达式。左边的子表达式可以分配给 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++ 语句:
与之前的例子相比,这个例子需要一些技巧。可以先从右边的表达式开始,并将其保存在 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 ;商