REF:
http://masm1215.myweb.hinet.net/
組合語言的假指令,可分為「一般的假指令」、「條件式組譯」及「高階指令」等三種,而一般,我們只需用到第一種,至於第二種,則式各人需要使用於巨集檔中,第三種,則不建議使用,因為要使用高階指令,那你不如用C語言去寫就好了,何必學組合語言呢,整個程式的整體架構是由程式設計師一人開發的,假如你加入了高階指令,那麼這個組合語言程式,不就沒有了他的本質(速度快,檔案小)。
以下會將這三種假指令取其常用的,分別一一為您介紹。
捷徑:
條件式組譯、
高階指令
一般的假指令
一般假指令的種類很多,例如:.model, .code, .data, .stack, end, ;, .186, .286, .386, .486, .586, equ, org, label, offset, segment, .data?, const, @, .fardata, .fardata?, seg, assume,dup等等。
假指令本身,並不是程式碼,而是您在組譯時,組譯程式(MASM)會去看得懂這些假指令,而加以處理你所寫的程式碼,我將所有會用到的指令分別介紹如下:
1.指定微處理器模式 .8086、.186、.286、.386、.486
若指定.286,那麼程式在組譯時,就無法組譯386以上的組合語言指令碼,例如 EAX與 EBX 等32位元的暫存器,只有386以上CPU才有,所以若程式裡有 mov eax,eax 等指令時,組譯會發生組譯錯誤的訊息,唯有將微處理器模式改回 .386 或 .486 時才會組譯成功。所以小弟建議,一般都採用 .486 即可。功能就是將以上的程式碼組譯成 80486 的程式碼。
2.定義記憶體模式 .model
[模式」
於本站「組合語言基礎」=>『
組合語言原始檔基本架構』中,欲編寫組合語言程式一定要在原始檔中定義
記憶體模式,而其模式的種類與使用場合分別於下說明
記憶體模式 |
用途及記憶體使用範圍
|
tiny | 用於成立 .com 檔,程式段、資料段、額外段及堆疊段等四個區共用在同一個區段(64Kb)中,檔案最大僅能64Kb。 |
small | 程式段及資料段,各有64Kb的空間。為一般最常用的 .exe 檔的模式,建議使用。 |
medium | 程式段可超過64Kb,而資料段只有64Kb的空間。 |
compact | 資料段可超過64Kb,而程式段只有64Kb的空間。 |
large | 程式段與資料段,皆可大於 64 Kb。 |
huge | 同上,但更強的是,連同資料段中的任意某陣列,也可大於 64Kb 的範圍。 |
flat | ???? 用於 OS/2 系統。用途不詳...... |
使用範例
或
3.程式區段的起始 .code
[名稱]
定義程式區段的起始,如果我們定義兩個以上的程式段時,就必需為這些程式段命不同的名字;當我們只定義一個程式區段時,名稱可以省略不必命名,但在 tiny、small、compact 三種模式下,只能有一個程式段,所以就不必名稱了。
4.堆疊段起始 .stack
[size]
定義堆疊段的起始,定義時可指定大小,其中以 byte 為單位,若省略而不指定大小時,會以 1024 bytes 取代之。
5.一般資料段起始 .data、.fardata
[名稱]
近程資料段以 .data 起始,但其超出 64Kb 範圍的資料段稱為遠程資料段需以 .fardata 起始,而名稱,只有在定義兩個以上的遠程資料段時,才需命名。
6.無初始值之資料段與常數資料段 .data?、.fardata?、.const
我們如果只編寫單獨為組合語言程式時,這三個區段則無需定義,但若我們寫的程式要與高階語言作連結時,就必需將無初始值的資料移至 data? 或 fardata? 區段中,且需將用來定義字串、實數及常數的部份移至 .const 區段中。如下
- .model small
- .486
- .code
- :
- :
- .data
- :
- .data?
- mag01 db ? ; 單一變數,無初始值,單位 byte
- mag02 dw ?,?,? ; 一般變數,無初始值,單位 word 共 3 word = 6 bytes
- mag03 db 24 dup(?) ; 陣列,無初始值,容量大小為 24 bytes
-
- .stack
end |
7.結束整個模組的組譯 END
[程式段起始位址標名]
當我們寫程式區段的程式碼時,必需在啟始執行的位置設一個標名,那麼該程式被組譯後,程式就會從這個標名開始執行,假設我們把它放在程式段的第一列。那麼於 END 之後也必需加上這個標名,才算是指定成功,程式才會以這個標名做起始,如下:
- .model tiny
- .486
- .code
- begin0: mov ax,ds
- mov ......
- :
-
- begin1: ......
-
- .exit
-
-
-
-
- end begin0 ; 在此指定 .code 段的起始位址 cs:ip 值為 begin0
|
8.各區段暫存器之段位址初值的取得 @code、@data、@stack、@data? .....
利用 mov 指令,即可將各區段段位值址取得,方法如下
- :
- .stack 100h
- :
- .data
- :
- .code
- start0:
- :
- mov ax,@code ; 取出 .code 段的區段位址,存入 ax
- mov bx,@data ; 取出 .data 段的區段位址,存入 bx
- mov dx,@stack ; 取出 .stack 段的區段位址,存入 dx
- :
- end start0
|
程式開始執行時,cs=@code 、ss=@stack、sp=100h(因為如上的設定100h),而 ds 與 es 則是指向另一個程式前置區(psp),所以 ds 與 es 的值要由我們自行在程式段中寫程式來指定,用 mov ax,@data 與 mov ds,ax 來讓 ds 指向資料段,如此,我們才取得到資料段中所存的資料、變數,所以我們可以將程式的基本架構寫成如下的格式。
- .model small
- .486
- .code
- begin0: mov ax,@data
- mov ds,ax
-
-
-
- .exit
- .data
-
-
-
-
- .stack
- end begin0
|
9.程式段起始位址的假指令 .startup
使用時必需省略如上中的 end 後面的 begin0 及程式段的 begin0:,而且 .startup 也會自動執行 mov ax,@data 及 mov ds,ax,但如果是 .com 檔,它會自己加入 org 100h 指令,所以修改後架構,可參考「組合語言基礎」=>『
組合語言原始檔基本架構』。
也就是說用於 .com 檔時,可做下列改變
- :
- .code
- org 100h
- start:
- mov ..... ; 程式開始
- :
- end start ; 程式結束
|
轉換為
- :
- .code
- .startup
- mov ...... ; 程式開始 ( start 標名不見了,org 100h 也可省略了)
- :
- end ; 程式結束
|
然而用於 .exe 檔時,與上有所不同,如下
- :
- .code
- start:
- mov ax,@data
- mov ds,ax
- mov ...... ; 程式開始
- :
- end start ; 程式結束
|
轉換為
- :
- .code
- .startup
- mov ...... ; 程式開始 ( start 不見了,ds指向資料段也自動執行了)
- :
- end ; 程式結束
|
10.程式結束返回 DOS 之假指令 .exit [錯誤碼](errorlevel)
組譯程式在碰到 .exit 時,會自動翻譯為執行下列程式碼
- mov ah,4ch
- mov al,[錯誤碼] ; <= 若無輸入錯誤碼,則此列將省略不會執行
- int 21h
|
上面的功能是將程式結束,主控權交還給 DOS。
11.重覆定義資料 dup
可以重覆定一指定個數的記憶體變數內容,例如我們要定一個名稱為 poss0的記憶體變數,而內容是18個空白鍵,那麼我們的定義方式,可以有以下幾種為例:
方法一:
- poss0 db 20h,20h,20h,20h,20h,20h,20h,20h,20h
- db 20h,20h,20h,20h,20h,20h,20h,20h,20h
方法二:
poss0 db 18 dup(20h) |
12.常數符號之定義 equ 及 "="
可利用定義功能,將程式中的某數值,以一個名稱取代,這種定義的用途的好處,就是修改程式容易,當我們寫一個程式,但如檔案內有個A數值為98,我們將它用等號定義,名稱定義為 numa,有一天我們要修改程式的A數值時,我們可以直接從定義的部份更改其A值即可將整個檔的A值都改了,這樣方便吧,如下:
至於 equ 與 = 的差別,有兩個地方不同,第一就是 = 可以重覆定義同一個符號,但 equ 卻不行,第二就是 equ 可以定義字串等值,也就是定義變數的別名,但 = 卻不行。如下說明:
- .model small
- .code
- .startup
-
- aaa = 23
- aaa = aaa+2
-
- bbb equ 23
- bbb equ bbb+2 ; 這裡會有錯,equ不可重複定義同一符號
-
- ccc equ <aaa+bbb>
- ddd equ <ax,ccc>
-
- eee = <aaa+bbb> ; 這裡有錯,"=" 不能定義字串或變數別名
-
- mov ax,aaa
- mov bx,bbb
- mov cx,ccc
- mov ddd
- .exit
|
org, label,offset, segment,@,seg, assume,include等等。
條件式組譯
條件式組譯通常用在巨集檔裡面,因為同一個巨集中,為了避免佔用太大的記憶體,所以不是整個巨集裡的內容都需要用到,那麼那些沒有用到的部份就可不必組譯,可節省記憶體空間。
1.假設指令集 if....(else)....endif
使用格式 [ ifxx 條件運算式 ],其中 ifxx 可為以下各模式,當條件成立時,才會將 ifxx 與 endif 之間的內容組譯。
if 假如不等於 0
ife 假如等於 0
ifdef 假如已定義過
ifndef 假如沒定義過
ifb 假如為空白
ifnb 假如不為空白
ifidn A,B 假如A等於B
ifidni A,B 假如A等於B(但區分大小寫)
ifdif A,B 假如A不等於B
ifdifi A,B 假如A不等於B(但區分大小寫)
使用範例如下
abcd macro pra1,pra2 ifb <para1> ; 假如引數1沒有輸入任何東西,則令 ax =1
mov ax,1
endif
:
exitm |
2.條件二 else 指令集
else、elsee、elsedef、elseb、elsenb、...... 功能同上,需用在 if 之後。
3.重覆組譯指令 repeat.....endm
使用的格式為,[repeat 次數],當組譯器在組譯時,就會重複組譯 repeat 與 endm 之間的內容,所以通常用於巨集指令之內。
4.條件重覆組譯 while.....endm
使用格式為 [while 條件運算式],當條件運算式成立時,重複組譯 while 與 endm之間的程式敘述。
5.重覆參數組譯 for.....endm
使用格式為 [ for 參數,<數值1,數值2,數值3.....> ],重複組譯的次數是取決於共輸入幾個數值,如果只輸入三個數值,代表藉於 for 與 endm 之間的程式碼重複組譯三次,每一次的參數值都不一樣,第一次時參數是放入數值1,第二次是放人數值2,第三次是放人數值3。
6.重覆文字參數組譯 forc
使用格式為 [ forc 參數,<字串> ],重複組譯的次數是取決字串的長度,如果只輸入 345 三個字,代表藉於 forc 與 endm 之間的程式碼重複組譯三次,每一次的參數值都不一樣,第一次時參數是放入數值3,第二次是放人數值4,第三次是放人數值5。
高階指令
高階指令,也可稱為
流程控制指令,可以讓使用者在寫迴圈時更為簡便化,且由於它的語法與 C 語言相近,所以語法也就比較高階化,讓人容易一看就知道在寫什麼。
而可用的高階指令有 .if、.while、.repeat、.break、.continue 等五種,使用的語法分別列出如下:
(1) .if 的使用方式
下面是簡單的 .if 控制流程,首先會檢查條件式 A 是否有成立,如果有成立則往下一行執行,執行完 .if 的部份再跳至 .endif 離開迴圈,如果不成立則跳到下一個條件式 B 去檢查是否成立,依此類推,直到每個條件式都不成立時,才會去執行 .else 裡面的東西,執行完一樣是跳至 .endif 離開迴圈。
.if 條件式 A :
.elseif 條件式 B
:
.elseif 條件式 C ( 可以有無限多個 .elseif )
:
.else
:
.endif |
(2) .while 之使用方式
條件式 A如果為真,則執行 .while .... .endw 之間的指令的部份,直至條件式 A為假,才停止執行。
.while 條件式A : (假如條件式A為真則執行此處指令)
.endw |
(3) .repeat 之使用方式
有兩種使用方法,第一種是 .repeat 與 .until 配合,即當執行裡面的指令一次後,去判斷條件式 A是否為真,如果為假則繼續重覆此段的指令,如果為真則結束迴圈,如下
.repeat : (此段指令即欲執行之迴圈)
.until 條件式A |
第二種方法,是 .repeat 與 .untilcxz,即當執行完裡面的指令一次後,去判斷條件式A是否為真,且亦去判斷 cx 是否為 0,如果條件式A為假且cx不為0則繼續重覆此段的指令,如果為條件式A為真或者 cx=0 則結束迴圈,如下
.repeat : (此段指令即欲執行之迴圈)
.untilcxz 條件式A |
(4) .break 與 .continue
.break為結束迴圈,而 .continue 則為跳至迴圈的開頭,如下:
.while ..... :
.break
:
.endw
<=== 執行至 .break時,會直接結束迴圈,而從這開始執行
|
.while ..... <=== 執行至 .continue 時,會跳回這裡開始執行
:
.continue
:
.endw |
.break 與 .continue 也能與 .if 搭配使用,如下
.while ...... :
.continue .if 條件式A <=== 條件式A成立時,跳至迴圈開頭
:
.break .if 條件式B <== 條件式B成立時,結束迴圈
:
.endw |
(5)條件式的使用格式與方法
運算符號
|
意義
|
==
!=
>
<
>=
<=
&
!
&&
||
|
等於
不等於
大於
小於
大於等於
小於等於
AND
NOT
AND
OR
|