




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
第7章子程序和庫子程序是程序設計所常見的基本概念,匯編語言也提供了編寫子程序的方法。本章主要介紹子程序的定義、調用和返回、子程序的參數傳遞等知識。此后,還將講解如何構造自己的子程序庫。7.1子程序的定義如果某程序段在源程序內反復出現,那么,就可把該程序段定義為子程序。這樣可以縮短源程序長度、節省目標程序的存儲空間,也可提高程序的可維護性和共享性。定義子程序的一般格式如下:子程序PROC[NEAR|FAR]名… ;子程序體子程序ENDP名對子程序定義的具體規定如下:、“子程序名”必須是一個合法的標識符,并前后二者要一致;、PROC和ENDP必須是成對出現的關鍵字,它們分別表示子程序定義開始和結束;、子程序的類型有近(NEAR)、遠(FAR)之分,其缺省的類型是近類型;、如果一個子程序要被另一段的程序調用,那么,其類型應定義為FAR否則,其類型可以是NEAR。顯然,NEAR類型的子程序只能被與其同段的程序所調用;、子程序至少要有一條返回指令,也可有多條返回指令。返回指令是子程序的出口語句,但它不一定是子程序的最后一條語句;、子程序名有三個屬性:段值、偏移量和類型。其段值和偏移量對應于子程序的入口地址,其類型就是該子程序的類型。編寫子程序除了要考慮實現子程序功能的方法外,還要養成書寫子程序說明信息的好習慣。其說明信息一般包括以下幾方面內容:、功能描述、入口和出口參數;可選項,最好采用寄存器的保護和恢復方法,使、所用寄存器 之使用透明化、所用額外存儲單;可選項,可以減少為子程序定義自己的局部變量元、子程序的所米用;可選項,如果算法簡單,可以不寫的算法、調用時的注意事;可選項,盡量避免除入口參數外還有其它的要求項、子程序的編寫者;可選項,為將來的維護提供信息、子程序的編寫日;可選項,用于確定程序是否是最新版本期這些說明性信息雖然不是子程序功能的一部分,但其他程序員可通過它們對該子程序的整體信息有一個較清晰認識,為準確地調用它們提供直接的幫助,與此同時,也為實現子程序的共享提供了必要的資料。7.2子程序的調用和返回指令子程序的調用和返回是一對互逆操作,也是一種特殊的轉移操作。一方面,之所以說是轉移,是因為當調用一個子程序時,程序的執行順序被改變,CPU將轉而執行子程序中的指令序列,在這方面,調用子程序的操作含有轉移指令的功能,子程序的返回指令的轉移特性與此類似;另一方面,轉移指令是一種“一去不復返”的操作,而當子程序完后,還要求CPU能轉而執行調用指令之下的指令,它是一種“有去有回”的操作。為了滿足子程序調用和返回操作的特殊性,在指令系統中設置了相應的特定指令。7.2.1調用指令(CALL)調用子程序指令的格式如下:CALL子程序名/Reg/Mem子程序的調用指令分為近(near)調用和遠(far)調用。如果被調用子程序的屬性是近的,那么,CALL指令將產生一個近調用,它把該指令之后地址的偏移量(用一個字來表示的)壓棧,把被調用子程序入口地址的偏移量送給指令指針寄存器IP即可實現執行程序的轉移。近調用指令的堆棧操作如圖7.1所示。圖7.1近調用指令進棧操作示意圖如果被調用子程序的屬性是遠的,那么,CALL指令將產生一個遠調用。這時,調用指令不僅要把該指令之后地址的偏移量壓進棧,而且也要把段寄存器CS的值壓進棧。在此之后,再把被調用子程序入口地址的偏移量和段值分別送給IP和CS,這樣完成了子程序的遠調用操作。遠調用指令的堆棧操作如圖7.2所示。圖7.2遠調用指令進棧操作示意圖子程序調用指令本身的執行不影響任何標志位,但子程序體中指令的執行會改變標志位,所以,如果希望子程序的執行不能改變調用指令前后的標志位,那么,就要在子程序的開始處保護標志位,在子程序的返回前恢復標志位。例如:[BX][BX]CALLDISPLAYCALLBXCALLWORD1[BX][BX]CALLDISPLAYCALLBXCALLWORD1CALLDWORD1CALLwordptr;DISPLAY是子程序名;BX的內容是子程序的偏移量;WORD1是內存字變量,其值是子程序的偏移量;DWORD1是雙字變量,其值是子程序的偏移量和段值;BX所指內存字單元的值是子程序的偏移量CALLdwordptr;BX所指內存雙字單元的值是子程序的偏移量和段值7.2.2返回指令(RET)當子程序執行完時,需要返回到調用它的程序之中。為實現此功能,指令系統提供了一條專用的返回指令。其格式如下:RET/RETN/RETF[Imm]子程序的返回在功能上是子程序調用的逆操作。為了與子程序的遠、近調用相對應,子程序的返回也分:遠返回和近返回。返回指令在堆棧操作方面是調用指令的逆過程(如圖7.3所示)。其具體規定如下:、在近類型的子程序中,返回指令RET是近返回,其功能是把棧頂之值彈出到指令指針寄存器IP中,SP會被加2(如圖7.3所示);、在遠類型的子程序中,返回指令RET是遠返回,其功能是:先彈出棧頂之值到IP中,再彈出棧頂之值到CS之中,SP總共會被加4(如圖7.4所示)。圖7.3近返回指令的出棧操作示意圖圖7.4遠返回指令的出棧操作示意圖如果返回指令后面帶有立即數(其值通常為偶數),則表示在得到返回地址之后,SP還要增加的偏移量,它不是類似于高級語言中子程序的返回值(如圖7.5所示)。圖7.5帶立即數的返回指令的出棧操作示意圖在MASM5.0及其以后版本中,可用指令RETN或RETF來顯式地告訴匯編程序是本子程序的返回是近返回,還是遠返回。例如:RET;可能是近返回,也可能是遠返回RETN;近返回指令RETF;遠返回指令RET6;子程序返回后,(SP)^(SP)+6例7.1編寫一個子程序UPPER,實現把寄存器AL中存放的字符變大寫。解:;子程序功能:把AL中存放的字符變大寫;入口參數:AL;出口參數:AL;算法描述:判斷AL中字符必須在'a'~'z'之間才能把該字符變為大寫UPPERPROCCMPAL,'a' ;書寫'a'的ASCII碼61H也可以JBoverCMPAL,'z'JAoverSUBAL,20H ;書寫指令ANDAL,0DFH也可以over:RETUPPERENDP例7.2編寫一個求字符串長度的子程序StrLen,該字符串以0為結束標志,其首地址存放在DS:DX,其長度保存在CX中返回。解:;子程序功能:求字符串的長度;入口參數:DS:DX存放字符串的首地址,該字符串以0為結束標志;出口參數:CX存放該字符串的長度;算法描述:用BX來指針來掃描字符串中的字符,如果遇到其結束標志,則停止掃描字符串操作StrLenPROCPUSHAXPUSHBX;用堆棧來保存子程序所用到的寄存器內容XORCX,CXXORAL,ALMOVBX,DXagain:CMP[BX],ALJZoverINCCX;增加字符串的長度INCBX;訪問字符串的指針向后移JMPagainover:POPBX;恢復在子程序開始時所保存的寄存器內容POPAXRETStrLenENDP7.3子程序的參數傳遞子程序一般都是完成某種特定功能的程序段。當一個程序調用一個子程序時,通常都向子程序傳遞若干個數據讓它來處理;當子程序處理完后,一般也向調用它的程序傳遞處理結果,我們稱這種在調用程序和子程序之間的信息傳遞為參數傳遞。用程序向子程序傳遞的參數稱為子程序的入口參數,子程序向調用它的程序傳遞的參數稱為子程序的出口參數。子程序的入口參數和出口參數都是任意項,對某個具體的子程序來說,要根據具體情況來確定其入口和出口參數,也可以二者都沒有。程序和被調用子程序之間的參數傳遞方法是程序員自己或和別人事先約定好的信息傳遞方法。這種信息傳遞方法可以是多種多樣的,在本節,我們只介紹
常用的、行之有效的參數傳遞方法有:寄存器傳遞參數、約定存儲單元傳遞參數和堆棧傳遞參數等。如果對其它的參數傳遞方法感興趣的話,可參考其它《匯編語言程序設計》書籍。7.3.1寄存器傳遞參數一方面,由于CPU中的寄存器在任何程序中都是“可見”的,一個程序對某寄存器賦值后,在另一個程序中就能直接使用,所以,用寄存器來傳遞參數最直接、簡便,也是最常用的參數傳遞方式。但另一方面,CPU中寄存器的個數和容量都是非常有限,所以,該方法適用于傳遞較少的參數信息。例7.1是用寄存器傳遞參數的例子,子程序處理的數據被保存在寄存器AL中。假設有下列的程序段:MOVCALLAL,'b'UPPER;子返回時,MOVCALLAL,'b'UPPER;子返回時,(AL)='B'MOVCALLAL,'2'UPPER;子返回時,AL的值不變,因為'2'不是字母例7.3按五位十進制的形式顯示寄存器BX中的內容,如果BX的值小于0,則應在顯示數值之前顯示負號'-'。例如:(BX)=123,顯示:00123;(BX)=-234,顯示:-00234;解:;子程序功能:把寄存器BX的內容按十進制有符號數顯示出來;入口參數:BX;出口參數:無,只有顯示信息;算法描述:1、定義6個字節的存儲單元2、 先判斷BX是否小于零,如果是,則先顯示負號'-',再取BX的絕對值;3、 采用除10,得余數的方法,從低位向高位求出每位十進制位;4、 輸出數據的字符串。SubDataSEGMENTDB5DB5DUP('0'),0ah,0dh,'$';0ah、0dh:換行、回車SubDataDISPBXSubDataDISPBXENDSPROCDSDSDXCXAXASSUMEDS:SubDataPUSHPUSHPUSHPUSH
MOVAX,SubData;取子程序所用的數據區段地址MOVDS,AXCMPBX,0JGEnextMOVDL,'-'MOVAH,2INT21H;顯示負號'-'NEGBX;求-BX,使其值為正數next:MOVSI,4MOVAX,BXMOVCX,10Dagain:XORDX,DXIDIVCX;DX存放余數,AX存放商ADDDL,'0'MOV[SI],DLDECSIJGEagainXORDX,DXMOVAH,9INT21H;調用中斷21的功能9,顯示DS:DX指向的字符串POPAXPOPCXPOPDXPOPDSRETDISPBXENDP7.3.2約定存儲單元傳遞參數在調用子程序時,當需要向子程序傳遞大量數據時,因受到寄存器容量的限制,就不能采用寄存器傳遞參數的方式,而要改用約定存儲單元的傳送方式。這種參數傳遞方式有點象情報人員和聯絡人員之間的傳遞信息方式,一個向指定地點放情報,另一個從指定地點取情報。例7.2是采用約定存儲單元傳遞參數的例子,所處理的數據不是直接傳給子程序,而是把存儲它們的地址告訴子程序。例7.4:編寫一個子程序分類統計出一個字符串中數字字符、字母和其它字符的個數。該字符串的首地址用DS:DX來指定(以0為字符串結束),各類字符個數分別存放BX、CX和DI中。解:;子程序功能:分類統計出字符串中數字字符、字母和其它字符的個數;入口參數:DS:DX指向被統計的字符串
;出口參數:BX、CX和DI分別保存數字字符、字母和其它字符的個數;算法描述:1、當字符在'0'~'9'范圍時,數字字符個數BX加1;2、 為了判斷簡單,先把字字母變成大寫字母;3、 當字符在'A'~'Z'范圍時,字母個數CX加1;4、 否則,其它字符個數DI加1。COUNTPROCPUSHAXPUSHSIXORBX,BXXORCX,CXXORDI,DI;上三條指令使各類字符計數清零MOVSI,DXagain:MOVAL,[SI]INCSICMPAL,0JEoverCMPAL,'0'JLotherCMPAL,'9'JGnextINCBX;數字字符個數加1JMPagainnext:CALLUPPER;調用子程序把AL中的字母變成大寫字母CMPAL,'A'JLotherCMPAL,'Z'JGotherINCCX;字母個數加1JMPagainother:INCDI;其它字符個數加1JMPagainover:POPSIPOPAXRETCOUNTENDP例7.5顯示出任意字符串中數字字符、字母和其它字符的個數。解:.MODELSMALL.DATAMSGDB'KSDJL0984/[]3oiuOIUOIU(*&(5341',0.CODE.STARTUPLEADX,MSG;DS:DX指向待統計的字符串CALLCOUNT;調用子程序統計出各類字符的個數CALLDISPBXMOVBX,CX;調用子程序顯示數字字符的個數CALLDISPBXMOVBX,DI;調用子程序顯示字母的個數CALLDISPBX.EXIT0END;調用子程序顯示其它字符的個數7.3.3堆棧傳遞參數堆棧是一個特殊的數據結構,它通常是用來保存程序的返回地址。當用它來傳遞參數時,勢必會造成數據和返回地址混合在一起的局面,用起來要特別仔細。具體做法如下:、當用堆棧傳遞入口參數時,要在調用子程序前把有關參數依次壓棧,子程序從堆棧中取到入口參數;、當用堆棧傳遞出口參數時,要在子程序返回前,把有關參數依次壓棧(這里還需要做點額外操作,要保證返回地址一定在棧頂),調用程序就可以從堆棧中取到出口參數。在通常情況下,我們用堆棧傳入口參數,用寄存器傳出口參數。1、用堆棧傳遞入口參數的調用方法:PUSHPara1???PUSHParan;把n個字的參數壓棧CALLSUBPRO;調用子程序SUBPRO2、在子程序中取入口參數的方法:、段內調用子程序由于是段內調用,所以,CALL指令只把返回地址的偏移量(即IP的內容)壓棧,如圖7.6(a)所示。在進入子程序后,為了能讀取傳遞過來的參數,需要用BP來訪問堆棧,所以要先保護BP原來的值,再把當前SP的值傳送給BP。于是,當前BP所指向的堆棧單元與最后一個參數Paran之間隔著BP的原值和返回地址的偏移量,也就是說:二者之間相差4個字節。具體情況如圖7.6(b)所示。(a)、進入子程序時堆棧情況 (b)、子程序寄存器保護后的堆棧情況圖7.6在段內調用情況下子程序所能訪問的堆棧情況SUBPROPROCNEARPUSHBPMOVBP,SPSUBPROPROCNEARPUSHBPMOVBP,SPMOVParan,[BP+4];保護寄存器BP;用寄存器BP來訪問堆棧,讀取參數;保護其它寄存器的指令;保護其它寄存器的指令MOVPara1,[BP+4+2*(n-1)]SUBPROENDP、段間調用子程序在段間調用子程序時,CALL指令會把返回地址的偏移量和段寄存器CS的內容都壓棧,如圖7.7(a)所示。在進入子程序后,與前面“段內調用子程序”一樣,也需要用BP來讀取傳遞過來的參數,所以,也要先保護BP原來的值,再把當前SP的值傳送給BP。這時,當前BP所指向的堆棧單元與最后一個參數Paran之間隔著BP的原值、返回地址的偏移量和段地址,所以,二者之間相差6個字節。具體情況如圖7.7(b)所示。(a)、進入子程序時堆棧情況 (b)、子程序寄存器保護后的堆棧情況圖7.7在段間調用情況下子程序所能訪問的堆棧情況在段間調用時,除了多一個返回段地址外,其它的內容與“段內調用”的情況完全一致,所以,在讀取第i個參數時,只要用[BP+6+4*(n-i)]代替[BP+4+2(n-i)]即可(假設每個參數都是字類型)。7.4寄存器的保護與恢復由于計算機的硬件資源只有一套,當子程序修改了寄存器的內容后,返回到調用它的程序時,這些寄存器的內容也就不會是調用子程序前的內容。這樣,子程序修改寄存器內容就可能變成了調用它的副作用,這種副作用常常會導致調用程序的出錯。為此,在編寫子程序時,除了能對作為入口和出口參數的寄存器進行修改外,對其它寄存器的修改對調用程序來說都要是透明的,也就是說,在調用子程序指令的前后,除了作為入口和出口參數的寄存器內容可以不同外,其它寄存器的內容要保持不變。有時,也要求作為入口參數的寄存器內容保持不變。在子程序中,保存和恢復寄存器內容的主要方法是:在子程序的開始把它所用到的寄存器壓進棧,在返回前,再把它們彈出棧。這樣編寫的好處是該子程序可以被任何其它程序來調用。在調用指令前,不需要保存寄存器,在調用指令后,也無需恢復寄存器。XXXXXPROCPUSHREG1XXXXXPROCPUSHREG1…PUSHREGn………POPREGn;把子程序要使用的寄存器壓棧,REGi代表某個寄存器;子程序的處理功能語句;把前面壓棧的寄存器彈出,注意它們的次序POPREG1RETXXXXXENDP例7.2就是一個在子程序中利用堆棧來保存和恢復寄存器內容的例子。利用堆棧來實現此項功能時,應注意以下幾點:、用堆棧保存和恢復寄存器的內容,要注意堆棧“先進后出”的操作特點;、通常情況下不保護入口參數寄存器的內容,當然,也可以根據事先的約定而對它們加以保護;、如果用寄存器帶回子程序的處理結果,那么,這些寄存器就一定不能加以保護;、整個子程序的執行幾乎肯定要改變標志位,可用PUSHF和POPF來保護和恢復標志位,但一般在子程序中不保護標志位,除非有此特殊需要;7.5子程序的完全定義在7.1節所給出的子程序定義格式是一個最基本的、最簡單的定義格式,它不能為子程序提供更簡潔的調用方式。在宏匯編MASM6.11系統中,為微機匯編語言的子程序提供了更加豐富的定義方式。雖然子程序的這種定義方式顯得稍微有點復雜,但它不僅為子程序的調用帶來了極大的方便,而且其調用方式與高級語言中子程序的調用方式相一致,這就大大地降低了程序員熟練掌握它的難度。7.5.1子程序完全定義格式子程序PROC[distance][langtype][visibility][<prologuearg>]名 [USES寄存器列表〕[,參數[:數據類型]]...[LOCALvarlist]子程序的程序體子程序-ENDP名定義子程序時,可使用參數表來直接指明其所要的參數,但程序員必須先用也些也指令,或使用<langtype>參數來說明本子程序所使用的程序設計語言類型。程序員在定義子程序時,最好能象在高級語言(如:C/C++)定義過程那樣,先說明該子程序的原型(用偽指令PROTO),這樣,在調用時,系統可以自動進行類型檢查,也可以使用更方便的調用偽指令INVOKE來調用該子程序。有關子程序的原型說明偽指令和調用偽指令在隨后第7.5.8和7.5.9小節中加以介紹。子程序通常用RET指令來結束其執行,也可用指令“RETn”來指明在結束子程序執行后從堆棧彈出n個字節。有關返回指令請參閱7.2.2節中的敘述。匯編程序在處理子程序時能自動產生“起始”代碼(PROLOGUECode)和“結束”代碼(EPILOGUEcode)。這兩段特殊的代碼分別完成:在調用子程序時,能把傳遞給子程序的參數壓棧,在子程序結束時能把先前壓棧的參數彈出。有了這兩段代碼,程序員在調用子程序時就不用自行考慮子程序的參數傳遞問題。若子程序用指令RETN、RETF或IRETF作為子程序的結束指令,那么,匯編程序將不生成“結束”代碼。程序員可以用自己定義宏來替代缺省的“起始”和“結束”的代碼段。這種替代方法是使用偽指令:OPTIONPROLOGUE和OPTIONEPILOGUEo若子程序沒有參數、局部變量,沒使用USES子句,也不會產生新的段或段組,那么,子程序是可以嵌套定義的。程序員也可以使用返回指令RETN和RETF來避免子程序的嵌套。在子程序內部,可以在指令之前使用偽指令LOCAL來說明其局部變量,有關規定在隨后的第7.5.10節中有詳細的說明。下面就來介紹該定義格式中各個說明項的作用。7.5.2子程序的位距子程序的位距(Distance)有:Near、Far、Near16、Far16、Near32和Far32。子程序位距描述符告訴匯編程序該子程序是在本段之內(Near),還是在本段之外(Far)。Near和Far描述符表示使用當前的段規模(SegmentSize),Near16、Far16、Near32和Far32描述符是告訴匯編程序忽略當前的段規模,而使用指定16位或32位的段規模。若選用類型Near或Far,那么,匯編程序將根據當前段的規模來決定選用16位,還是32位的Near或Far。若程序員不指定該選項,那么匯編程序將根據當前的存儲模式(由.MODEL來決定)和處理機類型來決定子程序類型。若不使用偽指令.MODEL,那么,Near是缺省的類型。7.5.3子程序的語言類型子程序語言類型(LanguageType)可以是任何一種有效的程序設計語句類型,由它來告訴匯編程序將使用什么樣的標識符的命名風格、子程序的調用和返回約定。該語言類型說明可使匯編語言程序與其它語言程序達到共享的目的。所有有效的語言類型及其書寫規定如表7.1所列。表7.1語言類型及其書寫規定SYSCALLSTDCALLBasicFortranPascalXXXnXXXXXXXXXX字母大寫化XXXXXXXXXX參數從左到右參數從右到左調用程序清空堆棧保存指針寄存器BP使用VARARG參數*若使用:VARARG參數,則調用程序清空堆棧,否則,被調用的子程序清空堆棧。程序員可用另外三種方法來設置程序的語言類型:.MODEL、OPTIONLANGTYPE:和命令行選項/Gx。若在程序和命令行中都說明了語言類型,那么,前者的說明優先。另外,程序員也可用命令行選項/H來限定標識符的最大長度。例如:Pascal語言風格:OPTIONLANGUAGE:PASCAL、/GcC語言風格:OPTIONLANGUAGE:C、/Gd7.5.4子程序的可見性子程序的可見性(Visibility)決定該子程序對其它模塊是否可用。它共有三個屬性值:PRIVATE、PUBLIC和EXPORToPUBLIC屬性是子程序標準的缺省屬性,但該缺省屬性可以用偽指令OPTIONPROC來修改。EXPORT屬性意味著該子程序是一個“遠”的、具有PUBLIC屬性的子程序,并要求連接程序在生成可執行文件時把其入口地址放入導出入口地址表中。例如:OPTIONPROC:PRIVATE ;說明子程序的可見性為:PRIVATEOPTIONPROC:EXPORT ;說明子程序的可見性為:EXPORT7.5.5子程序的起始和結束操作當程序員想用自己定義的宏來替代缺省的“起始”和“結束”的代碼段時,可用下列說明語句來實現:OPTIONPROLOGUE:MacroNamelOPTIONEPILOGUE:MacroName2PROLOGUE和EPILOGUE分別指定MacroName1和MacroName2為“起始”和“結束”代碼段的宏名。匯編程序對用戶定義的宏MacroName1和MacroName2的形式有較嚴格的規定,要求宏的定義形式如下:MacroNameMACROProcName,flags,argbytes,localbytes,<reglist>,userparms:VARARG該宏定義的每個參數都有詳細的說明,感興趣的讀者可看有關技術資料或MASM6.11中的幫助,詳細的說明在此從略,但建議使用缺省的宏。如果想取消當前指定的宏名,而恢復使用缺省的“起始”和“結束”代碼段的宏名,那么,可用下列說明語句,即指定二個缺省的宏名PrologueDef和EpilogueDef。OPTIONPROLOGUE:PrologueDefOPTIONEPILOGUE:EpilogueDef若程序員不要匯編程序自動產生“起始”和“結束”代碼,則可用NONE來代替說明語句中的宏名,即:OPTIONPROLOGUE:NONEOPTIONEPILOGUE:NONE7.5.6寄存器的保護和恢復保護寄存器說明子句的說明格式:USES寄存器列表該說明子句要求匯編程序為其生成保護和恢復寄存器的指令序列,即:在進入子程序執行指令之前,把寄存器列表中的寄存器壓進堆棧,在結束子程序執行時,把先前壓進堆棧的寄存器彈出,以達到保護寄存器的目的。寄存器列表:列舉出在子程序中需要保護的寄存器名,即:在子程序開始時需要把內容進棧的寄存器名。若有多個寄存器名,則在寄存器名之間要用“空格”來分開。例如:DsipPROCUSESAXDX,FUNC:WORD,MSG:PTRBYTEMOVDX,MSGMOVAX,FUNCINT21HRETDispENDP匯編程序在處理該子程序時,會根據子句USES的作用,在第一條指令“MOVDX,MSG”之前,插入把寄存器AX和DX進棧的指令序列,即:PUSHAXPUSHDX而在返回指令RET之前插入把寄存器DX和AX的值彈出的指令序列,即:POPDXPOPAX注意:若子程序含有多個RET或IRET指令,那么,匯編程序在每個RET或IRET指令前都將增加相應的彈出堆棧指令序列。從子句USES的功能來看,它與前面7.4節“寄存器的包含與恢復”中所用的方法完全一致,所不同的是:用USE子句進行寄存器保護和恢復的代碼是由匯編程序自動產生的,程序員不用關心如何去做,有點象高級語言的編程風格,而7.4節中的代碼則是由程序員自己來安排的。7.5.7子程序的參數傳遞子程序參數是用來向子程序傳遞信息的數據。若有多個參數,則參數之間要用逗號分割。為了能說明子程序的參數,程序員必須事先指定參數所遵循的語言類型或使用“語言類型”參數。參數的數據類型可以是任何一個有效的數據類型說明符或VARARG。VARARG數據類型允許向子程序傳遞“個數”不定的參數,其參數之間要用逗號“,”來分開。若參數表中含有VARARG說明的參數,那么,該參數一定是該子程序的最后一個參數。其規定隱含地說明了在參數表中只能有一個用VARARG說明的參數。當子程序的語言類型是C、SYSCALL和STDCALL時,在其參數表中才能使用VARARG數據類型的參數。見前面的表7.1中所列。如果沒有顯式地指定某個參數的數據類型,那么,在16位段規模的情況下,其缺省的數據類型是WORD;在32位段規模的情況下,其缺省的數據類型是DWORDo7.5.8子程序的原型說明子程序原型的說明格式如下:子程序名PROTO[distance][langtype][,[parameter]:tag]...該說明語句告訴匯編程序該子程序的若干屬性,如:位距、語語言類型、參數個數及其類型等。這樣,匯編程序就可以對其定義進行適當的檢查。如果對所有基于堆棧的過程都定義一個原型,那么,就可把這些原型存放在一個獨立的包含文件(用偽指令INCLDUE來裝入)中。使用這種方法對將來把所有子程序放入自定義的庫文件中是非常方便的。該原型說明語句中參數distance、langtype、parameter和tag等的含義與前面的敘述相一致,在此不再重復。7.5.9子程序的調用偽指令子程序調用偽指令INVOKE與子程序的調用指令CALL在功能上是一致的,但它使匯編語言的子程序調用方法高級語言化,程序員可不用理會一些調用細節問題。調用偽指令INVOKE的使用格式如下:INVOKEexpression[,arguments]其中:expression—地址表達式,通常為子程序名;arguments-傳遞的各參數之間用逗號',’分開,參數可以是寄存器、表達式或ADDR標識符等。該偽指令是調用基于堆棧的子程序的方法,它把所有參數壓棧,子程序結束時,又把參數自動彈出堆棧。在參數傳遞時,匯編程序將根據子程序的原型進行數據類型檢查。若需要進行參數類型轉換的話,匯編程序則會自動生成一段代碼來滿足其數據類型轉換的要求。例如:INVOKETEST,AX,12+34,ADDRMSG其中:TEST是子程序名,寄存器AX和表達式“12+34”是參數,“ADDRMSG”是傳遞變量MSG的地址。例7.6編寫一個累加參數數值的子程序。其中參數的個數不定,參數的個數由第一個參數來確定。解:.MODELSMALL.STACK256.CODE;第一個參數parmcount確定其后面參數parmvalues中所含參數的個數ADDUPPROCNEARC,parmcount:WORD,parmvalues:VARARGXORAX,AXXORSI,SIMOVCX,parmcount.REPEATADDAX,parmvalues[SI]ADDSI,2.UNTILCXZRETADDUPENDP.STARTUPINVOKEADDUP,3,5,2,4 ;調用子程序ADDUP,計算5+2+4INVOKEADDUP,4,1,2,3,4 ;調用子程序ADDUP,計算1+2+3+4.EXIT0.END7.5.10局部變量的定義局部變量的定義格式:LOCAL變量名[[數量]][:數據類型][,變量名[[數量]][:數據類型]]...偽指令LOCAL的作用是說明一個或多個臨時的局部變量(位于堆棧中)。局部變量必須在任何指令之前加以說明,并可用多個LOCAL偽指令來說明其局部變量。在子程序中,若說明了某個局部變量,則子程序體中的指令就可使用該局部變量。匯編程序會把對它的引用轉換成用指針寄存器BP來訪問其在堆棧中的實際存儲單元。在局部變量的作用域與高級語言中局部變量的作用域相一致,即:局部變量只能在當前子程序中使用,離開該子程序,它們就不能再被引用。但在局部變量的命名規則上有所不同,高級語言中的局部變量可與外層變量同名,而匯編語言中的局部變量不能與其它任何變量同名,否則,在匯編時,將會給出“重定義”(Symbolredefinition)的錯誤信息。“數量”用來說明該變量所具有的元素個數。象高級語言的數組定義一樣,該數量必須寫在括號“[]”之中。“數量”說明項是可選項。局部變量的類型說明符可以是任何合法的數據類型說明符。在16位段環境下,該缺省的數據類型是WORD,而在32位段環境下,該缺省的數據類型是DWORD。此處偽指令LOCAL的作用與9.3.1節中偽指令LOCAL的作用是完全不同的,具體的差異請見9.3.1節中的比較。例如:LOCALdata[20]:BYTE,num:WORD在上例的說明中,定義了二個局部變量:data和num。前者是字節類型,并有20個元素,后者是字類型,只有其自身1個元素。7.6子程序庫庫文件對學過C/C++語言程序設計的讀者來說應該是不會陌生的,該語言的程序設計環境提供了大量的庫文件,也就是說,提供了大量的標準函數或過程。在本節里,介紹讀者如何創建自己的庫文件。7.6.1建立庫文件命令LIB宏匯編MASM系統提供了建立庫文件的命令文件LIB.EXE。其通常是在命令行環境(MS-DOS方式)下使用的,當然,也可在Windows95/98等環境下利用其“開始”菜單下的“運行”功能項來使用。一、 MS-DOS系統顯示命令LIB用法的命令如下:???>lib/?該命令的顯示結果如圖7.9中所示。二、 Windows系統圖7.8運行LIB命令的畫面圖7.9顯示LIB命令功能的畫面命令LIB的使用方式和顯示結果如圖7.8和7.9所示。三、命令顯示內容的解釋1)、各選項的解釋選項含義/?、/HELP顯示LIB命令的用法,描述各命令行參數的含義/IGNORECASE/NOIGNORECASE忽略子程序名中的大小寫 在實踐中,作用不明不忽略子程序名中的大顯小寫/NOEXTDICTIONARY不建立擴展的目錄/NOLOGO不顯示版本號和版權信息/PAGESIZE:n設置庫文件的每頁字節數為n2)、命令項的解釋:選項含義+name向庫文件中加一個新的目標文件name從庫文件中刪除一個指定的目標文件+name用新的目標文件替換掉庫文件中原有的目標文件*name拷貝出指定的目標文件*name從庫文件中移出指定的目標文件在弄懂了LIB的各項功能含義后,讀者就可根據自己的需要來建立庫文件了。7.6.2建立庫文件舉例假設現有目標文件subl.obj、sub2.obj和sub3.obj,要用它們建立庫文件mylib.lib。可用下列方法來建立該庫文件:方法1:所有目標文件都準備好了,可一次性把它們加入到庫文件中???>libmylib+sub1+sub2+sub3方法2:隨著目標文件的逐個生成,而依次把它們加入到庫文件中???>libmylib+sub1???>libmylib+sub2???>libmylib+sub3假如源文件sub3.asm已修改,并也生成了新的目標文件sub3.obj,這時,就需要把庫文件mylib.lib中的sub3.obj替換成新的目標文件。于是,可用下面命令來實現替換:???>libmylib-+sub3當提示輸入目標庫文件名(Outputlibrary)時,可按“回車”用默認的原庫文件名。如果想查看庫文件mylib.lib中各文件的大小和存放的先后次序,可用下列命令:???>libmylib,list ;把庫文件mylib.lib中的文件結構生成到文件list中…〉typelist7.6.3庫文件的應用在開發一個功能較弱的應用程序時,其執行文件通常可由一個目標文件連接而成,當開發一個功能較強、關系較復雜的應用程序時,其執行文件很難由一個目標文件連接而成,常常是由多個目標文件(模塊)連接而成的。各模塊之間無疑會存在著相互調用、相互訪問數據單元等內在聯系,各模塊之間的相互聯系就產生了這樣的問題:程序員如何在源程序中來表達這種聯系?為了解決描述各模塊之間的聯系,匯編語言提供了二條偽指令PUBLIC和EXTRN,它們的作用有點象C/C++語言說明變量、過程和函數是“全局的”或“外部的”。這二條偽指令的具體用法和含義如下:1、 偽指令PUBLIC偽指令PUBLIC是用來說明:當前模塊中哪些標識符是能被其它模塊引用的公共標識符。其說明的一般格式如下:PUBLIC標識符1,標識符2,……其中:“標識符”可以是變量名、過程名和程序標號,各標識符之間要用逗號分開。上面說明語句說明了標識符1、標識符2等是公共標識符,可以被其它模塊引用。在一個模塊中,可用多條PUBLIC偽指令來說明公共標識符。2、 偽指令EXTRN偽指令EXTRN是用來說明:在當前模塊所使用的標識符中,哪些標識符是已在其它模塊中被定義為指定類型的標識符。如果當前模塊使用了其它模塊的標識符,而對它又不加以說明的話,那么,在匯編時,匯編程序將會給出下列出錯信息:errornnnnn:undefinedsymbol:XXXXXX其中:“nnnnn”是錯誤號,“XXXXXX”是當前模塊中沒有定義的標識符。偽指令EXTRN的一般說明格式如下:EXTRN標識符1:類型1,標識符2:類型2,……其中:“標識符”和“類型”之間要用冒號“:”連接。上面語句說明了標識符1、標識符2等是外部標識符,它們在其它模塊中已被分別定義為類型1、類型2等,該類型說明符可以是:NEAR、FAR、BYTE、WORD、DWORD等之一。如果在一條說明偽指令中說明了多個標識符,那么,各標識符之間要用逗號分開。在一個模塊中,可用多條EXTRN偽指令來說明本模塊所引用的外部標識符。注意:偽指令EXTRN中所說明的標識符必須在其定義的模塊中被PUBLIC偽指令說明為公共標識符,并且其說明的標識符類型要與該標識符在定義是的類型相一致,否則,要么不能生成其可執行文件,要么其執行文件不能正確運行。例7.7把例7.3、7.4和7.5合并在一起生成一個可執行文件,假設它們所對應的源程序名分別為Count.ASM、DispBX.ASM和Main.ASM。解:由于在源文件Count.ASM中調用了子程序UPPER,所以,例7.1的程序也必須加入到本題中。假設其源文件名為Upper.ASM。由于生成本題的執行文件需要四個模塊,模塊之間存在著調用關系,所以,在有關源文件中需要說明某些標識符為外部屬性,或說明其為公共屬性。為了把前面例子中的子程序改寫成可匯編的程序,需要添加一些簡單的說明語句或進行簡單修改,其添加或改寫的部分已在下面用“下劃線”表示出來。;源文件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 ;調用子程序統計出各類字符的個數CALLDISPBX ;調用子程序顯示數字字符的個數MOVBX,CXCALLDISPBX ;調用子程序顯示字母的個數MOVBX,DICALLDISPBX ;調用子程序顯示其它字符的個數.EXIT0END經過以上改寫后,可用下列命令把它們分別匯編成目標文件(假設已安裝了MASM編程環境):…〉MASMupper…〉MASMdispbx…〉MASMcount…〉MASMmain有了這些目標文件后,可用以下二種方法來生成可執行文件。方法1:把所有的目標文件連接在一起???>linkmain+upper+count+dispbx方法2:把目標文件upper.obj、count.obj和dispbx.obj加到自己開發的庫文件中,然后在連接時,與該庫文件連接。???>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. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 車輛借用與駕駛資格認證合同協議書
- 節能環保項目投資誠意金合同
- LED信息發布屏采購安裝及后期維護服務協議
- 護理技能培訓要點
- 2025年農產品收購協議
- 功和機械能 專項訓練(解析版)-2025年中考物理一輪復習
- 人體細胞更新周期
- 高考英語3500考綱核心高頻詞突破Day2【詞匯拓展+練習】
- 放療護理查房流程
- 2025屆高三英語基礎寫作:一次難忘的活動(2024屆高三第二次學業質量評價) T8聯考應用文課件(共27張)
- Rexroth (博世力士樂)VFC 3610系列變頻器使用說明書
- 辦公用品采購管理制度及流程
- 《洪水影響評價技術導則》
- 行政事業單位預算管理手冊
- 鐵橋林場森林經營方案文字材料樣本
- 名人簡介英文版(帶中文的)-周恩來(1) 2
- 45#(S45C)鋼的MSDS資料(英文版)
- 中國各省市地圖模塊化可編輯
- YC/T 207-2014煙用紙張中溶劑殘留的測定頂空-氣相色譜/質譜聯用法
- 關于印發《民醫院卒中篩查制度》的通知
- 工會基礎工作操作實務(培訓)課件
評論
0/150
提交評論