彙編僞指令
段定義僞指令
段定義僞指令是表示一個段開始和結束的命令,80x86有兩種段定義的方式:完整段定義和簡化段定義,分別使用不同的段定義僞指令來表示各種段。
1 完整的段定義僞指令
完整段定義僞指令的格式如下:
段名 SEGMENT
.
.
.
段名 ENDS
段名由用戶命名。對於數據段、附加段和堆棧段來說,段內一般是存儲單元的定義、分配等僞指令語句;對於代碼段中則主要是指令及僞指令語句。
定義了段還必須說明哪個段是代碼段,哪個段是數據段。ASSUME僞指令就是建立段和段寄存器關係的僞指令,其格式爲:
ASSUME 段寄存器名: 段名,…
段寄存器名必須是CS、DS、ES和SS中的一個,而段名必須是由SEGMENT定義的段名。
·定位類型:說明段的起始邊界值(物理地址)。
·組合類型:說明程序連接時的段組合方法。
·類別:在單引號中給出連接時組成段組的類型名。連接程序可把相同類別的段的位置靠在一起。
DD(define doubleword)
表
DT(define ten bytes)
表
DUP(duplicate)
表
對數據定義僞指令前面的變量還要注意它的類型屬性問題。變量表示該僞指令中的第一個數據項 的偏移地址,此外,它還具有一個類型屬性,用來表示該語句中的每一個數據項的長度(以字節爲單位表示),因此DB僞指令的類型屬性爲1,DW爲2,DD爲 4,DQ爲8,DT爲10。變量表達式的屬性和變量是相同的。彙編程序可以用這種隱含的類型屬性來確定某些指令是字指令還是字節指令。
下例中變量OPER1爲字節類型屬性,OPER2爲字類型屬性,所以第一條MOV指令應爲字節指令,第二條MOV指令應爲字指令。而第三條指令的變量表 達式OPER1+1爲字節類型屬性,AX卻爲字寄存器,第四條指令的OPER2爲字類型屬性,AL爲字節寄存器,因此,彙編程序將指示這兩條MOV指令出 錯:"類型不匹配"。
OPER1 DB ?, ?
OPER2 DW ?, ?
.
.
.
MOV OPER1, 0 ;字節指令
MOV OPER2, 0 ;字指令
MOV AX, OPER1+1 ;錯誤指令:類型不匹配
MOV AL, OPER2 ;錯誤指令:類型不匹配
PTR屬性操作符
下例中的兩條MOV指令把OPER1+1的類型屬性指定爲字,把OPER2的類型屬性指定爲字節,這樣指令中兩個操作數的屬性就一致了,彙編時就不會出錯了。
OPER1 DB ?, ?
OPER2 DW ?, ?
.
.
.
MOV AX, WORD PTR OPER1+1
MOV AL, BYTE PTR OPER2
LABEL僞指令
例如:
BYTE_ARRAY LABEL BYTE
WORD_ARRAY DW 50 DUP (?)
在50個字數組中的第一個字節的地址賦予兩個不同類型的變量名:字節類型的變量BYTE_ARRAY和字類型變量WORD_ARRAY。
在程序中訪問數組單元時,要按指令類型來選擇變量,如下面兩條指令:
MOV WORD_ARRAY + 2,0 ; 字指令,
; 把該數組的第3個和第4個字節置0
MOV BYTE_ARRAY + 2,0 ; 字節指令,
; 把該數組的第3個字節置0
段定義僞指令是表示一個段開始和結束的命令,80x86有兩種段定義的方式:完整段定義和簡化段定義,分別使用不同的段定義僞指令來表示各種段。
1 完整的段定義僞指令
完整段定義僞指令的格式如下:
段名 SEGMENT
.
.
.
段名 ENDS
段名由用戶命名。對於數據段、附加段和堆棧段來說,段內一般是存儲單元的定義、分配等僞指令語句;對於代碼段中則主要是指令及僞指令語句。
定義了段還必須說明哪個段是代碼段,哪個段是數據段。ASSUME僞指令就是建立段和段寄存器關係的僞指令,其格式爲:
ASSUME 段寄存器名: 段名,…
段寄存器名必須是CS、DS、ES和SS中的一個,而段名必須是由SEGMENT定義的段名。
·定位類型:說明段的起始邊界值(物理地址)。
·組合類型:說明程序連接時的段組合方法。
·類別:在單引號中給出連接時組成段組的類型名。連接程序可把相同類別的段的位置靠在一起。
例4.1
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg1 segment ; 定義數據段
.
.
.
data_seg1 ends
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg2 segment ; 定義附加段
.
.
.
data_seg2 ends
; * * * * * * * * * * * * * * * * * * * * * * *
code_seg segment ; 定義代碼段
assume cs:code_seg, ds:data_seg1, es:data_seg2
start: ; 程序執行的起始地址
; set DS register to current data segment
mov ax, data_seg1 ; 數據段地址
mov ds, ax ; 存入DS寄存器
; set ES register to current extra segment
mov ax, data_seg2 ; 附加段地址
mov es, ax ; 存入ES寄存器
.
.
.
code_seg ends ; 代碼段結束
; * * * * * * * * * * * * * * * * * * * * * * * * * *
end start
由於ASSUME僞指令只是指定某個段分配給哪一個段寄存器,它並不能把段地址裝入段寄存器中,所以在代碼段中,還必須把段地址裝入相應的段寄存器中:
MOV AX,DATA_SEG1 ; 數據段地址
MOV DS,AX ; 存入DS寄存器
MOV AX,DATA_SEG2 ; 附加段地址
MOV ES,AX ; 存入ES寄存器
如果程序中還定義了堆棧段STACK_SEG,也需要把段地址裝入SS中:
MOV AX,STACK_SEG ; 堆棧段地址
MOV SS,AX ; 存入ES寄存器
注意,在程序中不需要用指令裝入代碼段的段地址,因爲在程序初始化時,裝入程序已將代碼段的段地址裝入CS寄存器了。
爲了對段定義作進一步地控制,SEGMENT僞指令還可以增加類型及屬性的說明,其格式如下:
段名 SEGMENT [定位類型][組合類型]['類別']
.
.
.
段名 ENDS
[ ]中的內容是可選的,一般情況下,這些說明可以不用。但是,如果需要用連接程序把本程序與其他程序模塊相連接時,就需要提供類型和屬性的說明。
表 ·定位類型:說明段的起始邊界值(物理地址)。
注意:
定位類型的缺省項是PARA,即在未指定定位類型的情況下,則連接程序默認爲PARA。BYTE和WORD用於把其它段(通常是數據段)連入一個段時使用;DWORD一般用於運行在80386及後繼機型上的程序。
表 ·組合類型:說明程序連接時的段組合方法。
注意:組合類型的缺省項是PRIVATE。
例 在連接之前已定義兩個目標模塊如下:
模塊1 SSEG SEGMENT PARA STACK
DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
模塊2 DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
以上兩個模塊分別彙編後產生 .OBJ 文件,經連接程序連接後產生的 .EXE模塊如下:
模塊1 CSEG SEGMENT PARA 'Code'
模塊2 CSEG SEGMENT PARA 'Code'
模塊1+2 DSEG1 SEGMENT PARA PUBLIC 'Data'
模塊1 DSEG2 SEGMENT PARA
模塊2 DSEG2 SEGMENT PARA
模塊1 SSEG SEGMENT PARA STACK
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg1 segment ; 定義數據段
.
.
.
data_seg1 ends
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg2 segment ; 定義附加段
.
.
.
data_seg2 ends
; * * * * * * * * * * * * * * * * * * * * * * *
code_seg segment ; 定義代碼段
assume cs:code_seg, ds:data_seg1, es:data_seg2
start: ; 程序執行的起始地址
; set DS register to current data segment
mov ax, data_seg1 ; 數據段地址
mov ds, ax ; 存入DS寄存器
; set ES register to current extra segment
mov ax, data_seg2 ; 附加段地址
mov es, ax ; 存入ES寄存器
.
.
.
code_seg ends ; 代碼段結束
; * * * * * * * * * * * * * * * * * * * * * * * * * *
end start
由於ASSUME僞指令只是指定某個段分配給哪一個段寄存器,它並不能把段地址裝入段寄存器中,所以在代碼段中,還必須把段地址裝入相應的段寄存器中:
MOV AX,DATA_SEG1 ; 數據段地址
MOV DS,AX ; 存入DS寄存器
MOV AX,DATA_SEG2 ; 附加段地址
MOV ES,AX ; 存入ES寄存器
如果程序中還定義了堆棧段STACK_SEG,也需要把段地址裝入SS中:
MOV AX,STACK_SEG ; 堆棧段地址
MOV SS,AX ; 存入ES寄存器
注意,在程序中不需要用指令裝入代碼段的段地址,因爲在程序初始化時,裝入程序已將代碼段的段地址裝入CS寄存器了。
爲了對段定義作進一步地控制,SEGMENT僞指令還可以增加類型及屬性的說明,其格式如下:
段名 SEGMENT [定位類型][組合類型]['類別']
.
.
.
段名 ENDS
[ ]中的內容是可選的,一般情況下,這些說明可以不用。但是,如果需要用連接程序把本程序與其他程序模塊相連接時,就需要提供類型和屬性的說明。
表 ·定位類型:說明段的起始邊界值(物理地址)。
定位類型
|
說 明
|
BYTE
|
段可以從任何地址邊界開始
|
WORD
|
段從字邊界開始,即段的起始邊界值爲偶數
|
DWORD
|
段從雙字的邊界開始,即段的起始邊界值爲4的倍數
|
PARA
|
段從小段邊界開始,即段的起始邊界值爲16 (或10H) 的倍數
|
PAGE
|
段從頁邊界開始,即段的起始邊界值爲256 (或100H) 的倍數
|
注意:
定位類型的缺省項是PARA,即在未指定定位類型的情況下,則連接程序默認爲PARA。BYTE和WORD用於把其它段(通常是數據段)連入一個段時使用;DWORD一般用於運行在80386及後繼機型上的程序。
表 ·組合類型:說明程序連接時的段組合方法。
組合類型
|
說 明
|
PRIVATE
|
該段爲私有段,連接時將不與其它模塊中的同名段合併
|
PUBLIC
|
該段連接時將與其它同名段連接在一起,連接次序由連接命令指定
|
COMMON
|
該段在連接時與其它同名段有相同的起始地址,所以會產生覆蓋
|
AT 表達式
|
段地址=表達式的值,其值必爲16位但AT不能用來指定代碼段
|
MEMORY
|
與PUBLIC同義
|
STACK
|
將多個同名堆棧段連接在一起,SP設置在第一個堆棧段的開始
|
注意:組合類型的缺省項是PRIVATE。
例 在連接之前已定義兩個目標模塊如下:
模塊1 SSEG SEGMENT PARA STACK
DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
模塊2 DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
以上兩個模塊分別彙編後產生 .OBJ 文件,經連接程序連接後產生的 .EXE模塊如下:
模塊1 CSEG SEGMENT PARA 'Code'
模塊2 CSEG SEGMENT PARA 'Code'
模塊1+2 DSEG1 SEGMENT PARA PUBLIC 'Data'
模塊1 DSEG2 SEGMENT PARA
模塊2 DSEG2 SEGMENT PARA
模塊1 SSEG SEGMENT PARA STACK
2 存儲模型與簡化段定義僞指令
較新版本的彙編程序(MASM5.0與MASM6.0)除支持完整段定義僞指令外,還提供了一種新的簡單易用的存儲模型和簡化的段定義僞指令。
1.存儲模型僞指令
存儲模型的作用是什麼呢?存儲模型決定一個程序的規模,也確定進行子程序調用、指令轉移和數據訪問的缺省屬性(NEAR或FAR)。當使用簡化段定義的源程序格式時,在段定義語句之前必須有存儲模型 .MODEL語句,說明在存儲器中應如何安放各個段。
MODEL僞指令的常用格式如下:
. .MODEL 存儲模型
2. 簡化的段僞指令
簡化的段定義語句書寫簡短,語句.CODE、.DATA和.STACK分別表示代碼數據段和堆棧段的開始,一個段的開始自動結束前面一個段。採用簡化段指令之前必須有存儲模型語句.MODEL。
3.與簡化段定義有關的預定義符號
彙編程序給出了與簡化段定義有關的一組預定義符號,它們可在程序中出現,並由彙編程序識別使用。有關的預定義符號如下:
(1)@code 由.CODE 僞指令定義的段名或段組名。
(2)@data 由.DATA 僞指令定義的段名,或由 .DATA 、.DATA?、
.CONST和 .STACK所定義的段組名。
(3)@stack 堆棧段的段名或段組名。
4.簡化段定義舉例
1. 存儲模型僞指令
表 MASM 5.0和MASM 6.0支持的存儲模型:
注意:Small 模型是一般應用程序最常用的一種模型,因爲只有一個代碼段和一個數據段,所以數據和代碼都是近訪問的。這種模型的數據段是指數據段、堆棧段和附加段的總和。
在DOS下用彙編語言編程時,可根據程序的不同特點選擇前6種模型,一般可以選用SMALL模型。另外,TINY模型將產生COM程序,其他模型產生 EXE程序。FLAT模型只能運行在32位x86 CPU上,DOS下不允許使用這種模型。當與高級語言混合編程時,兩者的存儲模型應當一致。
2. 簡化的段僞指令
表 簡化段僞指令的格式如下表:
3.與簡化段定義有關的預定義符號
下面的舉例說明預定義符號的使用方法。在完整的段定義情況下,在程序的一開始,需要用段名裝入數據段寄存器,如例4.1中的
mov ax,data_seg1
mov ds,ax
若用簡化段定義,則數據段只用.data來定義,而並未給出段名,此時可用
mov ax,@data
mov ds,ax
這裏預定義符號@data就給出了數據段的段名。
4.簡化段定義舉例
例
較新版本的彙編程序(MASM5.0與MASM6.0)除支持完整段定義僞指令外,還提供了一種新的簡單易用的存儲模型和簡化的段定義僞指令。
1.存儲模型僞指令
存儲模型的作用是什麼呢?存儲模型決定一個程序的規模,也確定進行子程序調用、指令轉移和數據訪問的缺省屬性(NEAR或FAR)。當使用簡化段定義的源程序格式時,在段定義語句之前必須有存儲模型 .MODEL語句,說明在存儲器中應如何安放各個段。
MODEL僞指令的常用格式如下:
. .MODEL 存儲模型
2. 簡化的段僞指令
簡化的段定義語句書寫簡短,語句.CODE、.DATA和.STACK分別表示代碼數據段和堆棧段的開始,一個段的開始自動結束前面一個段。採用簡化段指令之前必須有存儲模型語句.MODEL。
3.與簡化段定義有關的預定義符號
彙編程序給出了與簡化段定義有關的一組預定義符號,它們可在程序中出現,並由彙編程序識別使用。有關的預定義符號如下:
(1)@code 由.CODE 僞指令定義的段名或段組名。
(2)@data 由.DATA 僞指令定義的段名,或由 .DATA 、.DATA?、
.CONST和 .STACK所定義的段組名。
(3)@stack 堆棧段的段名或段組名。
4.簡化段定義舉例
1. 存儲模型僞指令
表 MASM 5.0和MASM 6.0支持的存儲模型:
存儲模型
|
功 能
|
適用操作系統
|
Tiny (微型) | 所有數據和代碼都放在一個段內,其訪問都爲NEAR型,整個程序≤64K,並會產生.COM文件。 | MS-DOS |
Small (小型) | 所有代碼在一個64KB的段內,所有數據在另一個64KB的段內(包括數據段,堆棧段和附加段)。 | MS-DOS Windows |
Medium (中型) | 所有代碼>64K時可放在多個代碼段中,轉移或調用可爲FAR型。所有數據限在一個段內,DS可保持不變。 | MS-DOS Windows |
Compact(緊湊型) | 所有代碼限在一個段內,轉移或調用可爲NEAR型。數據>64K時,可放在多個段中。 | MS-DOS Windows |
Large (大型) | 允許代碼段和數據段都可超過64K,被放置在有多個段內,所以數據和代碼都是遠訪問。 | MS-DOS Windows |
Huge (巨型) | 單個數據項可以超過64K,其它同Large模型 | MS-DOS Windows |
Flat (平展型) | 所有代碼和數據放置在一個段中,但段地址是32位的,所以整個程序可爲4GB。MASM 6.0支持該模型。 | OS/2 WindowsNT |
注意:Small 模型是一般應用程序最常用的一種模型,因爲只有一個代碼段和一個數據段,所以數據和代碼都是近訪問的。這種模型的數據段是指數據段、堆棧段和附加段的總和。
在DOS下用彙編語言編程時,可根據程序的不同特點選擇前6種模型,一般可以選用SMALL模型。另外,TINY模型將產生COM程序,其他模型產生 EXE程序。FLAT模型只能運行在32位x86 CPU上,DOS下不允許使用這種模型。當與高級語言混合編程時,兩者的存儲模型應當一致。
2. 簡化的段僞指令
表 簡化段僞指令的格式如下表:
簡化段僞指令
|
功 能
|
註釋
|
.CODE [段名] | 創建一個代碼段 | 段名爲可選項,如不給出段名,則採用默認段名。對於多個代碼段的模型,則應爲每個代碼段指定段名。 |
.DATA | 創建一個數據段 | 段名是:_DATA |
.DATA? | 創建無初值變量的數據段 | 段名是:_BSS |
.FARDATA [段名] | 建立有初值的遠調用數據段 | 可指定段名,如不指定,則將以FAR_DATA命名。 |
.FARDATA? [段名] | 建立無初值的遠調用數據段 | 可指定段名,如不指定,則將以FAR_BSS命名。 |
.CONST | 建立只讀的常量數據段 | 段名是:CONST |
.STACK [大小] | 創建一個堆棧段並指定堆棧段大小 | 段名是:stack。如不指定堆棧段大小,則缺省值爲1KB |
3.與簡化段定義有關的預定義符號
下面的舉例說明預定義符號的使用方法。在完整的段定義情況下,在程序的一開始,需要用段名裝入數據段寄存器,如例4.1中的
mov ax,data_seg1
mov ds,ax
若用簡化段定義,則數據段只用.data來定義,而並未給出段名,此時可用
mov ax,@data
mov ds,ax
這裏預定義符號@data就給出了數據段的段名。
4.簡化段定義舉例
例
.MODEL SMALL
.STACK 100H ; 定義堆棧段及其大小
.DATA ; 定義數據段
.
.
.
.CODE ; 定義代碼段
START: ; 起始執行地址標號
MOV AX, @DATA ; 數據段地址
MOV DS, AX ; 存入數據段寄存器
.
.
.
MOV AX, 4C00H
INT 21H
END START ; 程序結束
從例可以看出,簡化段定義比完整的段定義簡單得多。但由於完整的段定義可以全面地說明段的各種類型與屬性,因此在很多情況下仍需使用它。
.STACK 100H ; 定義堆棧段及其大小
.DATA ; 定義數據段
.
.
.
.CODE ; 定義代碼段
START: ; 起始執行地址標號
MOV AX, @DATA ; 數據段地址
MOV DS, AX ; 存入數據段寄存器
.
.
.
MOV AX, 4C00H
INT 21H
END START ; 程序結束
從例可以看出,簡化段定義比完整的段定義簡單得多。但由於完整的段定義可以全面地說明段的各種類型與屬性,因此在很多情況下仍需使用它。
段組定義僞指令
段組定義僞指令能把多個同類段合併爲一個64KB的物理段,並用一個段組名統一存取它。段組定義僞指令GROUP的格式如下:
段組名 GROUP 段名 [, 段名 …]
我們已經知道在各種存儲模型中,彙編程序自動地把各數據段組成一個段組DGROUP,以便程序在訪問各數據段時使用一個數據段寄存器DS,而GROUP僞指令允許用戶自行指定段組。
段組定義僞指令能把多個同類段合併爲一個64KB的物理段,並用一個段組名統一存取它。段組定義僞指令GROUP的格式如下:
段組名 GROUP 段名 [, 段名 …]
我們已經知道在各種存儲模型中,彙編程序自動地把各數據段組成一個段組DGROUP,以便程序在訪問各數據段時使用一個數據段寄存器DS,而GROUP僞指令允許用戶自行指定段組。
例 將兩個數據段DSEG1 和DSEG2合併在一個段組DATAGROUP中。
;----------------------------------------------------
DSEG1 SEGMENT WORD PUBLIC 'DATA'
.
.
.
DSEG1 ENDS
;---------------------------------------------------
DSEG2 SEGMENT WORD PUBLIC 'DATA'
.
.
.
DSEG2 ENDS
MOV AX, @DATA ; 數據段地址
MOV DS, AX ; 存入數據段寄存器
.
.
.
;---------------------------------------------------
DATAGROUP GROUP DSEG1, DSEG2 ;組合成段組
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS : CSEG, DS : DATAGROUP
START: MOV AX, DATAGROUP
MOV DS, AX ;DS賦值爲段組地址
.
.
.
MOV AX, 4C00H
INT 21H
CSEG ENDS
;-----------------------------------------------------
END START
利用GROUP僞指令定義段組後,段組內統一爲一個段地址,各段定義的變量和標號都可以用同一個段寄存器進行訪問。
;----------------------------------------------------
DSEG1 SEGMENT WORD PUBLIC 'DATA'
.
.
.
DSEG1 ENDS
;---------------------------------------------------
DSEG2 SEGMENT WORD PUBLIC 'DATA'
.
.
.
DSEG2 ENDS
MOV AX, @DATA ; 數據段地址
MOV DS, AX ; 存入數據段寄存器
.
.
.
;---------------------------------------------------
DATAGROUP GROUP DSEG1, DSEG2 ;組合成段組
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS : CSEG, DS : DATAGROUP
START: MOV AX, DATAGROUP
MOV DS, AX ;DS賦值爲段組地址
.
.
.
MOV AX, 4C00H
INT 21H
CSEG ENDS
;-----------------------------------------------------
END START
利用GROUP僞指令定義段組後,段組內統一爲一個段地址,各段定義的變量和標號都可以用同一個段寄存器進行訪問。
程序開始和結束僞指令
在程序的開始可以用NAME或TITLE作爲模塊的名字,其格式爲:
NAME 模塊名
TITLE 文件名
表示源程序結束的僞指令的格式爲:
END [標號]
在程序的開始可以用NAME或TITLE作爲模塊的名字,其格式爲:
NAME 模塊名
TITLE 文件名
表示源程序結束的僞指令的格式爲:
END [標號]
注意:NAME及TITLE僞指令並不是必需的,如果程序中既無NAME又無TITLE僞指令,則將用源文件名作爲模塊名。程序中經常使用TITLE,這樣可以在列表文件中打印出標題來。
END僞指令中的"標號"指示程序開始執行的起始地址。如果多個程序模塊相連接,則只有主程序的END要加上標號,其他子程序模塊則只用END而不必指 定標號。例4.1~4.3的最後使用了END START僞指令。彙編程序將在遇END時結束彙編,並且程序在運行時從START開始執行。
END僞指令中的"標號"指示程序開始執行的起始地址。如果多個程序模塊相連接,則只有主程序的END要加上標號,其他子程序模塊則只用END而不必指 定標號。例4.1~4.3的最後使用了END START僞指令。彙編程序將在遇END時結束彙編,並且程序在運行時從START開始執行。
數據定義及存儲器分配僞指令
80x86提供了各種數據及存儲器分配僞指令,這些僞指令在彙編程序對源程序進行彙編期間,由彙編程序完成數據類型定義及存儲器分配等功能。
數據定義及存儲器分配僞指令的格式是:
80x86提供了各種數據及存儲器分配僞指令,這些僞指令在彙編程序對源程序進行彙編期間,由彙編程序完成數據類型定義及存儲器分配等功能。
數據定義及存儲器分配僞指令的格式是:
[變量] 助記符 操作數[, …,操作數] [ ;註釋]
下面介紹ORG僞指令以及常用的數據定義僞指令。
ORG(origin)
ORG僞指令用來表示起始的偏移地址,緊接着ORG的數值就是偏移地址的起始值。ORG僞操作常用在數據段指定數據的存儲地址,有時也用來指定代碼段的起始地址。
DB(define byte)
DB僞指令用來定義字節,對其後的每個數據都存儲在一個字節中。DB能定義十進制數、二進制數、十六進制數和ASCII字符,二進制數和十六進制數要分 別用"B"和"H"表示,ASCII字符用單引號(' ')括起來。DB還是唯一能定義字符串的僞操作,串中的每個字符佔用一個字節。
DW(define word)
DW僞指令用來定義字,對其後的每個數據分配2個字節(1個字),數據的低8位存儲在低字節地址中,高8位存儲在高字節地址中,如下例中的變量 DATA8的數據存儲在0070字地址中,其中0070字節存儲0BAH,0071字節存儲03H。DW還可存儲變量或標號的偏移地址。見左面DW僞指令 的例子。
DD(define doubleword)
DD僞指令用來定義雙字,對其 後的每個數據分配4個字節(2個字)。該僞指令同樣將數據轉換爲十六進制,並根據低地址存儲低字節,高地址存儲高字節的規則來存放數據。如下例 DATA15的存儲情況是:00A8:0F2H,00A9H:57H,00AAH:2AH,00ABH:5CH。
用DD存入地址時,第一個字爲偏移地址,第二個字爲段地址。
DQ(define quadword)
DQ僞指令用來定義4字,即64位字長的數據,DQ之後的每個數據佔用8個字節(4個字)。
DT(define ten bytes)
DT僞指令用來爲壓縮的BCD數據分配存儲單元,它雖然可以分配10個字節(5個字),但最多隻能輸入18個數字,要注意的是,數據後面不需要加"H"。左面是DQ和DT的例子。
DUP(duplicate)
DUP僞指令可以按照給定的次數來複制某個(某些)操作數,它可以避免多次鍵入同樣一個數據。例如,把6個FFH存入相繼字節中,可以用下面兩種方法,顯然用DUP的方法更簡便些。
存入6字節的FFH
DATA20 DB 0FFH 0FFH 0FFH 0FFH 0FFH 0FFH;
DATA21 DB 6 DUP(0FFH)
DUP操作一般用來保留數據區,如用數據定義僞指令"DB 64 DUP(?)"可爲堆棧段保留64個字節單元。DUP還可以嵌套,其用法見左例。
PTR屬性操作符
PTR指定操作數的類型屬性,它優先於隱含的類型屬性。其格式爲:
類型 PTR 變量[ ± 常數表達式]
其中類型可以是BYTE、WORD、DWORD、FWORD、QWORD或TBYTE,這樣變量的類型就可以指定了。
LABEL僞指令
LABEL可以使同一個變量具有不同的類型屬性。其格式爲:
變量名 LABEL 類型
或 標號 LABEL 類型
其中變量的數據類型可以是BYTE,WORD,DWORD,標號的代碼類型可以是NEAR或FAR。
數據定義及存儲器分配僞指令格式中的"變量"是操作數的符號地址,它是可有可無的,它的作用與指令語句前的標號相同,區別是變量後面不加冒號。如果語句中有變量,那麼彙編程序將操作數的第一個字節的偏移地址賦於這個變量。
"註釋"字段用來說明該僞指令的功能,它也不是必須有的。
"助記符"字段說明所用僞指令的助記符。
DB(define byte)
請看下面數據定義的例子,注意DB定義的每個數據的存儲情況,左邊第一列是彙編程序爲數據分配的字節地址,第二列是相應地址中存儲的數據或ASCII字符 (均用十六進制表示)。變量DATA7定義了3個數據和一個字符串,每個數據或串用","分開,它們分別存儲在偏移地址002E開始的6個字節單元中。
表
下面介紹ORG僞指令以及常用的數據定義僞指令。
ORG(origin)
ORG僞指令用來表示起始的偏移地址,緊接着ORG的數值就是偏移地址的起始值。ORG僞操作常用在數據段指定數據的存儲地址,有時也用來指定代碼段的起始地址。
DB(define byte)
DB僞指令用來定義字節,對其後的每個數據都存儲在一個字節中。DB能定義十進制數、二進制數、十六進制數和ASCII字符,二進制數和十六進制數要分 別用"B"和"H"表示,ASCII字符用單引號(' ')括起來。DB還是唯一能定義字符串的僞操作,串中的每個字符佔用一個字節。
DW(define word)
DW僞指令用來定義字,對其後的每個數據分配2個字節(1個字),數據的低8位存儲在低字節地址中,高8位存儲在高字節地址中,如下例中的變量 DATA8的數據存儲在0070字地址中,其中0070字節存儲0BAH,0071字節存儲03H。DW還可存儲變量或標號的偏移地址。見左面DW僞指令 的例子。
DD(define doubleword)
DD僞指令用來定義雙字,對其 後的每個數據分配4個字節(2個字)。該僞指令同樣將數據轉換爲十六進制,並根據低地址存儲低字節,高地址存儲高字節的規則來存放數據。如下例 DATA15的存儲情況是:00A8:0F2H,00A9H:57H,00AAH:2AH,00ABH:5CH。
用DD存入地址時,第一個字爲偏移地址,第二個字爲段地址。
DQ(define quadword)
DQ僞指令用來定義4字,即64位字長的數據,DQ之後的每個數據佔用8個字節(4個字)。
DT(define ten bytes)
DT僞指令用來爲壓縮的BCD數據分配存儲單元,它雖然可以分配10個字節(5個字),但最多隻能輸入18個數字,要注意的是,數據後面不需要加"H"。左面是DQ和DT的例子。
DUP(duplicate)
DUP僞指令可以按照給定的次數來複制某個(某些)操作數,它可以避免多次鍵入同樣一個數據。例如,把6個FFH存入相繼字節中,可以用下面兩種方法,顯然用DUP的方法更簡便些。
存入6字節的FFH
DATA20 DB 0FFH 0FFH 0FFH 0FFH 0FFH 0FFH;
DATA21 DB 6 DUP(0FFH)
DUP操作一般用來保留數據區,如用數據定義僞指令"DB 64 DUP(?)"可爲堆棧段保留64個字節單元。DUP還可以嵌套,其用法見左例。
PTR屬性操作符
PTR指定操作數的類型屬性,它優先於隱含的類型屬性。其格式爲:
類型 PTR 變量[ ± 常數表達式]
其中類型可以是BYTE、WORD、DWORD、FWORD、QWORD或TBYTE,這樣變量的類型就可以指定了。
LABEL僞指令
LABEL可以使同一個變量具有不同的類型屬性。其格式爲:
變量名 LABEL 類型
或 標號 LABEL 類型
其中變量的數據類型可以是BYTE,WORD,DWORD,標號的代碼類型可以是NEAR或FAR。
數據定義及存儲器分配僞指令格式中的"變量"是操作數的符號地址,它是可有可無的,它的作用與指令語句前的標號相同,區別是變量後面不加冒號。如果語句中有變量,那麼彙編程序將操作數的第一個字節的偏移地址賦於這個變量。
"註釋"字段用來說明該僞指令的功能,它也不是必須有的。
"助記符"字段說明所用僞指令的助記符。
DB(define byte)
請看下面數據定義的例子,注意DB定義的每個數據的存儲情況,左邊第一列是彙編程序爲數據分配的字節地址,第二列是相應地址中存儲的數據或ASCII字符 (均用十六進制表示)。變量DATA7定義了3個數據和一個字符串,每個數據或串用","分開,它們分別存儲在偏移地址002E開始的6個字節單元中。
表
; DB 例子的列表文件 0000 19 DATA1 DB 25 ; 十進制數 0001 89 DATA2 DB 10001001B ; 二進制數 0002 12 DATA3 DB 12H ; 十六進制數 0010 ORG 0010H ; 指定偏移地址爲10h 0010 32 35 39 31 DATA4 DB '2591' ; ASCII碼數 0018 ORG 0018H ; 指定偏移地址爲18h 0018 00 DATA5 DB ? ; 保留一個字節 0020 ORG 0020H ; 指定偏移地址爲20h 0020 4D 79 20 6E 61 6D DATA6 DB 'My name is Joe' ; ASCII碼字符 65 20 69 73 20 4A 6F 65 002E 0A 10 02 31 30 42 DATA7 DB 10,10H,10B,'10B' ; 不同的數據類型 |
DW(define word)
表
表
; DW 僞指令例子的列表文件 0070 0RG 70H ;指定起始地址 0070 03BA DATA8 DW 954 ; 十進制數 0072 0954 DATA9 DW 100101010100B ; binary 0074 253F DATA10 DW 253FH ; 十六進制數 0076 FFFB DATA11 DW -5 ; 負數 0080 ORG 80H 0080 0009 FFFF 0007 000C DATA12 DW 9,-1,7,0CH,00100000B,100,'HI' 0020 0064 4849 ; 各種類型數據 |
DD(define doubleword)
表
; DD例子的列表文件 00A0 ORG 00A0H ; 指定起始地址 00A0 FF030000 DATA13 DD 1023 ; 十進制數 00A4 5C960800 DATA14 DD 10001001011001011100B ; 二進制數 00A8 F2572A5C DATA15 DD 5C2A57F2H ; 十六進制數 00AC 23000000 89470300 DATA16 DD 23H,34789H,65533 ; 各種數據 FDFF0000 |
DT(define ten bytes)
表
; DQ、DT例子的列表文件 00C0 ORG 00C0H 00C0 C223450000000000 DATA17 DQ 4523C2H ; 十六進制數 00C8 4948000000000000 DATA18 DQ 'HI' ; ASCII字符 00D0 0000000000000000 DATA19 DQ ? ; 分配8個字節單元 00E0 ORG 00E0H 00E0 2998564379860000 DATA20 DT 867943569829 ; 壓縮的BCD數 0000 00EA 0000000000000000 DATA21 DT ? ; 分配10個字節單元 0000 |
DUP(duplicate)
表
; DUP例子的列表文件 0100 ORG 0100H ; 數據區的起始地址 0100 0020[ DATA22 DB 32 DUP(?) ; 保留32字節 ?? ] 0120 ORG 0120H 0120 0005[ DATA23 DB 5 DUP(2 DUP(99)); 存入10個字節的99 0002[ 63 ] ] 012A 0008[ ATA24 DW 8 DUP(?) ; 保留8個字節 ???? ] |
對數據定義僞指令前面的變量還要注意它的類型屬性問題。變量表示該僞指令中的第一個數據項 的偏移地址,此外,它還具有一個類型屬性,用來表示該語句中的每一個數據項的長度(以字節爲單位表示),因此DB僞指令的類型屬性爲1,DW爲2,DD爲 4,DQ爲8,DT爲10。變量表達式的屬性和變量是相同的。彙編程序可以用這種隱含的類型屬性來確定某些指令是字指令還是字節指令。
下例中變量OPER1爲字節類型屬性,OPER2爲字類型屬性,所以第一條MOV指令應爲字節指令,第二條MOV指令應爲字指令。而第三條指令的變量表 達式OPER1+1爲字節類型屬性,AX卻爲字寄存器,第四條指令的OPER2爲字類型屬性,AL爲字節寄存器,因此,彙編程序將指示這兩條MOV指令出 錯:"類型不匹配"。
OPER1 DB ?, ?
OPER2 DW ?, ?
.
.
.
MOV OPER1, 0 ;字節指令
MOV OPER2, 0 ;字指令
MOV AX, OPER1+1 ;錯誤指令:類型不匹配
MOV AL, OPER2 ;錯誤指令:類型不匹配
PTR屬性操作符
下例中的兩條MOV指令把OPER1+1的類型屬性指定爲字,把OPER2的類型屬性指定爲字節,這樣指令中兩個操作數的屬性就一致了,彙編時就不會出錯了。
OPER1 DB ?, ?
OPER2 DW ?, ?
.
.
.
MOV AX, WORD PTR OPER1+1
MOV AL, BYTE PTR OPER2
LABEL僞指令
例如:
BYTE_ARRAY LABEL BYTE
WORD_ARRAY DW 50 DUP (?)
在50個字數組中的第一個字節的地址賦予兩個不同類型的變量名:字節類型的變量BYTE_ARRAY和字類型變量WORD_ARRAY。
在程序中訪問數組單元時,要按指令類型來選擇變量,如下面兩條指令:
MOV WORD_ARRAY + 2,0 ; 字指令,
; 把該數組的第3個和第4個字節置0
MOV BYTE_ARRAY + 2,0 ; 字節指令,
; 把該數組的第3個字節置0
表達式賦值僞操作EQU
EQU是一個賦值僞操作(僞指令),它給一個數據標號賦於一個常數值,但這個常數不佔用存儲單元。當這個數據標號出現在程序中時,彙編程序即用它的常數值代替數據標號。EQU可以在數據段之外使用,甚至可用在代碼段中間。
= 僞操作
賦值僞操作"="的作用與EQU類似。它們之間的區別是,EQU僞操作中的標號名是不允許重複定義的,而=僞操作是允許重複定義的。
使用EQU操作的優點可從下面的例子中看出:
COUNT EQU 25
COUNTER DB COUNT
MOV AL, COUNT
假定在數據段和代碼段中要多次使用一個數據(如25),那麼在編程時凡是用到25的地方都可用數據標號COUNT來表示。如果程序想修改這個數據,那麼只需修改EQU的賦值,而無須修改程序中其它部分,如COUNTER和MOV語句就不必修改。
EQU還可給表達式賦予一個名字,EQU的用法舉例如下:
DATA EQU HEIGHT + 12 ; 地址表達式賦以符號名
ALPHA EQU 7 ; 常數賦以符號名
BETA EQU ALPHA-2 ; 把7-2=5賦以符號名BETA
ADDR EQU VAR + BETA ; VAR+5賦以符號名ADDR。
B EQU [BP + 8] ; 變址引用賦以符號名 B
P8 EQU DS:[BP + 8] ; 加段前綴的變址引用賦以符號名P8
注意:在EQU語句的表達式中,如果有變量或標號的表達式,則在該語句前應該先給出它們的定義。如上例,ALPHA必須在BETA之前定義,否則彙編程序將指示出錯。
例如, TMP EQU 5
TMP EQU TMP+1 則是錯誤語句,因爲TMP已賦值爲5,就不能再把它定義爲其它數值。
而 TMP = 5
TMP = TMP+1 則是允許使用的,因爲=僞操作允許重複定義。第一個語句TMP的值爲5,第二個語句TMP的值就爲6了。
EQU是一個賦值僞操作(僞指令),它給一個數據標號賦於一個常數值,但這個常數不佔用存儲單元。當這個數據標號出現在程序中時,彙編程序即用它的常數值代替數據標號。EQU可以在數據段之外使用,甚至可用在代碼段中間。
= 僞操作
賦值僞操作"="的作用與EQU類似。它們之間的區別是,EQU僞操作中的標號名是不允許重複定義的,而=僞操作是允許重複定義的。
使用EQU操作的優點可從下面的例子中看出:
COUNT EQU 25
COUNTER DB COUNT
MOV AL, COUNT
假定在數據段和代碼段中要多次使用一個數據(如25),那麼在編程時凡是用到25的地方都可用數據標號COUNT來表示。如果程序想修改這個數據,那麼只需修改EQU的賦值,而無須修改程序中其它部分,如COUNTER和MOV語句就不必修改。
EQU還可給表達式賦予一個名字,EQU的用法舉例如下:
DATA EQU HEIGHT + 12 ; 地址表達式賦以符號名
ALPHA EQU 7 ; 常數賦以符號名
BETA EQU ALPHA-2 ; 把7-2=5賦以符號名BETA
ADDR EQU VAR + BETA ; VAR+5賦以符號名ADDR。
B EQU [BP + 8] ; 變址引用賦以符號名 B
P8 EQU DS:[BP + 8] ; 加段前綴的變址引用賦以符號名P8
注意:在EQU語句的表達式中,如果有變量或標號的表達式,則在該語句前應該先給出它們的定義。如上例,ALPHA必須在BETA之前定義,否則彙編程序將指示出錯。
例如, TMP EQU 5
TMP EQU TMP+1 則是錯誤語句,因爲TMP已賦值爲5,就不能再把它定義爲其它數值。
而 TMP = 5
TMP = TMP+1 則是允許使用的,因爲=僞操作允許重複定義。第一個語句TMP的值爲5,第二個語句TMP的值就爲6了。
地址計數器與對準僞指令
1.地址計數器$
在彙編程序對源程序彙編的過程中,使用地址計數器來保存當前正在彙編的指令的地址。地址計數器的值在彙編語言中可用$來表示。
當$用在僞指令的參數字段時,它所表示的是地址計數器的當前值
2.EVEN僞指令
EVEN僞指令使下一個變量或指令開始於偶數字節地址。
3. ALIGN僞指令
ALIGN僞指令使它後面的數據或指令從2的整數倍地址開始。其格式爲:
ALIGN 2n (n爲任意整數)
1.地址計數器$
在彙編程序對源程序彙編的過程中,使用地址計數器來保存當前正在彙編的指令的地址。地址計數器的值在彙編語言中可用$來表示。
當$用在僞指令的參數字段時,它所表示的是地址計數器的當前值
2.EVEN僞指令
EVEN僞指令使下一個變量或指令開始於偶數字節地址。
3. ALIGN僞指令
ALIGN僞指令使它後面的數據或指令從2的整數倍地址開始。其格式爲:
ALIGN 2n (n爲任意整數)
1.地址計數器$
彙編語言允許用戶直接用$來引用地址計數器的值,例如指令:
JMP $+ 6
它的轉向地址是JMP指令的首地址加上6。當$用在指令中時,它表示本條指令的第一個字節的地址。在這裏,$+ 6必須是另一條指令的首地址。否則,彙編程序將指示出錯信息。
當$用在僞指令的參數字段時,則和它用在指令中的情況不同,它所表示的是地址計數器的當前值。例如指令:
ARRAY DW 1, 2, $+ 4, 3, 4, $+ 4
假設彙編時ARRAY 分配的偏移地址爲0074H,則彙編後,$+ 4所在的兩個字單元:
(ARRAY+4)=0078+4=007CH
(ARRAY+0A)=007E+4=0082H
應當注意,ARRAY數組中的兩個$+ 4得到的結果是不同的,這是由於$的值是在不斷變化的緣故。當在指令中用到$時,它只代表該指令的首地址,而與$本身所在的字節無關。
2.EVEN僞指令
例如:
DATA_SEG SEGMENT
BYTE_DAT DB ?
EVEN
WORD_DAT DW 100 DUP (?)
DATA_SEG ENDS
一個字的地址最好從偶地址開始,所以對於字數組爲了保證它從偶地址開始,可以在DW定義之前用EVEN僞指令來達到這一目的。
3. ALIGN僞指令
例如:
.
ALIGN 4
ARRAY DD 100 DUP (?)
ALIGN僞指令保證了雙字數組ARRAY地址邊界從4的倍數開始。
ALIGN僞指令是將當前偏移地址指針指向2的乘方的整數倍的地址,如果源地址指針以指向2的乘方的整數倍的地址,則不作調整;否則將指針加以一個數,使地址指針指向下一個2的乘方的整數倍的地址。
當然,ALIGN 2和EVEN是等價的。
彙編語言允許用戶直接用$來引用地址計數器的值,例如指令:
JMP $+ 6
它的轉向地址是JMP指令的首地址加上6。當$用在指令中時,它表示本條指令的第一個字節的地址。在這裏,$+ 6必須是另一條指令的首地址。否則,彙編程序將指示出錯信息。
當$用在僞指令的參數字段時,則和它用在指令中的情況不同,它所表示的是地址計數器的當前值。例如指令:
ARRAY DW 1, 2, $+ 4, 3, 4, $+ 4
假設彙編時ARRAY 分配的偏移地址爲0074H,則彙編後,$+ 4所在的兩個字單元:
(ARRAY+4)=0078+4=007CH
(ARRAY+0A)=007E+4=0082H
應當注意,ARRAY數組中的兩個$+ 4得到的結果是不同的,這是由於$的值是在不斷變化的緣故。當在指令中用到$時,它只代表該指令的首地址,而與$本身所在的字節無關。
2.EVEN僞指令
例如:
DATA_SEG SEGMENT
BYTE_DAT DB ?
EVEN
WORD_DAT DW 100 DUP (?)
DATA_SEG ENDS
一個字的地址最好從偶地址開始,所以對於字數組爲了保證它從偶地址開始,可以在DW定義之前用EVEN僞指令來達到這一目的。
3. ALIGN僞指令
例如:
.
ALIGN 4
ARRAY DD 100 DUP (?)
ALIGN僞指令保證了雙字數組ARRAY地址邊界從4的倍數開始。
ALIGN僞指令是將當前偏移地址指針指向2的乘方的整數倍的地址,如果源地址指針以指向2的乘方的整數倍的地址,則不作調整;否則將指針加以一個數,使地址指針指向下一個2的乘方的整數倍的地址。
當然,ALIGN 2和EVEN是等價的。
基數控制僞指令
.RADIX僞指令
.RADIX可以把默認的基數改變爲2~16範圍內的任何基數。其格式如下:
.RADIX 基數值
其中基數值用十進制數來表示。
.RADIX僞指令
.RADIX可以把默認的基數改變爲2~16範圍內的任何基數。其格式如下:
.RADIX 基數值
其中基數值用十進制數來表示。
例如:
MOV BX, 0FFH ;16進制數標記爲H
MOV BL, 10000101B ;二進制數標記爲B
MOV BX, 178 ;10進製爲默認的基數,可無標記
.RADIX 16 ;以下程序默認16進制數
MOV BX, 0FF ;16進製爲默認的基數,可無標記
MOV BX, 178D ;10進制數應加標記D
應當注意,在用 .RADIX 16把基數定爲十六進制後,十進制數後面都應跟字母D。在這種情況下,如果某個十六進制數的末字符爲D,則應在其後跟字母H,以免與十進制數發生混淆。
MOV BX, 0FFH ;16進制數標記爲H
MOV BL, 10000101B ;二進制數標記爲B
MOV BX, 178 ;10進製爲默認的基數,可無標記
.RADIX 16 ;以下程序默認16進制數
MOV BX, 0FF ;16進製爲默認的基數,可無標記
MOV BX, 178D ;10進制數應加標記D
應當注意,在用 .RADIX 16把基數定爲十六進制後,十進制數後面都應跟字母D。在這種情況下,如果某個十六進制數的末字符爲D,則應在其後跟字母H,以免與十進制數發生混淆。