跳轉到內容

x86 彙編/算術

來自華夏公益教科書,自由的教科書

所有算術指令都在(一個)ALU中執行。 ALU 只能執行整數運算,對於浮點運算指令,請參見“浮點”章節

基本運算

[編輯 | 編輯原始碼]

算術指令有兩個運算元:目標和源。

  • 目標必須是暫存器或記憶體位置。
  • 可以是記憶體位置、暫存器或常數值。

注意,最多隻有一個運算元可以是記憶體位置。

加法和減法

[編輯 | 編輯原始碼]
add addend, destination GAS 語法
add destination, addend Intel 語法

這將 addend 新增到 destination,並將結果儲存在 destination 中。


sub subtrahend, destination GAS 語法
sub destination, subtrahend Intel 語法

add 相似,只是它從 destination 中減去 subtrahend。 在 C 中:destination -= subtrahend;

無符號乘法

[編輯 | 編輯原始碼]

mul multiplicand

這將 multiplicand 乘以累加器中相應位元組長度的值。

multiplicand 的寬度 1 位元組 2 位元組 4 位元組 8 位元組
相應的 multiplier AL AX EAX RAX
product 高位部分儲存在 AH DX EDX RDX
product 低位部分儲存在 AL AX EAX RAX
mul 使用的結果暫存器

在第二種情況下,目標不是 EAX,這是為了與為舊處理器編寫的程式碼向後相容。

受影響的標誌是

  • OF ≔ product 的高位部分 ≠ 0
  • CF ≔ product 的高位部分 ≠ 0

所有其他標誌都未定義。

有符號乘法

[編輯 | 編輯原始碼]

imul multiplicand

此指令幾乎與 mul 相同,但它對符號位(MSB)的處理不同。

imul 指令還接受其他兩種格式


imul multiplicand, destination GAS 語法
imul destination, multiplicand Intel 語法

這將 destination 乘以 multiplicand,並將結果(乘積)放入 destination 中。


imul multiplicand, multiplier, product GAS 語法
imul product, multiplier, multiplicand Intel 語法

這將 multiplier 乘以 multiplicand 並將其放入 product 中。

div divisor

這將被除數暫存器中的值除以 divisor,見下表。


idiv arg

div 相同,只是有符號。

divisor 的寬度 1 位元組 2 位元組 4 位元組 8 位元組
被除數 AX DX AX EDX EAX RDX RAX
remainder 儲存在 AH DX EDX RDX
quotient 儲存在 AL AX EAX RAX
div 的結果暫存器

圓圈()表示串聯。 對於除數大小為 4,這意味著 EDX 是輸入數字的位 32-63,EAX 是位 0-31(低位數字的權重較低,在本例中)。

由於通常對於有符號除法,輸入值通常是 32 位或 64 位,因此您通常需要使用CDQCQO 在除法之前將 EAX 符號擴充套件到 EDXRAXRDX

如果商不能放入商暫存器,則會發生算術溢位中斷。 運算後,所有標誌都處於未定義狀態。

符號反轉

[編輯 | 編輯原始碼]

neg arg

算術否定引數(即二進位制補碼否定)。

進位算術指令

[編輯 | 編輯原始碼]
adc src, dest GAS 語法
adc dest, src Intel 語法


加帶進位。 將 src + CF 新增到 dest,將結果儲存在 dest 中。 通常在正常加法指令之後使用,以處理比暫存器大小大兩倍的值。 在以下示例中,source 包含一個 64 位數字,它將被新增到 destination 中。

mov eax, [source] ; read low 32 bits
mov edx, [source+4] ; read high 32 bits
add [destination], eax ; add low 32 bits
adc [destination+4], edx ; add high 32 bits, plus carry


sbb src, dest GAS 語法
sbb dest, src Intel 語法

減帶借位。 從 dest 中減去 src + CF,將結果儲存在 dest 中。 通常在正常減法指令之後使用,以處理比暫存器大小大兩倍的值。

增量和減量

[編輯 | 編輯原始碼]

inc augend

此指令將暫存器值 augend 增加 1。 它執行速度比 add arg, 1 快得多,但它不會影響 CF

dec 被減數

被減數 中的值減 1,但這比語義上等效的 sub 被減數, 1 快得多。

運算元

[編輯 | 編輯原始碼]

被減數 可以是暫存器或記憶體運算元。

  • 一些程式語言使用所有位為零或所有位設定為 1 來表示布林值。當您編寫布林函式時,需要考慮這一點。 dec 指令可以幫助您做到這一點。通常,您根據標誌設定最終的(布林)結果。透過選擇一個與預期相反的指令,然後遞減結果值,您將獲得滿足程式語言要求的值。以下是一個測試零的簡單示例。
    xor rax, rax   ; rax ≔ false (ensure result is not wrong due to any residue)
    test rdi, rdi  ; rdi ≟ 0 (ZF ≔ rax = 0)
    setnz al       ;  al ≔ ¬ZF
    dec rax        ; rax ≔ rax − 1
    
    如果您打算設定false,則“錯誤”設定的 1 將透過 dec “修復”。如果您打算設定true,則表示為 -1,您將遞減值為零,它的“下溢”將導致所有位翻轉。注意,一些架構執行 dec 緩慢,因為標誌暫存器僅被部分覆蓋。因此,使用 neg 通常更有效
    setz al        ;  al ≔ ZF
    neg rax        ; rax ≔ 0 − rax
    
    ,儘管它也會影響 CF
  • 由於 incdec 不會影響 CF,您可以使用這些指令來更新迴圈的計數變數,而不會覆蓋其中儲存的一些資訊。如果您需要一個不影響任何標誌的指令,同時隱式地執行 dec,您可以使用相當慢的 loop

指標算術

[編輯 | 編輯原始碼]

lea 指令可用於算術運算,尤其是在指標上。請參閱 “資料傳輸”一章,§“載入有效地址”

華夏公益教科書