
以下自考復習資料均由浙江自考網整理并發布,考生想要了解更多關于浙江自考報名、考試、成績查詢、畢業、歷年真題、常見問答等相關信息請關注浙江自考網,獲取浙江自考更多信息。
一、匯編語言程序的格式
(一)基本概念
程序是為實現某一特定目的(例如對數據進行某種處理)而編寫的一組指令有序集合。匯編語言程序就是用匯編語言編寫的源程序。匯編語言是一種面向機器的語言,它是與計算機硬件密切關連的,因而熟悉計算機硬件是匯編語言程序員必須具備的條件。與用高級語言編寫的程序相比較,匯編語言程序具有更高的效率,它的程序執行時間短且占用內存少,這在計算機實時控制和實時處理中是十分重要的,因而在實時領域得到廣泛應用。用匯編語言編寫的源程序,必須由匯編程序(一種系統軟件)進行匯編,將它轉換成用機器語言表示的目標程序后,才能由CPU識別執行。因此編制程序時必須遵循規定的格式和語法,這是本章討論的主要內容之一。對于不同型號的CPU和不同版本的匯編程序,其匯編語言是不同的。對于同一系列的CPU,則是向上兼容的。本書是針對8086/8088CPU進行討論的。
(二)匯編語言源程序的特點和格式
下面列舉了一個匯編語言程序,其功能是對10個字節數據a1 ~ a10求和。
DATASEGMENTAT 2000H
ARRAY DBA1,A2,A3,…,A10
COUNTEQU$-ARRAY
SUMDW?
DATA ENDS
STACK SEGMENT PARA STACK ‘STACK’
STAKDB100 DUP(?)
TOPEQULENGTH STAK
STACKENDS
CODESEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACK
START:MOVAX,DATA
MOVDS,AX
MOVAX,0
MOVDI,OFFSET SUM
MOVBX,OFFSET ARRAY
MOVCX,COUNT
LOP:ADDAL,[BX]
ADCAH,0
INCBX
LOOPLOP
MOV[DI],AX
MOVAH,4CH
INT21H
CODE ENDS
ENDSTART
此例明顯地示出了匯編語言程序的兩個組成特點:分段結構和浯句行。
1.分段結構
在第二章中,我們已討論了8086/8088的程序分段,知道程序最多可由4段組成,并分別由段寄存器CS、DS、ES和SS的內容作為段基值,每段所占內存容量最大可達64KB。
上例程序共有3段,它們分別是數據段(段名DATA)、堆棧段(段名STACK)和代碼段(段名CODE),各段由命令SEGMENT開始,并由命令ENDS結束。
2.語句行
上例程序共有26行,每行1個語句,即共有26個語句行。匯編語言程序的語句有兩類:指令性語句和指示性語句。
(1)指令性語句。指令性語句是主要由指令構成的語句,其格式為
[標號:] 操作碼[操作數] [;操作數] [;注釋];
其中操作碼和操作數是用助記符表示的指令的兩個部分,8086/8088的指令系統已在第三章中討論過,此處不再贅述。
其中帶方括號的三項(標號操作數和注釋)是任意選用的項,即根據具體編程需要該項可有或沒有,當然在實際語句中該項的方括號是不寫出來的。標號具有該語句指令所在內存地址的屬性,通常在轉移指令中用作目的地址。注意,標號必須用冒號“:”結尾,這是語法的規定。用分號“;”開始的注釋用來說明該語句在程序中的作用,以方便程序的閱讀和修改,這項也是任選的。
如上例的第18語句行
LOP:ADD AL,[BX]
其中指令是ADD AL,[BX],標號是LOP。LOP在第21語句行指令LOOP LOP中是轉移的目的地址。又此語句中未用注釋項。
(2)指示性語句。指示性語句是主要由命令(亦稱偽指令)構成的語句,是用來指示匯編程序進行匯編操作的,其格式為:
[名字/變量] 命令[參數] [;注釋]
其中命令指示匯編程序進行某種匯編操作,參數是有關的數據,帶方括號的項是任選的。
如上例的第1語句行
DATA SEGMENT AT 2000H
其中命令SEGMENT指出這是一個段的開始,參數AT 2000H指定該段的段基值為2000H,而DATA則是該段的名字。
又如上例的第5語句行
DATAENDS
則表明了段名為DATA的段的結束。指示性語句的應用使程序員編程時不需進行很多計算,既方便又簡化了編程工作。
二、常量、標識符和表達式
常量、標識符和表達式在匯編語言源程序中經常用到,本節對它們分別作一介紹。
(一)常量
常量是具有一定數值的量,匯編語言程序中的常量有:數字常量、字符常量和符號常量:
1.數字常量
數字常量可用二進制數、八進制數、十進制數或十六進制數表示。所用的數制要用后綴示明:二進制為B、八進制為Q、十進制為D、十六進制為H。對于十進制數可以用后綴D,也可以不用后綴。對于十六進制數,若最高位的數為A-F,須在它的前面加上數字0,以表明是數值數據。
例如:10100110B,246Q,166D,166,6AH,0A6H。
2.字符常量
字符常量用帶單引號的ASCII字符表示,它所代表的數值就是該字符的ASCII碼。例如:‘A’就是41H,‘1’就是31H等。
3.符號常量
為編程方便,程序員在編程時可將一個標識符定義為一個符號常量,它具有一個設定的數值而可被引用。
例如符號常量定義語句ALLONE=llllllllB將ALLONE定義為11111111B。在程序中即可引用如MOV AL,ALLONE,此條指令和MOV AL,llllllllB是等效的。
(二)標識符
標識符是程序員在編程時建立的有特定意義的字符序列,標識符可用作符號常量、名
字、變量和標號等。
對于標識符,有以下規定:
(1)組成標識符的字符有:英文大寫字母A、B:…、Z;英文小寫字母a、b、…、z ,數字0、1、2、…、9;字符?、@、—。
(2)標識符的有效長度為不超過31個字符,若超過就忽略其超過部分。
(3)除數字以外,所有規定的字符都可作為標識符的第一個字符。
(4)問號?不能單獨作為一個標識符。
(5)不能把保留字用作標識符。保留字包括:指令和命令的助記符,如MOV、SEGMENT等;寄存器名,如AX等;語句中的規定用詞,如PTR、LENGTH等。
(三)表達式
表達式由操作數和運算苻組成。操作數可為常量、名字、變量和標號等。運算符包括
算術運算符、邏輯運算符等很多種,如表4-1所示。表中各運算符是按運算優先級由高向低逐行排列的。
例如指令性語句:MOV CX,COUNT-1,其中COUNT-1為表達式,設符號常量COUNT已用COUNT=10定義,則匯編時將表達式匯編成9(注:COUNT-l=10-1=9),即將該指令按
MOV CX,9匯編成機器碼供程序運行時由CPU執行。
又如指令性語句:MOV AL,COUNT LT 20,其中COUWT LT 20為表達式,其中 LT為比較運算符“小于”,設COUNT已定義為10,由于10<20,即表達式是成立的,
亦即表達式的規定條件是“真”(TRUE)。匯編時將該表達式匯編成0FFH即“真”(即全“1”),則將該指令按MOV AL,0FFH匯編成機器碼供程序運行,若指令性語句為
MOV AL,COUNT GT 20,其中GT為表示“大于”的比較運算符,則表達式是不成立的,即是“假”(False),匯編時就將該表達式匯編成00H。
表達式中的運算操作按運算符的優先級別先高后低地依次進行,對于相同優先級則按從左到右的次序進行運算操作。
三、指示性語句
指示性語句的格式如下:
[名字/變量] 命令 [參數] [;注釋]
(一)程序開始和結束語句
程序開始和程序結束語句的命令有NAME、TITLE和END等。
1.應用命令NAME的程序命名語句命令NAME用來給程序模塊命名,其格式為
NAME名字
其中名字是程序員按標識符規定所取的程序模塊名,匯編后它就成為該程序模塊的名字了。
2.應用命令TITLE的標題命名語句
若所用匯編語言沒有NAME命令,則可用命令TITLE,其格式為
TITLE名字
則其中由程序員所取的名字在程序的每一頁作為標題打印出來。
標題的名字最多可有60個字符。若程序沒有使用NAME命令,就用標題名字中的前面6個字符作為模塊名。注意:程序開始時不用NAME和TITLE命令的語句是允許的,此時可直接由段定義語句開始編寫源程序。
3.應用命令END的程序結束語句
程序結束語句的格式為
END[標號]
其中標號是程序中第1句指令性語句(或第1條指令)的標號。當程序由多個模塊組成時,只需在主程序中的結束語句中寫出標號,其它子程序模塊的結束語句只要寫出命令END即可。
(二)段定義語句
分段結構是8086/8088的特點,程序和存儲器都是按段來組織的。段定義語句用來定義一個段,命令有SEGMENT、ENDS、ASSUME和ORG等。
1.應用命令SEGMENT和ENDS的段定義語句
其格式為
段名SEGMENT[參數]
┅
段名ENDS
其中段名為程序員編程時按標識符規定為該段所取的名字,在匯編和連接時系統將給該段名的段分配一個具體的段基址。
命令SEGMENT和ENDS必須成對使用,它們前面的段名必須是一致的,SEGMENT語句和ENDS語句之間就是該段的內容。
例如:DATA SEGMENT
┅
DATAENDS
CODESEGMENT
┅
CODEENDS.
此例共有兩段,段名分別為DATA和CODE,且顯然是特意這樣取名以表示第一段是數據段,而第二段是代碼段。
2.SEGMENT語句中的參數部分
SEGMENT語句中的參數共有三項,語句的格式為
段名SEGMENT[定位類型] [組合類型] [‘類別’]
這三個參數用來設定該段在內存中的位置,且都是任選項。
(1)定位類型(align-type)。定位類型用來指定該段段地址的邊界條件、定位類型有以下四種:
1)BYTE該段可從任何地址開始,即段地址 =
X X X X, X X X X, X X X X, X X X X, X X X X B,
其中X表示任意值,即1或0;
2)WORD該段必須從字的邊界開始,即段地址 =
X X X X, X X X X, X X X X, X X X X, X X X O B
3)PARA該段必須從節的邊界開始,即段地址 =
X X X X, X X X X, X X X X, X X X X ,0 0 0 0 B
4)PAGE該段必須從頁的邊界開始,即段地址 =
X X X X, X X X X, X X X X, 0 0 0 0, 0 0 0 0 B。
注意:當定位類型缺省(即不寫)時,隱含值為PARA。
(2)組合類型(combine-type)。在匯編和連接時,當該段與其他段組合在一起時,組合類型用來設定該段與其它段的連接關系,組合類型有以下六種:
1)PUBLIC該段連接時將與其他同名段依次連接起來,其連接次序由連接程序確定;
2)COMMON該段連接時將與其他同名段有相同的段基址.即共享相同的存儲空間.亦即各段會產生覆蓋,但可節省內存容量;
3)AT exp使段基值等于按表達式exp計算所得的16位數。必須指出,對于代碼段不能用ATexp來設定段基值;
4)STACK僅用于堆棧段,使同名段都從同一段基址開始:
5)MEMORY指定該段在同名段的最后,即該段在同名段中位于最高的地址空間,若連接時有幾個組合類型為MEMORY的段,則只有最前面(最先遇到)的段按組合類型 MEMORY處理,其他段均按組合類型PUBLIC處理;
6)NONE表示本段與其它段邏輯上不發生關系,各段都有自己的段基址。注意:當組合類型缺省(即不寫)時,隱含值為NONE。
(3)‘類別’(‘dass’)。類別必須用單引號括起來。在定位時,連接程序將各程序模塊中具有相同類別的邏輯段集中在一起,形成一個統一的物理段。
3.應用命令ORG的偏移地址定位語句
程序中有時需要指定某一語句所在內存單元在段內的偏移地址,這可用ORG命令的語句來實現,語句的格式為
ORGexp
其中表達式exp,可計算得出16位地址。此時ORG語句的下一個語句所在內存單元在段內的偏移地址就被指定為按表達式計算得出的16位地址。
例如:
ORG 1000H
MOVAL, BL
本例示明指令MOV AL,BL所在內存單元在本段內的偏移地址為1000H。
4.用命令ASSUME示明段寄存器內容的語句
一個程序通常由很多段組成,對于某一代碼段,它所用到的數據段、附加段和堆棧段只是程序中的某幾個有關段,因此在代碼段開始時必須用ASSUME命令語句示明該代碼段所用到的段,以便進行匯編。
ASSUME命令語句緊跟在SEGMENT命令語句之后,其格式為
ASSUMECS:段名1,DS:段名2,ES:段名3,SS:段名4
其中段名1、2、3和4分別為該代碼段所用到的作為代碼段、數據段、附加段和堆棧段的段的段名,亦即將這些段的段基值1、2、3和4作為段寄存器CS、DS、ESS5的內容。
注意:在ASSUME語句中示明的段寄存器和相應的段名是該代碼段中實際用到的,對于該代碼段中未用到的段寄存器及相應的段名是不需示明的。
例如:CODESEGMENT
ASSUMECS:CODE,DS:DATA
CODEENDS
本例表明在段名為CODE的代碼段中,所用到的段寄存器為CS和DS,它們的內容分別是CODE段和DATA段的段基值。
(三)過程定義語句
過程是程序的一部分,即子程序。過程可用程序中的CALL指令調用。當過程中的指令執行完后,用RET指令返回調用它的程序。
應用命令PROC和ENDP的過程定義語句的格式為:
過程名PROC 類型
┅
RET
過程名ENDP
過程名是程序員編程時按標識符規定取定的。類型表明該過程是供段內調用,還是供段間調用,對于前者用NEAR表示,而后者則用FAR表示,當類型項缺省(即不寫明)時,隱含值為NEAR。
PROC和ENDP是成對使用的,兩語句之間就是該過程的內容(程序),且用RET指令結尾以返回調用它的程序。
成對使用的命令PROC和ENDP的前面必須均寫明該過程名。
(四)數據定義語句
數據定義語句用來為數據分配存儲單元,例如在內存中設置原始數據以及為存放結果數據而保留內存單元等。數據段、附加段和堆棧段都是存放數據的,其中所用的語句主要是數據定義語句。數據定義的命令有DB、DW、DD、DQ和DT等,它們分別用來定義不同類型(長度)的數據。
數據定義語句的格式為
[變量]命令參數1,參數2,… ,[;注釋]
其中變量是由程序員在編程時按標識符規定取定的,如ARRAY、BUFFER、SUM等,一般都是按照數據的功用取名的。
其中命令的表示符號(助記符)及功能為
DB定義長度為1字節(8位)的數據(字節數據)。
DW定義長度為1個Z字(16位)的數據(字數據)。
DD定義長度為2個字(32位)的數據(雙字數據)。
DQ定義長度為8字節(64位)的數據(8字節數據)。
DT定義長度為10字節(80位)的數據(10字節數據)。
其中參數就是相應內存單元中的數據,它可以是常數(可用各種規定的數制表示)、字符常數(用單引號括起來的ASCII字符)或符號常數,當它是保留以備存入有關數據時就以問號(?)表示。參數可以有多個,相互間要用逗號(,)隔開,若連續多個數據是
重復的,就可應用復制符DUP以簡化書寫,DUP的用法為
復制次數DUP(數據)
其中數據可以不只一個,且數據還可有復制部分。
例1段名為DATA的段由以下語句組成
DATASEGMENT
DATA1DB20H
ARRAYDB12H,12,‘A’
SUMDB?
DATAENDS
設本段的段基值為2000H,則相應內存分配為內存
地址內容
段基值:偏移地址
2000H: 0000H20H
0001H12H
0002H0CH
0003H41H
0004H
其中第1個數據20H在該段的起點,故相應內存單元的偏移地址為0000H,后面數據所在內存單元的偏移地址依次類推。
其中所有內存單元的段基值都是相同的(=2000H),其中內容的數據都是以16進制表示的(注:由匯編程序在匯編時進行轉換)。
其中“?”表示保留,實際該內存單元的內容為隨機數,但對本程序來說,目前是無效的。
例2DATA2DB2DUP(12H,34H,56H)
此時內存分配為
注:未列出具體地址
例3DATA3DB‘ABCD’
其中參數部分‘ABCD’是‘A’,‘B’,‘C’,‘D’的簡寫。
例4DATA4DW1234H,5678H,9AH,?
其內存分配為
用DW定義的是字數據,每個數據分配2個內存單元,如數據9AH實則上是009AH。
例5 DATA5DW‘AB’,‘CD’
其內存分配為
注意:不能寫為DW‘CDAB’,因為匯編語言語法規定除用DB定義的字符串常量外,單引號中ASCII字符的個數不得超過2個,若只有1個,例DW‘C’,就相當于DW0043H。
例6 DATA6 DD 12345678H,‘AB’
其內存分配為
例7STAKDB100 DUP(?);保留100個字節內存單元作為堆棧區
(五)符號定義語句
符號定義語句的命令有EQU、= 和PURGE
1.應用命令EQU和PURGE的符號定義語句
應用EQU命令的語句的格式為
名字EQUexp
其中名字是程序員取定的,表達式exp可以計算得出一個具體的數值。這實際上就是給名字賦值,在程序中就可引用這個名字來表示表達式的實際計算值。
若需對已賦值的名字撤消原賦值并賦以新值。則需先用PURGE命令語句撤消原賦值,再用EQU命令語句賦新值。PURGE命令語句的格式為
PURGE名字
其中名字可不只一個,即可同時撤消幾個已賦值。
例如:COUNT EQU20;給COUNT賦值為20
┅
MOVAL,COUNT;即MOV AL,20
┅
PURGECOUNT;撤消原賦值
COUNTEQU10;給COUNT賦新值為10
┅
MOVBL,COUNT;即MOV BL,10
┅
2.應用命令=的符號常量定義語句,其格式為
名字 = exp
命令 = 的功能與EQU類似,唯一的差別是命令;可隨時對名字(符號常量)賦新值,而不必使用PURGE命令。
如上例,可寫為
COUNT=20
┅
MOV AL,COUNT
┅
COUNT=10
┅
MOV BL,COUNT
┅
(六)名字和變量
1.名字
前面已討論過的名字有文件名、標題名、段名、過程名和符號常量等,它們都是程序員編程時按標識符規定來命名的,其中有些名字可在編程時引用,以方便編程。
(1)段名。段名是在源程序的段定義語句中命名取定的,如段定義語句
DATASEGMENT, 段名為 DATA。
源程序在進行匯編連接時,系統分配給該段一個段基值,設為2000H。這時段名
DATA就可作為段基值2000H被引用。
例如:給段寄存器賦值的指令序列為
MOVAX,DATA;相當于MOV AX,2000H
MOVDS,AX;將段基值賦給段寄存器
(2)過程名。過程名是在源程序的過程定義語句中命名的。如過程定義語句
SORTPROC NEAR,過程名為SORT。
匯編連接源程序時,系統分配給過程一個地址,即該過程第一條指令所在內存單元的地址,亦即該過程的入口地址,這也就是調用該過程的CAl厶指令中的目的地址。過程名在匯編語言程序中可作為調用指令的目的地址使用,例如指令CALL SORT,其中 SORT就表示過程名為SORT的過程的入口地址,執行該指令就是轉移到過程SORT,運行。
(3)符號常量。符號常量是在源程序的符號常量定義浯句中命名取定的,如符號常量定義語句COUNT EQU 20將數值20賦給COUNT。COUNT就可在指令中作為常量20
被引用,如MOV AL,COUNT就相當于MOV AL,20。
例1部分程序內容為
DATASEGMENT
ARRAYDB10H,24H,5AH,0C7H,98H,‘ABCDE’
COUNTEQU$-ARRAY
MAXDB?
DATAENDS
其中第3語句行為給符號常量COUNT賦值的語句,句中表達式為$-ARRAY,其計算值就是賦給COUNT的常量數值。下面先討論表達式中$和ARRAY的含義及具體值,然后由表達式計算出賦給COUNT的具體數值。如本小節(四)所討論,變量ARRAY所在語句中的第一個數據10H所在內存單元的偏移地址為0000H,也就是該數組的起始地址,后續數據依次存在后續偏移地址的內存單元中,最后一個數據‘E’所在內存單元的偏移地址為0009H。變量ARRAY所在語句的起始偏移地址為0000H,這就是變量 ARRAY的偏移地址屬性(注:變量的屬性將在下面詳細討論)。由于ARRAY語句的末 地址為0009H,故下一語句行(COUNT語句行)所在的偏移地址就是000AH,此語句中的 $ 就是該行(當前行)的偏移地址,即000AH。故表達式可計算得出為
$-ARRAY=000AH-0000H=000AH=10
因而賦給符號常量COUNT的值為10,COUNT的英文含義為計數,它實際上表示的是ARRAY數組的數據元素個數,即10十字節數據。在程序中用MOVCX,COUNT 指令來設置計數器CX的初值,然后就可用對CX進行減一計數的方法來控制對10個數據的處理。
例2部分程序內容為
STACKSEGMENT
STAKDB100 DUP(?);保留100個內存單元(字節)作堆棧區
TOPEQU$—STAK;給TOP賦值為100
STACKENDS
CODESEGMENT
┅
MOVAX,STACK;將段基值STACK賦給段寄存器SS
MOVSS,AX
MOVSP,TOP;設置堆棧指針
此時,內存中堆棧段的分配如下:
2.變量
(1)變量的定義和屬性。如前所述,變量是數據定義語句中的一項,它是由程序員在編程時按照標識符規定取定的。
當在數據定義語句的第一項對變量命名后,該變量就是已定義了的,已定義的變量具有下列五種屬性:
1)段屬性
表示格式:SEG變量
它表示變量所在段的段基值。
2)偏移地址屬性
表示格式: OFFSET變量
它表示變量所在處的偏移地址。
3)類型屬性
表示格式: TYPE變量
它表示變量所在內存數據的類型。數據類型有字節、字、雙字、8字節和10字節,是在該語句中用命令DB、DW、DD、DQ和DT予以定義的。對于不同的數據類型,變量的類型屬性具有如下不同的值:
字節數據時,TYPE=1
宇數據時,TYPE=2
雙字數據時,TYPE=4
8字節數據時, TYPE=8
10字節數據時,TYPE=10
4)長度屬性
表示格式: LENGTH 變量
它表示變量所在數組的數據元素個數。
需注意,只有當數據用復制符 DUP 定義時,LENGTH才等于數組的元素個數,否則
LENGTH就等于1。
5)規模屬性、
表示格式:SIZE變量
它表示變量所在數組的字節總數,且
SIZE:LENGTH*TYPE。
同以上LENGTH的情況,只有當數據用復制符DUP定義時,LENGTH才等于數組的元素個數,否則LENGTH就等于1。
這些屬性,程序員編程時均可引用,從而方便編程。
例如:部分程序內容為
DATASEGMENT
BUFlDBNI,N2,N3,…,N10;N1~N10為10個字節數據
BUF2DB10 DUP(0)
BUF3DW10 DUP(?)
DATAENDS
設該段的段基值為2000H,則
SEG BUF1=2000H
OFFSET BUF1=0000H
TYPE BUF1=1
LENGTH BUF1=1
SIZEBUF1=1
SEGBUF2=2000H
OFFSETBUF2=000AH
TYPEBUF2=1
LENGTH BUF2=10
SIZEBUF2=10
SEGBUF3=2000H
OFFSETBUF3=0014H
TYPE BUF3=2
LENGTHBUF3=10
SIZE BUF3=20
這些屬性在程序中的應用舉例
MOV AX,SEG BUFl;設置段寄存器DS
MOV DS,AX
MOVSI,OFFSET BUFl ;設置地址指針SI
MOV CX,LENGTH BUF2;設置計數器CX
MOV BL,SIZEBUF3;設置計數器BL
MOV AL,BUF1;從內存取數據到寄存器AL。此指令是指令
;MOV AL,OFFSET BUFl]的簡便寫法,是匯編程序能接受的。
;本指令的功能是將數據N1送到寄存器AL,
MOV AH,BUFl+2;這是MOVAH,[OFFSETBUFl+2]的簡便寫法,指令的功能是將
;數據N3送到寄存器AH
(2)屬性運算符。如前所述,定義了的變量具有一定的屬性。對變量屬性的引用可方便程序員的編程工作。但變量的類型屬性有時會限制它的應用。
設數據定義語句為:
BUFWDW1234H,5678H
其中變量BUFW的類型屬性為字,編程時可很方便地用指令:
MOV AX,BUFW
將字數據1234H傳送到寄存器AX。但若要傳送字節數據就有問題了,因為指令MOVAL,BUFW是非法的,所以非法是由于指令中的兩個操作數AL(字節數據)和BUFW(字數據)的類型不同,這樣的指令是不能匯編和執行的。為了解決這個問題,匯編語言提供了屬性運算符PTR、THIS和LABEL:
1)類型(重新)指定運算符PTR
格式:類型PTR exp
其中類型可以是BYTE、WORD、DWORD、NEAR和FAR(注:前三個是變量的類型屬性,后兩個是標號的類型屬性);exp是表達式,是存儲器操作段,當為變量重新指定類型時exp就是變量名。
運算符PIR的作用是仍按后面的表達式去尋址,不管它原來有無類型或是那一種類型,PTR定義后,就按PIR前面類型項指定的類型看待,實際上,PTR是給后面的存儲器操作數賦予新的前面的數據類型(注:對于標號則為地址類型)。運算符PTR的應用如下:
a.重新指定變量類型
BUFWDW1234H,5678H
下列指令均為合法的
MOV AX,BUFW;AX ← 1234H
MOVAL,BYTE PTRBUFW;AL ← 34H
b.指定內存操作數的類型,下列指令
INC[BX]非法的,因為基址尋址的內存操作數的類型未示明,無法進行匯編和執行。
用PTR指定類型后,指令就是合法的了,如
INC BYTE PTR[BX]
INC WORD PTR[BX]
c.用EQU和PTR定義一個新的變量
BUFWDW1234H,5678H
BUFBEQUBYTE PTR BUFW
這使新變量BUFB具有和變量BUFW相同的段屬性、偏移地址屬性,但兩者類型不
同,BUFW類型屬性為字,而BUFB類型屬性為字節。 此時,下列指令就是合法的。
MOVAX,BUFW;AX ← 1234H
MOVAL,BUFB;AL ← 34H
2)屬性指定運算符THIS ,格式:
THIS類型
類似于上節PTR運算符的應用。THIS和EQU一起用來定義一個新變量,它與原變量具有相同的段屬性和偏移地址屬性,但類型屬性不同。
例如:BUFBEQU THISBYTE
BUFWDW1234H,5678H
這樣,BUFB和BUFW具有相同的段屬性和偏移地址屬性,但BUFB的類型屬性是字節,此時下列指令都是合法的。
MOV AX,BUFW;AX ← 1234H
MOVAL,BUFB;AL ← 34H
注意,BUFB語句和BUFW語句必須是緊鄰的,且BUFB語句在BLN語句的前
面。
3)命令LABEL
格式: 變量/標號LABEL類型
命令LABEL用來定義其語句中的變量(或標號)的類型屬性為語句中設定的類型, 此時變量(或標號)的段屬性和偏移地址屬性是由該語句的位置確定的。
例如:BUFBLABEL BYTE
BUFW DW1234H,5678H
則下列指令是合法的
MOVAX,BUFW;AX← 1234H
MOVAL,BUFB;AL ← 34H
注意,BUFB語句和BUFW語句必須是緊鄰的,且BUFB語句在BUFW語句的前面。
四、指令性語句
指令性語句由指令組成。指令性語句是構成代碼段的基礎。指令性語句只是在代碼段中才有,在數據段、附加段和堆棧段中是沒有指令性語句的。程序運行時,CPU執行指令性浯句中的指令,而指示性語句在程序運行時,是不由CPU執行的,指示性語句是用來指示匯編程序進行匯編操作的。
指令性語句的格式為
[標號]操作碼[操作數] [;注釋]
其中操作碼和操作數就是指令,本書第三章已討論了8086/8088指令系統,此處不再贅述。
本節將對標號和操作數進行討論。
(一)標號
標號是程序員編程時按標識符規定取定的,并常常具有它在程序中的作用的含義,如
NEXT、AGAIN等,并且標號一定要用冒號(:)結尾。
在指令性語句中寫上標號后,就定義了該標號,定義了的標號具有下列三種屬性。
1段屬性
表示格式:SEG標號
它表示標號所在段的段基址。
2.偏移地址屬性
表示格式:OFFSET標號
它表示標號所在位置的偏移地址,即該語句的指令的第一字節所在內存單元的偏移地址。
3.類型屬性
表示格式:TYPE標號
標號通常用作轉移指令的目的操作數,即轉移去的目的地址。我們知道,轉移有NEAR(近轉移,即段內轉移)和FAR(遠轉移,即段間轉移)兩種,NEAR和FAR是轉移的兩種類型,因而也就是標號的類型。類型為NEAR的標號供段內轉移用,而類型為FAR的標號則供段間轉移用。
標號的類型屬性和標號的類型有關:
NEAR時,TYPE = -1
FAR時,TYPE = -2
其中的-1和-2投有真正的物理意義,只是以這樣的具體數值表示而已(注:由于要和變量的類型屬性清楚地區別,所以用的是負值)。
指令性語句中的標號,其類型一般都是NEAR。為了適應段間調用的需要,要把它的類型改換成FAR。與前面變量改變類型相類似,標號類型的改變可應用運算符PTR、THIS和LABEL,可以是重新指定類型,也可以定義一個新標號。
設程序中標號為METER,其類型為NEAR。若定義一個新變量KILOMT,其類型為FAR,而KILOMT的段屬性和偏移地址屬性則是和METER的相同。這樣,段內轉移時用METER作為目的操作數,而段間轉移時則用KILOMT作為目的操作數,兩個標號表示的目的地址是同一個,即均轉移到程序中標號為METER處。具體方法如下。
(1)用PTR重新指定類型:
段內轉移用指令
JMP METER
段間轉移用指令
JMPFARPTR METER
(2)用EQU和PTR定義新標號KILOMT
METER:┅
KILOMTEQU FAR PTRMETER
(3)用EQU和THIS定義新標號KILOMT
KILOMT EQU THISFAR
METER:┅
(4)用LABEL定義新標號KILOMT
KILOMTLABELFAR
METER:┅
這四種方法和前面變量改變類型的方法是一樣的,這里不贅述。
(二)操作數
指令中的操作數可按尋址方式表示,尋址方式在前面第三章中已討論,此處不再贅述。操作數也可以用段名、符號常量、變量、屬性、過程名和標號來表示,如下例所示
MOVAX,DATA;DATA是段名,立即尋址方式
MOVCX,COUNT;COUNT是符號常量,立即尋址方式
MOV BL,BUFFER;BUFFER是變量,直接尋址方式
MOVSI,OFFSET ARRAY;OFFSETARRAY是屬性,立即尋址方式
CALL SBRT1;SBRT1是過程名。直接尋址方式
JMPDONE;DONE是標號,直接尋址方式
LOOP AGAIN;AGAIN是標號,直接尋址方式
五、宏指令
程序員用匯編語言編程時,對于程序中多次重復使用的指令序列(即很小的程序段),可定義一條宏指令,編寫程序時就用這條宏指令代替該指令序列,從而簡化書寫工作:
(一)宏定義、宏名字、宏調用和宏展開
宏定義就是定義宏指令,宏定義的命令是MACRO和ENDM。宏定義的格式為
宏名字MACRO[形式參數]
┅
ENDM
其中宏名字是宏指令的名字,是程序員按標識符規定取定的。命令MACR0和 ENDM之間的指令序列就是該宏指令的內容,稱為宏體。帶方括號的形式參數是任選項,當無形式參數時就無該項,當有多個形式叁數時相互間由逗號隔開。形式參數亦稱啞參數、啞元或變元,它是宏體中有關指令的操作碼、操作數或它們的一部分。形式參數是沒有物理童義的,只有用實參數代替形式參數后,相應的指令才有實際意義。
在程序中應用宏指令稱為宏調用,宏調用的格式是:
宏名字[實參數]
其中實參數是與宏定義中的形式參數一一對應的。
匯編時,匯編程序用已由實參數取代形式參數的宏體取代程序中的宏指令,稱為宏展開,此時程序中已全部是可執行的指令序列了。
例1無形式參數的宏指令
宏定義SAVEREG MACRO
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSHDI
ENDM
宏指令名為SAVEREG,功能是有關寄存器內容進棧保護現場。
宏調用
程序
┅
SAVEREG
┅
SAVEREG
宏展開
程序
┅
PUSHAX
PUSHBX
PUSHCX
PUSHDX
PUSHSI
PUSHDI
┅
PUSHAX
PUSHBX
PUSHCX
PUSHDX
PUSHSI
PUSHDI
匯編程序再將此宏展開后的程序轉換成機器碼生成目標程序,
例2帶有形式參數的宏指令
宏定義MULTIPLY MACRO OPRl,ORR2,RESULT
PUSHAX
MOVAL,OPRl
1MULOPR2
MOVRESULT,AX
POPAX
ENDM
其中有三個形式參數OPRl、OPR2和RESULT,它們都是宏體指令中的操作數。
宏調用程序
MULTIPLY CL,VAR,XYZ[BX];實參數CL是寄存器,VAR是變量,
XYZ[BX]是基址尋址的內存操作數,
MULTIPLY 240,BL,SAVE;實參數240是立即數,BL是寄存器,
SAVE是變量
宏展開程序
PUSHAX
MOVAL,CL
IMULVAR
MOVXYZ[BX],AX
POPAX
┅
PUSH AX
MOV AL,240
IMULBL
MOV SAVE,AX
POP AX
┅
匯編程序將此宏展開后的程序轉換成機器碼生成目標程序。
(二)宏指令與子程序的差別
如上所述,宏指令是將一段程序(指令序列)用一條宏指令來代替,以簡化書寫源程序。子程序(過程)也有類似的功能,但兩者是有差別的,具體如下:
(1)宏指令簡化了源程序的書寫。但在匯編時,匯編程序對宏指令的匯編處理是將宏指
令的宏體(即程序段)原原本本地插入到宏指令調用處,然后轉換成機器碼生成目標程序。因此,宏指令雖簡化了源程序,但并沒有簡化目標程序,有多少次宏調用,在目標程序中就有同樣多次數的目標代碼插入。所以宏指令不節省目標程序需占用的內存單元。
子程序(過程)在執行時是由CPU用調用(CALL)來處理的。若在一個源程序中多次調用同一個子程序,則在目標程序中,主程序中只有調用(CALL)指令的目標代碼,CALL指令的目標代碼只有幾個字節,該目標代碼出現的次數就是調用次數。而子程序的目標代碼在整個目標程序中只出現一次,所以相應地其目標程序就占用較少的內存單元,即可節省內存單元。
(2)采用子程序方式時,每調用一次就需執行一次CALL和RET指令,而宏指令方式時,并無此兩條指令。因此,使用宏指令時的程序執行時間比子程序時的程序執行時間要短一,即宏指令時程序執行速度快。
由上可知,宏指令和子程序各有特點,宏指令執行速度快而子程序占用內存少。一般,對于程序段較長的情況,采用子程序可節省很多內存而對執行速度影響不大;對于程序段較短的情況,采用宏指令可加快速度而對增加占用內存容量影響不大,尤其對于程序段較短而形式參數較多的情況,宏指令就更能顯示其突出的優點了。