1. 程式人生 > >特殊的算術操作指令

特殊的算術操作指令

們的 狀態 都是 過程 之前 mage 也會 接下來 例子

 我們先來看看這些指令的大致介紹,如果各位看過上一章的話,會發現這裏的指令有的會有些眼熟,但是它們的作用卻截然不同。以下是書中的一張概圖。

技術分享圖片

  第一個指令有些眼熟吧,它就是我們上一章當中的imul乘法指令的雙字形式。不過可以看出,這裏的imull指令已經完全變了味道,它將結果存入兩個寄存器。接下來,我們來仔細看看這些指令。

imull、mull指令

  這兩個指令一看就是雙胞胎,它們一個負責有符號全64位乘法,一個負責無符號全64位乘法。細心的猿友會發現,imull這個指令好像是負責乘法的指令,而且在之前的乘法並沒有區分有符號和無符號,現在怎麽又成雙胞胎指令了。

  我們上一章當中出現的指令是imul指令,當它操作雙字的時候,也就是imull指令。不過不同的是,它的一般形式是imull S D,這裏有兩個操作數,它將計算S和D的乘積並截斷為雙字,然後存儲在D當中。由於在截斷時,無符號以及有符號的二進制序列是一樣的,因此此處的乘法指令並不區分有符號和無符號。

  本次我們討論的imull指令,則與上面的普通乘法指令稍有不同,它只有一個操作數,也就是說,它的一般形式為imull S,這點在書中的表格中也能看出來,而另外一個操作數默認為%eax寄存器。最終的結果,會將高32位存入%edx寄存器,而低32位存入%eax寄存器。

  試想一下,如果我們只取%eax寄存器當中的32位結果,那其實這裏計算的結果就是S*%eax,此時imull S的作用就與imull S D是一樣的,只是目的操作數被固定為%eax罷了。

  接下來我們看一個簡單的示例,我們去看下指令imull $0x3的結果,我們假設此時%eax寄存器的值為0x82345600。也就是我們需要計算0x3*0x82345600的值,這裏LZ直接給出兩者相乘的16進制表示,各位有興趣的可以私下乘一下,為0xFFFF FFFE 869D 0200。這個結果為64位的,因此我們寄存器的前後狀態如下所示。

技術分享圖片

  可以看到,%eax保存著低32位的結果,單說這32位的話,它的有符號數值為-2036530688,正是我們直接計算0x3*0x82345600的32位截斷後的有符號值,顯然這個結果溢出了。如果組合上高32位,則結果為-6331497984,將它加上或者取模4294967296(2的32次方)將得到我們32位的結果。這裏的有符號乘法采取的是先符號擴展被乘數,然後兩者相乘,將結果再截斷為64位所得。

  對於mull的單操作數指令來講,就比較簡單了,它采用的是無符號乘法,因此就和我們平時的十進制乘法運算類似,只是同樣的,它也會將結果的高32位存入%edx,將低32位存入%eax。

cltd指令

  這個指令相對來說就非常簡單了,它就是簡單的將%eax寄存器的值符號擴展32位到%edx寄存器,也就是說,如果%eax寄存器的二進制序列的最高位為0,則cltd指令將把%edx置為32個0,相反,如果%eax寄存器的二進制序列最高位為1,則cltd指令將會自從填充%edx寄存器為32個1。

idivl、divl指令

  這兩個指令與前面的imull以及mull類似,它也將計算結果存放在兩個寄存器當中,其中余數存放在%edx寄存器,商存放在%eax寄存器。如果各位理解了前面的imull以及mull,那麽這裏idivl和divl理解起來會非常簡單。

  這裏LZ舉一個簡單的例子,考慮指令idivl $0x3的結果,我們假設此時%eax寄存器的值為0x82345600。也就是我們需要計算0x82345600/0x3的值,這裏LZ直接給出兩者相除的16進制表示,各位有興趣的也可以私下除一下,商為0xD6117200,余數為0x0。因此我們寄存器的前後狀態如下所示。

技術分享圖片

  可以看到,在idivl這個指令執行的過程中,其實對被除數進行了符號擴展,類似於cltd指令,或者有時也會將%eax移動到%edx,然後對%edx進行算術右移31位的運算。這兩種方式的結果是一樣的,都是將%eax符號擴展32位並存儲在%edx當中。

轉自:


作者:zuoxiaolong(左瀟龍)

出處:博客園左瀟龍的技術博客--http://www.cnblogs.com/zuoxiaolong

特殊的算術操作指令