一、與宏有關(guān)的偽指令 在宏定義時,為了滿足某種特殊需要,匯編語言還提供了幾個偽指令。 9.3.1 局部標號偽指令LOCAL 在宏定義體中,如果存在標號,則該標號要用偽指令LOCAL說明為局部標號,否則,當在源程序中,有多于一次引用該宏時,匯編程序在進行宏擴展后將會給出:標號重復定義的錯誤。 偽指令LOCAL的一般格式如下: LOCAL 標號1, 標號2, …… 偽指令LOCAL必須是偽指令MACRO后的第一條語句,并且在MACRO和LOCAL之間也不允許有注釋和分號標志。 匯編程序在每次進行宏擴展時,總是把由LOCAL說明的標號用一個唯一的符號(從??0000到??FFFF)來代替,從而避免標號重定義的錯誤。 例9.7 編寫求一個求絕對值的宏。 解: 方法1: ABSMACROword1 CMPword1, 0 JGEnext NEGword1 next: ENDM 假設(shè)對宏ABS有以下兩次引用,點擊它們將會顯示匯編程序?qū)λ鼈冞M行宏擴展后所得到程序片段: ABS BX 1 CMP BX, 0 1 JGE next 1 NEG BX 1 next: … ABS AL 1 CMP AL, 0 1 JGE next 1 NEG AL 1 next: 在上述程序片段中,顯然標號next定義了二次,所以,匯編程序?qū)@示“標號重復定義”的錯誤信息。為了避免這種情況的發(fā)生,我們需要用下面的方法來定義該宏。 方法2: ABSMACROword1 LOCALnext CMPword1, 0 JGEnext NEGword1 next: ENDM 假設(shè)對宏ABS有以下兩次引用,點擊它們將會顯示匯編程序?qū)λ鼈冞M行宏擴展時所得到程序片段: ABS BX 1 CMP BX, 0 1 JGE ??0000 1 NEG BX 1 ??0000: … ABS AL 1 CMP AL, 0 1 JGE ??0001 1 NEG AL 1 ??0001: 在上述程序片段中,宏體內(nèi)部的局部標號next分別用符號??0000和??0001來對應(yīng)它的二次引用。因此,匯編程序不會再顯示“標號重復定義”的錯誤信息。 偽指令LOCAL在子程序中也可起作用(參見7.5.10節(jié)),但它的作用與宏定義的作用是不同的,有關(guān)該偽指令在子程序的宏定義中功能的主要差異如表9.1所列。 表9.1 偽指令LOCAL在子程序和宏中的比較 在子程序中在宏定義中 語句的位置在所有指令之前在所有指令之前 偽指令的作用說明局部變量說明局部標號 偽指令的格式可用一條偽指令來說明多個局部變量,也可連續(xù)用多條偽指令來說明可用一條偽指令來說明多個局部標號,也可連續(xù)用多條偽指令來說明 調(diào)用或引用子程序的不同調(diào)用,其局部變量名保存不變在每次宏引用的擴展時,將會自動產(chǎn)生出一個唯一的局部標號 二、取消宏定義偽指令 偽指令PURGE的一般格式如下: PURGE 宏名1, 宏名2, …… 該偽指令通知匯編程序取消“宏名1, 宏名2, ……”宏名表中的宏定義。在此語句后,如果還有這些宏的引用語句,則匯編程序不會把它們當作宏引用來進行擴展,并且還將顯示出錯信息。 偽指令PURGE的使用頻率較低。 9.3.3 中止宏擴展偽指令 偽指令EXITM的一般格式如下: EXITM 該偽指令書寫在宏定義體中,用來告訴匯編程序:如果遇到該偽指令,那么,立即中止對該偽指令之下語句的擴展。如果在嵌套的內(nèi)層宏中遇到了該偽指令,則退出到宏嵌套的外層。 在一般情況下,偽指令EXITM與條件偽指令一起使用,以便在不同的條件下挑選出不同的語句。 偽指令EXITM的使用頻率也很低。 三、重復匯編偽指令 在編寫源程序時,有時會出現(xiàn)連續(xù)相同或相似的語句(組)。當出現(xiàn)這種情況時,可利用重復偽指令來重復語句,從而達到簡化程序的目的。 重復匯編偽指令所定義的重復塊是宏的一種特殊形式,也是由偽指令ENDM來結(jié)束重復塊。用重復匯編偽指令定義的重復塊也可帶有參數(shù),并在匯編過程中參數(shù)被實參代替,但重復塊不會被命名,不能在程序的其它地方引用。 9.4.1 偽指令REPT 偽指令REPT的作用是把一組語句重復指定的次數(shù),該重復次數(shù)由偽指令后面的數(shù)值表達式來確定。其一般使用格式如下: REPT數(shù)值表達式 重復的語句組 ENDM 例9.8 定義100個初值為32的字節(jié)單元,該存儲單元的起始符號地址為Table。 解: 方法1:用偽指令REPT來實現(xiàn)左邊重復塊的匯編結(jié)果如下: Table LABEL TYPE Table LABEL TYPE REPT 100 DB 32 DB 32 … ENDM DB 32 ;上述字節(jié)定義重復100次 方法2:用重復操作符DUP來實現(xiàn) Table DB 100 DUP(32) 從上例來看,用偽指令REPT重復定義的存儲單元可以用重復操作符DUP來代替,其實前者的功能會更靈活、更強大。 例9.9 定義100個初值分別為1,2,…,100的字節(jié)單元,該存儲單元的起始符號地址為Table。 解: Table LABEL TYPE 左邊重復塊的匯編結(jié)果相當于: COUNT = 1 Table LABEL TYPE REPT 100 DB 1 DB COUNT DB 2 COUNT = COUNT + 1 … ENDM DB 100 上面定義了100個字節(jié),其初值為1,2,…,100。本例好象不能用重復操作符DUP來說明字節(jié)存儲單元。 例9.10 計算1+2+…+1000,并把其值存入寄存器AX。 解: 方法1:用偽指令REPT來實現(xiàn)左邊重復塊的匯編結(jié)果與下面程序段相一致: … … MOV AX, 0 MOV AX, 0 COUNT = 1 ADD AX, 1 REPT 1000 ADD AX, 2 ADD AX, COUNT … COUNT = COUNT + 1 ADD AX, 1000 ;把AX從1累加到1000 ENDM … … 雖然上面這些語句的執(zhí)行能完成本例所指定的功能,但它是用1000條加法指令來直接計算的,這1000條指令無疑會大大增加目標代碼的長度。 方法2:用循環(huán)指令LOOP來實現(xiàn) … MOVAX, 0 MOVCX, 1000 again:ADDAX, CX LOOPagain … 由例9.10,不難看出:偽指令REPT與循環(huán)指令起作用的時期和方式是截然不同的。它們之間的主要差異如表9.1所列。 表9.1 偽指令REPT與循環(huán)指令LOOP之間的主要差異 偽指令REPT循環(huán)指令LOOP 起作用的時期匯編程序把源文件翻譯成目標文件時期程序的執(zhí)行時期 起作用的方式把被重復的指令(組)直接重復寫入目標文件通過反復執(zhí)行同一指令(組)來實現(xiàn)重復 重復次數(shù)對目標文件的影響由于重復次數(shù)決定著被重復指令(組)寫入目標文件的次數(shù),所以,改變重復次數(shù)一定會改變目標文件的字節(jié)數(shù)由于重復的指令數(shù)與重復次數(shù)無關(guān),所以,改變重復次數(shù)不會改變目標文件的字節(jié)數(shù) 四、重復匯編偽指令 偽指令I(lǐng)RP 偽指令I(lǐng)RP 偽指令I(lǐng)RP的作用是用每個參數(shù)創(chuàng)建一組語句,其重復次數(shù)由偽指令后面參數(shù)表中參數(shù)的個數(shù)來確定。其一般使用格式如下: IRP形式參數(shù), <實參1, 實參2, ……, 實參n> 重復的語句組 ENDM 例9.11 把16位通用寄存器之值相加,并把結(jié)果存入寄存器AX。 解:由于16位通用寄存器名是一些不同的符號,不能用計數(shù)的方法來依次訪問它們,所以,我們需要用偽指令I(lǐng)RP來實現(xiàn)。 IRPREG, <BX, CX, DX, SP, BP, SI, DI> ADD AX, REG ENDM 9.4.3 偽指令I(lǐng)RPC 偽指令I(lǐng)RPC的作用與IRP相似,其實參表是一個字符串,并對字符串中的每個字符創(chuàng)建一組語句,所以,其重復次數(shù)是由該字符串中的字符數(shù)來確定。其一般使用格式如下: IRPC形式參數(shù), 字符串 重復的語句組 ENDM 例9.12 定義10個字節(jié)存儲單元,保存數(shù)字0~9的平方數(shù)。 解: IRPCX, 0123456789 DB X*X ENDM 例9.13 把16位數(shù)據(jù)寄存器之值相加,并把結(jié)果存入寄存器DI。 解:由于16位數(shù)據(jù)寄存器是AX、BX、CX和DX,它們的名稱中只有第一個字符不同,所以,可以用偽指令I(lǐng)RPC來實現(xiàn)。 XORDI, DI IRPCREG, ABCD ADD DI, REG&X ;符號&是連接運算符 五、條件匯編偽指令 條件匯編偽指令是告訴匯編程序:根據(jù)某種條件確定一組程序段是否加入到目標程序中。使用條件匯編偽指令的主要目的是:同一個源程序能根據(jù)不同的匯編條件生成不同功能的目標程序,增強宏定義的使用范圍。 條件匯編偽指令與高級語言(如:C/C++)的條件編譯語句在書寫形式上相似,在所起作用方面是完全一致的。 9.5.1 條件匯編偽指令的功能 條件匯編偽指令的一般格式如下: IFnnnn條件表達式 語句組1 [ELSE 語句組2] ENDIF 其中:IFnnnn是表9.2中的偽指令,“[…]”內(nèi)的語句是可選的。 條件匯編偽指令是在匯編程序把源程序轉(zhuǎn)換成目標程序時起作用,其一般含義是:若條件匯編偽指令后面的“條件表達式”為真,那么,語句組1將被匯編;否則,語句組2將被匯編(如果含有ELSE偽指令)。 語句組1或語句組2內(nèi)還可以包有條件匯編偽指令,這時,就形成了嵌套的條件匯編偽指令。一個嵌套的ELSE偽指令總是與最近的、還沒有與其它ELSE偽指令相比配的IFnnnn偽指令相比配。 每條條件匯編偽指令的具體含義如表9.3所示。 表9.3 條件匯編偽指令及其功能一覽表 偽指令含義 IF exp若數(shù)值表達式exp的值不為0,則語句組1包含在目標文件中 IFE exp若數(shù)值表達式exp的值為0,則語句組1包含在目標文件中 IFDEF label若標號label有定義或被說明為EXTRN,則語句組1包含在目標文件中 IFNDEF label若標號label沒有定義,也沒被說明為EXTRN,則語句組1包含在目標文件中 IFB <參數(shù)>在宏引用時,若該形參沒有相應(yīng)的實參相對應(yīng),則語句組1包含在目標文件中 IFNB <參數(shù)>在宏引用時,若該形參沒有相應(yīng)的實參相對應(yīng),則語句組1包含在目標文件中 IFIDN <參數(shù)1>, <參數(shù)2>若參數(shù)1=參數(shù)2,則語句組1包含在目標文件中 IFDIF <參數(shù)1>, <參數(shù)2>若參數(shù)1≠參數(shù)2,則語句組1包含在目標文件中 IF1若匯編程序在第一遍掃描時,則語句組1包含在目標文件中 IF2若匯編程序在第二遍掃描時,則語句組1包含在目標文件中 9.5.2 條件匯編偽指令的舉例 例9.14 編寫一個可用DOS或BIOS功能調(diào)用輸入字符的宏定義。 解: 方法1:使用條件匯編偽指令I(lǐng)F INPUTMACRO IFDOS;當符號DOS不為0時,則使用DOS的功能調(diào)用 MOV AH, 1H INT 21H ELSE;否則,將使用BIOS的功能調(diào)用 MOV AH, 10H INT 16H ENDIF ENDM 在引用宏INPUT時,匯編程序會根據(jù)DOS是否為0來生成調(diào)用不同輸入功能的程序段。 方法2:使用條件匯編偽指令I(lǐng)FDEF INPUTMACRO IFDEFDOS;當定義了DOS,則使用DOS的功能調(diào)用 MOV AH, 1H INT 21H ELSE;否則,將使用BIOS的功能調(diào)用 MOV AH, 10H INT 16H ENDIF ENDM 在引用宏INPUT時,匯編程序會根據(jù)符號DOS是否已定義來生成調(diào)用不同輸入功能的程序段。 例9.15 編寫一個可用功能調(diào)用輸入字符的宏定義。 解: READCHMACRO char MOVAH, 1H INT21H;接受一個字符,并存入AL中 IFNB<char>;若參數(shù)char有實參與之對應(yīng) IFDIF <char>, <AL>;若參數(shù)char≠AL,則把所輸入字符保存到實參中 MOV char, AL ENDIF ENDIF ENDM 六、宏的擴充 MASM 6.11編程系統(tǒng)對宏定義及其相關(guān)語句進行了一定程度的擴充。雖然這些擴充給編程帶來了一些方便,但它們不一定能被其它的匯編語言編程系統(tǒng)所接受,所以,程序員在使用這些方便的擴充功能時,要注意到可能帶來的限制。 下面介紹MASM 6.11編程系統(tǒng)對宏及其相關(guān)語句的擴充。 9.6.1 宏定義形式 在MASM 6.11編程系統(tǒng)中,其宏定義的一般形式如下: 宏名MACRO [參數(shù)1[:tag]] [,參數(shù)2[:tag]...] [LOCAL varlist] … [EXITM [value]] ENDM;宏定義體內(nèi)的局部變量和標號 ;宏的定義體 對上述宏定義的說明與9.1.1節(jié)中的說明完全一致,其需要增加的說明如下: tag—— 其值可以是REQ、=<缺省值>或VARARG REQ指定該參數(shù)是不可缺少。在宏引用時,若該參數(shù)不對應(yīng)某個“實參”,那么,匯編程序會報錯; =<缺省值>在宏引用時,若不指定該參數(shù)所對應(yīng)的“實參”,那么,該參數(shù)就取其缺省值; VARARG該參數(shù)對應(yīng)一個可變長的實參表,各實參之間用逗號分開;若參數(shù)的屬性指定為VARARG,那么,該參數(shù)一定要是最后一個參數(shù)。 有關(guān)該屬性的應(yīng)用,請見隨后9.6.7節(jié)中的舉例。 value—— 宏功能的返回值,其為可選項。 9.6.2 重復偽指令REPEAT 重復偽指令REPEAT與前面9.4.1節(jié)中偽指令REPT在功能和使用方式方面完全一致,設(shè)置該偽指令的主要原因是保證與先前版本的兼容性。 偽指令REPEAT的使用方式如下: REPEAT數(shù)值表達式 語句序列;被重復的匯編語言語句 ENDM 9.6.3 循環(huán)偽指令WHILE 循環(huán)偽指令WHILE的使用方式如下: WHILEExp 語句序列;被重復的匯編語言語句 ENDM 其功能是先判斷表達式Exp是否為假(或為0),若是,則終止該偽指令的功能,否則,循環(huán)匯編下面的指令塊。表達式Exp是能在匯編時計算出其值的數(shù)值表達式。 例9.16:編寫一個帶有參數(shù)result和k的宏,其功能是把1+2+…+k的累加和存入result之中,其中:result是不可缺省的,k的缺省值為1。 解: SUMMACRO result:REQ, k:=<1> LOCAL n n = k movresult, 0 WHILEn add result, n n = n - 1 ENDM ENDM 有了上面的宏定義,就可書寫下面的宏引用來實現(xiàn)其相應(yīng)的功能: SUMax, 10;寄存器ax=1+2+3+…+10 SUMbh;寄存器bh=1,因為第二個形參取其缺省值 SUMecx, 100;寄存器ecx=1+2+3+…+100 SUMdata, 20;存儲單元data=1+2+3+…+20 七、循環(huán)偽指令FOR 循環(huán)偽指令FOR與9.4.2節(jié)中偽指令I(lǐng)RP在功能上完全一致,設(shè)置該偽指令的原因也是為了保證與先前版本的兼容性。 偽指令FOR的使用方式如下: FOR parameter[:REQ|:=<default>], <argument [, argument]...> 語句序列;被重復的匯編語言語句 ENDM 其中各參數(shù)的說明如下: parameter一個合法的標識符,它依次取后面參數(shù)表中的值。在指令序列中,該變量的每次出現(xiàn)都用其值所替換; :REQ說明該變量的取值不能為空; :=<default>指定該變量的缺省值,若后面的參數(shù)表缺省某個參數(shù)(用連續(xù)的逗號),這時,該循環(huán)變量將取其缺省值; Argument參數(shù)表中可含有文本、符號、字符串或數(shù)值常量,每個參數(shù)之間要用逗號分割。 例如: FORdata:=<?>, <"123", , 21, 0> DB data ENDM …… FORreg:REQ, <ax, bx, dx> push reg ENDM 該語句在宏展開時,將得到下列語句: DB "123" DB ? DB 21 DB 0 …… push ax push bx push dx 9.6.5 循環(huán)偽指令FORC 循環(huán)偽指令FOR與9.4.3節(jié)中偽指令I(lǐng)RPC在功能上完全一致,它也是為保證與先前版本的兼容性而設(shè)置的。 偽指令FORC的使用方式如下: FORCparameter, <string> 語句序列;被重復的匯編語言語句 ENDM 其中各參數(shù)的說明如下: parameter:一個合法的標識符,它依次取字符串中的每個字符。在語句序列中,該變量的每次出現(xiàn)都用其值所替換; String:一個字符串或被定義為字符串的符號名,字符串中的空格也被算為一個字符。括號"<"、">"是必不可少的。 例如: FORCdata, <1?3> DB data ENDM …… FORCreg, <abd> push reg&x ENDM 該語句在宏展開時,將得到下列語句: DB 1 DB ? DB 3 …… push ax push bx push dx 八、轉(zhuǎn)移偽指令GOTO 轉(zhuǎn)移偽指令GOTO用于實現(xiàn)宏定義體內(nèi)的轉(zhuǎn)移功能,其使用方式如下: GOTO 標號 … :標號 ;標號后不能寫指令,但可寫注釋 … 該偽指令的功能是使匯編程序轉(zhuǎn)移到“標號”處匯編,它只能在宏定義MACRO、REPEAT、WHILE、FOR和FORC等語句塊內(nèi)使用,該標號也只在該語句塊內(nèi)有效。 9.6.7 宏擴充的舉例 例9.17:編寫一個給任意寄存器或存儲單元清零的宏定義。 解: Clearlist : VARARG FORdata:REQ, <list> mov data, 0 ENDM ENDM 有了上面的宏定義,下面的二個宏引用都是正確的,盡管宏引用時所帶的參數(shù)個數(shù)是不同的。這正是參數(shù)屬性VARARG的作用所在。 Clear ax, bx …… Clear si, cl, MemVar, edx ;MemVar是內(nèi)存變量,任意類型都可以 這二個宏引用展開時所得到的指令如下: mov ax, 0 mov bx, 0 …… mov si, 0 mov cl, 0 mov MemVar, 0 mov edx, 0 |
|
來自: 昵稱16803116 > 《匯編》