版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章子程序结构(jiégòu)6.1子程序的概念(gàiniàn)6.2子程序结构形式与操作6.3子程序的参数传送6.4子程序的嵌套与递归习题6精品资料6.1子程序的概念(gàiniàn)6.1.1子程序概念把可以多次调用、能够完成(wánchéng)特定操作功能的程序段编写成独立的程序模块,该程序模块称为子程序,又称为过程。调用这些子程序的程序称为主程序。在主程序中,如果调用到子程序,就需要把控制转移到子程序,这个过程称为转子。子程序执行完了,要把控制再返回到主程序,这个过程称为返主。主程序与子程序之间的关系如图6.1所示。精品资料图6.1主程序与子程序之间的关系(guānxì)精品资料子程序具有如下4个特性:(1)重复性。一个子程序只占用一段存储区域,但可以多次被调用(diàoyòng),避免了编程人员的重复劳动,又节省程序的存储空间。由于增加了调用(diàoyòng)、返回等指令,因此程序执行时间会长些。如果一个程序段只用到一次,就没有必要编写成子程序形式。精品资料(2)通用性。只能完成特定功能的子程序用处不大,例如,只能实现5个字节加法运算的多字节加法子程序和只能在定长字符串上查找某一固定字符的子程序都没有通用性,因而(yīnér)用处也就不大。要能够得到广泛应用的通用的多字节加法子程序,字节数应该是任意的,字符查找子程序、字符串的长度和查找的字符都应是任意的。精品资料(3)可浮动性。所谓可浮动性,就是说子程序可以存放在存储区的任何地址处。假如(jiǎrú)子程序只能存放在固定的地址处,则在编写主程序时要特别注意存储单元的分配,不要使主程序占用了子程序的存储单元而破坏掉子程序,这样就会给编程人员带来很大麻烦,而且在装配主程序和子程序时往往造成存储空间的冲突或浪费。精品资料(4)可递归和可重入性。如果子程序能够调用其本身,则称其可递归调用。如果子程序可被中断,在中断处理中又被中断服务程序调用,并且能为中断服务程序和已中断的子程序两者都提供正确的结果,那么称该子程序是可重入的。为使子程序具有可递归和可重入性,应当利用堆栈和寄存器作为(zuòwéi)中间结果的暂存器,而不能用固定的存储单元作暂存器。精品资料6.2子程序结构形式(xíngshì)与操作本小节只给出MASM提供的基本的子程序定义伪操作,从宏汇编编译程序MASM5.1版本开始为用户提供了功能更强的子程序定义伪操作,这将在以后各有关(yǒuguān)章节加以说明。
精品资料子程序定义伪操作用在子程序的前后,使整个子程序形成(xíngchéng)清晰的、具有特定功能的代码块。其格式为PROCEDURENAMEPROCATTRIBUTE
PROCEDURENAMEENDP精品资料其中,子程序名为标识符,它又是子程序入口的符号地址。它的写法与标号(biāohào)的写法相同。属性(Attribute)是指子程序的类型属性,可以是NEAR或FAR。如前所述,CALL和RET指令都有NEAR和FAR的属性。段内调用使用NEAR属性,但可以隐含;段间调用使用FAR属性。为了使用户的工作更方便,80x86的汇编程序用PROC伪操作的类型属性来确定CALL和RET指令的属性。也就是说,如果所定义的子程序是FAR属性的,那么对它的调用和返回一定都是FAR属性的;精品资料如果(rúguǒ)所定义的子程序是NEAR属性的,那么对它的调用和返回也一定是NEAR属性的。这样,用户只需在定义子程序时考虑它的属性,而CALL和RET的属性可以由汇编程序来确定。子程序属性的确定原则很简单,即:①如调用程序和子程序在同一个代码段中,则使用NEAR属性;②如调用程序和子程序不在同一个代码段中,则使用FAR属性。
精品资料【例6-1】调用程序(chéngxù)和子程序(chéngxù)在同一代码段中。MAIN PROC FAR ;主程序(chéngxù) CALL SUBR1
RETMAINENDPSUBR1 PROC NEAR;子程序(chéngxù)(NEAR可省略)
RETSUBR1 ENDP精品资料由于调用程序MAIN和子程序SUBR1在同一代码段中,所以SUBR1定义为NEAR属性。这样,MAIN中对SUBR1的调用和SUBR1中的RET就都是NEAR属性的。但是一般说来,主子程序MAIN应定义为FAR属性,这是由于把程序的主子程序看作(kànzuò)DOS调用的一个子程序,因而DOS对MAIN的调用以及MAIN中的RET就是FRA属性的。当然,CALL和RET的属性是汇编程序确定的,用户只需正确选择PROC的属性就可以了。精品资料例6-1的情况也可以写成如下(rúxià)的程序:MAIN PROC FAR
CALL SUBR1 RETSUBR1 PROC NEAR
RETSUBR1 ENDPMAIN ENDP精品资料【例6-2】调用(diàoyòng)程序和子程序不在同一个代码段内。SEGX SEGMENT SUBT PROC FARRETSUBT ENDP CALL SUBTSEGX ENDSSEGY SEGMENT CALL SUBT SEGY ENDS精品资料 SUBT是一个子程序,它在两处被调用,一处是与SEGX同在段内,另一处是在SEGY段内。因此,SUBT必须具有FAR属性以适应SEGY段调用的需要。既然SUBT有FAR属性,则不论在SEGX段中还是SEGY段中,对SUBT的调用就都具有FAR属性,这样不会(bùhuì)发生什么错误;反之,如果这里的SUBT使用了NEAR属性,则在SEGY段内对它的调用就要出错了。精品资料6.2.2子程序的调用和返回子程序的正确执行是由子程序的正确调用和正确返回保证的。80x86的CALL和RET指令完成的就是调用和返回的功能。为保证其正确性,除PROC的属性要正确选择外,还应该注意子程序运行期间的堆栈状态。由于执行CALL时已使返回地址入栈,所以执行RET时应该使返回地址出栈,如果子程序中不能正确使用堆栈而造成执行RET前SP并未指向(zhǐxiànɡ)进入子程序时的返回地址,则必然会导致运行出错,因此子程序中对堆栈的使用应该特别小心,以免发生错误。精品资料6.2.3现场保护与现场恢复主程序和子程序通常是分别编制的,所以它们所使用的寄存器往往会发生冲突。如果主程序在调用子程序之前(zhīqián)的某个寄存器内容在从子程序返回后还有用,而子程序又恰好使用了同一个寄存器,这就破坏了该寄存器的原有内容,因而造成程序运行错误,这是不允许的。为避免这种错误的发生,在一进入子程序后,就应该把子程序所需要使用的寄存器内容保存在堆栈中,此过程称作现场保护。在退出子程序前把寄存器内容恢复原状,此过程称作现场恢复。现场保护与现场恢复分别使用压栈和弹出指令实现。精品资料【例6-3】保护现场(xiànchǎng)与恢复现场(xiànchǎng)示例。SUBT PROC PUSH AX;现场(xiànchǎng)保护 PUSH BX PUSH CX PUSH DX<子程序体>精品资料 POP DX;现场(xiànchǎng)恢复 POP CX POP BX POP AX RETSUBT ENDP精品资料在子程序设计时,应仔细考虑哪些寄存器必须保护,哪些不必保护。一般说来,子程序中用到的寄存器是应该保护的。但是,如果使用(shǐyòng)寄存器在主程序和子程序之间传送参数的话,则这种寄存器就不一定需要保护,特别是用来向主程序回送结果的寄存器,就更不应该因保存和恢复寄存器而破坏了应该向主程序传送的信息。 从80286CPU开始可用的PUSHA/POPA指令和从80386CPU开始的高档微机可用的PUSHAD/POPAD指令为子程序中保存和恢复寄存器内容提供了有力的支持。精品资料6.3子程序的参数(cānshù)传送主程序在调用子程序时,经常需要传送一些参数给子程序,子程序运行完后也经常要回送一些信息给主程序。这种调用程序和被调用程序之间的信息传送称为主—子程序参数传送(或称变量传送和过程(guòchéng)通信)。以下介绍参数传送的常用方式。精品资料6.3.1通过寄存器传送参数通过寄存器传送是最常用的一种参数传送方式,使用方便,但参数很多时不宜使用。【例6-4】十进制数到十六进制数转换程序,要求从键盘取得一个十进制数,然后把该数以十六进制形式在屏幕上显示(xiǎnshì)出来。精品资料 这里采用子程序结构,用一个子程序DECIBIN实现从键盘取得十进制数并把它转换为二进制数;另一个子程序BINIHEX把此二进制数以十六进制数的形式在屏幕上显示出来。为避免屏幕上的重叠,另外用CRLF子程序取得回车和换行效果。整个程序结构如图6.2所示。在这里,各个子程序之间用BX寄存器来传送信息。在子程序DECIBIN中取得的输入数据(shùjù)转换为二进制数后保存在BX寄存器中,而子程序BINIHEX需要把BX寄存器中的数用十六进制形式显示出来。也就是说,BX寄存器用来在子程序间传递要转换的数。精品资料图6.2十进制数到十六进制(shíliùjìnzhì)数转换的程序结构精品资料程序如下:DECIHEX SEGMENT ASSUME CS:DECIHEX MAIN PROC FARREPEAT: CALL DECIBIN;调用键入子程序 CALL CRLF ;调用回车换行子程序 CALL BINIHEX ;调用转换子程序 CALL CRLF ;调用回车换行子程序 JMP REPEAT ;继续(jìxù),键入Ctrl+Break返回DOS精品资料 RETMAIN ENDPDECIBIN PROC NEAR MOV BX,0 NEWCHAR: MOV AH,1;接受键入 INT 21H ;调用(diàoyòng)DOS SUB AL,30H ;ASCII字符转换二进制数 JL EXIT ;数小于0退出精品资料 CMP AL,9 ;数大于9?JGEXIT ;是,退出CBW ;否,调整成16位 XCHG AX,BX MOV CX,10 MUL CX ;乘10XCHG AX,BX ADD BX,AX JMP NEWCHAR ;取下一个(yīɡè)字符精品资料EXIT: RET DECIBIN ENDP BINIHEX PROC NEAR MOV CH,4 ;字数送CHROTATE: MOV CL,4 ;位数送CL ROL BX,CL;BX值循环(xúnhuán)左移 MOV AL,BL ;组装ASCII字符 AND AL,0FH 精品资料ADD AL,30H CMP AL,3AH ;数大于9吗? JL PRINTIT ;否,显示 ADD AL,7H ;大于加7成为‘A’~‘F’PRINTIT: MOV DL,AL ;显示字符(zìfú) MOV AH,2 INT 21H DEC CH ;是第四个字符(zìfú)? JNZ ROTATE ;不,继续 RET ;是,返回精品资料BINIHEX ENDPCRLF PROC NEAR MOV DL,0DH ;执行(zhíxíng)回车 MOV AH,2 INT 21H MOV DL,0AH ;执行(zhíxíng)换行 MOV AH,2 INT 21H RETCRLF ENDPDECIHEX ENDS END MAIN精品资料6.3.2直接参数传递若子程序(chéngxù)和调用程序(chéngxù)在同一源文件(同一程序(chéngxù)模块)中,则子程序(chéngxù)可直接访问模块中的变量,进行参数传递。【例6-5】主程序(chéngxù)MAIN和子程序(chéngxù)PROADD在同一源文件中,要求用子程序(chéngxù)PROADD累加数组中的所有元素,并把和(不考虑溢出的可能性)送到指定的存储单元中去。在这里,子程序(chéngxù)PROADD直接访问模块的数据区。精品资料 程序(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ù)段中有如下两个数组:DATA SEGMENT ARY DW 100DUP(?) COUNT DW 100 SUM DW ? NUM DW 100DUP(?) N DW 100 TOTAL DW ?DATA ENDS精品资料主程序两次调用PROADD要求分别累加ARY和NUM数组的内容。在这种情况下,如果还是采用例6-5中所述的参数传送方式,那只有在调用PROADD累加NUM数组前,先把NUM数组及N的内容全部传送到ARY数组和COUNT单元去,PROADD运行完后,再把SUM的结果送到TOTAL去。这显然不是一种(yīzhǒnɡ)好办法。为解决这一类问题,下面分别介绍“通过地址表传送参数地址”和“通过堆栈传送参数或参数地址”两种参数传送的方法。精品资料6.3.3通过地址表传送参数地址这种方法是在主程序中建立一个地址表,把要传送给子程序的参数都存放在地址表中,然后把地址表的首地址通过寄存器BX传送到子程序中去。子程序通过地址表取得所需参数,并把结果(jiēguǒ)存入指定的存储单元中去。这样做的结果(jiēguǒ),对于上述程序中要累加NUM数组的内容时,只需在程序中增加下述指令,再调用PROADD,就能在TOTAL中得到NUM的累加和。精品资料MOV TABLE,OFFSETNUM MOV TABLE+2,OFFSETN MOV TABLE+4,OFFSETTOTAL MOV BX,OFFSETTABLE CALL PROADD 此外,该程序采用.COM文件形式,代码(dàimǎ)、数据和堆栈都设在一个段中。当然,这并不说明这里必须使用.COM文件,只是给出另一种程序格式而已。精品资料例6-5采用通过地址(dìzhǐ)表传送参数方法的程序实现,程序如下: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通过堆栈传送参数或参数地址 在主程序里把参数地址保存到堆栈中,在子程序里从堆栈中取出参数以达到传送参数的目的。如本例中堆栈最满时的状态如图6.3所示。必须注意,子程序结束时的RET指令应使用带常数(chángshù)的返回指令,以便返回主程序后,堆栈能恢复原始状态不变。精品资料图6.3例6-5用堆栈传送参数地址(dìzhǐ)时堆栈最满时的状态精品资料例6-5采用通过堆栈传送参数地址法的程序(chéngxù)实现。程序(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增强功能的子程序定义伪指令从MASM5.1版开始(kāishǐ)为用户提供了增强功能的子程序定义伪指令。其格式为PROCNAMEPROC[ATTRIBUTESFIELD][USESREGISTERLIST][,PARAMETERFIELD]PROCNAMEENDP其中,属性字段由DISTANCE、LANGUAGETYPE、VISIBILITY和PROLOGUE组成。每一项均为可选,各项之间用一空格或制表符分开。精品资料DISTANCE就用NEAR或FAR,与6.1.1小节中所述的类型属性相同。MASM规定在简化段定义中已指定内存模型情况下,不必再在子程序定义中指定类型属性。汇编程序将自动(zìdòng)把TINY、SMALL、COMPACT和FLAT程序中的子程序指定为NEAR,而把MEDIUM、LARGE和HUGE程序中的子程序指定为FAR。如程序中未指定内存模型,则应由用户自行指定类型属性,如用户未指定,则默认值为NEAR。精品资料当某子程序作为某种高级语言程序的子程序时,LANGUAGETYPE说明调用它的高级语言的类型,如PASCAL,BASIC,FORTRAN或C等。如在程序的MODEL中已说明了所用语言,则此时可以省略(shěnglüè)。必须注意,如果子程序伪指令中使用了参数字段,但在MODEL和LANGUAGETYPE中均未指定语言类型的话,则MASM将指示出错。为此,即使子程序并不需要由高级语言调用,也应该指定一种语言类型。精品资料VISIBILITY说明该子程序的可见性,可用PRIVATE或PUBLIC。如用PRIVATE,则该子程序的可见性只能是当前的源文件;如用PUBLIC,则允许其他模块调用该子程序。该项的默认是PUBLIC。 PROLOGUE是一个宏的名字,允许用户用宏来控制(kòngzhì)与子程序的入口和出口有关的代码。 USES字段允许用户指定所需保存和恢复的寄存器表,MASM将在子程序入口自动生成PUSH指令来保存这些寄存器,并在子程序出口的RET指令前自动生成POP指令来恢复这些寄存器。精品资料参数字段允许用户指定该子程序所用参数。其格式为 IDENTIFIER:TYPE[,INDENTIFIER:TYPE]其中,IDENTIFIER给出参数的符号名,TYPE给出参数的类型。参数之间用逗号隔开。如果参数太多,一行放不下的话,可以用逗号结束此行,在下一行继续给出参数。MASM将自动把这些参数转换(zhuǎnhuàn)为[BP+4]、[BP+6]等形式。精品资料【例6-6】用增强(zēngqiáng)功能的子程序定义伪指令的程序实现例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 精品资料增强功能(gōngnéng)的子程序定义伪指令除具有以上功能(gōngnéng)外,还可在子程序中定义局部变量。 MASM规定,在子程序内可以用LOCAL为局部变量申请空间。其格式为 LOCALVARDEF[,VARDEF]其中,变量定义可用的格式为 LABEL LABEL:TYPELABEL[COUNT]:TYPE精品资料其中,未指定(zhǐdìng)类型者,MASM将使用WORD。TYPE可以指定(zhǐdìng)任意合法的类型说明,如BYTE,WORD,DWORD等。第三种格式为用户申请数组提供了方便,如可指定(zhǐdìng)M[20]:SWORD,SWORD表示带符号数的字类型,M为数组名,其中包括20个元素,用户可以用0~19的下标来访问该数组。精品资料LOCAL语句必须紧跟在子程序定义伪指令之后,并在任何80x86指令或可以产生任何代码的MASM语句之前(zhīqián)出现。它可以定义多个局部变量,如一行写不下,可用逗号结束前一行,并在下一行继续定义。MASM将为所定义的变量在堆栈中的BP负偏移区生成空间,并对每个局部变量名生成如[BP-2],[BP-4]等代码。精品资料下面用示例来说明局部变量的使用方法。 【例6-7】编写把以ASCII形式表示的十进制数转换为二进制数的程序。 程序所用的算法很简单:从最低位起,每个数位先将ASCII字符转换为数字,然后乘以该位的权(乘法因子),累加后就成为(chéngwéi)所要求的二进制数。例如,数'12345'的计算步骤是:精品资料十进制 十六进制 步骤(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精品资料在程序中的数据区内,定义了两个变量,ASCVAL存放着以ASCII形式表示的十进制数,要求把转换成的二进制数存放在BINVAL单元中。在主程序中,把这两个变量的地址作为参数传送(chuánsònɡ)给子程序CONVASCBIN。子程序的子程序定义伪指令中除定义参数域外,还定义了两个局部变量:ASCLEN和MULFACT,分别用于存放子程序内部所用的ASCII字符串长度和乘法因子。“例6-7经汇编后的CONVASCBIN子程序”中给出了经MASM汇编后的CONVASCBIN子程序。精品资料可以看出汇编程序完成了原先要用户自行编制的保存和恢复(huīfù)寄存器、参数区和局部变量区的建立及释放等工作,为用户提供了方便。图6.4给出了在该程序运行子程序中,堆栈最满时的状态。如前所述,BP指针的正偏移区为参数区,而BP指针的负偏移区为局部变量区。局部变量区在子程序返回主程序前将自动释放。精品资料图6.4运行子程序堆栈(duīzhàn)最满时的状态精品资料程序(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子程序的嵌套我们已经知道,一个子程序也可以(kěyǐ)作为调用程序去调用另一个子程序,这种情况就称为子程序的嵌套。嵌套的层次不限,其层数称为嵌套深度。图6.5表示了嵌套深度为2时的子程序嵌套情况。精品资料嵌套子程序的设计并没有特殊要求,除子程序的调用和返回应正确使用CALL和RET指令外,还要注意寄存器的保存和恢复,以避免各层次子程序之间因寄存器冲突而出错的情况发生。如果程序中使用了堆栈,例如,使用堆栈来传送参数等,则对堆栈的操作要格外(géwài)小心,避免发生因堆栈使用中的问题而造成子程序不能正确返回的错误。精品资料图6.5子程序的嵌套精品资料在编制子程序时,特别是在编制嵌套或递归子程序时,堆栈的使用十分(shífēn)频繁。在堆栈的使用中,应该注意堆栈溢出的问题。堆栈区域是在堆栈定义时就确定了的,因而堆栈工作在子程序中有可能产生溢出。堆栈溢出有两种情况可能发生:一种情况是,如堆栈已满,但还想再存入信息,这种情况称为堆栈上溢;另一种情况是,如堆栈已空,但还想再取出信息,这种情况称为堆栈下溢。不论上溢或下溢,都是不允许的。精品资料因此在编制程序时,如果可能发生堆栈溢出,则应在程序中采取保护措施。可以给SP规定上、下限,在进栈或出栈操作前先做SP和边界值的比较,如溢出则作溢出处理,以避免破坏其他存储区或使程序出错的情况发生。主程序调用(diàoyòng)子程序,子程序还可以调用(diàoyòng)其他子程序,这就是子程序的嵌套调用(diàoyòng),子程序可以多重嵌套调用(diàoyòng)。精品资料【例6-8】假设从BUF开始存放若干无符号字节数据(shùjù),找出其中的最小值并以十六进制形式输出。分析:本题用子程序SEARCH来求最小数字节数并输出,其中再调用一个子程序用于输出1位十六进制数,因此用到了子程序的嵌套。DATA SEGMENT BUF DB 13,25,23,100,423,78,90,134 ;定义数据(shùjù) CNT EQU$-BUF ;数据(shùjù)个数
精品资料DATA ENDSCODE SEGMENT ASSUMECS:CODE,DS:DATASTART: MOV AX,DATA MOV DS,AX MOV CX,CNT?1 ;比较(bǐjiào)次数 MOV SI,OFFSETBUF ;首地址 CALL SEARCH MOV AH,4CH ;返回DOS INT 21H精品资料SEARCH PROC NEAR MOV BL,[SI] ;假定(jiǎdìng)第一个数为最小数SEAR1: INC SI ;指向下—个数 CMP BL,[SI] ;比较 JBE SEAR2 ;BL中的数小,转SEAR2 MOV BL,[SI] ;BL中的数大,把它替换掉SEAR2: DEC CX JNZ SEAR1 ;循环比较 MOV DL,BL ;最小值送DL精品资料MOV CL,4 SHR DL,CL ;分离出高4位 CALL DISP ;调用于程序显示(xiǎnshì)输出 MOV DL,BL ;最小值送DL AND DL,0FH ;分离出低4位 CALL DISP ;调用子程序显示(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子程序的递归调用在子程序嵌套的情况下,如果一个子程序调用的子程序就是它自身,这就称为递归调用,这样的子程序称为递归子程序。递归子程序对应(duìyìng)于数学上对函数的递归定义,采用递归的方法往往能设计出效率较高的程序,完成相当复杂的计算,所以它是很有用的。一般来说,能写出递归公式的问题,都可以编写出相应的递归子程序。精品资料【例6-9】求自然数N(N≥)的阶乘。分析:求自然数N(N≥1)的阶乘的递归公式(gōngshì)如下:N!=1(N=1)N×(N-1)!(N>1)精品资料子程序有一个入口参数,求阶乘的数值,使用(shǐyòng)寄存器AX;一个出口参数,求得的阶乘值,使用(shǐyòng)寄存器DX。需要说明的是,本程序中,在求N!时,(N-1)!的值不能大于一个字节的范围,即本程序中N的值不能大于6,有兴趣的读者可以改写这个程序,以便求得比较大的阶乘值。程序如下:精品资料DATA SEGMENTBUF DW 5RESU DW ?DATA ENDSCODE SEGMENT ASSUME CS:CODE,DS:DATASTART: MOV AX,DATA MOV DS,AX MOV AX,BUF ;取出数值(shùzí)N送AX精品资料CALL FACT ;调用(diàoyòng)子程序 MOVRESU,DX ;存储结果 MOV AH,4CH ;返回DOS INT 21HFACT PROC NEAR ;子程序定义开始 CMP AX,1 ;AX的值(N)和1比较 JA NEXT ;大于1,转NEXT MOV DX,1 ;等于1,则其阶乘值为1 RET ;返回精品资料NEXT: PUSH AX ;AX的值(N)压栈 DEC AX ;AX的值减1 CALL FACT ;递归调用(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éshù)CODE ENDS END START精品资料习题(xítí)66.1下面(xiàmian)的程序段有错吗?若有,请指出错误。CRAYPROCPUSH AX
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 销售话术与沟通技巧训练培训
- 2024年设备监理师考试题库附答案【轻巧夺冠】
- 跨学科合作教学的实践探索计划
- 旅游行业前台员工工作心得
- 钢铁冶炼工艺创新
- 2024年设备监理师考试题库及答案(必刷)
- 2024年美术教案经典
- 分包采购合作协议(2篇)
- 2024年联通智家工程师(初级)认证理论备考试题库(附答案)
- 通关03 世界史选择题专练(原卷版)
- 中央2025年全国人大机关直属事业单位招聘18人笔试历年典型考点(频考版试卷)附带答案详解
- 2024年度美团平台商家入驻服务框架协议
- 2024至2030年四氯苯醌项目投资价值分析报告
- 北京市海淀区2024-2025学年七年级上学期期中考试英语试卷(含答案)
- 中资企业出海报告:潮涌浪阔四海扬帆
- 房地产销售主管岗位招聘笔试题及解答(某大型央企)2024年
- 岩溶区水文地质参数研究-洞察分析
- 2024-2025学年人教版八年级上册地理期末测试卷(二)(含答案)
- 80、沈阳桃仙机场二平滑工程冬期施工方案
- 《STM32Cube嵌入式系统应用》HAL库版本习题及答案
- 一年级数学练习题-20以内加减法口算题(4000道)直接打印版
评论
0/150
提交评论