ULONGLONG乘法分析

From: http://blog.csdn.net/syf442/article/details/6077715

64位乘法分析:

VC6 Console Code:

 

int main(int argc, char* argv[])

{

      ULONGLONG    a = 0x87654321;

      ULONGLONG    b = 0x100000001;

      a*=b;

      //::MessageBox(NULL,"Content","Title",MB_OK);

      return 0;

}

 

Debug模式下汇编Code:

9:        ULONGLONG   a = 0x87654321;

00401028   mov         dword ptr [ebp-8],87654321h

0040102F   mov         dword ptr [ebp-4],0

10:       ULONGLONG   b = 0x100000001;

00401036   mov         dword ptr [ebp-10h],1

0040103D   mov         dword ptr [ebp-0Ch],1

11:       a*=b;

00401044   mov         eax,dword ptr [ebp-0Ch]

00401047   push        eax     ;压入b高4 bytes

00401048   mov         ecx,dword ptr [ebp-10h]

0040104B   push        ecx     ;压入b低4 bytes

0040104C   mov         edx,dword ptr [ebp-4]

0040104F   push        edx     ;压入a高4 bytes

00401050   mov         eax,dword ptr [ebp-8]

00401053   push        eax     ;压入a低4 bytes

00401054   call        _allmul (004010d0)   ; _allmul计算64位乘法

00401059   mov         dword ptr [ebp-8],eax

0040105C   mov         dword ptr [ebp-4],edx

12:

13:   //  ::MessageBox(NULL,"Content","Title",MB_OK);

14:       return 0;

0040105F   xor         eax,eax

 

_allmul:

 

--- intel/llmul.asm  -------------------------------------------------

_allmul:

004010D0   mov     eax,dword ptr [esp+8]   ;取a高4字节

004010D4   mov     ecx,dword ptr [esp+10h] ;取b高4字节

004010D8   or       ecx,eax  ; 当ecx==0 && eax == 0时, go on

004010DA   mov     ecx,dword ptr [esp+0Ch] ;取 b低4字节

004010DE   jne      hard (004010e9)

004010E0   mov     eax,dword ptr [esp+4]

004010E4   mul      eax,ecx

004010E6   ret       10h

hard:

004010E9   push     ebx      ;保存ebx,使原栈内多偏移了4个字节

004010EA   mul      eax,ecx  ; a高4字节*b低4字节

004010EC   mov     ebx,eax  ;只保存乘法结果的低4字节到ebx

004010EE   mov     eax,dword ptr [esp+8] ;取a低4字节,因为push ebx,所以a在栈中多偏移4字节。

004010F2   mul      eax,dword ptr [esp+14h] ;a低4字节*b高4字节

004010F6   add      ebx,eax   ;将两次乘法低4位结果保存到ebx

004010F8   mov     eax,dword ptr [esp+8]  ;a低4字节

004010FC   mul      eax,ecx   ;a低4字节*b低4字节

004010FE   add      edx,ebx   ;加上之前的运算结果低4字节ebx。

00401100   pop      ebx

00401101   ret       10h

 

代码详解:

      

004010D8   or       ecx,eax  ; 当ecx==0 && eax == 0时, go on

004010DA   mov     ecx,dword ptr [esp+0Ch] ;取 b低4字节

004010DE   jne      hard (004010e9)

 

● or   ecx,eax 该条指令判断了ecx与eax是否同时为0。即仅当

eax==0 && ecx==0时,or ecx,eax的运算结果ecx才==0,此时才会置标志位ZF=1。否则ZF=0。所以,与后面的jne实现逻辑:

if(!(eax==0&&ecx==0))

jmp hard (004010e9);

      所以当   ecx==0 && eax==0,即a和b的高4字节都为0 时,此时a*b即为进行普通的32位寄存器运算,运算结果高4字节放在edx,低4字节放在eax。

or后紧跟mov 是为了保存 b低4字节到ecx,且因为mov等传送指令不会影响标志寄存器。

跳到hard标号处则是真正的“64位”乘法运算了:

 

      算法很简单,即是小学乘法展开公式:(a+b)*(c+d) = a*c+a*d+b*c+b*d。

 

我们令aHigh4B为a高4字节,aLow4B为a低4字节,则得:

a*b = (aHigh4B<<32+aLow4B)*( bHigh4B<<32+bLow4B)

      左移32位,即左移4字节。由于(aHigh4B<<32)* (bHigh4B<<32)即该式结果等于(aHigh4B* bHigh4B)<<64,所以肯定会溢出,所以该结果忽略。为什么忽略?因为a*b运算的最终结果要保存为64位,所以忽略超出64位的值。于是该式拆分得:

 

      (aHigh4B<<32 )* bHigh4B + aLow4B*( bHigh4B<<32) + aLow4B* bLow4

 

(aHigh4B<<32 )* bHigh4B对应指令为:

004010EA   mul      eax,ecx  ; a高4字节*b低4字节

004010EC   mov     ebx,eax  ;只保存乘法结果的低4字节到ebx

     

mul运算后,edx为结果高4字节,eax为结果低4字节。但为什么只保存eax低4字节的结果?而不保存edx高4字节的结果?

      因为(eax*ecx)<<32 = (edx:eax)<<32。所以真实结果的高4字节edx左移32位再次溢出了64位,所以只需保存eax即可。之后将两次左移32位的乘法操作结果低4字节用ebx累积下来,再加上两个低4字节的乘法高位结果,即为最终运算结果的高4字节edx。

 

你可能感兴趣的:(ULONGLONG乘法分析)