匯編第6章 子程序結(jié)構(gòu)_第1頁
匯編第6章 子程序結(jié)構(gòu)_第2頁
匯編第6章 子程序結(jié)構(gòu)_第3頁
匯編第6章 子程序結(jié)構(gòu)_第4頁
匯編第6章 子程序結(jié)構(gòu)_第5頁
已閱讀5頁,還剩99頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第6章子程序結(jié)構(gòu)(jiégòu)6.1子程序的概念(gàiniàn)6.2子程序結(jié)構(gòu)形式與操作6.3子程序的參數(shù)傳送6.4子程序的嵌套與遞歸習(xí)題6精品資料6.1子程序的概念(gàiniàn)6.1.1子程序概念把可以多次調(diào)用、能夠完成(wánchéng)特定操作功能的程序段編寫成獨(dú)立的程序模塊,該程序模塊稱為子程序,又稱為過程。調(diào)用這些子程序的程序稱為主程序。在主程序中,如果調(diào)用到子程序,就需要把控制轉(zhuǎn)移到子程序,這個(gè)過程稱為轉(zhuǎn)子。子程序執(zhí)行完了,要把控制再返回到主程序,這個(gè)過程稱為返主。主程序與子程序之間的關(guān)系如圖6.1所示。精品資料圖6.1主程序與子程序之間的關(guān)系(guānxì)精品資料子程序具有如下4個(gè)特性:(1)重復(fù)性。一個(gè)子程序只占用一段存儲(chǔ)區(qū)域,但可以多次被調(diào)用(diàoyòng),避免了編程人員的重復(fù)勞動(dòng),又節(jié)省程序的存儲(chǔ)空間。由于增加了調(diào)用(diàoyòng)、返回等指令,因此程序執(zhí)行時(shí)間會(huì)長些。如果一個(gè)程序段只用到一次,就沒有必要編寫成子程序形式。精品資料(2)通用性。只能完成特定功能的子程序用處不大,例如,只能實(shí)現(xiàn)5個(gè)字節(jié)加法運(yùn)算的多字節(jié)加法子程序和只能在定長字符串上查找某一固定字符的子程序都沒有通用性,因而(yīnér)用處也就不大。要能夠得到廣泛應(yīng)用的通用的多字節(jié)加法子程序,字節(jié)數(shù)應(yīng)該是任意的,字符查找子程序、字符串的長度和查找的字符都應(yīng)是任意的。精品資料(3)可浮動(dòng)性。所謂可浮動(dòng)性,就是說子程序可以存放在存儲(chǔ)區(qū)的任何地址處。假如(jiǎrú)子程序只能存放在固定的地址處,則在編寫主程序時(shí)要特別注意存儲(chǔ)單元的分配,不要使主程序占用了子程序的存儲(chǔ)單元而破壞掉子程序,這樣就會(huì)給編程人員帶來很大麻煩,而且在裝配主程序和子程序時(shí)往往造成存儲(chǔ)空間的沖突或浪費(fèi)。精品資料(4)可遞歸和可重入性。如果子程序能夠調(diào)用其本身,則稱其可遞歸調(diào)用。如果子程序可被中斷,在中斷處理中又被中斷服務(wù)程序調(diào)用,并且能為中斷服務(wù)程序和已中斷的子程序兩者都提供正確的結(jié)果,那么稱該子程序是可重入的。為使子程序具有可遞歸和可重入性,應(yīng)當(dāng)利用堆棧和寄存器作為(zuòwéi)中間結(jié)果的暫存器,而不能用固定的存儲(chǔ)單元作暫存器。精品資料6.2子程序結(jié)構(gòu)形式(xíngshì)與操作本小節(jié)只給出MASM提供的基本的子程序定義偽操作,從宏匯編編譯程序MASM5.1版本開始為用戶提供了功能更強(qiáng)的子程序定義偽操作,這將在以后各有關(guān)(yǒuguān)章節(jié)加以說明。

精品資料子程序定義偽操作用在子程序的前后,使整個(gè)子程序形成(xíngchéng)清晰的、具有特定功能的代碼塊。其格式為PROCEDURENAMEPROCATTRIBUTE

PROCEDURENAMEENDP精品資料其中,子程序名為標(biāo)識(shí)符,它又是子程序入口的符號(hào)地址。它的寫法與標(biāo)號(hào)(biāohào)的寫法相同。屬性(Attribute)是指子程序的類型屬性,可以是NEAR或FAR。如前所述,CALL和RET指令都有NEAR和FAR的屬性。段內(nèi)調(diào)用使用NEAR屬性,但可以隱含;段間調(diào)用使用FAR屬性。為了使用戶的工作更方便,80x86的匯編程序用PROC偽操作的類型屬性來確定CALL和RET指令的屬性。也就是說,如果所定義的子程序是FAR屬性的,那么對(duì)它的調(diào)用和返回一定都是FAR屬性的;精品資料如果(rúguǒ)所定義的子程序是NEAR屬性的,那么對(duì)它的調(diào)用和返回也一定是NEAR屬性的。這樣,用戶只需在定義子程序時(shí)考慮它的屬性,而CALL和RET的屬性可以由匯編程序來確定。子程序?qū)傩缘拇_定原則很簡單,即:①如調(diào)用程序和子程序在同一個(gè)代碼段中,則使用NEAR屬性;②如調(diào)用程序和子程序不在同一個(gè)代碼段中,則使用FAR屬性。

精品資料【例6-1】調(diào)用程序(chéngxù)和子程序(chéngxù)在同一代碼段中。MAIN PROC FAR ;主程序(chéngxù) CALL SUBR1

RETMAINENDPSUBR1 PROC NEAR;子程序(chéngxù)(NEAR可省略)

RETSUBR1 ENDP精品資料由于調(diào)用程序MAIN和子程序SUBR1在同一代碼段中,所以SUBR1定義為NEAR屬性。這樣,MAIN中對(duì)SUBR1的調(diào)用和SUBR1中的RET就都是NEAR屬性的。但是一般說來,主子程序MAIN應(yīng)定義為FAR屬性,這是由于把程序的主子程序看作(kànzuò)DOS調(diào)用的一個(gè)子程序,因而DOS對(duì)MAIN的調(diào)用以及MAIN中的RET就是FRA屬性的。當(dāng)然,CALL和RET的屬性是匯編程序確定的,用戶只需正確選擇PROC的屬性就可以了。精品資料例6-1的情況也可以寫成如下(rúxià)的程序:MAIN PROC FAR

CALL SUBR1 RETSUBR1 PROC NEAR

RETSUBR1 ENDPMAIN ENDP精品資料【例6-2】調(diào)用(diàoyòng)程序和子程序不在同一個(gè)代碼段內(nèi)。SEGX SEGMENT SUBT PROC FARRETSUBT ENDP CALL SUBTSEGX ENDSSEGY SEGMENT CALL SUBT SEGY ENDS精品資料 SUBT是一個(gè)子程序,它在兩處被調(diào)用,一處是與SEGX同在段內(nèi),另一處是在SEGY段內(nèi)。因此,SUBT必須具有FAR屬性以適應(yīng)SEGY段調(diào)用的需要。既然SUBT有FAR屬性,則不論在SEGX段中還是SEGY段中,對(duì)SUBT的調(diào)用就都具有FAR屬性,這樣不會(huì)(bùhuì)發(fā)生什么錯(cuò)誤;反之,如果這里的SUBT使用了NEAR屬性,則在SEGY段內(nèi)對(duì)它的調(diào)用就要出錯(cuò)了。精品資料6.2.2子程序的調(diào)用和返回子程序的正確執(zhí)行是由子程序的正確調(diào)用和正確返回保證的。80x86的CALL和RET指令完成的就是調(diào)用和返回的功能。為保證其正確性,除PROC的屬性要正確選擇外,還應(yīng)該注意子程序運(yùn)行期間的堆棧狀態(tài)。由于執(zhí)行CALL時(shí)已使返回地址入棧,所以執(zhí)行RET時(shí)應(yīng)該使返回地址出棧,如果子程序中不能正確使用堆棧而造成執(zhí)行RET前SP并未指向(zhǐxiànɡ)進(jìn)入子程序時(shí)的返回地址,則必然會(huì)導(dǎo)致運(yùn)行出錯(cuò),因此子程序中對(duì)堆棧的使用應(yīng)該特別小心,以免發(fā)生錯(cuò)誤。精品資料6.2.3現(xiàn)場保護(hù)與現(xiàn)場恢復(fù)主程序和子程序通常是分別編制的,所以它們所使用的寄存器往往會(huì)發(fā)生沖突。如果主程序在調(diào)用子程序之前(zhīqián)的某個(gè)寄存器內(nèi)容在從子程序返回后還有用,而子程序又恰好使用了同一個(gè)寄存器,這就破壞了該寄存器的原有內(nèi)容,因而造成程序運(yùn)行錯(cuò)誤,這是不允許的。為避免這種錯(cuò)誤的發(fā)生,在一進(jìn)入子程序后,就應(yīng)該把子程序所需要使用的寄存器內(nèi)容保存在堆棧中,此過程稱作現(xiàn)場保護(hù)。在退出子程序前把寄存器內(nèi)容恢復(fù)原狀,此過程稱作現(xiàn)場恢復(fù)?,F(xiàn)場保護(hù)與現(xiàn)場恢復(fù)分別使用壓棧和彈出指令實(shí)現(xiàn)。精品資料【例6-3】保護(hù)現(xiàn)場(xiànchǎng)與恢復(fù)現(xiàn)場(xiànchǎng)示例。SUBT PROC PUSH AX;現(xiàn)場(xiànchǎng)保護(hù) PUSH BX PUSH CX PUSH DX<子程序體>精品資料 POP DX;現(xiàn)場(xiànchǎng)恢復(fù) POP CX POP BX POP AX RETSUBT ENDP精品資料在子程序設(shè)計(jì)時(shí),應(yīng)仔細(xì)考慮哪些寄存器必須保護(hù),哪些不必保護(hù)。一般說來,子程序中用到的寄存器是應(yīng)該保護(hù)的。但是,如果使用(shǐyòng)寄存器在主程序和子程序之間傳送參數(shù)的話,則這種寄存器就不一定需要保護(hù),特別是用來向主程序回送結(jié)果的寄存器,就更不應(yīng)該因保存和恢復(fù)寄存器而破壞了應(yīng)該向主程序傳送的信息。 從80286CPU開始可用的PUSHA/POPA指令和從80386CPU開始的高檔微機(jī)可用的PUSHAD/POPAD指令為子程序中保存和恢復(fù)寄存器內(nèi)容提供了有力的支持。精品資料6.3子程序的參數(shù)(cānshù)傳送主程序在調(diào)用子程序時(shí),經(jīng)常需要傳送一些參數(shù)給子程序,子程序運(yùn)行完后也經(jīng)常要回送一些信息給主程序。這種調(diào)用程序和被調(diào)用程序之間的信息傳送稱為主—子程序參數(shù)傳送(或稱變量傳送和過程(guòchéng)通信)。以下介紹參數(shù)傳送的常用方式。精品資料6.3.1通過寄存器傳送參數(shù)通過寄存器傳送是最常用的一種參數(shù)傳送方式,使用方便,但參數(shù)很多時(shí)不宜使用。【例6-4】十進(jìn)制數(shù)到十六進(jìn)制數(shù)轉(zhuǎn)換程序,要求從鍵盤取得一個(gè)十進(jìn)制數(shù),然后把該數(shù)以十六進(jìn)制形式在屏幕上顯示(xiǎnshì)出來。精品資料 這里采用子程序結(jié)構(gòu),用一個(gè)子程序DECIBIN實(shí)現(xiàn)從鍵盤取得十進(jìn)制數(shù)并把它轉(zhuǎn)換為二進(jìn)制數(shù);另一個(gè)子程序BINIHEX把此二進(jìn)制數(shù)以十六進(jìn)制數(shù)的形式在屏幕上顯示出來。為避免屏幕上的重疊,另外用CRLF子程序取得回車和換行效果。整個(gè)程序結(jié)構(gòu)如圖6.2所示。在這里,各個(gè)子程序之間用BX寄存器來傳送信息。在子程序DECIBIN中取得的輸入數(shù)據(jù)(shùjù)轉(zhuǎn)換為二進(jìn)制數(shù)后保存在BX寄存器中,而子程序BINIHEX需要把BX寄存器中的數(shù)用十六進(jìn)制形式顯示出來。也就是說,BX寄存器用來在子程序間傳遞要轉(zhuǎn)換的數(shù)。精品資料圖6.2十進(jìn)制數(shù)到十六進(jìn)制(shíliùjìnzhì)數(shù)轉(zhuǎn)換的程序結(jié)構(gòu)精品資料程序如下:DECIHEX SEGMENT ASSUME CS:DECIHEX MAIN PROC FARREPEAT: CALL DECIBIN;調(diào)用鍵入子程序 CALL CRLF ;調(diào)用回車換行子程序 CALL BINIHEX ;調(diào)用轉(zhuǎn)換子程序 CALL CRLF ;調(diào)用回車換行子程序 JMP REPEAT ;繼續(xù)(jìxù),鍵入Ctrl+Break返回DOS精品資料 RETMAIN ENDPDECIBIN PROC NEAR MOV BX,0 NEWCHAR: MOV AH,1;接受鍵入 INT 21H ;調(diào)用(diàoyòng)DOS SUB AL,30H ;ASCII字符轉(zhuǎn)換二進(jìn)制數(shù) JL EXIT ;數(shù)小于0退出精品資料 CMP AL,9 ;數(shù)大于9?JGEXIT ;是,退出CBW ;否,調(diào)整成16位 XCHG AX,BX MOV CX,10 MUL CX ;乘10XCHG AX,BX ADD BX,AX JMP NEWCHAR ;取下一個(gè)(yīɡè)字符精品資料EXIT: RET DECIBIN ENDP BINIHEX PROC NEAR MOV CH,4 ;字?jǐn)?shù)送CHROTATE: MOV CL,4 ;位數(shù)送CL ROL BX,CL;BX值循環(huán)(xúnhuán)左移 MOV AL,BL ;組裝ASCII字符 AND AL,0FH 精品資料ADD AL,30H CMP AL,3AH ;數(shù)大于9嗎? JL PRINTIT ;否,顯示 ADD AL,7H ;大于加7成為‘A’~‘F’PRINTIT: MOV DL,AL ;顯示字符(zìfú) MOV AH,2 INT 21H DEC CH ;是第四個(gè)字符(zìfú)? JNZ ROTATE ;不,繼續(xù) RET ;是,返回精品資料BINIHEX ENDPCRLF PROC NEAR MOV DL,0DH ;執(zhí)行(zhíxíng)回車 MOV AH,2 INT 21H MOV DL,0AH ;執(zhí)行(zhíxíng)換行 MOV AH,2 INT 21H RETCRLF ENDPDECIHEX ENDS END MAIN精品資料6.3.2直接參數(shù)傳遞若子程序(chéngxù)和調(diào)用程序(chéngxù)在同一源文件(同一程序(chéngxù)模塊)中,則子程序(chéngxù)可直接訪問模塊中的變量,進(jìn)行參數(shù)傳遞。【例6-5】主程序(chéngxù)MAIN和子程序(chéngxù)PROADD在同一源文件中,要求用子程序(chéngxù)PROADD累加數(shù)組中的所有元素,并把和(不考慮溢出的可能性)送到指定的存儲(chǔ)單元中去。在這里,子程序(chéngxù)PROADD直接訪問模塊的數(shù)據(jù)區(qū)。精品資料 程序(chéngxù)如下:DATA SEGMENT ARY DW 100 DUP(?) COUNT DW 100 SUM DW ?DATA ENDSCODE SEGMENT MAIN PROC FAR ASSUME CS:CODE,DS:DATA精品資料START: PUSH DS SUB AX,AX PUSH AX MOV AX,DATA MOV DS,AX CALL NEARPTRPROADD RETMAIN ENDP 精品資料PROADD PROC NEAR PUSH AX PUSH CX PUSH SI LEA SI,ARY MOV CX,COUNT XOR AX,AX 精品資料NEXT:ADD AX,[SI] ADD SI,2 LOOP NEXT MOV SUM,AX POP SI POP CX POP AX RET PROADD ENDP CODE ENDS END START 精品資料如果數(shù)據(jù)(shùjù)段中有如下兩個(gè)數(shù)組:DATA SEGMENT ARY DW 100DUP(?) COUNT DW 100 SUM DW ? NUM DW 100DUP(?) N DW 100 TOTAL DW ?DATA ENDS精品資料主程序兩次調(diào)用PROADD要求分別累加ARY和NUM數(shù)組的內(nèi)容。在這種情況下,如果還是采用例6-5中所述的參數(shù)傳送方式,那只有在調(diào)用PROADD累加NUM數(shù)組前,先把NUM數(shù)組及N的內(nèi)容全部傳送到ARY數(shù)組和COUNT單元去,PROADD運(yùn)行完后,再把SUM的結(jié)果送到TOTAL去。這顯然不是一種(yīzhǒnɡ)好辦法。為解決這一類問題,下面分別介紹“通過地址表傳送參數(shù)地址”和“通過堆棧傳送參數(shù)或參數(shù)地址”兩種參數(shù)傳送的方法。精品資料6.3.3通過地址表傳送參數(shù)地址這種方法是在主程序中建立一個(gè)地址表,把要傳送給子程序的參數(shù)都存放在地址表中,然后把地址表的首地址通過寄存器BX傳送到子程序中去。子程序通過地址表取得所需參數(shù),并把結(jié)果(jiēguǒ)存入指定的存儲(chǔ)單元中去。這樣做的結(jié)果(jiēguǒ),對(duì)于上述程序中要累加NUM數(shù)組的內(nèi)容時(shí),只需在程序中增加下述指令,再調(diào)用PROADD,就能在TOTAL中得到NUM的累加和。精品資料MOV TABLE,OFFSETNUM MOV TABLE+2,OFFSETN MOV TABLE+4,OFFSETTOTAL MOV BX,OFFSETTABLE CALL PROADD 此外,該程序采用.COM文件形式,代碼(dàimǎ)、數(shù)據(jù)和堆棧都設(shè)在一個(gè)段中。當(dāng)然,這并不說明這里必須使用.COM文件,只是給出另一種程序格式而已。精品資料例6-5采用通過地址(dìzhǐ)表傳送參數(shù)方法的程序?qū)崿F(xiàn),程序如下:PROG_SEG SEGMENT ORG 100H ASSUMECS:PROG_SEG,DS:PROG_SEG,SS:PROG_SEGMAIN PROC NEAR MOV AX,PROG_SEG MOV DS,AX精品資料MOV TABLE,OFFSETARY MOV TABLE+2,OFFSETCOUNT MOV TABLE+4,OFFSETSUM MOV BX,OFFSETTABLE CALL PROADD

MOV AX,4C00H INT 21HMAIN ENDP

精品資料PROADD PROC NEAR PUSH AX PUSH CX PUSH SI PUSH DI MOV SI,[BX] MOV DI,[BX+2] MOV CX,[DI] MOV DI,[BX+4] XOR AX,AX 精品資料NEXT: ADD AX,[SI] ADD SI,2 LOOP NEXT MOV [DI],AX POP DI POP SI POP CX POP AX RET 精品資料PROADD ENDP ARY DW 100DUP(?) COUNT DW 100 SUM DW ? TABLE DW 3 DUP(?)PROG_SEG ENDS END MAIN精品資料6.3.4通過堆棧傳送參數(shù)或參數(shù)地址 在主程序里把參數(shù)地址保存到堆棧中,在子程序里從堆棧中取出參數(shù)以達(dá)到傳送參數(shù)的目的。如本例中堆棧最滿時(shí)的狀態(tài)如圖6.3所示。必須注意,子程序結(jié)束時(shí)的RET指令應(yīng)使用帶常數(shù)(chángshù)的返回指令,以便返回主程序后,堆棧能恢復(fù)原始狀態(tài)不變。精品資料圖6.3例6-5用堆棧傳送參數(shù)地址(dìzhǐ)時(shí)堆棧最滿時(shí)的狀態(tài)精品資料例6-5采用通過堆棧傳送參數(shù)地址法的程序(chéngxù)實(shí)現(xiàn)。程序(chéngxù)如下:PARM_SEG SEGMENT ARY DW 100DUP(?)COUNT DW 100SUM DW ?PARM_SEG ENDSSTACK_SEG SEGMENT DW100DUP(?)TOS LABEL WORD精品資料STACK_SEG ENDSCODE1 SEGMENT MAIN PROC FAR ASSUMECS:CODE1,DS:PARM_SEG,SS:STACK_SEGSTART: MOV AX,STACK_SEG MOV SS,AX MOV SP,OFFSETTOS PUSH DS SUB AX,AX 精品資料PUSH AX MOV AX,PARM_SEG MOV DS,AX MOV BX,OFFSETARY PUSH BX MOV BX,OFFSETCOUNT PUSH BX MOV BX,OFFSETSUM PUSH BX CALL FARPTRPROADD RET精品資料MAIN ENDPCODE1 ENDSCODE2 SEGMENT ASSUME CS:CODE2PROADD PROC FAR PUSH BP PUSH AX PUSH CX PUSH SI PUSH DI MOV SI,[BP+0AH] 精品資料MOV DI,[BP+8] MOV CX,[DI] MOV DI,[BP+6] XOR AX,AX NEXT: ADD AX,[SI] ADD SI,2 LOOP NEXT MOV [DI],AX POP DI POP SI精品資料POP CX POP AX POP BP RET 6 PROADD ENDPCODE2 ENDS END START 精品資料6.3.5增強(qiáng)功能的子程序定義偽指令從MASM5.1版開始(kāishǐ)為用戶提供了增強(qiáng)功能的子程序定義偽指令。其格式為PROCNAMEPROC[ATTRIBUTESFIELD][USESREGISTERLIST][,PARAMETERFIELD]PROCNAMEENDP其中,屬性字段由DISTANCE、LANGUAGETYPE、VISIBILITY和PROLOGUE組成。每一項(xiàng)均為可選,各項(xiàng)之間用一空格或制表符分開。精品資料DISTANCE就用NEAR或FAR,與6.1.1小節(jié)中所述的類型屬性相同。MASM規(guī)定在簡化段定義中已指定內(nèi)存模型情況下,不必再在子程序定義中指定類型屬性。匯編程序?qū)⒆詣?dòng)(zìdòng)把TINY、SMALL、COMPACT和FLAT程序中的子程序指定為NEAR,而把MEDIUM、LARGE和HUGE程序中的子程序指定為FAR。如程序中未指定內(nèi)存模型,則應(yīng)由用戶自行指定類型屬性,如用戶未指定,則默認(rèn)值為NEAR。精品資料當(dāng)某子程序作為某種高級(jí)語言程序的子程序時(shí),LANGUAGETYPE說明調(diào)用它的高級(jí)語言的類型,如PASCAL,BASIC,F(xiàn)ORTRAN或C等。如在程序的MODEL中已說明了所用語言,則此時(shí)可以省略(shěnglüè)。必須注意,如果子程序偽指令中使用了參數(shù)字段,但在MODEL和LANGUAGETYPE中均未指定語言類型的話,則MASM將指示出錯(cuò)。為此,即使子程序并不需要由高級(jí)語言調(diào)用,也應(yīng)該指定一種語言類型。精品資料VISIBILITY說明該子程序的可見性,可用PRIVATE或PUBLIC。如用PRIVATE,則該子程序的可見性只能是當(dāng)前的源文件;如用PUBLIC,則允許其他模塊調(diào)用該子程序。該項(xiàng)的默認(rèn)是PUBLIC。 PROLOGUE是一個(gè)宏的名字,允許用戶用宏來控制(kòngzhì)與子程序的入口和出口有關(guān)的代碼。 USES字段允許用戶指定所需保存和恢復(fù)的寄存器表,MASM將在子程序入口自動(dòng)生成PUSH指令來保存這些寄存器,并在子程序出口的RET指令前自動(dòng)生成POP指令來恢復(fù)這些寄存器。精品資料參數(shù)字段允許用戶指定該子程序所用參數(shù)。其格式為 IDENTIFIER:TYPE[,INDENTIFIER:TYPE]其中,IDENTIFIER給出參數(shù)的符號(hào)名,TYPE給出參數(shù)的類型。參數(shù)之間用逗號(hào)隔開。如果參數(shù)太多,一行放不下的話,可以用逗號(hào)結(jié)束此行,在下一行繼續(xù)給出參數(shù)。MASM將自動(dòng)把這些參數(shù)轉(zhuǎn)換(zhuǎnhuàn)為[BP+4]、[BP+6]等形式。精品資料【例6-6】用增強(qiáng)(zēngqiáng)功能的子程序定義偽指令的程序?qū)崿F(xiàn)例6-5。 .MODELMEDIUM .DATAARYDW 100DUP(?)COUNT DW 100SUM DW ? .STACK 200H .CODE CODE1MAIN PROC精品資料START: MOV AX,@DATA MOV DS,AX MOV BX,OFFSETARY PUSH BX MOV BX,OFFSETCOUNT PUSH BX MOV BX,OFFSETSUM PUSH BX CALL PROADD精品資料MOV AX,4C00H INT 21HMAIN ENDP .CODE CODE2PROADD PROC PASCALUSESAXCXSIDI, PARA:WORD,PARC:WORD,PARS:WORD MOV SI,PARA MOV DI,PARC MOV CX,[DI] MOV DI,PARS XOR AX,AX 精品資料NEXT: ADD AX,[SI] ADD SI,2 LOOP NEXT MOV [DI],AX RET PROADD ENDP END START 精品資料增強(qiáng)功能(gōngnéng)的子程序定義偽指令除具有以上功能(gōngnéng)外,還可在子程序中定義局部變量。 MASM規(guī)定,在子程序內(nèi)可以用LOCAL為局部變量申請(qǐng)空間。其格式為 LOCALVARDEF[,VARDEF]其中,變量定義可用的格式為 LABEL LABEL:TYPELABEL[COUNT]:TYPE精品資料其中,未指定(zhǐdìng)類型者,MASM將使用WORD。TYPE可以指定(zhǐdìng)任意合法的類型說明,如BYTE,WORD,DWORD等。第三種格式為用戶申請(qǐng)數(shù)組提供了方便,如可指定(zhǐdìng)M[20]:SWORD,SWORD表示帶符號(hào)數(shù)的字類型,M為數(shù)組名,其中包括20個(gè)元素,用戶可以用0~19的下標(biāo)來訪問該數(shù)組。精品資料LOCAL語句必須緊跟在子程序定義偽指令之后,并在任何80x86指令或可以產(chǎn)生任何代碼的MASM語句之前(zhīqián)出現(xiàn)。它可以定義多個(gè)局部變量,如一行寫不下,可用逗號(hào)結(jié)束前一行,并在下一行繼續(xù)定義。MASM將為所定義的變量在堆棧中的BP負(fù)偏移區(qū)生成空間,并對(duì)每個(gè)局部變量名生成如[BP-2],[BP-4]等代碼。精品資料下面用示例來說明局部變量的使用方法。 【例6-7】編寫把以ASCII形式表示的十進(jìn)制數(shù)轉(zhuǎn)換為二進(jìn)制數(shù)的程序。 程序所用的算法很簡單:從最低位起,每個(gè)數(shù)位先將ASCII字符轉(zhuǎn)換為數(shù)字,然后乘以該位的權(quán)(乘法因子),累加后就成為(chéngwéi)所要求的二進(jìn)制數(shù)。例如,數(shù)'12345'的計(jì)算步驟是:精品資料十進(jìn)制 十六進(jìn)制 步驟(bùzhòu) 乘積 步驟(bùzhòu) 乘積 5×1 = 5 5×01H = 5H 4×10 = 40 4×0AH = 28H 3×100 = 300 3×64H = 12CH 2×1000 = 2000 2×3E8H = 7D0H 1×10000 = 10000 1×2710H = 2710H求和 = 12345 求和 = 3039H精品資料在程序中的數(shù)據(jù)區(qū)內(nèi),定義了兩個(gè)變量,ASCVAL存放著以ASCII形式表示的十進(jìn)制數(shù),要求把轉(zhuǎn)換成的二進(jìn)制數(shù)存放在BINVAL單元中。在主程序中,把這兩個(gè)變量的地址作為參數(shù)傳送(chuánsònɡ)給子程序CONVASCBIN。子程序的子程序定義偽指令中除定義參數(shù)域外,還定義了兩個(gè)局部變量:ASCLEN和MULFACT,分別用于存放子程序內(nèi)部所用的ASCII字符串長度和乘法因子?!袄?-7經(jīng)匯編后的CONVASCBIN子程序”中給出了經(jīng)MASM匯編后的CONVASCBIN子程序。精品資料可以看出匯編程序完成了原先要用戶自行編制的保存和恢復(fù)(huīfù)寄存器、參數(shù)區(qū)和局部變量區(qū)的建立及釋放等工作,為用戶提供了方便。圖6.4給出了在該程序運(yùn)行子程序中,堆棧最滿時(shí)的狀態(tài)。如前所述,BP指針的正偏移區(qū)為參數(shù)區(qū),而BP指針的負(fù)偏移區(qū)為局部變量區(qū)。局部變量區(qū)在子程序返回主程序前將自動(dòng)釋放。精品資料圖6.4運(yùn)行子程序堆棧(duīzhàn)最滿時(shí)的狀態(tài)精品資料程序(chéngxù)如下: .MODEL SMALL .386 .STACK 200H .DATAASCVAL DB '12345'BINVAL DW ? .CODEMAIN PROC精品資料START: MOV AX,@DATA MOV DS,AX LEA BX,ASCVAL PUSH BX LEA BX,BINVAL PUSH BX CALL CONVASCBIN MOV AX,4C00H INT 21HMAIN ENDP精品資料CONVASCBINPROCPASCALUSESAXBXCXSI,DI, PAR1:WORD,PAR2:WORDLOCAL ASCLEN:WORD,MULFACT:WORD MOV BX,10 MOV SI,PAR1 MOV DI,PAR2 SUB DI,SI MOV ASCLEN,DI MOV CX,ASCLEN 精品資料ADD SI,ASCLEN DEC SI MOV MULFACT,1 MOV DI,PAR2 MOV [DI],0 NEXT: MOV AL,[SI] AND AX,000FH MUL MULFACT ADD [DI],AX MOV AX,MULFACT 精品資料MUL BX MOV MULFACT,AX DEC SI LOOP NEXT RET CONVASCBINENDP END MAIN;CONVASCBIN子程序 PUSH BP MOV BP,SP精品資料ADD SP,0FCH PUSH AX PUSH BX PUSH CX PUSH SI PUSH DI MOV BX,0AH MOV SI,[BP+6] MOV DI,[BP+4] SUB DI,SI MOV [BP?2],DI精品資料MOV CX,[BP?2] ADD SI,[BP?2] DEC SI MOV WORDPTR[BP-4],000L MOV DI,[BP+4] MOV WORDPTR[DI],0NEXT: MOV AL,[SI] AND AX,000FH MUL WORDPTR[BP?4] ADD [DI],AX精品資料MOV AX,[BP?4] MUL BX MOV [BP?4],AX DEC SI LOOP NEXT POP DI POP SI POP CX POP BX POP AX MOV SP,BP POP BP RET 0004精品資料6.4子程序的嵌套與遞歸6.4.1子程序的嵌套我們已經(jīng)知道,一個(gè)子程序也可以(kěyǐ)作為調(diào)用程序去調(diào)用另一個(gè)子程序,這種情況就稱為子程序的嵌套。嵌套的層次不限,其層數(shù)稱為嵌套深度。圖6.5表示了嵌套深度為2時(shí)的子程序嵌套情況。精品資料嵌套子程序的設(shè)計(jì)并沒有特殊要求,除子程序的調(diào)用和返回應(yīng)正確使用CALL和RET指令外,還要注意寄存器的保存和恢復(fù),以避免各層次子程序之間因寄存器沖突而出錯(cuò)的情況發(fā)生。如果程序中使用了堆棧,例如,使用堆棧來傳送參數(shù)等,則對(duì)堆棧的操作要格外(géwài)小心,避免發(fā)生因堆棧使用中的問題而造成子程序不能正確返回的錯(cuò)誤。精品資料圖6.5子程序的嵌套精品資料在編制子程序時(shí),特別是在編制嵌套或遞歸子程序時(shí),堆棧的使用十分(shífēn)頻繁。在堆棧的使用中,應(yīng)該注意堆棧溢出的問題。堆棧區(qū)域是在堆棧定義時(shí)就確定了的,因而堆棧工作在子程序中有可能產(chǎn)生溢出。堆棧溢出有兩種情況可能發(fā)生:一種情況是,如堆棧已滿,但還想再存入信息,這種情況稱為堆棧上溢;另一種情況是,如堆棧已空,但還想再取出信息,這種情況稱為堆棧下溢。不論上溢或下溢,都是不允許的。精品資料因此在編制程序時(shí),如果可能發(fā)生堆棧溢出,則應(yīng)在程序中采取保護(hù)措施??梢越oSP規(guī)定上、下限,在進(jìn)?;虺鰲2僮髑跋茸鯯P和邊界值的比較,如溢出則作溢出處理,以避免破壞其他存儲(chǔ)區(qū)或使程序出錯(cuò)的情況發(fā)生。主程序調(diào)用(diàoyòng)子程序,子程序還可以調(diào)用(diàoyòng)其他子程序,這就是子程序的嵌套調(diào)用(diàoyòng),子程序可以多重嵌套調(diào)用(diàoyòng)。精品資料【例6-8】假設(shè)從BUF開始存放若干無符號(hào)字節(jié)數(shù)據(jù)(shùjù),找出其中的最小值并以十六進(jìn)制形式輸出。分析:本題用子程序SEARCH來求最小數(shù)字節(jié)數(shù)并輸出,其中再調(diào)用一個(gè)子程序用于輸出1位十六進(jìn)制數(shù),因此用到了子程序的嵌套。DATA SEGMENT BUF DB 13,25,23,100,423,78,90,134 ;定義數(shù)據(jù)(shùjù) CNT EQU$-BUF ;數(shù)據(jù)(shùjù)個(gè)數(shù)

精品資料DATA ENDSCODE SEGMENT ASSUMECS:CODE,DS:DATASTART: MOV AX,DATA MOV DS,AX MOV CX,CNT?1 ;比較(bǐjiào)次數(shù) MOV SI,OFFSETBUF ;首地址 CALL SEARCH MOV AH,4CH ;返回DOS INT 21H精品資料SEARCH PROC NEAR MOV BL,[SI] ;假定(jiǎdìng)第一個(gè)數(shù)為最小數(shù)SEAR1: INC SI ;指向下—個(gè)數(shù) CMP BL,[SI] ;比較 JBE SEAR2 ;BL中的數(shù)小,轉(zhuǎn)SEAR2 MOV BL,[SI] ;BL中的數(shù)大,把它替換掉SEAR2: DEC CX JNZ SEAR1 ;循環(huán)比較 MOV DL,BL ;最小值送DL精品資料MOV CL,4 SHR DL,CL ;分離出高4位 CALL DISP ;調(diào)用于程序顯示(xiǎnshì)輸出 MOV DL,BL ;最小值送DL AND DL,0FH ;分離出低4位 CALL DISP ;調(diào)用子程序顯示(xiǎnshì)輸出 RETSEARCH ENDPDISP PROC NEAR CMP DL,9 ;DL和9比較精品資料JBE DISP1 ;小于等于(děngyú)9加30H,否則加37H ADDDL,7DISP1:ADD DL,30H MOVAH,2 ;輸出 INT 21H RETDISP ENDPCODE ENDS END START精品資料6.4.2子程序的遞歸調(diào)用在子程序嵌套的情況下,如果一個(gè)子程序調(diào)用的子程序就是它自身,這就稱為遞歸調(diào)用,這樣的子程序稱為遞歸子程序。遞歸子程序?qū)?yīng)(duìyìng)于數(shù)學(xué)上對(duì)函數(shù)的遞歸定義,采用遞歸的方法往往能設(shè)計(jì)出效率較高的程序,完成相當(dāng)復(fù)雜的計(jì)算,所以它是很有用的。一般來說,能寫出遞歸公式的問題,都可以編寫出相應(yīng)的遞歸子程序。精品資料【例6-9】求自然數(shù)N(N≥)的階乘。分析:求自然數(shù)N(N≥1)的階乘的遞歸公式(gōngshì)如下:N!=1(N=1)N×(N-1)!(N>1)精品資料子程序有一個(gè)入口參數(shù),求階乘的數(shù)值,使用(shǐyòng)寄存器AX;一個(gè)出口參數(shù),求得的階乘值,使用(shǐyòng)寄存器DX。需要說明的是,本程序中,在求N!時(shí),(N-1)!的值不能大于一個(gè)字節(jié)的范圍,即本程序中N的值不能大于6,有興趣的讀者可以改寫這個(gè)程序,以便求得比較大的階乘值。程序如下:精品資料DATA SEGMENTBUF DW 5RESU DW ?DATA ENDSCODE SEGMENT ASSUME CS:CODE,DS:DATASTART: MOV AX,DATA MOV DS,AX MOV AX,BUF ;取出數(shù)值(shùzí)N送AX精品資料CALL FACT ;調(diào)用(diàoyòng)子程序 MOVRESU,DX ;存儲(chǔ)結(jié)果 MOV AH,4CH ;返回DOS INT 21HFACT PROC NEAR ;子程序定義開始 CMP AX,1 ;AX的值(N)和1比較 JA NEXT ;大于1,轉(zhuǎn)NEXT MOV DX,1 ;等于1,則其階乘值為1 RET ;返回精品資料NEXT: PUSH AX ;AX的值(N)壓棧 DEC AX ;AX的值減1 CALL FACT ;遞歸調(diào)用(diàoyòng)子程序求(N?1)!送DX POP AX ;彈出值(N)送AX MUL DL ;AX=AL*DL,即N!=N(N?1)! MOV DX,AX ;存入DX RET ;返回精品資料FACT ENDP ;子程序定義結(jié)束(jiéshù)CODE ENDS END START精品資料習(xí)題(xítí)66.1下面(xiàmian)的程序段有錯(cuò)嗎?若有,請(qǐng)指出錯(cuò)誤。CRAYPROCPUSH AX

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論