匯編語言之程序的基本結(jié)構(gòu).doc
《匯編語言之程序的基本結(jié)構(gòu).doc》由會員分享,可在線閱讀,更多相關(guān)《匯編語言之程序的基本結(jié)構(gòu).doc(31頁珍藏版)》請在裝配圖網(wǎng)上搜索。
第6章 程序的基本結(jié)構(gòu) 在前面幾章,我們分別介紹了用匯編語言進行程序設(shè)計所需要的幾個最基本的知識:內(nèi)存單元的尋址方式,變量定義和各種匯編指令格式。在掌握了這些基本內(nèi)容之后,就需要學習如何把它們組成一個完整的匯編語言程序。 6.1 源程序的基本組成 匯編語言源程序的組成部分有:模塊、段、子程序和宏等。一個模塊對應一個目標文件,當開發(fā)較大型的應用程序時,該程序可能由若干個目標文件或庫結(jié)合而成的。有關(guān)模塊和子程序的知識和宏在第7章介紹,有關(guān)宏的知識將在第9章中敘述。 6.1.1 段的定義 微機系統(tǒng)的內(nèi)存是分段管理的,為了與之相對應,匯編語言源程序也分若干個段來構(gòu)成。8086CPU有四個段寄存器,在該系統(tǒng)環(huán)境下運行的程序在某個時刻最多可訪問四個段,而80386及其以后的CPU都含有六個段寄存器,于是,在這些系統(tǒng)環(huán)境下開發(fā)的運行程序在某個時刻最多可訪問六個段。 不論程序在某個時刻最多能訪問多少個段,在編程序時,程序員都可以定義比該段數(shù)更多的段。在通常情況下,一個段的長度不能超過64K,在80386及其以后系統(tǒng)的保護方式下,段基地址是32位,段的最大長度可達4G。 段的長度是指該段所占的字節(jié)數(shù): 、如果段是數(shù)據(jù)段,則其長度是其所有變量所占字節(jié)數(shù)的總和; 、如果段是代碼段,則其長度是其所有指令所占字節(jié)數(shù)的總和。 在定義段時,每個段都有一個段名。在取段名時,要取一個具有一定含義的段名。 段定義的一般格式如下: 段名 SEGMENT [對齊類型] [組合類型] [類別] … ;段內(nèi)的具體內(nèi)容 … 段名 ENDS 其中:“段名”必須是一個合法的標識符,前后二個段名要相同。可選項“對齊類型”、“組合類型”和“類別”的說明作用請見6.3節(jié)中的敘述。 一個數(shù)據(jù)段的定義例子: DATA1 SEGMENT word1 DW 1, 9078H, ? byte1 DB 21, World DD 12345678H DATA1 ENDS 一個代碼段的例子: CODE1 SEGMENT MOV AX, DATA1 ;把數(shù)據(jù)段DATA1的段值送AX MOV DS, AX ;把AX的值送給DS,即:DS存儲數(shù)據(jù)段的段值 … MOV AX, 4C00H INT 21H ;調(diào)用DOS功能,結(jié)束程序的運行 CODE1 ENDS 6.1.2 段寄存器的說明語句 在匯編語言源程序中可以定義多個段,每個段都要與一個段寄存器建立一種對應關(guān)系。建立這種對應關(guān)系的說明語句格式如下: ASSUME 段寄存器名:段名[, 段寄存器名:段名, ……] 其中:段寄存器是CS、DS、ES、SS、FS和GS,段名是在段定義語句說明時的段名。 在一條ASSUME語句中可建立多組段寄存器與段之間的關(guān)系,每種對應關(guān)系要用逗號分隔。例如, ASSUME CS:CODE1, DS:DATA1 上面的語句說明了:CS對應于代碼段CODE1,DS對應于數(shù)據(jù)段DATA1。 在ASSUME語句中,還可以用關(guān)鍵字NOTHING來說明某個段寄存器不與任何段相對應。下面語句說明了段寄存器ES不與某段相對應。 ASSUME ES:NOTHING 在通常情況下,代碼段的第一條語句就是用ASSUME語句來說明段寄存器與段之間的對應關(guān)系。在代碼段的其它位置,還可以用另一個ASSUME語句來改變前面ASSUME語句所說明的對應關(guān)系,這樣,代碼段中的指令就用最近的ASSUME語句所建立的對應關(guān)系來確定指令中的有關(guān)信息。 例6.1 匯編語言段及其段說明語句的作用。 DATA1 SEGMENT ;定義數(shù)據(jù)段DATA1 word1 DW 5678h byte1 DB "ABCDEFG" DATA1 ENDS DATA2 SEGMENT ;定義數(shù)據(jù)段DATA2 word2 DW 1234h word3 DW 9876h DATA2 ENDS DATA3 SEGMENT ;定義數(shù)據(jù)段DATA3 byte2 DB ? DATA3 ENDS CODE1 SEGMENT ;編寫代碼段CODE1 ASSUME CS:CODE1, DS:DATA1, ES:DATA2 ;(1) MOV AX, DATA1 ;(2) MOV DS, AX ;(3) MOV AX, DATA2 ;(4) MOV ES, AX ;(5) … MOV AX, word1 ;訪問段DATA1中的字變量word1 MOV word2, AX ;訪問段DATA2中的字變量word2 … ASSUME DS:DATA3, ES:NOTHING ;(6) MOV AX, DATA3 MOV DS, AX MOV BL, byte2 ;訪問段DATA3中的字節(jié)變量byte2 … MOV AX, 4C00H ;(7) INT 21H ;(8) CODE1 ENDS 語句(1)和(6)分別說明了段和段寄存器之間的對應關(guān)系,其中語句(6)重新說明語句(1)所指定的對應關(guān)系。 二組語句(2)和(3)、(4)和(5)實現(xiàn)對段寄存器DS和ES賦初值。ASSUME說明語句只起說明作用,它不會對段寄存器賦值,所以,必須對有關(guān)段寄存器賦值。在以后的其它源程序中也都是用此方法來實現(xiàn)對數(shù)據(jù)段寄存器賦值的。 語句(7)和(8)是調(diào)用中斷21H的4CH號功能來結(jié)束本程序的執(zhí)行,程序的返回代碼由寄存器AL來確定。結(jié)束本程序執(zhí)行的指令是所有主模塊必須書寫的語句。 注意:代碼段寄存器不能由程序員在源程序中對其賦值,其值是由操作系統(tǒng)在裝入它進入系統(tǒng)運行時自動賦值的。 6.1.3 堆棧段的說明 堆棧段是一個特殊的段,在程序中可以定義它,也可以不定義。除了要生成COM型執(zhí)行文件的源程序外,一個完整的源程序一般最好定義堆棧段。如果在程序中不定義堆棧段,那么,操作系統(tǒng)在裝入該執(zhí)行程序時將自動為其指定一個64K字節(jié)的堆棧段。 在程序沒有定義堆棧段的情況下,在由連接程序生成執(zhí)行文件時,將會產(chǎn)生一條如下的警告信息,但程序員可以不理會它,所生成的執(zhí)行文件是可以正常運行的。 warning xxxx: no stack segment (其中:xxxx是錯誤號) 在源程序中,可用以下方法來定義堆棧段。 方法1: STACK1 SEGMENT DB 256 DUP(?) ;256是堆棧的長度,可根據(jù)需要進行改變 TOP LABEL WORD STACK1 ENDS 以上堆棧段的定義如圖6.1所示。由于堆棧是按地址從大到小的存儲單元順序來存放內(nèi)容的,所以,在堆棧存儲單元說明語句之后,再說明一個棧頂別名,這樣,對棧頂寄存器SP的賦值就很方便。 在源程序的代碼段中,還要添加如下程序段,才能把段STACK1當作堆棧段來使用。 圖6.1 堆棧段定義示意圖 … ASSUME SS:STACK1 ;可在代碼段的段指定語句中一起說明 CLI ;禁止響應可屏蔽中斷 MOV AX, STACK1 MOV SS, AX MOV SP, offset TOP ;給堆棧段的棧頂寄存器SP賦初值 STI ;恢復響應可屏蔽中斷 … 方法2: STACK1 SEGMENT STACK ;定義一個堆棧段,其段名為STACK1 DB 256 DUP(?) STACK1 ENDS 上述段定義說明了該段是堆棧段,系統(tǒng)會自動把段寄存器SS和棧頂寄存器SP與該堆棧段之間建立相應的關(guān)系,并設(shè)置其初值,而不用在代碼段對它們進行賦值。 除了以上二種方法外,還有一種更簡潔的方法來定義堆棧段,有關(guān)內(nèi)容請見第6.4.2節(jié)中的敘述。 6.1.4 源程序的結(jié)構(gòu) 下面的程序是一個完整的源程序,其功能是在屏幕上顯示字符串“Hello, World.”。讀者可參考此結(jié)構(gòu)編寫自己的簡單程序。 例6.2 在屏幕上顯示字符串“HELLO,WORLD.” 解:可運行下面的控件,用鼠標左鍵單擊程序中的某一行,可閱讀其含義;單擊“內(nèi)存”可切換內(nèi)存內(nèi)容的顯示方式。 偽指令END表示源程序到此為止,匯編程序?qū)υ撜Z句之后的任何內(nèi)容都不作處理,所以,通常情況下,偽指令END是源程序的最后一條語句。 偽指令END后面可附帶一個在程序中已定義的標號,由該標號指明程序的啟動位置。 如果源程序是一個獨立的程序或主模塊,那么,偽指令END后面一定要附帶一個標號;如果源程序僅是一個普通模塊,那么,其END后面就一定不能附帶標號。 6.2 程序的基本結(jié)構(gòu) 在學習高級語言程序設(shè)計時,我們知道了程序的三大主要結(jié)構(gòu):順序結(jié)構(gòu)、分支結(jié)構(gòu)和循環(huán)結(jié)構(gòu)。在匯編語言的源程序也同樣有此三大結(jié)構(gòu),所不同的是它們的表現(xiàn)形式不同。用高級語言編寫程序時,由于不使用“轉(zhuǎn)移語句”而使這三種結(jié)構(gòu)清晰明了。 但在匯編語言的源程序中,很難不使用“轉(zhuǎn)移語句”(除非是一些只有簡單功能的程序),有時甚至會有各種各樣的“轉(zhuǎn)移語句”。由于存在這些轉(zhuǎn)移語句,就使得:匯編語言源程序的基本結(jié)構(gòu)顯得不太明確。如果源程序的編寫者思維混亂,編寫出來的源程序在結(jié)構(gòu)上就會顯得雜亂無章,反之,如果編寫者條理清晰,安排的操作井然有序,那么,編寫出來的程序在結(jié)構(gòu)上就會一目了然。 總之,不論是高級語言的源程序,還是匯編語言的源程序,其程序的三大基本結(jié)構(gòu)也還是萬變不離其宗的。 6.2.1 順序結(jié)構(gòu) 順序結(jié)構(gòu)是最簡單的程序結(jié)構(gòu),程序的執(zhí)行順序就是指令的編寫順序,所以,安排指令的先后次序就顯得至關(guān)重要。另外,在編程序時,還要妥善保存已得到的處理結(jié)果,為后面的進一步處理直接提供前面的處理結(jié)果,從而避免不必要的重復操作。 例6.3 編寫程序段,完成下面公式的計算(其中:變量X和Y是32位有符號數(shù),變量A,B和Z是16位有符號數(shù))。 A←(X-Y+24)/Z的商,B←(X-Y+24)/Z的余數(shù) 解: DATA1 SEGMENT X DD ? Y DD ? Z DW ? A DW ? B DW ? … DATA1 ENDS CODE1 SEGMENT … MOV AX, X MOV DX, X+2 ;用(DX:AX)來保存32位變量X的數(shù)值 SUB AX,Y SBB DX, Y+2 ;(DX:AX)-(Y+2:Y) ADD AX, 24D ADC DX, 0 ;(DX:AX)+24 IDIV Z MOV A, AX MOV B, DX … CODE1 ENDS 在編程序時,常常需要交換二變量之值。假設(shè)需要交換值的變量名為:var1和var2,臨時增加的變量名為temp。常用的算法如下: temp = var1 var1 = var2 var2 = temp 例6.4 假設(shè)有二個字變量word1和word2,編寫程序段實現(xiàn)交換其值的功能。 解: 方法1:用匯編語言指令簡單“直譯”上面的 交換數(shù)據(jù)方法 DATA1 SEGMENT … word1 DW ? word2 DW ? temp DW ? … DATA1 ENDS CODE1 SEGMENT … MOV AX, word1 MOV temp, AX ;上二語句實現(xiàn)語句“temp=word1” MOV AX, word2 MOV word1, AX ;上二語句實現(xiàn)語句“word1=word2” MOV AX, temp MOV word2, AX ;上二語句實現(xiàn)語句“word2=temp” … CODE1 ENDS 這種方法雖然也能完成功能,但顯然其不能充分利用匯編語言的特點,程序效率很低。 方法2:用匯編語言指令的特點來直接編譯 DATA1 SEGMENT … word1 DW ? word2 DW ? … DATA1 ENDS CODE1 SEGMENT … MOV AX, word1 XCHG AX, word2 MOV word1, AX ;能XCHG word1, word2來代替這三條指令嗎? … CODE1 ENDS 該方法充分利用了匯編語言的特點,不僅省去了中間變量temp的定義,而且程序的效率也提高了。 6.2.2 分支結(jié)構(gòu) 分支結(jié)構(gòu)是一種非常重要的程序結(jié)構(gòu),也是實現(xiàn)程序功能選擇所必要的程序結(jié)構(gòu)。由于匯編語言需要書寫轉(zhuǎn)移指令來實現(xiàn)分支結(jié)構(gòu),而轉(zhuǎn)移指令肯定會破壞程序的結(jié)構(gòu),所以,編寫清晰的分支結(jié)構(gòu)是掌握該結(jié)構(gòu)的重點,也是用匯編語言編程的基本功。 在程序中,當需要進行邏輯分支時,可用每次分二支的方法來達到程序最終分多支的要求,也可是用地址表的方法來達到分多支的目的。 一、顯示轉(zhuǎn)移指令實現(xiàn)的分支結(jié)構(gòu) 在高級語句中,分支結(jié)構(gòu)一般用IF語句來實現(xiàn),在匯編語言中,課用無條件轉(zhuǎn)移指令或條件轉(zhuǎn)移指令實現(xiàn)的分支結(jié)構(gòu)。如圖6.2給出了二種常用的分支結(jié)構(gòu)。 在編寫分支程序時,要盡可能避免編寫“頭重腳輕”的結(jié)構(gòu),即:當前分支條件成立時,將執(zhí)行一系列指令,而條件不成立時,所執(zhí)行的指令很少。這樣就使后一個分支離分支點較遠,有時甚至會遺忘編寫后一分支程序。這種分支方式不僅不利于程序的閱讀,而且也不便將來的維護。 所以,在編寫分支結(jié)構(gòu)時,一般先處理簡單的分支,再處理較復雜的分支。對多分支的情況,也可遵循“由易到難”的原則。因為簡單的分支只需要較少的指令就能處理完,一旦處理完這種情況后,在后面的編程過程中就可集中考慮如何處理復雜的分支。 (a) if … endif結(jié)構(gòu) (b) if…else…endif結(jié)構(gòu) 圖6.2 分支結(jié)構(gòu)的二種結(jié)構(gòu) 例6.5 已知字節(jié)變量CHAR1,編寫一程序段,把它由小寫字母變成大寫字母。 解: DATA1 SEGMENT … CHAR1 DB ? … DATA1 ENDS CODE1 SEGMENT … MOV AL, CHAR1 CMP AL, ‘a(chǎn)’ JB next CMP AL, ‘z’ JA next SUB CHAR1, 20H ;指令AND CHAR1, 0DFH也可以 next: … … CODE1 ENDS 例6.6 編寫一程序段,計算下列函數(shù)值。其中:變量X和Y是有符號字變量。 解: DATA1 SEGMENT … X DW ? Y DW ? … DATA1 ENDS CODE1 SEGMENT … MOV AX, X CMP AX, 0 JGE case23 ADD AX, 10 ;第一種情況的計算結(jié)果 JMP result case23: CMP AX, 10D JG case3 MOV BX, 30D IMUL BX ;第二種情況的計算結(jié)果 JMP result case3: SUB AX, 9 ;第三種情況的計算結(jié)果 result: MOV Y, AX ;把計算結(jié)果保存到變量Y中 … CODE1 ENDS 例6.7 把下列C語言的語句改寫成等價的匯編語言程序段(不考慮運算過程中的溢出)。 If (a+b > 0 && c%2 == 0) a = 62; else a = 21; 其中:變量a,b和c都是有符號的整型(int)變量。 解: DATA1 SEGMENT … A DW ? B DW ? C DW ? … DATA1 ENDS CODE1 SEGMENT … MOV AX, A ADD AX, B JLE _ELSE ;ADD指令會改變算術(shù)標志位 TEST C, 1 ;C%2==0,也就是:看C的最低位是否為0 JNZ _ELSE MOV A, 62D JMP NEXT _ELSE: MOV A, 21D NEXT: … CODE1 ENDS 例6.8 用地址轉(zhuǎn)移表實現(xiàn)下列C語言的switch語句,其中:變量A和B是有符號的整型(int)變量。 switch (a%8) {case 0: b = 32; break; case 1: case 2: b = a + 43; break; case 3: b = 2*a; break; case 4: b--; break; case 5: case 6: case 7: printf(“Function 5_6_7”); break; } 解: DATA1 SEGMENT … A DW ? B DW ? Table DW case0. case12, case12, case3 DW case4, case567, case567, case567 MSG DB Function 5_6_7$ … DATA1 ENDS CODE1 SEGMENT … MOV AX, A MOV BX, AX AND BX, 7 ;得到BX的低三位,實現(xiàn)a%8的計算 SHL BX, 1 ;由于地址表是字類型,其下標要乘2 JMP Table[BX] ;利用地址表實現(xiàn)多路轉(zhuǎn)移 case0: MOV B, 32D JMP next case12: ADD AX, 43D MOV B, AX JMP next case3: SHL AX, 1 MOV B, AX JMP next case4: DEC B JMP next case567: LEA DX, MSG MOV AH, 9 INT 21H JMP next next: … CODE1 ENDS 用地址表實現(xiàn)多路轉(zhuǎn)移的關(guān)鍵在于:轉(zhuǎn)移入口的地址表和轉(zhuǎn)移情況可整數(shù)化。如果這二個要求有一個不滿足,或很難構(gòu)造,則無法使用該方法。 為了改善匯編語言源程序的結(jié)構(gòu),減少顯式轉(zhuǎn)移語句所帶來混亂,在宏匯編MASM 6.11系統(tǒng)中,增加了表達分支結(jié)構(gòu)的偽指令。該偽指令的書寫格式與高級語言的書寫方式相類似,匯編程序在匯編時會自動增加轉(zhuǎn)移指令和相應的標號。理解并掌握該知識,對將來學習《編譯原理》課程也有一定的幫助。 分支偽指令的具體格式如下: 格式1: .IF condition ;以英文“句號”開頭 指令序列 ;條件"condition"成立時所執(zhí)行的指令序列 .ENDIF 格式2: .IF condition 指令序列1 .ELSE 指令序列2 ;條件"condition"不成立時所執(zhí)行的指令序列 .ENDIF 格式3: .IF condition1 指令序列1 .ELSEIF condition2 指令序列2 ;條件"condition2"成立時所執(zhí)行的指令序列 .ENDIF 其中:條件表達式“condition”的書寫方式與C語言中條件表達式的書寫方式相似,也可用括號來組成復雜的條件表達式。 條件表達式中可用的操作符有:==(等于)、!=(不等)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、&(位操作與)、!(邏輯非)、&&(邏輯與)、||(邏輯或)等。 若在條件表達式中檢測標志位的信息,則可以使用的符號名有:CARRY?(相當于CF==1)、OVERFLOW?(OF==1)、PARITY?(PF==1)、SIGN?(SF==1)、ZERO?(ZF==1)等。例如: .IF CARRY? && AX != BX ;檢測CF==1且AX!=BX是否成立 ;匯編語言指令序列 .ENDIF 在指令序列中,還可再含有其它的.IF偽指令,即:允許嵌套。偽指令.ELSEIF引導出另一個二叉分支,但它不能作偽指令塊的第一個偽指令。 匯編程序在對“條件表達式”進行代碼轉(zhuǎn)換時將進行代碼優(yōu)化處理,以便盡可能生成最好的指令代碼。如: .IF ax == 0 匯編程序會把它轉(zhuǎn)換為指令“OR ax, ax”,而不是“CMP ax, 0”,因為前者比后者更好,而不是簡單直接地轉(zhuǎn)換為后者。 如果用偽指令來書寫分支結(jié)構(gòu),那么,例6.5的代碼段部分就可寫成如下程序段: … MOV AL, CHAR1 .IF AL>=a && AL<=z ;語句象C語言語句嗎? SUB CHAR1, 20H .ENDIF … 也可把例6.6的代碼段部分就可寫成如下程序段: … MOV AX, X .IFAX < 0 ADD AX, 10 ;計算第一種情況的結(jié)果 .ELSEIF AX <= 10 MOV BX, 30D IMUL BX ;計算第二種情況的結(jié)果 .ELSE SUB AX, 9 ;計算第三種情況的結(jié)果 .ENDIF MOV Y, AX … ;把計算結(jié)果保存到變量Y中 例6.9 根據(jù)當前計算機的時間和日期,顯示上午(AM)或下午(PM),以及所在的季節(jié)。 解:顯示解答 6.2.3 循環(huán)結(jié)構(gòu) 循環(huán)結(jié)構(gòu)是一個重要的程序結(jié)構(gòu),它具有重復執(zhí)行某段程序的功能。通常,循環(huán)結(jié)構(gòu)包括以下四個組成部分: 1、循環(huán)初始化部分——初始化循環(huán)控制變量、循環(huán)體所用到變量; 2、循環(huán)體部分——循環(huán)結(jié)構(gòu)的主體; 3、循環(huán)調(diào)整部分——循環(huán)控制變量的修改、或循環(huán)終止條件的檢查; 4、循環(huán)控制部分——程序執(zhí)行的控制轉(zhuǎn)移。 以上四部分可以在程序中用各種不同的形式體現(xiàn)出來,有時也并非清析地表達出來。常用的循環(huán)結(jié)構(gòu)如圖6.3所示。 (a)、Do—While結(jié)構(gòu) (b)、While結(jié)構(gòu) 圖6.3 常用的循環(huán)結(jié)構(gòu)示意圖 一、用循環(huán)指令構(gòu)成循環(huán)結(jié)構(gòu) 在編寫循環(huán)結(jié)構(gòu)的程序片段時,我們可以多種方法來循環(huán)結(jié)構(gòu)。如:循環(huán)次數(shù)是已知的,可用LOOP指令來構(gòu)造循環(huán);當循環(huán)次數(shù)是未知或不定的,則可用條件轉(zhuǎn)移或無條件轉(zhuǎn)移來構(gòu)成循環(huán)結(jié)構(gòu)。 例6.10 分類統(tǒng)計字數(shù)組data中正數(shù)、負數(shù)和零的個數(shù),并分別存入內(nèi)存字變量Positive、Negative和Zero中,數(shù)組元素個數(shù)保存在其第一個字中。 解:顯示解答 例6.11 計算數(shù)組score的平均整數(shù),并存入內(nèi)存字變量Average中,數(shù)組以-1為結(jié)束標志。 解: DATA1 SEGMENT data DW 90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1 Average DW 0 DATA1 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:DATA1 START: MOV AX, DATA1 MOV DS, AX XOR AX, AX XOR DX, DX ;用(DX,AX)來保存數(shù)組元素之和 XOR CX, CX ;用CX來保存數(shù)組元素個數(shù) LEA SI, data ;用指針SI來訪問整個數(shù)組 again: MOV BX, word ptr [SI] CMP BX, 0 JL over ADD AX, BX ADC DX, 0 ;把當前數(shù)組元素之值加到(DX,AX)中 INC CX ;數(shù)組元素個數(shù)加1 ADD SI, 2 JMP again over: JCXZ exit ;防止零作除數(shù),即數(shù)組是空數(shù)組 DIV CX MOV Average, AX exit: MOV AX, 4C00H INT 21H CODE1 ENDS END START 二、用偽指令實現(xiàn)的循環(huán)結(jié)構(gòu) 在宏匯編MASM 6.11系統(tǒng)中,還增加了表達循環(huán)結(jié)構(gòu)的偽指令,以便更清晰地表達WHILE循環(huán)、REPEAT-UNTIL循環(huán)。另外,還增加兩個輔助循環(huán)的偽指令。這些偽指令的書寫格式和含義與高級語言中相應語句的書寫格式和含義相一致,所以,這些偽指令是很容易掌握的,也是非常有用的。 循環(huán)偽指令的格式和含義如下: 1、WHILE型循環(huán)偽指令 .WHILE condition 循環(huán)體的指令序列 ;條件"condition”成立時所執(zhí)行的指令序列 .ENDW 其中:.ENDW與前面的.WHILE相匹配,它標志著其循環(huán)體到此結(jié)束。 如果條件表達式“condition”在循環(huán)開始時,就為“假”(false),那么,該循環(huán)體一次也不會被執(zhí)行。 2、REPEAT型循環(huán)偽指令 .REPEAT 循環(huán)體的指令序列 .UNTIL condition .REPEAT 循環(huán)體的指令序列 .UNTILCXZ [condition] REPEAT型循環(huán)在執(zhí)行完循環(huán)體后,才判定邏輯表達式condition的值。若該表達式的值為真,則終止該循環(huán),并將執(zhí)行偽指令.UNTIL[CXZ]后面的指令,否則,將向上跳轉(zhuǎn)到偽指令.REPEAT之后的指令,為繼續(xù)執(zhí)行其循環(huán)體作準備。 如果.UNTILCXZ后面沒有寫邏輯表達式,那么,由.REPEAT-.UNTILCXZ所構(gòu)成的循環(huán)與用LOOP指令所過程的循環(huán)是一致的,它們都是以“CX=0”為循環(huán)終止條件。 如果.UNTILCXZ后面書寫了邏輯表達式,那么,該邏輯表達式的形式只能是:“EXP1==EXP2”或“EXP1!=EXP2”。所以,這時由“.REPEAT-.UNTILCXZ condition”所構(gòu)成的循環(huán)就與用LOOPNE/LOOPE指令所過程的循環(huán)是一致的,它們都是以“condition || CX=0”為循環(huán)終止條件。 和高級語言的REPEAT型的循環(huán)一樣,.REPEAT-.UNTIL[CXZ]的循環(huán)體也會至少被執(zhí)行一次。 .WHILE-.ENDW和.REPEAT-.UNTIL[CXZ]的循環(huán)體內(nèi)還可再含有循環(huán)偽指令,這樣就構(gòu)成了循環(huán)結(jié)構(gòu)的嵌套。 匯編程序在生產(chǎn)指令代碼時會進行代碼優(yōu)化,以便盡可能得到最優(yōu)化的指令序列。 3、輔助循環(huán)偽指令 (1)、終止循環(huán)偽指令 .BREAK .BREAK .IF condition 該偽指令用來終止包含它的最內(nèi)層循環(huán)。前者是無條件終止循環(huán),后者是僅當邏輯表達式condition為真時,才終止循環(huán)。 .WHILE 1 .REPEAT … .BREAK .IF condition … … .BREAK .IF condition … ENDW .UNTIL 0 對于以上二個循環(huán),如果沒有指令來終止循環(huán)的話,它們都將進入死循環(huán)狀態(tài),但如果在該層循環(huán)體內(nèi),存在偽指令“.BREAK .IF condition”的話,那么,當邏輯表達式condition為真時,該循環(huán)就會被終止了。 (2)、循環(huán)繼續(xù)偽指令 .CONTINUE .CONTINUE .IF condition 該偽指令用于直接跳轉(zhuǎn)到包含它的最內(nèi)層循環(huán)的計算循環(huán)條件表達式的代碼處。前者是無條件轉(zhuǎn)移到計算循環(huán)條件表達式的代碼處,后者是僅當條件表達式condition為真時,才進行這樣的跳轉(zhuǎn)。 輔助循環(huán)偽指令.BREAK和.CONTINUE只能在偽指令.WHILE-.ENDW和.REPEAT-.UNTIL的循環(huán)體內(nèi)使用。 例6.12 顯示9個數(shù)字字母1~9,26個大寫字母,和顯示任意輸入的數(shù)字字符,并用按“回車”鍵來結(jié)束本程序的運行。 解: DATA1 SEGMENT MSG1 DB 13, 10, "Iteration: " NUM1 DB 1, "$" MSG2 DB 13, 10, "Alphabet: $" NUM2 DB A, " $" MSG3 DB 13, 10, "Type digits, then press ENTER: $" DATA1 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:DATA1 START: MOV AX, DATA1 MOV DS, AX MOV CX, 9 MOV AH, 09H MOV DX, OFFSET MSG1 .REPEAT INT 21H INC NUM1 ;顯示Iteration: 1,2,~,9 .UNTILCXZ MOV DX, OFFSET MSG2 INT 21H ;顯示字符串"Alphabet:" MOV AH, 09H MOV DX, OFFSET NUM2 .REPEAT INT 21H INC NUM2 ;顯示當前字母 ;當前字母向后移 .UNTIL NUM2 > Z ;顯示整個大寫字母表 MOV AH, 09H MOV DX, OFFSET MSG3 INT 21H .WHILE 1 ;循環(huán)條件為永真的循環(huán) MOV AH, 07H INT 21H ;不帶回顯地從鍵盤讀一個字符 .BREAK .IF AL == 13 ;如果輸入“回車”鍵,則終止循環(huán) .CONTINUE .IF (AL<0) || (AL>9) ;如果字符不是數(shù)字字符,則繼續(xù)循環(huán) MOV DL, AL MOV AH, 02H INT 21H ;顯示所輸入的數(shù)字字母 .ENDW MOV AX, 4C00H INT 21H CODE1 ENDS END START 6.3 段的基本屬性 在通常情況下,一個復雜的應用程序會由若干個模塊組成,一個模塊又會含有多個段。而不同模塊的段之間、同一模塊的段之間往往存在某種聯(lián)系,這種聯(lián)系就要體現(xiàn)在段屬性的說明上。 段定義的一般格式如下: 段名 SEGMENT [對齊類型] [組合類型] [類別] … 段名 ENDS 段屬性“對齊類型”、“組合類型”和“類別”要按此順序說明,但這些可選項可根據(jù)需要選擇書寫。如果源程序中不指定某個屬性,那么,匯編程序?qū)⑹褂迷搶傩缘娜笔≈怠? 程序中的段名可以是唯一的,也可以與其它段同名。在同一模塊中,如果有二個段同名,則后者被認為是前段的后續(xù),這樣,它們就屬同一段。 當同一模塊出現(xiàn)二個同名段時,則后者的可選項屬性要么與前者相同,要么不寫其屬性而選用前者的段屬性。 例6.13 同段名的作用 DATA1 SEGMENT ;第一個數(shù)據(jù)段 MSG DB "Hello, " DATA1 ENDS CODE1 SEGMENT ;第一個代碼段 ASSUME CS:CODE1, DS:DATA1 START: MOV AX, DATA1 MOV DS, AX MOV DX, offset MSG MOV AH, 9 INT 21H CODE1 ENDS DATA1 SEGMENT ;第二個數(shù)據(jù)段 DB "World.$" DATA1 ENDS CODE1 SEGMENT ;第二個代碼段 MOV AX, 4C00H INT 21H CODE1 ENDS END START END 在上面的例子中,第二個數(shù)據(jù)段是第一個數(shù)據(jù)段的后續(xù),匯編程序把它們是合二為一,上述的代碼段也如此。 下面,詳細說明段屬性的含義及其作用。 6.3.1 對齊類型(ALIGN) 對齊類型表示當前段對起始地址的要求,連接程序(LINK.EXE)按表6.1的地址格式來定位段的起始地址。在進行段定位時,會根據(jù)其定位類型進行定位的,所以,各段之間就有可能出現(xiàn)一些空閑字節(jié),即可能浪費幾個字節(jié)單元。 段對齊類型PARA是一個適用于所有段類型的對齊類型,它也是缺省的對齊類型。對齊類型BYTE和WORD通常用于數(shù)據(jù)段的定位,對齊類型DWORD通常用于80386及其以后CPU代碼段的定位。 表6.1 段對齊類型與段起始地址之間的對應關(guān)系 對齊類型 起始地址(二進制) 功能說明 最多的空閑字節(jié)數(shù) BYTE xxxx xxxx xxxx xxxx xxxx 下一個字節(jié)地址 0 WORD xxxx xxxx xxxx xxxx xxx0 下一個字地址 1 DWORD xxxx xxxx xxxx xxxx xx00 下一個雙字地址 3 PARA xxxx xxxx xxxx xxxx 0000 下一個節(jié)地址 15 PAGE xxxx xxxx xxxx 0000 0000 下一個頁地址 127 6.3.2 組合類型(COMBINE) 組合類型是告訴連接程序如何把不同模塊中段名相同的段合并在一起。具體的組合類型如下: NONE 表示當前段在邏輯上獨立于其它模塊,并有其自己的基地址。NONE是缺省的組合類型。 PUBLIC 表示當前段與其它模塊中同段名的PUBLIC類型段組合成一個段。組合的先后次序取決于LINK程序中目標模塊排列的次序。在組合時,后續(xù)段的起始地址要按其對齊類型進行定位,所以,同名段之間可能有間隔。 COMMON 表示當前段與其它模塊中同名段重疊,也就是說,它們的起始地址相同。最終段的長度是同名段的最大長度。由于段覆蓋,所以,前一同名段中的初始化數(shù)據(jù)被后續(xù)段的初始數(shù)據(jù)覆蓋掉。 STACK 組合類型STACK表示當前段是堆棧棧,其組合情況與PUBLIC相同。 AT 數(shù)值表達式 該數(shù)值表達式是當前段所指定的絕對起始地址的段地址。 6.3.3 類別(CLASS) 類別是一個由程序員指定的用單引號括起來的字符串。如果一個段沒有給出類別,那么,這個段的類別就為空。類別是用于段的分類,連接程序利用該類別來調(diào)整同名、同類別的段,并使它們相鄰。典型的類別是"Data"和"Code"。如果指定某段的類別是"Code",那么,該段最好是代碼段,這樣,有的調(diào)試程序(如:CodeView)就可以順序工作。 例如: DATA1 SEGMENT WORD PUBLIC "Data" … DATA1 ENDS 上述段定義說明了該段的起始地址是下一個字地址、組合類型為PUBLIC、段類別是"Data"。 6.3.4 段組(GROUP) 段組偽指令GROUP是用于把源程序模塊中若干個段結(jié)合成一個組,并對該段組定義一個段組名。段組偽指令的格式如下: 段組名 GROUP 段名[, 段名, ……] 其中:段名之間要用逗號間隔,段名也可以用表達式“SEG 變量”或“SEG 標號”。 下面舉例說明段組偽指令的使用方法和作用。 例6.12 段組的作用 方法1:用一個段寄存器對應二個數(shù)據(jù)段 DATA1 SEGMENT ;第一個數(shù)據(jù)段 b1 DB 10h DATA1 ENDS DATA2 SEGMENT ;第二個數(shù)據(jù)段 b2 DB 23h DATA2 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:DATA1 ;(1) START: MOV AX, DATA1 MOV DS, AX ;(2)把數(shù)據(jù)段DATA1的段值賦給段寄存器DS … MOV BL, b1 ;(3)引用DS來訪問DATA1中的變量b1 … ASSUME DS:DATA2 ;(4) MOV AX, DATA2 MOV DS, AX ;(5)把數(shù)據(jù)段DATA2的段值賦給段寄存器DS … MOV AL, b2 ;(6)引用DS來訪問DATA2中的變量b2 … CODE1 ENDS END START 在上例中,語句(1)說明DS與DATA1建立聯(lián)系,語句(2)對DS賦值,語句(3)用DS來訪問DATA1段的變量名。語句(4)說明DS與DATA2建立聯(lián)系,語句(5)對DS賦值,語句(6)用DS來訪問DATA2段的變量名。 在該例子中,因為只使用一個段寄存器DS來對應二個數(shù)據(jù)段,所以,需要切換DS的對應關(guān)系(如:語句(4))。但我們也可以用段寄存器DS和ES來分別對應段DATA1和DATA2,這樣,方法1就可變成方法2。 方法2:用二個段寄存器對應二個數(shù)據(jù)段 DATA1 SEGMENT b1 DB 10h DATA1 ENDS DATA2 SEGMENT b2 DB 23h DATA2 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:DATA1, ES:DATA2 START: MOV AX, DATA1 MOV DS, AX ;把數(shù)據(jù)段DATA1的段值賦給段寄存器DS MOV AX, DATA2 MOV ES, AX ;把數(shù)據(jù)段DATA2的段值賦給段寄存器ES … MOV BL, b1 ;引用DS來訪問DATA1中的變量b1 … MOV AL, b2 ;引用ES來訪問DATA2中的變量b2 … CODE1 ENDS END START 我們還可以用段組來簡化段寄存器的使用,把段DATA1和DATA2組成一個數(shù)據(jù)段。所以,把方法2再改寫成方法3的形式。 方法3:用一個段組組成二個數(shù)據(jù)段 GSEG GROUP DATA1, DATA2 ;把段DATA1和DATA2定義成一個段組 DATA1 SEGMENT b1 DB 10h DATA1 ENDS DATA2 SEGMENT b2 DB 23h DATA2 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:GSEG START: MOV AX, GSEG MOV DS, AX ;把段組GSEG的段值賦給段寄存器DS … MOV BL, b1 ;引用DS來訪問DATA1中的變量b1 … MOV AL, b2 ;引用DS來訪問DATA2中的變量b2 … CODE1 ENDS END START 定義段組后,段組內(nèi)各段所定義的標號和變量,除了與定義它們的段起始點相關(guān)外,還與段組的起始點相關(guān)。規(guī)定如下: 、 如果在ASSUME偽指令中說明段組與段寄存器相對應,那么,有關(guān)標號或變量的偏移量就相對于段組起點計算; 、 如果在ASSUME偽指令中說明段組內(nèi)的某各段與段寄存器相對應,那么,有關(guān)標號或變量的偏移量就相對于該段的起點。 所以,在使用段組后,程序員要謹慎使用ASSUME偽指令,并保證段寄存器的值與段組或段相一致。 6.4 簡化的段定義 前面,我們介紹了完整的段定義格式,用完整的段定義格式雖然可以控制段的各種屬性,但程序員很少會這樣做。現(xiàn)在的匯編程序提供了一種簡化的段定義方式,它使定義段更簡單、方便。 6.4.1 存儲模型說明偽指令 在使用簡化的段定義方式之前,必須使用存儲模式說明偽指令來描述源程序所采用的存儲模式。該偽指令說程序所使用的存儲模式,匯編程序?qū)⒂迷摯鎯δJ缴上鄳腁SSUME和GROUP語句,同時也為其它的簡化段創(chuàng)建等價的預定義。 程序存儲模式說明偽指令的格式如下: .MODEL 存儲模式[,語言類型] [,操作系統(tǒng)類型] [,堆棧類型] 程序可選的存儲模式有:TINY、SMALL、COMPACT、MEDIUM、LARGE、HUGE和FLAT。 偽指令.MODEL必須寫在源程序的首部,且只能出現(xiàn)一次,其前內(nèi)容只能是注釋。 如果用偽指令來指定程序所遵循的語言類型,那么,將不允許子程序的嵌套定義。與子程序定義有關(guān)的內(nèi)容請見第7.5節(jié)。 一、存儲模式 如果要用匯編語言編寫被高級語言調(diào)用的子程序,那么,該匯編程序的存儲模式必須與該高級語言編譯(或解釋)程序所使用的存儲模式相匹配。匯編語言程序所能使用的存儲模式、符號及其相關(guān)信息如表6.2所列。 在程序中,還可偽指令OPTION SEGMENT和SEGMENT來指定段的規(guī)模。 有關(guān)存儲模式的具體規(guī)定如下: 、TINY 在匯編程序MASM 6.11和TASM 4.0,該存儲類型是為編寫COM文件類型而設(shè)置的。程序員還可用匯編命令行選項/AT和連接命令選項/TINY來達到此目的。 表6.2 存儲模式的符號及其相關(guān)含義 代碼的位距 數(shù)據(jù)的位距 段的寬度 數(shù)據(jù)段和代碼段能否合并 Code Distance Data Distance Segment Width Data & Code Combined? Tiny Small Compact Medium Large Huge Flat NEAR NEAR 16-bit Yes NEAR NEAR 16-bit No NEAR FAR 16-bit No FAR NEAR 16-bit No FAR FAR 16-bit No FAR FAR 16-bit No NEAR NEAR 32-bit Yes 、SMALL 所有的數(shù)據(jù)變量必須在一個數(shù)據(jù)段之內(nèi),所有的代碼也必須在一個代碼段之內(nèi)。在這種模型下,數(shù)據(jù)段寄存器的內(nèi)容保持不變,所有轉(zhuǎn)移也都是段內(nèi)轉(zhuǎn)移。 該存儲類型是獨立匯編語言源程序常用的存儲模型。 、MEDIUM 所有的數(shù)據(jù)變量必須在一個數(shù)據(jù)段之內(nèi),但代碼段可以有多個。在這種模型下,數(shù)據(jù)段寄存器的內(nèi)容保持不變,轉(zhuǎn)移可以是段間轉(zhuǎn)移。 、COMPACT 數(shù)據(jù)段可以有多個,但代碼段只能有一。 、LARGE 數(shù)據(jù)段和代碼段都可以有多個,但一個數(shù)組的字節(jié)數(shù)不能超過64KB。 、HUGE 數(shù)據(jù)段和代碼段都可以有多個,一個數(shù)組的字節(jié)數(shù)也可以超過64KB。 、FLAT FLAT存儲模式在創(chuàng)建執(zhí)行文件時,將使該程序僅含一個包括程序數(shù)據(jù)和代碼的32位段,并且只能在80386及其以后的計算機系統(tǒng)中運行。該程序的文件類型為EXE。 在使用該存儲模式之前,必須先用偽指令.386、.486或其它偽指令來說明更高性能的CPU類型。也就是說:FLAT模式僅在386及其以后CPU模式下才能使用。 在該程序中,所有代碼和數(shù)據(jù)位距的缺省值都是NEAR,子程序的類型也是NEAR,并且標識符@CodeSize,@DataSize和@Model的值分別為:0、0和7。 在FLAT存儲模式下,程序?qū)⒉皇褂枚渭拇嫫鱂S和GS。匯編程序在處理說明語句“.MODEL FLAT”時,將自動生成下列段寄存器說明語句: ASSUME CS:FLAT, DS:FLAT, SS:FLAT, ES:FLAT, FS:ERROR, GS:ERROR 當然,程序員也可把該段寄存器說明語句寫在其指令序列之中。 二、語言類型 其詳細說明請見7.5.3節(jié)中所述。 三、操作系統(tǒng)類型 OS_DOS是當前唯一支持的選項值,也是該選項的缺省值。 四、堆棧類型 堆棧類型的值主要影響偽指令.STARTUP所生成的指令序列。該選項有二個可選值:NEARSTACK和FARSTACK。其中:NEARSTACK是該選項的缺省堆棧類型。 、NEARSTACK——堆棧段和數(shù)據(jù)段是同一段; 、FARSTACK——堆棧段和數(shù)據(jù)段是不同的段,且堆棧不在段組DGROUP中。 例如: .MODEL SMALL, C, OS_DOS, FARSTACK 6.4.2 簡化段定義偽指令 簡化段定義偽指令在說明一個新段即將開始的同時,也說明了上一個段的結(jié)束。在本段定義結(jié)束時,也不必用偽指令“ENDS”來標識。 具體的偽指令說明形式及其功能描述如下: 1、代碼段定義 .CODE 作用:說明其下面的內(nèi)容是代碼段中內(nèi)容。 2、堆棧段定義 .STACK [堆棧字節(jié)數(shù)] 其中,“堆棧字節(jié)數(shù)”可以不寫,其缺省值為1024B。 3、數(shù)據(jù)段定義 .DATA / .DATA? / .CONST 作用:說明其下面的內(nèi)容是數(shù)據(jù)段中的變量定義。 在一個源程序中,可以有多個偽指令.DATA定義的數(shù)據(jù)段,這就好象在源程序中定義多個同段名的數(shù)據(jù)段一樣。 偽指令.DATA?說明下面是一個未初始化數(shù)據(jù)段的開始,偽指令.CONST說明下面是一個常數(shù)數(shù)據(jù)段的開始。這二條偽指令很少使用,除非在與高級語言編寫的程序相結(jié)合時,為了遵守高級語言的某些約定,而需要特殊說明時才使用。 匯編程序在處理簡化的堆棧段和數(shù)據(jù)段定義時,它會自動地把偽指令.STACK、.DATA、.DATA?和.CONST所定義的段組合成一個段組。如果想定義一個獨立的、不與其它段組合在一起的數(shù)據(jù)段的話,那么,就可選用下面的數(shù)據(jù)段定義方式。 4、遠程數(shù)據(jù)段定義 .FARDATA [段名] / .FARDATA? [段名] 其中:“段名”是可選項,如果不指定的話,則該段名就取其缺省段名。 作用:說明一個獨立的數(shù)據(jù)段。 偽指令.FARDATA?說明下面是一個未初始化的、獨立數(shù)據(jù)段的開始。通常情況下,很少使用該偽指令。 6.4.3 簡化段段名的引用 當使用簡化的段定義時,一般情況下,程序員可以不知道這些段的段名、段地址堆齊類型和組合類型等。但當把簡化定義的段和標準定義的段混合使用時,就需要知道簡化定義段的基本屬性。表6.3是在小模式下段的基本屬性對應表。 表6.3 小模式下簡化段定義的缺省屬性表 偽指令 缺省段名 對齊類型 組合類型 類別 段組名 .CODE _TEXT WORD PUBLIC CODE .FARDATA FAR_DATA PARA NONE FAR_DATA .FARDATA? FAR_BSS PARA NONE FAR_BSS .STACK STACK PARA STACK STACK DGROUP .DATA DATA WORD PUBLIC DATA DGROUP .DATA? BSS WORD PUBLIC BSS DGROUP .CONST CONST WORD PUBLIC CONST DGROUP 在其它存儲模型下,由偽指令".CODE"說明的代碼段段名在"_TEXT"之前還要加上其模塊名(源程序名)。假設(shè),某模塊名為ABC,則其缺省的代碼段段名就為ABC_TEXT。因此,在這種情況下,程序的模塊名或源程序名不要以數(shù)字開頭。 例6.15 簡化段定義的方法 .MODEL SMALL .STACK 128 .DATA MSG DB "Simplified Segment Directives.$" .CODE MOV AX, @DATA ;取數(shù)據(jù)段的段值 MOV DS, AX ;把給段寄存器DS賦值 MOV DX, offset MSG MOV AH, 9H INT 21h MOV AX, 4C00H INT 21h END 另外,在匯編程序MASM中,還提供了二組簡化的代碼偽指令:.STARTUP和.EXIT。 、.STARTUP——在代碼段的開始,用于自動初始化寄存器DS、SS和SP; 、.EXIT——用于結(jié)束程序的運行,它等價于下列二條語句: MOV AH, 4CH INT 21h 當使用匯編程序TASM時,以上二條偽指令分別改為:STARTUPCODE和- 1.請仔細閱讀文檔,確保文檔完整性,對于不預覽、不比對內(nèi)容而直接下載帶來的問題本站不予受理。
- 2.下載的文檔,不會出現(xiàn)我們的網(wǎng)址水印。
- 3、該文檔所得收入(下載+內(nèi)容+預覽)歸上傳者、原創(chuàng)作者;如果您是本文檔原作者,請點此認領(lǐng)!既往收益都歸您。
下載文檔到電腦,查找使用更方便
9.9 積分
下載 |
- 配套講稿:
如PPT文件的首頁顯示word圖標,表示該PPT已包含配套word講稿。雙擊word圖標可打開word文檔。
- 特殊限制:
部分文檔作品中含有的國旗、國徽等圖片,僅作為作品整體效果示例展示,禁止商用。設(shè)計者僅對作品中獨創(chuàng)性部分享有著作權(quán)。
- 關(guān) 鍵 詞:
- 匯編語言 程序 基本 結(jié)構(gòu)
鏈接地址:http://m.zhongcaozhi.com.cn/p-6608854.html