1. 程式人生 > 實用技巧 >Win32彙編 - 算數運算指令總結

Win32彙編 - 算數運算指令總結

整理複習組合語言的知識點,以前在學習《Intel組合語言程式設計 - 第五版》時沒有很認真的整理筆記,主要因為當時是以學習理解為目的沒有整理的很詳細,這次是我第三次閱讀此書,每一次閱讀都會有新的收穫,這次複習,我想把書中的重點,再一次做一個歸納與總結(注:16位彙編部分跳過),並且繼續嘗試寫一些有趣的案例,用自己的話描述出來,一來提高自己,二來分享知識。

ADD/SUB指令: ADD/SUB指令將將同尺寸的源運算元和目的運算元相加,且不改變原運算元,相加後的結果存入目的運算元中.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	MyList DWORD 10h,20h,30h,40h

.code
	main PROC
		; 將eax於ebx 兩數相加: 將相加後的結果放入eax
		mov eax,1024
		mov ebx,2048
		add eax,ebx
		
		; 同樣兩數相減,將結果放到eax中
		mov eax,1024
		sub eax,512
		
		; 針對陣列的相加同樣可以
		mov esi,offset MyList    ; 獲取到首地址
		mov eax,0
		mov ebx,0
		
		mov eax,dword ptr ds:[esi]            ; 找到第一個元素
		mov ebx,dword ptr ds:[esi + 1 * 4]    ; 找到第二個元素
		add eax,ebx                           ; 相加操作
		
		invoke ExitProcess,0
	main ENDP
END main

NEG 取反指令: 該指令通過將數字轉換為對應的補碼而求出其值的相反數,結合上面的加法與減法案例,我們來模擬編譯器處理特定語句的寫法Rval = -Xvar + (Yvar - Zvar)

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval SDWORD ?
	Xval SDWORD 26
	Yval SDWORD 30
	Zval SDWORD 40
.code
	main PROC
		; 1.寫出: Rval = -Xvar + (Yvar - Zvar) 彙編格式
		; 首先將Xval的值通過neg取反
		mov eax,dword ptr ds:[Xval]
		neg eax
		
		; 然後將Yval與Zval相減後複製到Yval
		mov ebx,dword ptr ds:[Yval]
		sub ebx,dword ptr ds:[Zval]
		
		;最後將兩個子項相加後放入到Rval中
		add eax,ebx
		mov dword ptr ds:[Rval],eax
		
		; 2.寫出: Rval = (Xval+Yval) - (Yval+Zval)
		mov eax,dword ptr ds:[Xval]
		add eax,dword ptr ds:[Yval]
		mov dword ptr ds:[Rval],eax
		
		mov ebx,dword ptr ds:[Yval]
		add ebx,dword ptr ds:[Zval]
	
		sub dword ptr ds:[Rval],ebx
		
		invoke ExitProcess,0
	main ENDP
END main

SHL/SHR 邏輯移位: SHL常用於對目標運算元執行邏輯左移(無符號數)操作,其左移後最低位以0填充,而移動出去的最高位則會送入CF(進位標誌)中,而SHR則相反,對目標運算元執行邏輯右移(無符號數)操作,移出的資料位用0代替,最低位被複制到CF(進位標誌)中,原來的進位標誌位丟失.

Intel處理器中定義,執行移位的源運算元的範圍必須在0-255之間,在任何處理器上都可以使用CL暫存器存放移位位數,例如在下面的指令中,AL暫存器被左移一位,最高位被複制到了進位標誌中,最低位被清零:

01251006 | B3 8F                | mov al,10001111b                            | AL = 10001111b
01251008 | D0E3                 | shl al,1                                    | CF = 1,AL = 00011110b

01251006 | B0 01                | mov al,10000000b                            | AL = 10000000b
01251008 | C0E0 02              | shl al,2                                    | CF = 0,AL = 00000000b

01251006 | B0 01                | mov al,10000000b                            | AL = 10000000b
01251008 | C0E0 01              | shl al,1                                    | CF = 1,AL = 00000000b

01251006 | B0 01                | mov al,10100000b                            | AL = 10100000b
01251008 | C0E0 03              | shl al,2                                    | CF = 0,AL = 10000000b

另外使用SHL指令還可以進行2的次冪的高速乘法運算,任何運算元左移動N位,就相當於該運算元乘以2的N次方,如下例子:

01311002 | B0 05                | mov al,5                                    | AL 左移動1位
01311004 | D0E0                 | shl al,1                                    | al * 2 = 10

01311007 | B0 05                | mov al,5                                    | AL左移2位
01311009 | C0E0 02              | shl al,2                                    | al * 4 = 20

01311007 | B0 05                | mov al,5                                    | AL左移3位
01311009 | C0E0 03              | shl al,3                                    | al * 8 = 40

下面是一個左移計算的案例,我們通過彙編來計算Rval = ((Xval + Yval) - (Yval + Zval)) * 8的結果.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval SDWORD ?
	Xval SDWORD 50
	Yval SDWORD 100
	Zval SDWORD 10
.code
	main PROC
		; Rval = ((Xval + Yval) - (Yval + Zval)) * 8
		mov eax,dword ptr ds:[Xval]
		add eax,dword ptr ds:[Yval]
		
		mov ebx,dword ptr ds:[Yval]
		add ebx,dword ptr ds:[Zval]
		
		sub eax,ebx
		
		; 乘以8也就是左移3位 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
		shl eax,3
		mov dword ptr ds:[Rval],eax
		
		invoke ExitProcess,0
	main ENDP
END main

對目標運算元執行SHR邏輯右移(無符號數)操作,移出的資料位用0代替,最低位被複制到CF進位標誌中,原來的進位標誌位丟失.

0131100D | B0 01                | mov al,10001111b                            | AL = 10001111b
0131100F | D0E8                 | shr al,1                                    | CF = 1,AL = 01000111b

0131100D | B0 01                | mov al,10001111b                            | AL = 10001111b
0131100F | D0E8                 | shr al,2                                    | CF = 1,AL = 00100011b

另外任何無符號運算元邏輯右移N位,就相當於該運算元除以2的N次方,如下例子:

01311012 | B2 20                | mov dl,20                                   | DL 右移1位 
01311014 | D0EA                 | shr dl,1                                    | dl/2 = 10

01311012 | B2 20                | mov dl,20                                   | DL 右移2位 
01311014 | D0EA                 | shr dl,2                                    | dl/4 = 5

下面是一個右移計算的案例,我們通過彙編來計算Rval = (Xval / 8) + (Yval * 16) - (Zval * 4)的結果.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval SDWORD ?
	Xval SDWORD 200
	Yval SDWORD 50
	Zval SDWORD 10
.code
	main PROC
		; Rval = (Xval / 8) + (Yval * 16) - (Zval * 4)
		; 混合移位計算: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
		; 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
		
		; 先通過右移3位,計算除法
		mov eax,dword ptr ds:[Xval]   ; Xval / 8
		shr eax,3
		
		; 再通過左移4位和2位分別計算乘法
		mov ebx,dword ptr ds:[Yval]   ; Yval * 16
		shl ebx,4
		
		mov ecx,dword ptr ds:[Zval]   ; Zval * 4
		shl ecx,2
		
		add eax,ebx
		sub eax,ecx
		mov dword ptr ds:[Rval],eax
		
		invoke ExitProcess,0
	main ENDP
END main

上面的這種計算方式屬於乘數剛好是2的次冪,如果不是2的次冪則需要拆分後計算,如下案例,為了計算無符號乘以36,可以把36分解成2的5次方和2的2次方,然後利用移位命令高效計算.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval SDWORD ?
.code
	main PROC
		; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
		; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
		
		; 計算 123 * 36
		; 等式拆分 EAX * 36 => EAX * (32 + 4) => (EAX * 32) + (EAX * 4)
		mov eax,123
		mov ebx,eax         ; 拷貝出一份
		shl eax,5           ; 計算 (EAX * 32)
		shl ebx,2           ; 計算 (EAX * 4)
		add eax,ebx         ; 最後相加
		mov dword ptr ds:[Rval],eax
		
		; 計算 123 * 24
		; 等式拆分: EAX * 24 => EAX * (16 + 8) => (EAX * 16) + (EAX * 8)
		mov eax,123
		mov ebx,eax
		shl eax,4           ; 計算 (EAX * 16)
		shl ebx,3           ; 計算 (EAX * 8)
		add eax,ebx
		mov dword ptr ds:[Rval],eax
		
		; 計算 123 * 21
		; 等式拆分: EAX * 21 => EAX * (16 + 4 + 1) => (EAX * 16) + (EAX * 4) + (EAX * 1)
		
		mov eax,123
		mov ebx,eax
		mov ecx,eax         ; 得到 (EAX * 1)
		shl eax,4           ; 計算 (EAX * 16)
		shl ebx,2           ; 計算 (EAX * 4)
		add eax,ebx
		add eax,ecx
		mov dword ptr ds:[Rval],eax
		
		invoke ExitProcess,0
	main ENDP
END main

下面是我通過尋找一些規律,能夠在不查表的情況下逆推出其左移或者是右移中乘數或除數的具體值,如果比較複雜的話還是直接查表來的容易一些,此處只是一種思考方式.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval DWORD ?
.code
	main PROC
	; 次方表: 1=>2 2=>4 3=>8 4=>16 5=>32 6=>64 7=>128
	; 次方表: 8=>256 9=>512 10=>1024 11=>2048 12=>4096 13=>8192 14=>16384
		
		; 乘法逆推: 28/7=4
		mov eax,7                      ; eax=7
		shl eax,2                      ; 求0x2是乘以幾,乘以4
		mov dword ptr ds:[Rval],eax    ;  eax = 28 計算出: 0x2 => 28/7=4
		
		; 乘法逆推: 96/6 = 16 => 4*4=16
		mov eax,6                      ; eax = 6
		shl eax,2                      ; 4
		shl eax,2                      ; 4
		mov dword ptr ds:[Rval],eax    ; eax = 96
		
		; 乘法逆推: 4*4*8
		mov eax,4                      ; eax = 4
		shl eax,2                      ; 執行到此處 eax=16 通過16/4 = 4 故乘以4
		shl eax,3                      ; 執行到此處 eax  =128  通過 128/4=32 , 32/4=8 故乘以8
		mov dword ptr ds:[Rval],eax
		
		; 除法逆推: 7/1.75 = 4
		mov eax,7                      ; eax = 7
		shr eax,2                      ; 此處乘以4
		mov dword ptr ds:[Rval],eax    ; eax = 1.75 => 7/1.75=4
		
		invoke ExitProcess,0
	main ENDP
END main

SAL/SAR 算數移位: SAL指令與SHL指令等價,SAR指令可以對有符號數進行快速除以2的次冪操作,也可以將一個AX暫存器中的值進行擴充套件,擴充套件成EAX.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.code
	main PROC
		; SAR => 算數右移
		mov al,0f0h         ; AL = 11110000b (-16)
		sar al,1            ; AL = 11111000b (-8)
		
		; SAR => 有符號除法 計算-128的,2的3次方
		; 2次方 => -32 3次方 => -16 4次方 => -8 5次方 => -4
		mov eax,-128        ; AL = 10000000b
		sar eax,3           ; AL = 11110000b EAX = -16
		
		; SAR => 符號擴充套件AX擴充套件到EAX
		; 先左移EAX 16位,然後算術右移EAX 16位
		mov ax,-128         ; EAX = ????FF80h
		shl eax,16          ; EAX = FF800000h
		sar eax,16          ; EAX = FFFFFF80h
		
		invoke ExitProcess,0
	main ENDP
END main

ROL/ROR 迴圈移位: ROL位迴圈左移,其左移1位後會把最高位同時複製到進位標誌位和最低位中,而ROR則是迴圈右移,其右移1位後,把最低位同時複製到進位標誌位和最高位中.

迴圈移位和普通移位不同之處在於前者並不會丟失任何資料位,從一端走的資料位會從另一端出現,如迴圈左移會將高位複製到低位中,迴圈右移則將低位複製到高位中,但需要注意不論是左移/右移,都是對二進位制格式進行操作的.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.code
	main PROC
		; ROL => 迴圈左移
		mov al,40h        ; AL = 01000000b
		rol al,1          ; AL = 10000000b , CF = 0
		rol al,1          ; AL = 00000001b , CF = 1
		rol al,1          ; AL = 00000010b , CF = 0
		
		mov al,00100000b
		rol al,3          ; AL = 00000001b , CF = 1
		
		; ROR => 迴圈右移
		mov al,01h        ; AL = 00000001b
		ror al,1          ; AL = 10000000b , CF = 1
		ror al,1          ; AL = 01000000b , CF = 0
		
		mov al,00000100b
		ror al,3          ; AL = 10000000b , CF = 1
		
		; ROL => 迴圈左移,交換位組
		; 交換 高半部分(位4-7) 低半部分(位0-3)
		mov al,26h
		rol al,4          ; rol 與 ror 結果一致
		ror al,4

		invoke ExitProcess,0
	main ENDP
END main

RCL/RCR 標誌移位: RCL指令在每位左移1位後,把CF進位標誌複製到最低有效位中,最高有效位複製到進位標誌中, RCR則相反,右移後把CF進位標誌複製到最高有效位中,並把最低有效位複製到進位標誌中.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.code
	main PROC
		; RCL 左移
		clc            ; 將CF進位標誌置0
		mov bl,88h     ; CF = 0 , BL = 10001000b
		rcl bl,1       ; CF = 1 , BL = 00010000b
		rcl bl,1       ; CF = 0 , BL = 00100001b
		
		; RCR 右移
		stc            ; 將CF進位標誌置1
		mov ah,10h     ; CF = 1 , ah = 00010000h
		rcr ah,1       ; CF = 1 , ah = 10001000h
		
		invoke ExitProcess,0
	main ENDP
END main

讓陣列整體左移或右移.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval SDWORD ?
	ArraySize = 3
	Array DWORD ArraySize DUP(99999999h)
	ByteArray BYTE 81h,20h,33h
	WordArray WORD 810dh,0c064h,93abh
.code
	main PROC
		; 多雙字同時右移
		mov esi,0
		shr Array[esi + 8],1     ; 高雙字
		rcr Array[esi + 4],1     ; 中間雙字
		rcr Array[esi],1         ; 低雙字
		
		; 多雙字同時左移
		mov esi,0
		shl Array[esi+8],1
		rcl Array[esi+4],1
		rcl Array[esi],1
		
		; 讓陣列整體右移 (從高位元組到低位元組)
		shr [ByteArray + 2],1
		rcr [ByteArray + 1],1
		rcr [ByteArray],1
		
		; 讓陣列整體左移 (從低位元組到高位元組)
		shl [ByteArray],1
		rcl [ByteArray + 2],1
		rcl [ByteArray + 4],1

		invoke ExitProcess,0
	main ENDP
END main

MUL/IMUL 乘法指令: MUL/IMUL分別可進行有符號與無符號的乘法運算,通常該指令都接受暫存器運算元,也接受記憶體運算元,但是不接受立即數,且乘數與被乘數大小必須相同,乘基尺寸是乘數/被乘數的兩倍.

MUL乘法指令有三種格式: 第一種將8位運算元與AL相乘,結果放入AX中,第二種將16位運算元與AX相乘,結果的高16位放入DX低16位放入AX,第三種將32位運算元與EAX相乘,結果高32位放入EDX第32位放入EAX中.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval DWORD ?
	VarWordA WORD 2000h
	VarWordB WORD 0100h
.code
	main PROC
		; 執行8位乘法運算 al = al*bl
		mov al,5h
		mov bl,10h
		mul bl
		mov byte ptr ds:[Rval],al
		
		; 執行16位乘法運算
		xor eax,eax
		xor edx,edx
		mov ax,word ptr ds:[VarWordA]
		mul word ptr ds:[VarWordB]
		mov word ptr ds:[Rval],ax     ; 低半部分
		mov word ptr ds:[Rval],dx     ; 高半部分  DX:AX = 00000020h
		
		; 執行32位乘法運算
		xor eax,eax
		xor edx,edx
		mov eax,12345h
		mov ebx,1000h
		mul ebx
		mov dword ptr ds:[Rval],eax   ; 低半部分
		mov dword ptr ds:[Rval],edx   ; 高半部分 EDX:EAX = 0000000012345000h
		
		invoke ExitProcess,0
	main ENDP
END main

IMUL指令主要用於執行有符號整數的乘法運算,並保留乘積的符號位,且在32位彙編中有三種格式:但運算元格式,雙運算元格式,三運算元格式,首先是單運算元模式,該模式把乘積儲存在累加器AX中,或者將符號位放入EDX將結果放入EAX中.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	EDX_Rval DWORD ?
	EAX_Rval DWORD ?
.code
	main PROC
		
		; 執行8位乘法運算: 48*8 得到的積+192 溢位
		xor eax,eax
		mov al,48
		mov bl,4
		imul bl          ; CF 進位 = 1 OF 溢位 = 1
		
		xor eax,eax
		mov al,-4
		mov bl,4
		imul bl           ; AX=FFF0h OF=0
		
		; 執行16位乘法運算: 48*4 得到的積 +192
		xor eax,eax
		mov ax,48
		mov bx,4
		imul bx
		mov word ptr ds:[EDX_Rval],dx
		mov word ptr ds:[EAX_Rval],ax  ; DX:AX = 000000C0h OF=0
		
		; 執行32位乘法運算: +4823424 *(-423)
		xor eax,eax
		xor ebx,ebx
		mov eax,+4823424
		mov ebx,-423
		imul ebx
		mov dword ptr ds:[EDX_Rval],edx   ; EDX為符號位
		mov dword ptr ds:[EAX_Rval],eax   ; EDX:EAX = FFFFFFFF86635D80h OF=0
		
		invoke ExitProcess,0
	main ENDP
END main

接著就是乘法語句的雙運算元與三運算元模式了,在雙運算元中第一個運算元必須是暫存器,第二個運算元可以是暫存器或記憶體等,在三運算元模式中,把乘積儲存在第一個運算元中,其他與雙運算元類似.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	wordp   SWORD 4
	dwordp  SDWORD 4
	Rval DWORD ?
.code
	main PROC
	
		; 雙運算元乘法運算
		xor eax,eax
		xor ebx,ebx
		
		mov ax,-16                   ; ax = -16
		mov bx,2                     ; bx = 2
		imul bx,ax                   ; bx = bx * ax
		imul bx,2                    ; bx = bx * 2
		imul bx,word ptr ds:[wordp]  ; bx = bx * wordp
		mov word ptr ds:[Rval],bx    ; 放入變數中儲存
		
		; 三運算元乘法運算
		xor eax,eax
		xor ebx,ebx
		
		imul bx,wordp,-16           ; bx = wordp * -16
		imul ebx,dwordp,-16         ; ebx = dwordp * -16
		imul ebx,dwordp,-20         ; ebx = dwordp * -20
		mov dword ptr ds:[Rval],ebx ; 放入變數中
		
		invoke ExitProcess,0
	main ENDP
END main

到此為止我們學會了通過移位的方式實現快速乘法運算,也學過使用MUL指令進行乘法計算,接下來我們可以編寫兩個案例分別通過移位和MUL計算EAX與36相乘的結果,看看哪一個效率更高一些.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.code
	shl_proc proc
		mov ecx,10
	s:
		push eax
		mov ebx,eax
		shl eax,5
		shl eax,2
		add eax,ebx
		pop eax
		loop s
		ret
	shl_proc endp
	
	mul_proc proc
		mov ecx,10
	s:
		push eax
		mov ebx,36
		mul ebx
		pop eax
		loop s
		ret
	mul_proc endp

	main PROC
		mov eax,10
		call shl_proc
		
		
		mov eax,10
		call mul_proc
	
		invoke ExitProcess,0
	main ENDP
END main

DIV/IDIV 除法指令: DIV是無符號除法指令,該指令支援8/16/32位無符號整數的除法運算,指令中唯的暫存器或記憶體運算元是除數,IDIV則是有符號除法指令,該指令與無符號除法幾乎一致,唯一的不同在於有符號除法在進行相除操作時需要符號擴充套件.

首先我們先來學習一下DIV無符號除法運算的使用技巧,分別演示8/16/32位無符號除法的使用方式.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval DWORD ?
	DivIdend QWORD 0000000800300020h
	DivIsor DWORD 00000100h
	Div_Eax DWORD ?
	Div_Edx DWORD ?
.code
	main PROC
		
		; 執行8位除法: 83/2 商al是41h 餘數ah是1
		xor eax,eax
		mov ax,0083h                  ; 被除數
		mov bl,2                      ; 除數
		div bl                        ; ax = ax / bl
		mov byte ptr ds:[Rval],ah     ; ah = 01h
		mov byte ptr ds:[Rval],al     ; al = 41h
		
		; 執行16位除法: 8003h/100h 商是80h 餘數是3
		xor edx,edx                   ; 清除edx暫存器
		mov ax,8003h                  ; 被除數
		mov cx,100h                   ; 除數
		div cx                        ; ax = ax / cx
		mov word ptr ds:[Rval],ax     ; ax = 0080h
		mov word ptr ds:[Rval],dx     ; dx = 0003h
		 
		; 執行32位除法
		mov edx,dword ptr DivIdend + 4  ; 高雙字
		mov eax,dword ptr DivIdend      ; 低雙字
		div DivIsor                     ; 與被除數相除
		
		mov dword ptr ds:[Div_Eax],eax  ; eax = 08003000h
		mov dword ptr ds:[Div_Edx],edx  ; edx = 00000020h

		invoke ExitProcess,0
	main ENDP
END main

針對IDIV有符號數的除法運算,需要對被除數進行除法操作之前,對其進行符號擴充套件,彙編中有三條擴充套件命令.

CBW 指令 將位元組符號擴充套件至字,擴充套件AL的符號位至AH中,保留了數字的符號.
CWD 指令 將字元號擴充套件至雙字,指令擴充套件AX的符號位至DX中.
CDQ 指令 雙字元號擴充套件至八位元組,指令擴充套件EAX的符號位至EDX中.

當使用符號擴充套件指令擴充套件後,暫存器就可以被用來計算有符號除法了,程式碼如下所示:

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	ByteVal SBYTE -48
	WordVal SWORD -5000
	DworeVal SDWORD +50000
	Rval DWORD ?
.code
	main PROC
		; 位元組擴充套件至字 -48/5
		xor eax,eax
		mov al,byte ptr ds:[ByteVal]  ; al = D0 取出 -48
		cbw                           ; ax = FFD0 將al擴充套件至ax
		mov bl,5                      ; bl = 05
		idiv bl                       ; ax=ax/bl
		mov word ptr ds:[Rval],ax     ; 結果: ax = FDF7
		
		mov byte ptr ds:[Rval],al     ; AL儲存商 -9
		mov byte ptr ds:[Rval],ah     ; AH儲存餘數 -3
		
		; 字擴充套件至雙字
		xor eax,eax
		mov ax,word ptr ds:[WordVal]  ; 除數
		cwd                           ; 擴充套件至雙字(擴充套件AX至DX)
		mov bx,+256                   ; 被除數
		idiv bx                       ; ax = ax/bx
		mov word ptr ds:[Rval],ax     ; 商AX=-19
		mov word ptr ds:[Rval],dx     ; 餘數DX=-136
		
		; 雙字元號擴充套件至八位元組
		mov eax,dword ptr ds:[DworeVal]
		cdq                             ; 擴充套件EAX到EDX
		mov ebx,-256
		idiv ebx
		mov dword ptr ds:[Rval],eax     ; 商 EAX = -195
		mov dword ptr ds:[Rval],edx     ; 餘數 EDX = +80

		invoke ExitProcess,0
	main ENDP
END main

學習了前面的這幾種計算方式以後,我們就可以將其總結起來實現計算複雜的表示式了,先來三個練手的.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval DWORD ?
	var1 DWORD 10
	var2 DWORD 15
	var3 DWORD 20
	var4 DWORD 25
.code
	main PROC
		; 實現計算: var4 = (var1 * 5) / (var2 - 3)
		mov eax,dword ptr ds:[var1] ; 先計算左邊 (var1 * 5)
		mov ebx,5
		mul dword ptr ds:[ebx]      ; EDX:EAX 乘積
		
		mov ebx,dword ptr ds:[var2] ; 計算右邊 (var2 - 3)
		sub ebx,3
		
		div dword ptr ds:[ebx]       ; 計算兩者的除法
		mov dword ptr ds:[var4],eax  ; 最後賦值操作
		
		; 實現計算: var4 = (var1+var2) * var3
		mov eax,dword ptr ds:[var1]
		add eax,dword ptr ds:[var2]   ; 計算前半部分
		
		mov ebx,dword ptr ds:[var3]   ; 計算後半部分
		mul ebx
		mov dword ptr ds:[var4],eax   ; 最後賦值操作
		
		; 實現計算: var1 = (var2/var3) * (var1+var2)
		mov eax,var2
		cdq                           ; 擴充套件為EDX:EAX
		idiv dword ptr ds:[var3]      ; 計算除法,存入eax
		
		mov ebx,dword ptr ds:[var1]   ; 計算加法
		add ebx,dword ptr ds:[var2]
		
		imul dword ptr ds:[ebx]       ; 最後計算乘法
		mov dword ptr ds:[var1],eax   ; 在eax中取值

		invoke ExitProcess,0
	main ENDP
END main

最後我們來實現一個相對複雜的案例,總體的複習一下,該案例計算var4 = (var1 * -5) / (-var2 % var3)返回值,我們可以從右邊開始計算,並把右邊的值儲存到EBX中,然後把被除數符號擴充套件到EDX,最後使用IDIV計算除法.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval DWORD ?
	var1 DWORD 54
	var2 DWORD 56
	var3 DWORD 5
	var4 DWORD 52
.code
	main PROC
		; 實現計算: var4 = (var1 * -5) / (-var2 % var3)
		mov eax,dword ptr ds:[var2]
		neg eax                      ; 將var2反轉
		
		cdq                          ; 將被除數符號擴充套件
		idiv var3                    ; 除以var3 則 EDX=餘數
		mov ebx,edx                  ; 將餘數給EBX
		
		mov eax,-5
		imul var1                    ; 計算 var1 * -5 結果給EAX
		
		idiv ebx                     ; eax = eax/ebx
		mov dword ptr ds:[var4],eax  ; 最後將結果給var4
		
		invoke ExitProcess,0
	main ENDP
END main

ADC/SBB 擴充套件加減法: 擴充套件加減法是指任意尺寸大小數字的加減法,其中ADC指令主要使用者實現帶進位加法,SBB指令則實現帶進位減法,起作用都是將源運算元與目的運算元以及進位等相加減.

以擴充套件加法為例,計算兩個8位整數相加(FFh+FFh)產生的16位結果將被存放在DL:AL (01feh)中,如果是計算兩個32位整數相加(FFFFFFFFh+FFFFFFFFh),則會在EDX和EAX中分別存放00000001h:FFFFFFFEh這兩個值,擴充套件SBB同理.

	.386p
	.model flat,stdcall
	option casemap:none

include windows.inc
include kernel32.inc
includelib kernel32.lib

.data
	Rval DWORD ?
.code
	main PROC
		; 計算8位加法操作
		xor edx,edx
		xor eax,eax
		mov dl,0                      ; 清0
		mov al,0ffh                   ; 設定加數
		add al,0ffh                   ; al = al + 0ffh
		adc dl,0                      ; 進位加法
		mov byte ptr ds:[Rval],dl     ; 存放高位
		mov byte ptr ds:[Rval],al     ; 存放低位
		
		; 計算32位加法操作
		xor edx,edx
		xor eax,eax
		mov edx,0
		mov eax,0ffffffffh
		add eax,0ffffffffh
		adc edx,0
		mov dword ptr ds:[Rval],edx    ; 存放高位
		mov dword ptr ds:[Rval],eax    ; 存放低位
		
		; 計算32位減法操作
		xor edx,edx
		xor eax,eax
		mov edx,1       ; 設定高半部分
		mov eax,0       ; 設定低半部分
		sub eax,1       ; eax減去1
		sbb edx,0       ; 減去1則高半部分為0
		
		mov dword ptr ds:[Rval],edx    ; 存放高位
		mov dword ptr ds:[Rval],eax    ; 存放低位
		
		invoke ExitProcess,0
	main ENDP
END main

程式碼都是自己思考後編寫的案例,也在辛苦的新增備註,轉載請添加出處