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