




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
第7章子程序和庫子程序是程序設(shè)計所常見的基本概念,匯編語言也提供了編寫子程序的方法。本章主要介紹子程序的定義、調(diào)用和返回、子程序的參數(shù)傳遞等知識。此后,還將講解如何構(gòu)造自己的子程序庫。7.1子程序的定義如果某程序段在源程序內(nèi)反復(fù)出現(xiàn),那么,就可把該程序段定義為子程序。這樣可以縮短源程序長度、節(jié)省目標(biāo)程序的存儲空間,也可提高程序的可維護性和共享性。定義子程序的一般格式如下:子程序PROC[NEAR|FAR]名… ;子程序體子程序ENDP名對子程序定義的具體規(guī)定如下:、“子程序名”必須是一個合法的標(biāo)識符,并前后二者要一致;、PROC和ENDP必須是成對出現(xiàn)的關(guān)鍵字,它們分別表示子程序定義開始和結(jié)束;、子程序的類型有近(NEAR)、遠(FAR)之分,其缺省的類型是近類型;、如果一個子程序要被另一段的程序調(diào)用,那么,其類型應(yīng)定義為FAR否則,其類型可以是NEAR。顯然,NEAR類型的子程序只能被與其同段的程序所調(diào)用;、子程序至少要有一條返回指令,也可有多條返回指令。返回指令是子程序的出口語句,但它不一定是子程序的最后一條語句;、子程序名有三個屬性:段值、偏移量和類型。其段值和偏移量對應(yīng)于子程序的入口地址,其類型就是該子程序的類型。編寫子程序除了要考慮實現(xiàn)子程序功能的方法外,還要養(yǎng)成書寫子程序說明信息的好習(xí)慣。其說明信息一般包括以下幾方面內(nèi)容:、功能描述、入口和出口參數(shù);可選項,最好采用寄存器的保護和恢復(fù)方法,使、所用寄存器 之使用透明化、所用額外存儲單;可選項,可以減少為子程序定義自己的局部變量元、子程序的所米用;可選項,如果算法簡單,可以不寫的算法、調(diào)用時的注意事;可選項,盡量避免除入口參數(shù)外還有其它的要求項、子程序的編寫者;可選項,為將來的維護提供信息、子程序的編寫日;可選項,用于確定程序是否是最新版本期這些說明性信息雖然不是子程序功能的一部分,但其他程序員可通過它們對該子程序的整體信息有一個較清晰認識,為準(zhǔn)確地調(diào)用它們提供直接的幫助,與此同時,也為實現(xiàn)子程序的共享提供了必要的資料。7.2子程序的調(diào)用和返回指令子程序的調(diào)用和返回是一對互逆操作,也是一種特殊的轉(zhuǎn)移操作。一方面,之所以說是轉(zhuǎn)移,是因為當(dāng)調(diào)用一個子程序時,程序的執(zhí)行順序被改變,CPU將轉(zhuǎn)而執(zhí)行子程序中的指令序列,在這方面,調(diào)用子程序的操作含有轉(zhuǎn)移指令的功能,子程序的返回指令的轉(zhuǎn)移特性與此類似;另一方面,轉(zhuǎn)移指令是一種“一去不復(fù)返”的操作,而當(dāng)子程序完后,還要求CPU能轉(zhuǎn)而執(zhí)行調(diào)用指令之下的指令,它是一種“有去有回”的操作。為了滿足子程序調(diào)用和返回操作的特殊性,在指令系統(tǒng)中設(shè)置了相應(yīng)的特定指令。7.2.1調(diào)用指令(CALL)調(diào)用子程序指令的格式如下:CALL子程序名/Reg/Mem子程序的調(diào)用指令分為近(near)調(diào)用和遠(far)調(diào)用。如果被調(diào)用子程序的屬性是近的,那么,CALL指令將產(chǎn)生一個近調(diào)用,它把該指令之后地址的偏移量(用一個字來表示的)壓棧,把被調(diào)用子程序入口地址的偏移量送給指令指針寄存器IP即可實現(xiàn)執(zhí)行程序的轉(zhuǎn)移。近調(diào)用指令的堆棧操作如圖7.1所示。圖7.1近調(diào)用指令進棧操作示意圖如果被調(diào)用子程序的屬性是遠的,那么,CALL指令將產(chǎn)生一個遠調(diào)用。這時,調(diào)用指令不僅要把該指令之后地址的偏移量壓進棧,而且也要把段寄存器CS的值壓進棧。在此之后,再把被調(diào)用子程序入口地址的偏移量和段值分別送給IP和CS,這樣完成了子程序的遠調(diào)用操作。遠調(diào)用指令的堆棧操作如圖7.2所示。圖7.2遠調(diào)用指令進棧操作示意圖子程序調(diào)用指令本身的執(zhí)行不影響任何標(biāo)志位,但子程序體中指令的執(zhí)行會改變標(biāo)志位,所以,如果希望子程序的執(zhí)行不能改變調(diào)用指令前后的標(biāo)志位,那么,就要在子程序的開始處保護標(biāo)志位,在子程序的返回前恢復(fù)標(biāo)志位。例如:[BX][BX]CALLDISPLAYCALLBXCALLWORD1[BX][BX]CALLDISPLAYCALLBXCALLWORD1CALLDWORD1CALLwordptr;DISPLAY是子程序名;BX的內(nèi)容是子程序的偏移量;WORD1是內(nèi)存字變量,其值是子程序的偏移量;DWORD1是雙字變量,其值是子程序的偏移量和段值;BX所指內(nèi)存字單元的值是子程序的偏移量CALLdwordptr;BX所指內(nèi)存雙字單元的值是子程序的偏移量和段值7.2.2返回指令(RET)當(dāng)子程序執(zhí)行完時,需要返回到調(diào)用它的程序之中。為實現(xiàn)此功能,指令系統(tǒng)提供了一條專用的返回指令。其格式如下:RET/RETN/RETF[Imm]子程序的返回在功能上是子程序調(diào)用的逆操作。為了與子程序的遠、近調(diào)用相對應(yīng),子程序的返回也分:遠返回和近返回。返回指令在堆棧操作方面是調(diào)用指令的逆過程(如圖7.3所示)。其具體規(guī)定如下:、在近類型的子程序中,返回指令RET是近返回,其功能是把棧頂之值彈出到指令指針寄存器IP中,SP會被加2(如圖7.3所示);、在遠類型的子程序中,返回指令RET是遠返回,其功能是:先彈出棧頂之值到IP中,再彈出棧頂之值到CS之中,SP總共會被加4(如圖7.4所示)。圖7.3近返回指令的出棧操作示意圖圖7.4遠返回指令的出棧操作示意圖如果返回指令后面帶有立即數(shù)(其值通常為偶數(shù)),則表示在得到返回地址之后,SP還要增加的偏移量,它不是類似于高級語言中子程序的返回值(如圖7.5所示)。圖7.5帶立即數(shù)的返回指令的出棧操作示意圖在MASM5.0及其以后版本中,可用指令RETN或RETF來顯式地告訴匯編程序是本子程序的返回是近返回,還是遠返回。例如:RET;可能是近返回,也可能是遠返回RETN;近返回指令RETF;遠返回指令RET6;子程序返回后,(SP)^(SP)+6例7.1編寫一個子程序UPPER,實現(xiàn)把寄存器AL中存放的字符變大寫。解:;子程序功能:把AL中存放的字符變大寫;入口參數(shù):AL;出口參數(shù):AL;算法描述:判斷AL中字符必須在'a'~'z'之間才能把該字符變?yōu)榇髮慤PPERPROCCMPAL,'a' ;書寫'a'的ASCII碼61H也可以JBoverCMPAL,'z'JAoverSUBAL,20H ;書寫指令A(yù)NDAL,0DFH也可以over:RETUPPERENDP例7.2編寫一個求字符串長度的子程序StrLen,該字符串以0為結(jié)束標(biāo)志,其首地址存放在DS:DX,其長度保存在CX中返回。解:;子程序功能:求字符串的長度;入口參數(shù):DS:DX存放字符串的首地址,該字符串以0為結(jié)束標(biāo)志;出口參數(shù):CX存放該字符串的長度;算法描述:用BX來指針來掃描字符串中的字符,如果遇到其結(jié)束標(biāo)志,則停止掃描字符串操作StrLenPROCPUSHAXPUSHBX;用堆棧來保存子程序所用到的寄存器內(nèi)容XORCX,CXXORAL,ALMOVBX,DXagain:CMP[BX],ALJZoverINCCX;增加字符串的長度INCBX;訪問字符串的指針向后移JMPagainover:POPBX;恢復(fù)在子程序開始時所保存的寄存器內(nèi)容POPAXRETStrLenENDP7.3子程序的參數(shù)傳遞子程序一般都是完成某種特定功能的程序段。當(dāng)一個程序調(diào)用一個子程序時,通常都向子程序傳遞若干個數(shù)據(jù)讓它來處理;當(dāng)子程序處理完后,一般也向調(diào)用它的程序傳遞處理結(jié)果,我們稱這種在調(diào)用程序和子程序之間的信息傳遞為參數(shù)傳遞。用程序向子程序傳遞的參數(shù)稱為子程序的入口參數(shù),子程序向調(diào)用它的程序傳遞的參數(shù)稱為子程序的出口參數(shù)。子程序的入口參數(shù)和出口參數(shù)都是任意項,對某個具體的子程序來說,要根據(jù)具體情況來確定其入口和出口參數(shù),也可以二者都沒有。程序和被調(diào)用子程序之間的參數(shù)傳遞方法是程序員自己或和別人事先約定好的信息傳遞方法。這種信息傳遞方法可以是多種多樣的,在本節(jié),我們只介紹
常用的、行之有效的參數(shù)傳遞方法有:寄存器傳遞參數(shù)、約定存儲單元傳遞參數(shù)和堆棧傳遞參數(shù)等。如果對其它的參數(shù)傳遞方法感興趣的話,可參考其它《匯編語言程序設(shè)計》書籍。7.3.1寄存器傳遞參數(shù)一方面,由于CPU中的寄存器在任何程序中都是“可見”的,一個程序?qū)δ臣拇嫫髻x值后,在另一個程序中就能直接使用,所以,用寄存器來傳遞參數(shù)最直接、簡便,也是最常用的參數(shù)傳遞方式。但另一方面,CPU中寄存器的個數(shù)和容量都是非常有限,所以,該方法適用于傳遞較少的參數(shù)信息。例7.1是用寄存器傳遞參數(shù)的例子,子程序處理的數(shù)據(jù)被保存在寄存器AL中。假設(shè)有下列的程序段:MOVCALLAL,'b'UPPER;子返回時,MOVCALLAL,'b'UPPER;子返回時,(AL)='B'MOVCALLAL,'2'UPPER;子返回時,AL的值不變,因為'2'不是字母例7.3按五位十進制的形式顯示寄存器BX中的內(nèi)容,如果BX的值小于0,則應(yīng)在顯示數(shù)值之前顯示負號'-'。例如:(BX)=123,顯示:00123;(BX)=-234,顯示:-00234;解:;子程序功能:把寄存器BX的內(nèi)容按十進制有符號數(shù)顯示出來;入口參數(shù):BX;出口參數(shù):無,只有顯示信息;算法描述:1、定義6個字節(jié)的存儲單元2、 先判斷BX是否小于零,如果是,則先顯示負號'-',再取BX的絕對值;3、 采用除10,得余數(shù)的方法,從低位向高位求出每位十進制位;4、 輸出數(shù)據(jù)的字符串。SubDataSEGMENTDB5DB5DUP('0'),0ah,0dh,'$';0ah、0dh:換行、回車SubDataDISPBXSubDataDISPBXENDSPROCDSDSDXCXAXASSUMEDS:SubDataPUSHPUSHPUSHPUSH
MOVAX,SubData;取子程序所用的數(shù)據(jù)區(qū)段地址MOVDS,AXCMPBX,0JGEnextMOVDL,'-'MOVAH,2INT21H;顯示負號'-'NEGBX;求-BX,使其值為正數(shù)next:MOVSI,4MOVAX,BXMOVCX,10Dagain:XORDX,DXIDIVCX;DX存放余數(shù),AX存放商ADDDL,'0'MOV[SI],DLDECSIJGEagainXORDX,DXMOVAH,9INT21H;調(diào)用中斷21的功能9,顯示DS:DX指向的字符串POPAXPOPCXPOPDXPOPDSRETDISPBXENDP7.3.2約定存儲單元傳遞參數(shù)在調(diào)用子程序時,當(dāng)需要向子程序傳遞大量數(shù)據(jù)時,因受到寄存器容量的限制,就不能采用寄存器傳遞參數(shù)的方式,而要改用約定存儲單元的傳送方式。這種參數(shù)傳遞方式有點象情報人員和聯(lián)絡(luò)人員之間的傳遞信息方式,一個向指定地點放情報,另一個從指定地點取情報。例7.2是采用約定存儲單元傳遞參數(shù)的例子,所處理的數(shù)據(jù)不是直接傳給子程序,而是把存儲它們的地址告訴子程序。例7.4:編寫一個子程序分類統(tǒng)計出一個字符串中數(shù)字字符、字母和其它字符的個數(shù)。該字符串的首地址用DS:DX來指定(以0為字符串結(jié)束),各類字符個數(shù)分別存放BX、CX和DI中。解:;子程序功能:分類統(tǒng)計出字符串中數(shù)字字符、字母和其它字符的個數(shù);入口參數(shù):DS:DX指向被統(tǒng)計的字符串
;出口參數(shù):BX、CX和DI分別保存數(shù)字字符、字母和其它字符的個數(shù);算法描述:1、當(dāng)字符在'0'~'9'范圍時,數(shù)字字符個數(shù)BX加1;2、 為了判斷簡單,先把字字母變成大寫字母;3、 當(dāng)字符在'A'~'Z'范圍時,字母個數(shù)CX加1;4、 否則,其它字符個數(shù)DI加1。COUNTPROCPUSHAXPUSHSIXORBX,BXXORCX,CXXORDI,DI;上三條指令使各類字符計數(shù)清零MOVSI,DXagain:MOVAL,[SI]INCSICMPAL,0JEoverCMPAL,'0'JLotherCMPAL,'9'JGnextINCBX;數(shù)字字符個數(shù)加1JMPagainnext:CALLUPPER;調(diào)用子程序把AL中的字母變成大寫字母CMPAL,'A'JLotherCMPAL,'Z'JGotherINCCX;字母個數(shù)加1JMPagainother:INCDI;其它字符個數(shù)加1JMPagainover:POPSIPOPAXRETCOUNTENDP例7.5顯示出任意字符串中數(shù)字字符、字母和其它字符的個數(shù)。解:.MODELSMALL.DATAMSGDB'KSDJL0984/[]3oiuOIUOIU(*&(5341',0.CODE.STARTUPLEADX,MSG;DS:DX指向待統(tǒng)計的字符串CALLCOUNT;調(diào)用子程序統(tǒng)計出各類字符的個數(shù)CALLDISPBXMOVBX,CX;調(diào)用子程序顯示數(shù)字字符的個數(shù)CALLDISPBXMOVBX,DI;調(diào)用子程序顯示字母的個數(shù)CALLDISPBX.EXIT0END;調(diào)用子程序顯示其它字符的個數(shù)7.3.3堆棧傳遞參數(shù)堆棧是一個特殊的數(shù)據(jù)結(jié)構(gòu),它通常是用來保存程序的返回地址。當(dāng)用它來傳遞參數(shù)時,勢必會造成數(shù)據(jù)和返回地址混合在一起的局面,用起來要特別仔細。具體做法如下:、當(dāng)用堆棧傳遞入口參數(shù)時,要在調(diào)用子程序前把有關(guān)參數(shù)依次壓棧,子程序從堆棧中取到入口參數(shù);、當(dāng)用堆棧傳遞出口參數(shù)時,要在子程序返回前,把有關(guān)參數(shù)依次壓棧(這里還需要做點額外操作,要保證返回地址一定在棧頂),調(diào)用程序就可以從堆棧中取到出口參數(shù)。在通常情況下,我們用堆棧傳入口參數(shù),用寄存器傳出口參數(shù)。1、用堆棧傳遞入口參數(shù)的調(diào)用方法:PUSHPara1???PUSHParan;把n個字的參數(shù)壓棧CALLSUBPRO;調(diào)用子程序SUBPRO2、在子程序中取入口參數(shù)的方法:、段內(nèi)調(diào)用子程序由于是段內(nèi)調(diào)用,所以,CALL指令只把返回地址的偏移量(即IP的內(nèi)容)壓棧,如圖7.6(a)所示。在進入子程序后,為了能讀取傳遞過來的參數(shù),需要用BP來訪問堆棧,所以要先保護BP原來的值,再把當(dāng)前SP的值傳送給BP。于是,當(dāng)前BP所指向的堆棧單元與最后一個參數(shù)Paran之間隔著BP的原值和返回地址的偏移量,也就是說:二者之間相差4個字節(jié)。具體情況如圖7.6(b)所示。(a)、進入子程序時堆棧情況 (b)、子程序寄存器保護后的堆棧情況圖7.6在段內(nèi)調(diào)用情況下子程序所能訪問的堆棧情況SUBPROPROCNEARPUSHBPMOVBP,SPSUBPROPROCNEARPUSHBPMOVBP,SPMOVParan,[BP+4];保護寄存器BP;用寄存器BP來訪問堆棧,讀取參數(shù);保護其它寄存器的指令;保護其它寄存器的指令MOVPara1,[BP+4+2*(n-1)]SUBPROENDP、段間調(diào)用子程序在段間調(diào)用子程序時,CALL指令會把返回地址的偏移量和段寄存器CS的內(nèi)容都壓棧,如圖7.7(a)所示。在進入子程序后,與前面“段內(nèi)調(diào)用子程序”一樣,也需要用BP來讀取傳遞過來的參數(shù),所以,也要先保護BP原來的值,再把當(dāng)前SP的值傳送給BP。這時,當(dāng)前BP所指向的堆棧單元與最后一個參數(shù)Paran之間隔著BP的原值、返回地址的偏移量和段地址,所以,二者之間相差6個字節(jié)。具體情況如圖7.7(b)所示。(a)、進入子程序時堆棧情況 (b)、子程序寄存器保護后的堆棧情況圖7.7在段間調(diào)用情況下子程序所能訪問的堆棧情況在段間調(diào)用時,除了多一個返回段地址外,其它的內(nèi)容與“段內(nèi)調(diào)用”的情況完全一致,所以,在讀取第i個參數(shù)時,只要用[BP+6+4*(n-i)]代替[BP+4+2(n-i)]即可(假設(shè)每個參數(shù)都是字類型)。7.4寄存器的保護與恢復(fù)由于計算機的硬件資源只有一套,當(dāng)子程序修改了寄存器的內(nèi)容后,返回到調(diào)用它的程序時,這些寄存器的內(nèi)容也就不會是調(diào)用子程序前的內(nèi)容。這樣,子程序修改寄存器內(nèi)容就可能變成了調(diào)用它的副作用,這種副作用常常會導(dǎo)致調(diào)用程序的出錯。為此,在編寫子程序時,除了能對作為入口和出口參數(shù)的寄存器進行修改外,對其它寄存器的修改對調(diào)用程序來說都要是透明的,也就是說,在調(diào)用子程序指令的前后,除了作為入口和出口參數(shù)的寄存器內(nèi)容可以不同外,其它寄存器的內(nèi)容要保持不變。有時,也要求作為入口參數(shù)的寄存器內(nèi)容保持不變。在子程序中,保存和恢復(fù)寄存器內(nèi)容的主要方法是:在子程序的開始把它所用到的寄存器壓進棧,在返回前,再把它們彈出棧。這樣編寫的好處是該子程序可以被任何其它程序來調(diào)用。在調(diào)用指令前,不需要保存寄存器,在調(diào)用指令后,也無需恢復(fù)寄存器。XXXXXPROCPUSHREG1XXXXXPROCPUSHREG1…PUSHREGn………POPREGn;把子程序要使用的寄存器壓棧,REGi代表某個寄存器;子程序的處理功能語句;把前面壓棧的寄存器彈出,注意它們的次序POPREG1RETXXXXXENDP例7.2就是一個在子程序中利用堆棧來保存和恢復(fù)寄存器內(nèi)容的例子。利用堆棧來實現(xiàn)此項功能時,應(yīng)注意以下幾點:、用堆棧保存和恢復(fù)寄存器的內(nèi)容,要注意堆?!跋冗M后出”的操作特點;、通常情況下不保護入口參數(shù)寄存器的內(nèi)容,當(dāng)然,也可以根據(jù)事先的約定而對它們加以保護;、如果用寄存器帶回子程序的處理結(jié)果,那么,這些寄存器就一定不能加以保護;、整個子程序的執(zhí)行幾乎肯定要改變標(biāo)志位,可用PUSHF和POPF來保護和恢復(fù)標(biāo)志位,但一般在子程序中不保護標(biāo)志位,除非有此特殊需要;7.5子程序的完全定義在7.1節(jié)所給出的子程序定義格式是一個最基本的、最簡單的定義格式,它不能為子程序提供更簡潔的調(diào)用方式。在宏匯編MASM6.11系統(tǒng)中,為微機匯編語言的子程序提供了更加豐富的定義方式。雖然子程序的這種定義方式顯得稍微有點復(fù)雜,但它不僅為子程序的調(diào)用帶來了極大的方便,而且其調(diào)用方式與高級語言中子程序的調(diào)用方式相一致,這就大大地降低了程序員熟練掌握它的難度。7.5.1子程序完全定義格式子程序PROC[distance][langtype][visibility][<prologuearg>]名 [USES寄存器列表〕[,參數(shù)[:數(shù)據(jù)類型]]...[LOCALvarlist]子程序的程序體子程序-ENDP名定義子程序時,可使用參數(shù)表來直接指明其所要的參數(shù),但程序員必須先用也些也指令,或使用<langtype>參數(shù)來說明本子程序所使用的程序設(shè)計語言類型。程序員在定義子程序時,最好能象在高級語言(如:C/C++)定義過程那樣,先說明該子程序的原型(用偽指令PROTO),這樣,在調(diào)用時,系統(tǒng)可以自動進行類型檢查,也可以使用更方便的調(diào)用偽指令I(lǐng)NVOKE來調(diào)用該子程序。有關(guān)子程序的原型說明偽指令和調(diào)用偽指令在隨后第7.5.8和7.5.9小節(jié)中加以介紹。子程序通常用RET指令來結(jié)束其執(zhí)行,也可用指令“RETn”來指明在結(jié)束子程序執(zhí)行后從堆棧彈出n個字節(jié)。有關(guān)返回指令請參閱7.2.2節(jié)中的敘述。匯編程序在處理子程序時能自動產(chǎn)生“起始”代碼(PROLOGUECode)和“結(jié)束”代碼(EPILOGUEcode)。這兩段特殊的代碼分別完成:在調(diào)用子程序時,能把傳遞給子程序的參數(shù)壓棧,在子程序結(jié)束時能把先前壓棧的參數(shù)彈出。有了這兩段代碼,程序員在調(diào)用子程序時就不用自行考慮子程序的參數(shù)傳遞問題。若子程序用指令RETN、RETF或IRETF作為子程序的結(jié)束指令,那么,匯編程序?qū)⒉簧伞敖Y(jié)束”代碼。程序員可以用自己定義宏來替代缺省的“起始”和“結(jié)束”的代碼段。這種替代方法是使用偽指令:OPTIONPROLOGUE和OPTIONEPILOGUEo若子程序沒有參數(shù)、局部變量,沒使用USES子句,也不會產(chǎn)生新的段或段組,那么,子程序是可以嵌套定義的。程序員也可以使用返回指令RETN和RETF來避免子程序的嵌套。在子程序內(nèi)部,可以在指令之前使用偽指令LOCAL來說明其局部變量,有關(guān)規(guī)定在隨后的第7.5.10節(jié)中有詳細的說明。下面就來介紹該定義格式中各個說明項的作用。7.5.2子程序的位距子程序的位距(Distance)有:Near、Far、Near16、Far16、Near32和Far32。子程序位距描述符告訴匯編程序該子程序是在本段之內(nèi)(Near),還是在本段之外(Far)。Near和Far描述符表示使用當(dāng)前的段規(guī)模(SegmentSize),Near16、Far16、Near32和Far32描述符是告訴匯編程序忽略當(dāng)前的段規(guī)模,而使用指定16位或32位的段規(guī)模。若選用類型Near或Far,那么,匯編程序?qū)⒏鶕?jù)當(dāng)前段的規(guī)模來決定選用16位,還是32位的Near或Far。若程序員不指定該選項,那么匯編程序?qū)⒏鶕?jù)當(dāng)前的存儲模式(由.MODEL來決定)和處理機類型來決定子程序類型。若不使用偽指令.MODEL,那么,Near是缺省的類型。7.5.3子程序的語言類型子程序語言類型(LanguageType)可以是任何一種有效的程序設(shè)計語句類型,由它來告訴匯編程序?qū)⑹褂檬裁礃拥臉?biāo)識符的命名風(fēng)格、子程序的調(diào)用和返回約定。該語言類型說明可使匯編語言程序與其它語言程序達到共享的目的。所有有效的語言類型及其書寫規(guī)定如表7.1所列。表7.1語言類型及其書寫規(guī)定SYSCALLSTDCALLBasicFortranPascalXXXnXXXXXXXXXX字母大寫化XXXXXXXXXX參數(shù)從左到右參數(shù)從右到左調(diào)用程序清空堆棧保存指針寄存器BP使用VARARG參數(shù)*若使用:VARARG參數(shù),則調(diào)用程序清空堆棧,否則,被調(diào)用的子程序清空堆棧。程序員可用另外三種方法來設(shè)置程序的語言類型:.MODEL、OPTIONLANGTYPE:和命令行選項/Gx。若在程序和命令行中都說明了語言類型,那么,前者的說明優(yōu)先。另外,程序員也可用命令行選項/H來限定標(biāo)識符的最大長度。例如:Pascal語言風(fēng)格:OPTIONLANGUAGE:PASCAL、/GcC語言風(fēng)格:OPTIONLANGUAGE:C、/Gd7.5.4子程序的可見性子程序的可見性(Visibility)決定該子程序?qū)ζ渌K是否可用。它共有三個屬性值:PRIVATE、PUBLIC和EXPORToPUBLIC屬性是子程序標(biāo)準(zhǔn)的缺省屬性,但該缺省屬性可以用偽指令OPTIONPROC來修改。EXPORT屬性意味著該子程序是一個“遠”的、具有PUBLIC屬性的子程序,并要求連接程序在生成可執(zhí)行文件時把其入口地址放入導(dǎo)出入口地址表中。例如:OPTIONPROC:PRIVATE ;說明子程序的可見性為:PRIVATEOPTIONPROC:EXPORT ;說明子程序的可見性為:EXPORT7.5.5子程序的起始和結(jié)束操作當(dāng)程序員想用自己定義的宏來替代缺省的“起始”和“結(jié)束”的代碼段時,可用下列說明語句來實現(xiàn):OPTIONPROLOGUE:MacroNamelOPTIONEPILOGUE:MacroName2PROLOGUE和EPILOGUE分別指定MacroName1和MacroName2為“起始”和“結(jié)束”代碼段的宏名。匯編程序?qū)τ脩舳x的宏MacroName1和MacroName2的形式有較嚴格的規(guī)定,要求宏的定義形式如下:MacroNameMACROProcName,flags,argbytes,localbytes,<reglist>,userparms:VARARG該宏定義的每個參數(shù)都有詳細的說明,感興趣的讀者可看有關(guān)技術(shù)資料或MASM6.11中的幫助,詳細的說明在此從略,但建議使用缺省的宏。如果想取消當(dāng)前指定的宏名,而恢復(fù)使用缺省的“起始”和“結(jié)束”代碼段的宏名,那么,可用下列說明語句,即指定二個缺省的宏名PrologueDef和EpilogueDef。OPTIONPROLOGUE:PrologueDefOPTIONEPILOGUE:EpilogueDef若程序員不要匯編程序自動產(chǎn)生“起始”和“結(jié)束”代碼,則可用NONE來代替說明語句中的宏名,即:OPTIONPROLOGUE:NONEOPTIONEPILOGUE:NONE7.5.6寄存器的保護和恢復(fù)保護寄存器說明子句的說明格式:USES寄存器列表該說明子句要求匯編程序為其生成保護和恢復(fù)寄存器的指令序列,即:在進入子程序執(zhí)行指令之前,把寄存器列表中的寄存器壓進堆棧,在結(jié)束子程序執(zhí)行時,把先前壓進堆棧的寄存器彈出,以達到保護寄存器的目的。寄存器列表:列舉出在子程序中需要保護的寄存器名,即:在子程序開始時需要把內(nèi)容進棧的寄存器名。若有多個寄存器名,則在寄存器名之間要用“空格”來分開。例如:DsipPROCUSESAXDX,FUNC:WORD,MSG:PTRBYTEMOVDX,MSGMOVAX,FUNCINT21HRETDispENDP匯編程序在處理該子程序時,會根據(jù)子句USES的作用,在第一條指令“MOVDX,MSG”之前,插入把寄存器AX和DX進棧的指令序列,即:PUSHAXPUSHDX而在返回指令RET之前插入把寄存器DX和AX的值彈出的指令序列,即:POPDXPOPAX注意:若子程序含有多個RET或IRET指令,那么,匯編程序在每個RET或IRET指令前都將增加相應(yīng)的彈出堆棧指令序列。從子句USES的功能來看,它與前面7.4節(jié)“寄存器的包含與恢復(fù)”中所用的方法完全一致,所不同的是:用USE子句進行寄存器保護和恢復(fù)的代碼是由匯編程序自動產(chǎn)生的,程序員不用關(guān)心如何去做,有點象高級語言的編程風(fēng)格,而7.4節(jié)中的代碼則是由程序員自己來安排的。7.5.7子程序的參數(shù)傳遞子程序參數(shù)是用來向子程序傳遞信息的數(shù)據(jù)。若有多個參數(shù),則參數(shù)之間要用逗號分割。為了能說明子程序的參數(shù),程序員必須事先指定參數(shù)所遵循的語言類型或使用“語言類型”參數(shù)。參數(shù)的數(shù)據(jù)類型可以是任何一個有效的數(shù)據(jù)類型說明符或VARARG。VARARG數(shù)據(jù)類型允許向子程序傳遞“個數(shù)”不定的參數(shù),其參數(shù)之間要用逗號“,”來分開。若參數(shù)表中含有VARARG說明的參數(shù),那么,該參數(shù)一定是該子程序的最后一個參數(shù)。其規(guī)定隱含地說明了在參數(shù)表中只能有一個用VARARG說明的參數(shù)。當(dāng)子程序的語言類型是C、SYSCALL和STDCALL時,在其參數(shù)表中才能使用VARARG數(shù)據(jù)類型的參數(shù)。見前面的表7.1中所列。如果沒有顯式地指定某個參數(shù)的數(shù)據(jù)類型,那么,在16位段規(guī)模的情況下,其缺省的數(shù)據(jù)類型是WORD;在32位段規(guī)模的情況下,其缺省的數(shù)據(jù)類型是DWORDo7.5.8子程序的原型說明子程序原型的說明格式如下:子程序名PROTO[distance][langtype][,[parameter]:tag]...該說明語句告訴匯編程序該子程序的若干屬性,如:位距、語語言類型、參數(shù)個數(shù)及其類型等。這樣,匯編程序就可以對其定義進行適當(dāng)?shù)臋z查。如果對所有基于堆棧的過程都定義一個原型,那么,就可把這些原型存放在一個獨立的包含文件(用偽指令I(lǐng)NCLDUE來裝入)中。使用這種方法對將來把所有子程序放入自定義的庫文件中是非常方便的。該原型說明語句中參數(shù)distance、langtype、parameter和tag等的含義與前面的敘述相一致,在此不再重復(fù)。7.5.9子程序的調(diào)用偽指令子程序調(diào)用偽指令I(lǐng)NVOKE與子程序的調(diào)用指令CALL在功能上是一致的,但它使匯編語言的子程序調(diào)用方法高級語言化,程序員可不用理會一些調(diào)用細節(jié)問題。調(diào)用偽指令I(lǐng)NVOKE的使用格式如下:INVOKEexpression[,arguments]其中:expression—地址表達式,通常為子程序名;arguments-傳遞的各參數(shù)之間用逗號',’分開,參數(shù)可以是寄存器、表達式或ADDR標(biāo)識符等。該偽指令是調(diào)用基于堆棧的子程序的方法,它把所有參數(shù)壓棧,子程序結(jié)束時,又把參數(shù)自動彈出堆棧。在參數(shù)傳遞時,匯編程序?qū)⒏鶕?jù)子程序的原型進行數(shù)據(jù)類型檢查。若需要進行參數(shù)類型轉(zhuǎn)換的話,匯編程序則會自動生成一段代碼來滿足其數(shù)據(jù)類型轉(zhuǎn)換的要求。例如:INVOKETEST,AX,12+34,ADDRMSG其中:TEST是子程序名,寄存器AX和表達式“12+34”是參數(shù),“ADDRMSG”是傳遞變量MSG的地址。例7.6編寫一個累加參數(shù)數(shù)值的子程序。其中參數(shù)的個數(shù)不定,參數(shù)的個數(shù)由第一個參數(shù)來確定。解:.MODELSMALL.STACK256.CODE;第一個參數(shù)parmcount確定其后面參數(shù)parmvalues中所含參數(shù)的個數(shù)ADDUPPROCNEARC,parmcount:WORD,parmvalues:VARARGXORAX,AXXORSI,SIMOVCX,parmcount.REPEATADDAX,parmvalues[SI]ADDSI,2.UNTILCXZRETADDUPENDP.STARTUPINVOKEADDUP,3,5,2,4 ;調(diào)用子程序ADDUP,計算5+2+4INVOKEADDUP,4,1,2,3,4 ;調(diào)用子程序ADDUP,計算1+2+3+4.EXIT0.END7.5.10局部變量的定義局部變量的定義格式:LOCAL變量名[[數(shù)量]][:數(shù)據(jù)類型][,變量名[[數(shù)量]][:數(shù)據(jù)類型]]...偽指令LOCAL的作用是說明一個或多個臨時的局部變量(位于堆棧中)。局部變量必須在任何指令之前加以說明,并可用多個LOCAL偽指令來說明其局部變量。在子程序中,若說明了某個局部變量,則子程序體中的指令就可使用該局部變量。匯編程序會把對它的引用轉(zhuǎn)換成用指針寄存器BP來訪問其在堆棧中的實際存儲單元。在局部變量的作用域與高級語言中局部變量的作用域相一致,即:局部變量只能在當(dāng)前子程序中使用,離開該子程序,它們就不能再被引用。但在局部變量的命名規(guī)則上有所不同,高級語言中的局部變量可與外層變量同名,而匯編語言中的局部變量不能與其它任何變量同名,否則,在匯編時,將會給出“重定義”(Symbolredefinition)的錯誤信息。“數(shù)量”用來說明該變量所具有的元素個數(shù)。象高級語言的數(shù)組定義一樣,該數(shù)量必須寫在括號“[]”之中?!皵?shù)量”說明項是可選項。局部變量的類型說明符可以是任何合法的數(shù)據(jù)類型說明符。在16位段環(huán)境下,該缺省的數(shù)據(jù)類型是WORD,而在32位段環(huán)境下,該缺省的數(shù)據(jù)類型是DWORD。此處偽指令LOCAL的作用與9.3.1節(jié)中偽指令LOCAL的作用是完全不同的,具體的差異請見9.3.1節(jié)中的比較。例如:LOCALdata[20]:BYTE,num:WORD在上例的說明中,定義了二個局部變量:data和num。前者是字節(jié)類型,并有20個元素,后者是字類型,只有其自身1個元素。7.6子程序庫庫文件對學(xué)過C/C++語言程序設(shè)計的讀者來說應(yīng)該是不會陌生的,該語言的程序設(shè)計環(huán)境提供了大量的庫文件,也就是說,提供了大量的標(biāo)準(zhǔn)函數(shù)或過程。在本節(jié)里,介紹讀者如何創(chuàng)建自己的庫文件。7.6.1建立庫文件命令LIB宏匯編MASM系統(tǒng)提供了建立庫文件的命令文件LIB.EXE。其通常是在命令行環(huán)境(MS-DOS方式)下使用的,當(dāng)然,也可在Windows95/98等環(huán)境下利用其“開始”菜單下的“運行”功能項來使用。一、 MS-DOS系統(tǒng)顯示命令LIB用法的命令如下:???>lib/?該命令的顯示結(jié)果如圖7.9中所示。二、 Windows系統(tǒng)圖7.8運行LIB命令的畫面圖7.9顯示LIB命令功能的畫面命令LIB的使用方式和顯示結(jié)果如圖7.8和7.9所示。三、命令顯示內(nèi)容的解釋1)、各選項的解釋選項含義/?、/HELP顯示LIB命令的用法,描述各命令行參數(shù)的含義/IGNORECASE/NOIGNORECASE忽略子程序名中的大小寫 在實踐中,作用不明不忽略子程序名中的大顯小寫/NOEXTDICTIONARY不建立擴展的目錄/NOLOGO不顯示版本號和版權(quán)信息/PAGESIZE:n設(shè)置庫文件的每頁字節(jié)數(shù)為n2)、命令項的解釋:選項含義+name向庫文件中加一個新的目標(biāo)文件name從庫文件中刪除一個指定的目標(biāo)文件+name用新的目標(biāo)文件替換掉庫文件中原有的目標(biāo)文件*name拷貝出指定的目標(biāo)文件*name從庫文件中移出指定的目標(biāo)文件在弄懂了LIB的各項功能含義后,讀者就可根據(jù)自己的需要來建立庫文件了。7.6.2建立庫文件舉例假設(shè)現(xiàn)有目標(biāo)文件subl.obj、sub2.obj和sub3.obj,要用它們建立庫文件mylib.lib??捎孟铝蟹椒▉斫⒃搸煳募悍椒?:所有目標(biāo)文件都準(zhǔn)備好了,可一次性把它們加入到庫文件中???>libmylib+sub1+sub2+sub3方法2:隨著目標(biāo)文件的逐個生成,而依次把它們加入到庫文件中???>libmylib+sub1???>libmylib+sub2???>libmylib+sub3假如源文件sub3.asm已修改,并也生成了新的目標(biāo)文件sub3.obj,這時,就需要把庫文件mylib.lib中的sub3.obj替換成新的目標(biāo)文件。于是,可用下面命令來實現(xiàn)替換:???>libmylib-+sub3當(dāng)提示輸入目標(biāo)庫文件名(Outputlibrary)時,可按“回車”用默認的原庫文件名。如果想查看庫文件mylib.lib中各文件的大小和存放的先后次序,可用下列命令:???>libmylib,list ;把庫文件mylib.lib中的文件結(jié)構(gòu)生成到文件list中…〉typelist7.6.3庫文件的應(yīng)用在開發(fā)一個功能較弱的應(yīng)用程序時,其執(zhí)行文件通??捎梢粋€目標(biāo)文件連接而成,當(dāng)開發(fā)一個功能較強、關(guān)系較復(fù)雜的應(yīng)用程序時,其執(zhí)行文件很難由一個目標(biāo)文件連接而成,常常是由多個目標(biāo)文件(模塊)連接而成的。各模塊之間無疑會存在著相互調(diào)用、相互訪問數(shù)據(jù)單元等內(nèi)在聯(lián)系,各模塊之間的相互聯(lián)系就產(chǎn)生了這樣的問題:程序員如何在源程序中來表達這種聯(lián)系?為了解決描述各模塊之間的聯(lián)系,匯編語言提供了二條偽指令PUBLIC和EXTRN,它們的作用有點象C/C++語言說明變量、過程和函數(shù)是“全局的”或“外部的”。這二條偽指令的具體用法和含義如下:1、 偽指令PUBLIC偽指令PUBLIC是用來說明:當(dāng)前模塊中哪些標(biāo)識符是能被其它模塊引用的公共標(biāo)識符。其說明的一般格式如下:PUBLIC標(biāo)識符1,標(biāo)識符2,……其中:“標(biāo)識符”可以是變量名、過程名和程序標(biāo)號,各標(biāo)識符之間要用逗號分開。上面說明語句說明了標(biāo)識符1、標(biāo)識符2等是公共標(biāo)識符,可以被其它模塊引用。在一個模塊中,可用多條PUBLIC偽指令來說明公共標(biāo)識符。2、 偽指令EXTRN偽指令EXTRN是用來說明:在當(dāng)前模塊所使用的標(biāo)識符中,哪些標(biāo)識符是已在其它模塊中被定義為指定類型的標(biāo)識符。如果當(dāng)前模塊使用了其它模塊的標(biāo)識符,而對它又不加以說明的話,那么,在匯編時,匯編程序?qū)o出下列出錯信息:errornnnnn:undefinedsymbol:XXXXXX其中:“nnnnn”是錯誤號,“XXXXXX”是當(dāng)前模塊中沒有定義的標(biāo)識符。偽指令EXTRN的一般說明格式如下:EXTRN標(biāo)識符1:類型1,標(biāo)識符2:類型2,……其中:“標(biāo)識符”和“類型”之間要用冒號“:”連接。上面語句說明了標(biāo)識符1、標(biāo)識符2等是外部標(biāo)識符,它們在其它模塊中已被分別定義為類型1、類型2等,該類型說明符可以是:NEAR、FAR、BYTE、WORD、DWORD等之一。如果在一條說明偽指令中說明了多個標(biāo)識符,那么,各標(biāo)識符之間要用逗號分開。在一個模塊中,可用多條EXTRN偽指令來說明本模塊所引用的外部標(biāo)識符。注意:偽指令EXTRN中所說明的標(biāo)識符必須在其定義的模塊中被PUBLIC偽指令說明為公共標(biāo)識符,并且其說明的標(biāo)識符類型要與該標(biāo)識符在定義是的類型相一致,否則,要么不能生成其可執(zhí)行文件,要么其執(zhí)行文件不能正確運行。例7.7把例7.3、7.4和7.5合并在一起生成一個可執(zhí)行文件,假設(shè)它們所對應(yīng)的源程序名分別為Count.ASM、DispBX.ASM和Main.ASM。解:由于在源文件Count.ASM中調(diào)用了子程序UPPER,所以,例7.1的程序也必須加入到本題中。假設(shè)其源文件名為Upper.ASM。由于生成本題的執(zhí)行文件需要四個模塊,模塊之間存在著調(diào)用關(guān)系,所以,在有關(guān)源文件中需要說明某些標(biāo)識符為外部屬性,或說明其為公共屬性。為了把前面例子中的子程序改寫成可匯編的程序,需要添加一些簡單的說明語句或進行簡單修改,其添加或改寫的部分已在下面用“下劃線”表示出來。;源文件Upper.ASM;子程序說明信息:……PUBLICUPPERSegUprSEGMENT'code'UPPERPROCFAR ;例7.1中的程序段,在此從略UPPERENDPSegUprENDSEND;源文件DispBX.ASM;子程序說明信息:……PUBLICDISPBXSubDataSEGMENTDB5DUP('0'),0ah,0dh,'$'SubDataENDSSegDispSEGMENT'code'DISPBXPROCFAR ;例7.3中的程序段,在此從略DISPBXENDPSegDispENDSEND;源文件Count.ASM;子程序說明信息:……PUBLICCOUNTEXTRNUPPER:FARSegCountSEGMENT'code'COUNTPROCFAR ;例7.4中的程序段,在此從略COUNTENDPSegCountENDSEND;源文件Main.ASMEXTRNCOUNT:FAR,DISPBX:FAR.MODELSMALL.DATASTRDB'KSDJL0984/[]3oiuOIUOIU(*&(5341',0.CODE.STARTUPLEADX,STRCALLCOUNT ;調(diào)用子程序統(tǒng)計出各類字符的個數(shù)CALLDISPBX ;調(diào)用子程序顯示數(shù)字字符的個數(shù)MOVBX,CXCALLDISPBX ;調(diào)用子程序顯示字母的個數(shù)MOVBX,DICALLDISPBX ;調(diào)用子程序顯示其它字符的個數(shù).EXIT0END經(jīng)過以上改寫后,可用下列命令把它們分別匯編成目標(biāo)文件(假設(shè)已安裝了MASM編程環(huán)境):…〉MASMupper…〉MASMdispbx…〉MASMcount…〉MASMmain有了這些目標(biāo)文件后,可用以下二種方法來生成可執(zhí)行文件。方法1:把所有的目標(biāo)文件連接在一起???>linkmain+upper+count+dispbx方法2:把目標(biāo)文件upper.obj、count.obj和dispbx.obj加到自己開發(fā)的庫文件中,然后在連接時,與該庫文件連接。???>libmylib+upper+count+dispbx???>linkmainMicrosoft(R)SegmentedExecutableLinkerVersion5.31.009Jul131992Copyright(C)MicrosoftCorp1984-1992.Allrightsreserved.RunFile[main.exe]:ListFile[nul.map]:Libraries[.lib]:mylib ;輸入要連接的庫文件,可用加號“+”連接多個庫文件DefinitionsFile[nul.def]:LINK:warningL4021:nostacksegment…〉main ;運行生成
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 廠房柵欄拆除方案(3篇)
- 小行公司貨場管理制度
- 小學(xué)心靈驛站管理制度
- 家電采購服務(wù)方案(3篇)
- 關(guān)鍵過程制訂管理制度
- DB62T 4398-2021 工業(yè)換熱設(shè)備節(jié)能管理規(guī)范
- 船舶購置方案(3篇)
- 生產(chǎn)經(jīng)營方案(3篇)
- 橋牌測試題及答案
- 田地土壤養(yǎng)護方案(3篇)
- 壁掛爐銷售合同協(xié)議書
- 2025年04月高等教育自學(xué)考試《00034社會學(xué)概論》試題
- 北京小升初試題及答案
- 北京市事業(yè)單位退役大學(xué)生士兵定向招聘筆試真題2024
- 大數(shù)據(jù)在醫(yī)療領(lǐng)域的應(yīng)用研究與實踐案例分享
- 大學(xué)生職業(yè)規(guī)劃大賽《服裝與服飾設(shè)計專業(yè)》生涯發(fā)展展示
- 2025年高考語文備考之古詩文名句名篇默寫(共80題含答案)
- T-CCMA 0113-2021 高空作業(yè)車 檢查與維護規(guī)程
- 社會學(xué)概論知識點梳理與復(fù)習(xí)指南
- 校園禁煙宣傳抵制煙草誘惑拒絕第一支煙課件
- 2025-2030中國理發(fā)行業(yè)市場發(fā)展前瞻及投資戰(zhàn)略研究報告
評論
0/150
提交評論