0%

x86 Assembly Programming Tutorial

x86組合語言指令集

● 摘要:

x86是組合語言的其中一種,在組合語言裡所有的運算都必須透過暫存器來計算,在這邊整理了組合語言的指令範例方便大家學習使用。

● 框架

在組合語言裡分為兩個部分,分別為資料與運行得程式碼。在資料(.data)裡就如同宣告一個變數一樣,在程式碼(.code)內就是你所要執行的內容。

1
2
3
4
5
6
7
8
INCLUDE Irvine32.inc 	;函式庫
.data
;資料
.code
main PROC
;運行的程式碼
exit
main ENDP

● 資料大小與暫存器

◆ 指令與資料大小

在宣告變數時你還需要指定這個變數的大小,因此你必須了結每個指令所對應的位元數。

指令 位元
BYTE 8-bit無號整數
SBYTE 8-bit有號整數
WORD 16-bit無號整數
SWORD 16-bit有號整數
DWORD 32-bit無號整數
SDWORD 32-bit有號整數
FWORD 48-bit整數
QWORD 64-bit整數
TBYTE 80-bit整數
REAL4 32-bit (4 byte) IEEE短實數
REAL8 64-bit (8 byte) IEEE長實數
REAL10 80-bit (10 byte) IEEE延伸實數

◆ 暫存器

在組合語言裡所有的運算都必須透過暫存器運算完後在放回變數內。

指令 暫存器
BYTE 8位元通用暫存器: AH、AL、BH、BL、CH、CL、DH、DL
WORD 16位元通用暫存器: AX、BX、CX、DX、SI、DI、SP、BP
DWORD 32位元通用暫存器: EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP

● 資料宣告

1
2
3
4
5
.data
Msg BYTE "hello ASM world!", 0
array DWORD 100 DUP(0) ;空陣列
ID BYTE 100 DUP (0) ;空字串
asec DWORD 0 ;變數

● 移動資料

1
2
3
mov eax, 10	                            ;eax = 10
mov ebx, eax ;ebx = eax
movzx al, eax ;較小值移至到較大值

● 運算範例

◆ 根號

mid為要被開根號的值。

1
2
3
4
5
6
7
8
9
10
11
12
mov ebx, 2
W1:
mov eax, ebx
imul ebx
cmp mid, eax
JB W2
inc ebx
jmp W1
W2:
dec ebx
mov eax, ebx
call WriteDec ;eax = 開完根號的值

◆ 乘法

5 * 5 = 25

1
2
3
4
5
mov ebx, 5
mov eax, 5
imul ebx ;有號
mul ebx ;無號
call WriteDec ;eax = 25

◆ 除法

10 / 5 = 2
相除完後的餘數會放在edx這個暫存器內,所以如果你列印edx他會顯示0。

1
2
3
4
5
6
mov edx, 0		;餘數
mov eax, 10 ;商
mov ebx, 5 ;除數
idiv ebx ;有號
div ebx ;無號
call WriteDec ;ans = 2

◆ 減法

10 - 5 = 5
兩數相減。

1
2
3
4
5
mov eax, 10
mov ebx, 5
sub eax, ebx ;小數
sbb eax, ebx ;大數
call WriteDec ;ans = 5

◆ 加法

10 + 5 = 15
兩數相加。

1
2
3
4
5
mov eax, 10
mov ebx, 5
add eax, ebx ;小數
adc eax, ebx ;大數
call WriteDec ;ans = 15

◆ 加一

你可以使inc來對暫存器直接加一,又或者用sub這個指令來作加一的動作。

1
2
3
4
mov eax, 1
add eax, 1
inc eax
call WriteDec ;ans = 2

◆ 減一

你可以使用dec來對暫存器直接減一,又或者用sub這個指令來作減一的動作。

1
2
3
4
mov eax, 2
sub eax, 1
dec eax
call WriteDec ;ans = 1

● 輸入與輸出

◆ 換行

1
call Crlf

◆ 輸入字串

多個字串輸入

1
2
3
4
lea EDX, ID[ESI]
mov ECX, ( SIZEOF ID ) - 1
call ReadString
add esi, 10 ;移動10格

單一字串輸入

1
2
3
4
mov edx, OFFSET ID
mov ecx, (SIZEOF ID) - 1
call ReadString
call WriteDec ;字串長度會暫存EAX

◆ 輸出字串

在這邊記得要依照陣列宣告的大小做對應的移動,例如字串就是BYTE所以每一個字都是移動一格。

多個字串輸出

1
2
3
lea EDX, ID[esi]
call WriteString
add esi, 12 ;移動12格

資料字串輸出

1
2
MOV EDX, OFFSET Msg1
call WriteString

◆ 輸入數字

單一數字輸入

1
2
call ReadDec		;暫存EAX
call RedaInt ;帶正負號

陣列數字輸入

1
2
3
call ReadDec
mov array2[esi*4], eax
inc esi

◆ 輸出數字

單一數字輸出

1
2
3
mov eax, 10
call WriteDec ;Output = 10
call WriteInt ;Output = +10

陣列數字輸出

1
2
3
mov  eax, array2 [esi*4]
call WriteDec
inc esi

● 比較

1
2
3
4
5
L1:
mov eax, 10
mov ebx, 5
cmp eax, ebx
JA L1 ;if(eax > ebx)則跳到L1

◆ 特殊的 flag 值

指令 運算 指標
JZ 若為零則跳 ZF=1
JNZ 若為不零則跳 ZF=0
JC 若進位則跳 CF=1
JNC 若不進位則跳 CF=0
JO 若溢位則跳 OF=1
JNO 若不溢位則跳 OF=0
JS 若負號則跳 SF=1
JNS 若非負號則跳 SF=0
JP 同位(偶)則跳 PF=1
JNP 非同位(奇)則跳 PF=0

◆ 兩運算元是否相等或(E)AX的值

指令 運算 範例
JZ 相等則跳 if(left == right)
JNZ 不相等則跳 if(left != right)
JC 若 CX = 0 則跳
JNC 若 ECX = 0 則跳

◆ 無號運算元的比較

指令 運算
JA 較大則跳
JNBE 不是較小或相等則跳(=JA)
JAE 較大或相等則跳
JNB 不是較小則跳(=JAE)
JB 較小則跳
JNAE 不是較大或相等則跳(=JB)
JBE 較小或相等則跳
JNA 不是較大則跳(=JBE)

◆ 帶正負號運算元間的比較

指令 運算
JG 較大則跳
JNLE 非較小或相等則跳(=JG)
JGE 較大或相等則跳
JNL 不是較小則跳(=JGE)
JL 較小則跳
JNGE 非較大或相等則跳(=JL)
JLE 較小或相等則跳
JNG 不是較大則跳(=JLE)

● 迴圈

◆ 32以內的迴圈

1
2
3
4
mov ecx, 5	;次數
L1:
;內容
loop L1

◆ 無限制迴圈

1
2
3
4
5
MOV EBX, 5	;次數
L1:
;內容
DEC EBX
JNE L1

◆ 無條件跳躍

1
2
3
L1:
;內容
JMP L1

◆ 巢狀迴圈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.data
count DWORD 0
.code
mov ecx, 5
;外迴圈
L1:
mov count, ecx ;儲存上一個外迴值
mov ecx, 10 ;內迴值
;內迴圈
L2:
;內容
loop L2
mov ecx, count ;放回外迴值
loop L1

● 函式

由於組合語言並非一半高階語言一樣方便,因此重複的代碼會非常多,所以你可以宣告一些函式來簡化你的程式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Prime0 PROTO ,
Wa:DWORD, StrA:PTR BYTE
mov edx, StrA
call WriteString

mov eax, Wa
call WriteDec
ret
Prime0 ENDP
END main

Prime PROC USES ESI EAX ECX EBX ;不改變主函式暫存器內存的值
;副函式
ret
Prime ENDP
END main