《Intel 汇编语言程序设计》第七章的一道编程题
如果乘积不超过32位这个过程很容易写。
但是如果要实现mul一样的功能(即乘积高32位放到edx中)就麻烦了,
假设被乘数eax为10h,乘数为12345678h,当shl eax,28的时候值溢出了,我们怎样把这个溢出了的数值弄到edx去呢?如果eax为100h,那么逻辑左移24位就溢出,这个移位数也不好控制。
下面是我的代码:
===================================
;参数 :ebx为乘数,eax为被乘数
;返回值:edx为结果高32位,eax为结果低32位。
push ebx
push ecx
push esi
push edi
mov edi,ebx
mov esi,eax
mov eax,0
mov edx,0
mov bl,0
mov ecx,32
clc
L1:
push ecx
shr edi,1
.if Carry?
mov cl,bl
push esi
shl esi,cl
add eax,esi
adc edx,0
pop esi
.endif
inc bl
pop ecx
loop L1
pop edi
pop esi
pop ecx
pop ebx
ret
===================================
------解决方案--------------------------------------------------------
假设被乘数eax为10h,乘数为12345678h,当shl eax,28的时候值溢出了,我们怎样把这个溢出了的数值弄到edx去呢?如果eax为100h,那么逻辑左移24位就溢出,这个移位数也不好控制。
看开码不如看算法,所以我提供算法,如下:
1.首先,谁乘谁都是一样的,所以我们用12345678H作为被乘数,用10H作为乘数,这样做可以减少移位次数。
2.因为要使用EDX,所以先对其进行清零操作。
3.乘数12345678H,显然是32位,放入EAX,被乘数10H即十进制数16,二进制数是10000B,占用5个二进制位。把4放入CX中。用CX控制循环次数,每次移动一个二进制位,用带进位的非循环左移指令,左移EAX一次,再用进带进位的非循环左移移动EDX一次,如此循环5次就可以完成12345678H*10H这个计算。
特别说明:如果乘数是100H,也就是十进制数的256,折成二进制位是8位,因此只要移动8次就可以了。我们移动的时候要根据乘数计算要移动的位数,要找一个算法计算出要移位的位数。如果要移位的位数是2的n次幂,则很容易计算,用循环右移,移到目标源操作数为1为止,如果乘数是6,也就是说,左移两位不够,左移3位又移多了,我们就要将6拆分成4和2,左移2位,和左移1位,然后采用相加的办法。
------解决方案--------------------------------------------------------
64位乘法啊,也来一段代码(变量使用伪码表示,稍后解释):
_allmul PROC NEAR
mov eax,HIDOWRD(A)
mov ecx,HIDOWRD(B)
or ecx,eax ;检测高32位是否为0
mov ecx,LODWORD(B)
jnz SHORT hard ;为0则只需要进行32位乘法
mov eax,LOWDWORD(A)
mul ecx
ret
hard:
push ebx
mul ecx ;高32位相乘
mov ebx,eax ;保存结果
mov eax,LOWDWORD(A2)
mul DWORD PTR HIDWORD(B2)
add ebx,eax ;ebx=((A的低32位*B的高32位)+(A的高32位+B的低32位))
mov eax,LOWDWORD(A2) ;ecx=B的低32位
mul ecx ;edx:eax=A的低32位*B的低32位
add edx,ebx ;edx即为所有高低位之和
pop ebx
ret
_allmul ENDP
HIDWORD(A)代表被乘数A的高32位LOWDOWRD(B)代表乘数B的低32位,其他以此类推。
这段代码反汇编自Microsoft C run-time library,也是编译器处理64位乘法的标准方案。
------解决方案--------------------------------------------------------
这样写代码可能简单一点:
LOCALS @@
SMUL PROC NEAR
PUSH ESI
PUSH EDI
PUSH 0
PUSH 0
MOV ECX,31
@@1:
MOV EDI,1
SHL EDI,CL
TEST EDX,EDI
JZ @@2
XOR ESI,ESI
MOV EDI,EAX
SHLD ESI,EDI,CL
SHL EDI,CL
ADD [ESP],EDI
ADC [ESP+4],ESI
@@2:
LOOP @@1
POP EAX
POP EDX
POP EDI
POP ESI
RET
SMUL ENDP
输入:EAX-被乘数,EDX-乘数
输出:EDX:EAX-64位结果,高32位在EDX中