《第五章 子程序.ppt》由会员分享,可在线阅读,更多相关《第五章 子程序.ppt(156页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、新新新新 编编编编汇编语言程序设计汇编语言程序设计汇编语言程序设计汇编语言程序设计1 1第五章第五章 子程序子程序 5.1子程序子程序5.2参数的传递参数的传递5.3嵌套和递归子程序嵌套和递归子程序5.4多模块程序设计多模块程序设计5.5汇编语言与汇编语言与C语言混合编程语言混合编程5.6DOS和和BIOS调用调用习题五习题五2 2子程序(子程序(Subroutine):把程序需要完成的任务分解为若干个把程序需要完成的任务分解为若干个“子任务子任务”,每个,每个“子任务子任务”由一段相对独立的程序完成,称为由一段相对独立的程序完成,称为“子程序子程序”。调用子程序的程序称为调用子程序的程序称为
2、“主程序主程序”或者或者“主调程序主调程序”。子程序也称为子程序也称为“过程过程”(Procedure),),在高级语言在高级语言里还称作里还称作“函数函数”(Function)。)。3 3子程序结构程序的优点子程序结构程序的优点:1.1.程序结构清晰,提高了程序的可阅读性和可维护性。程序结构清晰,提高了程序的可阅读性和可维护性。2.2.每个子程序可以独立地进行调试,由于程序规模较每个子程序可以独立地进行调试,由于程序规模较小,降低了调试难度。小,降低了调试难度。3.3.每个子程序就是一个具有特定功能的独立的程序,每个子程序就是一个具有特定功能的独立的程序,提高程序的提高程序的“可重用性可重用
3、性”,提高了软件开发效率。,提高了软件开发效率。4 4图图5-1子程序的调用和返回子程序的调用和返回5 5近程子程序近程子程序:只能被同一个代码段里的程序调用的子程序。只能被同一个代码段里的程序调用的子程序。由于主程序和子程序处于同一个代码段,调用和由于主程序和子程序处于同一个代码段,调用和返回时只需要改变返回时只需要改变IP寄存器的值,寄存器的值,CS寄存器的值寄存器的值保持不变。保持不变。近程子程序的入口地址用近程子程序的入口地址用16位段内偏移地址表示。位段内偏移地址表示。6 6远程子程序远程子程序:能够被不同代码段的程序调用,也能被同一代能够被不同代码段的程序调用,也能被同一代码段的程
4、序调用的子程序。码段的程序调用的子程序。调用这样的子程序时,需要同时改变调用这样的子程序时,需要同时改变CS和和IP寄寄存器的值,返回时,需要从堆栈里弹出存器的值,返回时,需要从堆栈里弹出32位的位的返回地址送入返回地址送入IP,CS寄存器。寄存器。远程子程序的入口地址用远程子程序的入口地址用16位段基址和位段基址和16位段位段内偏移地址表示。内偏移地址表示。子程序的类型在定义时说明子程序的类型在定义时说明7 75.15.1 子程序子程序 5.1.1 5.1.1 CALLCALL和和RETRET指令指令 5.1.2 5.1.2 子程序的定义子程序的定义 5.1.3 5.1.3 子程序文件子程序
5、文件 5.1.4 5.1.4 子程序应用子程序应用 8 8CALL(Call,调用)指令调用)指令段内直接调用段内直接调用格式:格式:CALL子程序名子程序名操作:操作:SPSP-2,SS:SPIP(保存保存16位返回地址)位返回地址)IP子程序入口的偏移地址子程序入口的偏移地址例如:例如:CALLPROC1段内间接调用段内间接调用格式:格式:CALLREG16/MEM16操作:操作:SPSP-2,SS:SPIP(保护保护16位返回地址)位返回地址)IPREG16/MEM165.1.1 5.1.1 CALL和和RET指令指令 9 9例如:调用名为例如:调用名为“PROC1”的近程子程序。的近程
6、子程序。(1)CALLPROC1(2)LEACX,PROC1CALLCX(3)ADDR_PROC1DWPROC1;子程序偏移地址放入存储器字变量子程序偏移地址放入存储器字变量CALL ADDR_PROC1;调用近程子程序调用近程子程序PROC1(4)LEABX,ADDR_PROC1CALL WORDPTRBX;调用近程子程序调用近程子程序PROC11010段间直接调用段间直接调用格式:格式:CALLFARPTR子程序名子程序名操作:操作:SPSP-2,SS:SPCSSPSP-2,SS:SPIP(保存保存32位返回地址,偏移地址保存在较小地址处)位返回地址,偏移地址保存在较小地址处)IP子程序入
7、口的偏移地址,子程序入口的偏移地址,CS子程序入口的段基址子程序入口的段基址CALLFARPTRPROC2例如:例如:1111段间间接调用段间间接调用格式:格式:CALL MEM32操作:操作:SPSP-2,SS:SPCSSPSP-2,SS:SPIPIPMEM32,CSMEM32+2例如:例如:ADD_PROC2DDPROC2;子程序入口地址放入存储器双字变量子程序入口地址放入存储器双字变量CALL ADD_PROC2;调用远程子程序调用远程子程序PROC21212无参数段内返回无参数段内返回格式:格式:RET操作:操作:IPSS:SP,SPSP+2有参数段内返回有参数段内返回格式:格式:RE
8、TD16操作:操作:IPSS:SP,SPSP+2SPSP+D16RET(Return,返回)指令返回)指令1313无参数段间返回无参数段间返回格式:格式:RET操作:操作:IPSS:SP,SPSP+2CSSS:SP,SPSP+2有参数段间返回有参数段间返回 格式:格式:RETD16操作:操作:IPSS:SP,SPSP+2CSSS:SP,SPSP+2SPSP+D1614145.1.2 5.1.2 子程序的定义子程序的定义 子程序名子程序名PROCNEAR/FAR子程序体子程序体子程序名子程序名ENDP1515 说明说明:子子程程序序名名应应为为合合法法的的标标识识符符,子子程程序序名名不不能能与
9、与同同一一个个源程序中的标号、变量名、其它子程序名相同。源程序中的标号、变量名、其它子程序名相同。方方括括号号中中的的内内容容是是子子程程序序的的远远近近属属性性选选项项,二二者者可可选选其一,如果缺省,默认为其一,如果缺省,默认为NEAR。用用NEAR说说明明的的子子程程序序是是“近近程程子子程程序序”,它它只只能能被被与它同一代码段的程序调用。与它同一代码段的程序调用。用用FAR说说明明的的子子程程序序是是“远远程程子子程程序序”,它它不不仅仅能能被被与与它它同同一一代代码码段段的的程程序序调调用用,也也能能被被其其它它代代码码段段的的程程序调用。序调用。子程序的定义要写在代码段内子程序的
10、定义要写在代码段内1616ZEROBYTESPROC;定义一个子程序定义一个子程序XOR AX,AX;AX清零清零MOV CX,128;循环次数送循环次数送CXZEROLOOP:MOV BX,AX;将将一一个个字字存存储储单单元清零元清零ADDBX,2;修改地址修改地址LOOPZEROLOOP;循环控制循环控制RET;返回主程序返回主程序ZEROBYTESENDP;子程序结束子程序结束思考题思考题:该子程序完成了什么功能?调用该子程序时,:该子程序完成了什么功能?调用该子程序时,应该先做什么准备工作?应该先做什么准备工作?1717ZEROBYTES:XORAX,AX;AX寄存器清零寄存器清零M
11、OV CX,128;计数器计数器CX置初值置初值ZEROLOOP:MOVBX,AX;一个字单元清零一个字单元清零ADDBX,2;修改地址指针,指向下一个字修改地址指针,指向下一个字LOOPZEROLOOP;循环控制循环控制RET;结束程序运行,返回主程序结束程序运行,返回主程序子程序也可以简单地写成下面的形式:子程序也可以简单地写成下面的形式:缺点:边界不容易清晰地区分;只能定义缺点:边界不容易清晰地区分;只能定义“近程子程序近程子程序”;只能被同一代码段内的程序调用只能被同一代码段内的程序调用。1818CODESEGMENT;代码段开始代码段开始MAINPROCFAR;主程序开始主程序开始;
12、主程序的指令序列主程序的指令序列MOV AX,4C00HINT21H;返回返回DOSMAINENDP;主程序结束主程序结束;其它程序其它程序CODEENDS;代码段结束代码段结束ENDMAIN;源程序结束源程序结束用户编写的用户编写的“主程序主程序”也可以看作是由操作系统调用的一个子程也可以看作是由操作系统调用的一个子程序:序:1919CODESEGMENT;代码段开始代码段开始MAINPROCFAR;主程序开始主程序开始PUSH DS;操作系统的返回点在操作系统的返回点在DS:0XOR AX,AXPUSH AX;把把32位返回点地址压入堆栈位返回点地址压入堆栈;主程序的指令序列主程序的指令序
13、列RET;返回返回DOSMAINENDP;主程序结束主程序结束;其它程序其它程序CODEENDS;代码段结束代码段结束ENDMAIN;源程序结束源程序结束还可以这样写:还可以这样写:2020例例5.1子程序子程序FRACTOR用来计算一个数的阶乘。主程序用来计算一个数的阶乘。主程序利用它计算利用它计算15的阶乘,存入的阶乘,存入FRA数组。数组。.386DATASEGMENTUSE16FRADW5DUP(?)DATAENDS2121CODESEGMENTUSE16ASSUMECS:CODE,DS:DATASTART:MOVAX,DATAMOV DS,AXMOV EBX,1;BX中存放待求阶乘的
14、数中存放待求阶乘的数MOV CX,5;求阶乘次数(循环次数)求阶乘次数(循环次数)LOOP0:CALLFRACTOR;调用调用FRACTOR求阶乘求阶乘MOV FRA2*EBX-2,AX;保存结果(阶乘)保存结果(阶乘)INCBX;产生下一个待求阶乘的数产生下一个待求阶乘的数LOOPLOOP0;循环控制循环控制MOVAX,4C00HINT21H2222FRACTORPROCNEARMOVCX,BX;待求阶乘的数转入待求阶乘的数转入;CX寄存器寄存器MOV AX,1;“累乘器累乘器”置初值置初值1FRALOOP:MUL CX;累乘累乘LOOPFRALOOP;循环控制循环控制RETFRACTORE
15、NDPCODEENDSENDSTART思考题:这个程序有问题,是什么问题?如何解决?思考题:这个程序有问题,是什么问题?如何解决?2323FRACTORPROCNEARPUSH CX;CX压入堆栈保护压入堆栈保护MOVCX,BX;待求阶乘的数转入待求阶乘的数转入CX寄存器寄存器MOV AX,1;“累乘器累乘器”置初值置初值1FRALOOP:MUL CX;累乘累乘LOOPFRALOOP;循环控制循环控制POPCX;从堆栈里弹出从堆栈里弹出CX的原值的原值RETFRACTORENDPCODEENDSENDSTART在子程序入口处把相关寄存器的值入栈保护,程序返回前在子程序入口处把相关寄存器的值入栈
16、保护,程序返回前再恢复它们的值,这个操作称为再恢复它们的值,这个操作称为“保护现场保护现场”和和“恢复现场恢复现场”。2424注意:上面的程序使用了上面的程序使用了32位寻址方式,连接时在位寻址方式,连接时在TLINK命令中要增加命令中要增加“/3”选择项:选择项:TLINK/3XXXXXX(XXXXXX为汇编得到的目标文件名)为汇编得到的目标文件名)2525子程序的基本格式:子程序的基本格式:子程序名子程序名PROCNEAR/FARPUSH;保护现场(寄存器保护现场(寄存器/存储器)存储器)PUSH;个数根据具体情况决定个数根据具体情况决定;子程序主体子程序主体POP;恢复现场,注意出栈次序
17、恢复现场,注意出栈次序POP;先进栈的寄存器后出栈先进栈的寄存器后出栈RET;返回返回子程序名子程序名ENDP26265.1.3 5.1.3 子程序文件子程序文件设计一个子程序之前,首先应该明确:设计一个子程序之前,首先应该明确:子程序的名字子程序的名字;子程序的功能子程序的功能;入入口口参参数数:为为了了运运行行这这个个子子程程序序,主主程程序序为为它它准准备备了了哪哪几几个个“已知条件已知条件”?这些参数存放在什么地方?这些参数存放在什么地方?出出口口参参数数:这这个个子子程程序序的的运运行行结结果果有有哪哪些些?存存放放在在什什么么地地方?方?影响寄存器影响寄存器:执行这个子程序会改变哪
18、几个寄存器的值?执行这个子程序会改变哪几个寄存器的值?其它需要说明的事项其它需要说明的事项。上述内容连同子程序源代码等合称为上述内容连同子程序源代码等合称为“子程序文件子程序文件”。常常把上述内容以常常把上述内容以“程序注释程序注释”的方式书写在一个子程的方式书写在一个子程序的首部。序的首部。2727;名称:名称:Square;功能:求功能:求16Bit无符号数的平方根无符号数的平方根;入口参数:入口参数:16Bit无符号数在无符号数在AX中中;出口参数:出口参数:8Bit平方根数在平方根数在AL中中;影响寄存器:影响寄存器:AX(AL)例如,一个名为例如,一个名为“SQUARE”的子程序,用
19、来求一个数的平方根的子程序,用来求一个数的平方根,源程序如下:源程序如下:2828SQUAREPROCNEARPUSH CX;保护现场保护现场PUSH BXMOV BX,AX;要求平方根的数送要求平方根的数送BXMOV AL,0;AL中存放平方根,初值中存放平方根,初值0MOV CX,1;CX置入第一个奇数置入第一个奇数1;利用公式:利用公式:N2=1+3+(2N-1)求平方根求平方根2929NEXT:SUBBX,CXJBDONEADDCX,2;形成下一个奇数形成下一个奇数INCAL;AL存放已减去奇数的个数存放已减去奇数的个数JMPNEXTDONE:POP BX;恢复现场恢复现场POPCXR
20、ET;返回返回SQUAREENDP课内练习:编写主程序,利用课内练习:编写主程序,利用SQUARE子程序,求数子程序,求数26的平方根,存放在变量的平方根,存放在变量ROOT中。中。30305.1.4 5.1.4 子程序应用子程序应用每调用一次子程序,主程序需要做三件事:每调用一次子程序,主程序需要做三件事:(1 1)为子程序准备入口参数)为子程序准备入口参数(2 2)调用子程序)调用子程序(3 3)处理子程序的返回参数)处理子程序的返回参数3131DATA SEGMENTX DW59,3500,139,199,77;欲求平方根的数组欲求平方根的数组ROOTDB5DUP(?);存放平方根内存区
21、存放平方根内存区DATA ENDSCODESEGMENTASSUMECS:CODE,DS:DATASTART:MOV AX,DATAMOV DS,AX例,为了求,为了求5个无符号数的平方根,编制主程序如下:个无符号数的平方根,编制主程序如下:3232LEABX,X;初始化指针初始化指针LEASI,ROOTMOV CX,5;设置计数器初值设置计数器初值ONE:MOV AX,BX;设置入口参数设置入口参数CALL SQUARE;调用子程序调用子程序MOV SI,AL;保存返回参数(平方根)保存返回参数(平方根)ADDBX,2;修改指针修改指针INCSI;修改指针修改指针LOOPONE;循环控制循环
22、控制MOV AX,4C00H;返回返回DOSINT21H3333例例5.2从键盘上输入从键盘上输入10个十进制数,从中找出最个十进制数,从中找出最大的数,在屏幕上显示出来。大的数,在屏幕上显示出来。INCLUDEYLIB.H;声明外部函数声明外部函数.DATANUMDW10DUP(?)PROMPT1DB0AH,0DH,“InputaNumber:$”PROMPT2DB0AH,0DH,“TheMaximumNumberis:$”3434.CODESTART:MOVAX,DATAMOVDS,AXMOVCX,10LEABX,NUMNEXT:LEADX,PROMPT1;设置设置READINT子程序的入
23、口参数子程序的入口参数CALLREADINTMOVBX,AX;保存读入的十进制数保存读入的十进制数ADD BX,2LOOPNEXT3535LEABX,NUM;为子程序为子程序MAX准备入口参数准备入口参数MOV CX,10CALLMAX;MAX子程序找出子程序找出N个数中最大的数,并将此数从个数中最大的数,并将此数从AX返回返回LEADX,PROMPT2CALL WRITEINT;在屏幕上显示在屏幕上显示AX中的数中的数MOV AX,4C00HINT21H3636;子程序子程序MAX;功能:求若干个数中的最大值功能:求若干个数中的最大值;入入口口参参数数:BX=第第一一个个数数据据的的偏偏移移
24、地地址址,CX=数据个数数据个数;出口参数:最大值在出口参数:最大值在AX中中3737MAX PROCMOV AX,8000H;最小的最小的16位有符号数位有符号数AGAIN:CMPAX,BXJGESKIPMOV AX,BX;将当前最大数送将当前最大数送AXSKIP:INCBXINCBXLOOPAGAINRET;AX返回最大数返回最大数MAX ENDPENDSTART38385.25.2 参数的传递参数的传递 传递的参数有两种类型传递的参数有两种类型:(1 1)值值传传递递:把把参参数数的的值值放放在在约约定定的的寄寄存存器器或或存存储储单单元元传传递递 给给 子子 程程 序序,或或 者者,由
25、由 子子 程程 序序 返返 回回 给给 主主 程程 序序。如如果果一一个个入入口口参参数数是是用用值值传传递递的的,子子程程序序可可以以使使用用这这个个值,但是无法改变它自身的值。值,但是无法改变它自身的值。(2 2)地址传递地址传递:把参数事先存放在某个存储单元,把这个把参数事先存放在某个存储单元,把这个存储单元的地址作为参数传递给子程序。存储单元的地址作为参数传递给子程序。如果一个参数使用它的地址来传递,子程序可以改变这如果一个参数使用它的地址来传递,子程序可以改变这个参数的值。例如,把存放结果的存储单元的地址作为个参数的值。例如,把存放结果的存储单元的地址作为“入入口参数口参数”传递给子
26、程序,子程序可以把运算结果直接存入这传递给子程序,子程序可以把运算结果直接存入这个单元。个单元。3939参数存放在寄存器中参数存放在寄存器中 参数存放在主、子程序参数存放在主、子程序“共享共享”的数据段内的数据段内 参数存放在堆栈内参数存放在堆栈内参数的存放位置,有三种情形:参数的存放位置,有三种情形:40401.1.用寄存器传递参数用寄存器传递参数.MODELSMALL.DATAFIBLSTDW1,1,18DUP(?)NDW20例例5.3求求菲波那契数列的前菲波那契数列的前n项。项。菲波那契数列的前两项为菲波那契数列的前两项为1,1,以后的每一项都是其前两项,以后的每一项都是其前两项之和。之
27、和。X0=1,X1=1,Xi=Xi-1+Xi-2(i=2)本例使用寄存器本例使用寄存器AX,BX来传递参数,传递的是参数的值来传递参数,传递的是参数的值。4141.CODESTART:MOVAX,DATAMOV DS,AXLEASI,FIBLSTMOV CX,NSUBCX,2ONE:MOV AX,SIMOV BX,SI+2CALLFIBMOV SI+4,AXADD SI,2LOOPONEMOV AX,4C00HINT21H4242;子程序子程序FIB;功能:求菲波那契数列数列的一项功能:求菲波那契数列数列的一项;入口参数:;入口参数:AXXi-1,BXXi-2;出口参数:出口参数:AXXiXi
28、-1+Xi-2FIBPROCADDAX,BXRETFIBENDPENDSTART4343.MODEL SMALL.DATAFIBLSTDW1,1,18DUP(?)NDW20下面的程序仍然利用寄存器传递参数,但是传递的是参下面的程序仍然利用寄存器传递参数,但是传递的是参数的地址。数的地址。4444.CODESTART:MOVAX,DATAMOVDS,AXLEASI,FIBLSTMOVCX,NSUBCX,2ONE:CALLFIBADDSI,2LOOPONEMOVAX,4C00HINT21H4545FIBPROCPUSHAXMOVAX,SI;取;取XI-2ADDAX,SI+2;加;加XI-1MOVS
29、I+4,AX;存入存入XIPOPAXRETFIBENDPENDSTART;子程序子程序FIB;功能:求菲波那契数列数列的一项功能:求菲波那契数列数列的一项;入口参数:;入口参数:SIXi-2的段内偏移地址的段内偏移地址;出口参数:无(结果已由子程序存入数组内);出口参数:无(结果已由子程序存入数组内)由于传递的是参数的地址,子程序根据这个地址取出由于传递的是参数的地址,子程序根据这个地址取出Xi-2和和Xi-1,计算得到的结果直接存入变量计算得到的结果直接存入变量Xi中。中。4646.MODEL SMALL.DATAFIBLSTDW1,1,18DUP(?)NDW20XI1DW?;存放存放XI-
30、1XI2DW?;存放存放XI-2XIDW?;存放存放XI.CODESTART:MOV AX,DATAMOV DS,AX2变量(共享数据段)传递参数变量(共享数据段)传递参数仍以例仍以例5.3为例,程序作如下修改:为例,程序作如下修改:4747LEA SI,FIBLST;设置地址指针设置地址指针MOV CX,NSUBCX,2;设置计数器初值设置计数器初值ONE:MOV AX,SI+2MOV XI1,AX;Xi-1置入置入XI1MOV AX,SIMOV XI2,AX;Xi-2置入置入XI2CALLFIB;调用子程序调用子程序MOV AX,XI;取出子程序计算结果取出子程序计算结果XiMOV SI+
31、4,AX;置入置入FIBLST数组数组ADD SI,2;修改地址指针修改地址指针LOOPONE;循环控制循环控制MOV AX,4C00HINT21H4848;子程序;子程序FIB;功能:计算菲波那契数列数列的一项功能:计算菲波那契数列数列的一项;入口参数:;入口参数:XI1XI-1,XI2XI-2;出口参数:出口参数:XIXI-1+XI-24949FIBPROCPUSH AX;保护现场保护现场MOV AX,XI1;AXXI-1ADDAX,XI2;AXXI-1+XI-2MOV XI,AX;XIAXPOPAX;恢复现场恢复现场RETFIBENDPEND START5050.MODELSMALL.D
32、ATAFIBLSTDW1,1,18DUP(?)NDW20.CODESTART:MOV AX,DATAMOV DS,AXLEASI,FIBLSTMOV CX,NSUBCX,23.堆栈传递参数堆栈传递参数仍以例仍以例5.3为例,程序作如下修改:为例,程序作如下修改:5151ONE:PUSH AX;为为保保存存结结果果,在在堆堆栈栈“预预留留”单单元元PUSHWORDPTRSI;XI-2入栈入栈PUSHWORDPTRSI+2;XI-1入栈入栈CALLFIB;调用子程序,执行后堆栈状态调用子程序,执行后堆栈状态1POPAX;从堆栈弹出结果,执行后堆栈状态从堆栈弹出结果,执行后堆栈状态4MOV SI+4
33、,AX;把结果存入把结果存入FIBLST数组数组ADD SI,2LOOPONEMOV AX,4C00HINT21H5252;子程序;子程序FIB;功能:计算菲波那契数列数列的一项功能:计算菲波那契数列数列的一项;入口参数:;入口参数:XI-1,XI-2在堆栈中在堆栈中;出口参数:;出口参数:XI在栈顶在栈顶FIBPROCPUSHBPMOV BP,SP;堆栈状态堆栈状态2MOV AX,BP+4;从堆栈取出从堆栈取出XI-1ADD AX,BP+6;AXXI-1+XI-2MOV BP+8,AX;结果存入堆栈结果存入堆栈POPBP;恢复恢复BPRET 4;返回,返回,SP+4,执行后堆栈状态执行后堆栈
34、状态3FIBENDPEND START5353调用子程序过程中堆栈状态的变化调用子程序过程中堆栈状态的变化54545.35.3 嵌套和递归子程序嵌套和递归子程序 5.3.15.3.1 子程序嵌套子程序嵌套 5.3.2 5.3.2 递归子程序递归子程序 55555.3.15.3.1 子程序嵌套子程序嵌套 被调用的子程序在返回前又调用了其他子程被调用的子程序在返回前又调用了其他子程序,称为序,称为子程序嵌套子程序嵌套。(也称过程嵌套(也称过程嵌套NestProcedureCall)5656;主程序主程序.CALLSUBA;.;子程序子程序SUBA.CALLSUBB;.RET;子程序子程序SUBB.
35、RET5757执行执行2条条CALL指令后堆栈状态指令后堆栈状态执行执行SUBB中中ret指令后堆栈状态指令后堆栈状态返回返回SUBA的地址的地址SP返回主程序的地址返回主程序的地址返回返回SUBA的地址的地址返回主程序的地址返回主程序的地址SP58585.3.2 5.3.2 递归子程序递归子程序 递归过程递归过程(RecursiveProcedure)是一个直接或间是一个直接或间接调用自身的过程。接调用自身的过程。如果一个问题可以分解为几个子问题,而这如果一个问题可以分解为几个子问题,而这些子问题又和原问题有相同的算法时,递归过程是些子问题又和原问题有相同的算法时,递归过程是非常方便的表达方
36、式。非常方便的表达方式。递归过程可以使用堆栈传递参数,也可以用递归过程可以使用堆栈传递参数,也可以用寄存器或存储单元传递参数。寄存器或存储单元传递参数。5959计算1N自然数之和可以定义为:SUM(N)=N=1时,时,SUM函数直接返回函数直接返回1。N1时,计算时,计算SUM(N)分成两步:分成两步:也可以定义为:SUM(N)=1+2+3+N计算计算SUM(N-1):):设置入口参数为设置入口参数为N-1,调用调用SUM函数(调用自身)函数(调用自身)进行加法运算:进行加法运算:N+SUM(N-1)6060.MODELSMALL.STACK100;定义定义100字节用作堆栈字节用作堆栈.CO
37、DETOTALDW?START:MOV CX,3;为子程序准备参数为子程序准备参数CALL SUM;调用子程序调用子程序SUMMOV CS:TOTAL,AX;保存结果保存结果MOV AX,4C00HINT21H;返回返回6161;递归过程;递归过程SUM;入口参数入口参数:CXN,出口参数出口参数:AX=1N的和的和SUMPROCPUSHCX;保存入口参数(保存入口参数(N)CMPCX,1;判;判CX(N)是否为是否为1JEBACK;CX(N)为)为1,转,转BACKDECCX;CX-1,为下一次调用准备参数为下一次调用准备参数CALLSUM;递归调用,计算递归调用,计算1N-1的和的和INC
38、CX;恢复恢复CX为为NADDAX,CX;加到加到AX中中JMPEXITBACK:MOVAX,1;N=1时,通过时,通过AX返回返回1EXIT:POPCX;恢复入口参数恢复入口参数RETSUMENDPENDSTART6262这个程序使用寄存器进行参数的传递。这个程序使用寄存器进行参数的传递。主程序中的主程序中的“CALLSUM”指令执行了一次。指令执行了一次。子程序子程序SUM中的指令中的指令“CALLSUM”执行了执行了2次。次。CALL指令先后共执行了指令先后共执行了3次,先后次,先后3次进入同一个子次进入同一个子程序程序SUM。6363递归调用和返回示意图编写递归程序时,必须保证要有一个
39、终止条件,而且,经编写递归程序时,必须保证要有一个终止条件,而且,经过有限次执行这个子程序,这个终止条件能够实现。否则,过有限次执行这个子程序,这个终止条件能够实现。否则,程序将产生程序将产生“死循环死循环”或者其它的错误状态。或者其它的错误状态。6464.MODELSMALL.STACK100;定义定义100字节用作堆栈字节用作堆栈.CODETOTALDW?START:PUSH AX;为为返返回回结结果果预预留留单单元元,;AX中内容无意义中内容无意义MOV AX,3;为子程序准备参数为子程序准备参数PUSH AX;入口参数压入堆栈入口参数压入堆栈CALL SUM;调用子程序调用子程序SUM
40、POPCS:TOTAL;保存结果保存结果MOV AX,4C00HINT21H;返回返回高级语言中的递归程序普遍使用堆栈传递参数。高级语言中的递归程序普遍使用堆栈传递参数。上面的例子改写如下:上面的例子改写如下:6565;递归过程;递归过程SUM;入口参数:入口参数:SP+2N,出口参数:出口参数:1N的和在堆栈栈顶的和在堆栈栈顶;影响寄存器:;影响寄存器:AX,CX6666SUMPROCPUSHBPMOVBP,SP;MOVCX,BP+4;取出入口参数取出入口参数N,存入存入CXCMPCX,1;N=1?JEBACK;N=1,转,转BACKDECCX;CX-1,为下一次调用准备参数为下一次调用准备
41、参数PUSHAX;为保留下一次调用结果预留单元为保留下一次调用结果预留单元;AX中内容无意义中内容无意义PUSHCX;入口参数(入口参数(N-1)压入堆栈压入堆栈CALLSUM;递归调用,计算递归调用,计算1N-1的和的和6767POPAX;从从堆堆栈栈弹弹出出子子程程序序运运算算结结果果(1N-1的的和和)ADDAX,BP+4;把;把N加到加到AX中,得到中,得到1N的和的和MOVBP+6,AX;1N的和存入堆栈预留单元的和存入堆栈预留单元JMPEXITBACK:MOVWORDPTRBP+6,1;N=1时,返回时,返回1EXIT:POPBPRET2SUMENDPENDSTART6868696
42、970705.4 5.4 多模块程序设计多模块程序设计 5.4.15.4.1 段的完整定义段的完整定义 5.4.2 5.4.2 简化段定义简化段定义 5.4.3 5.4.3 创建多模块程序创建多模块程序71711 1.一个应用程序的开发由一个小组,而不是一个人完成的。一个应用程序的开发由一个小组,而不是一个人完成的。每个程序员编制的源程序自然地构成一个(或者是多个)每个程序员编制的源程序自然地构成一个(或者是多个)源程序文件。源程序文件。每个源程序文件内包括了一个,或多个每个源程序文件内包括了一个,或多个“子程序子程序”。这些。这些源程序文件常常被称为模块(源程序文件常常被称为模块(Modul
43、eModule)。)。有时候也把位于同一个源程序文件内的各个有时候也把位于同一个源程序文件内的各个“子程序子程序”,或者一个或者一个“数据段数据段”定义等称为模块。定义等称为模块。一个可供实际使用的应用程序,它的源程序基本上都是由若干个程序文件组成的。72722.2.源程序文件规模过大,会造成管理,维护上的困难。即便是一个人开发的应用程序,也应把一个大的程序分解即便是一个人开发的应用程序,也应把一个大的程序分解成多个源代码文件。成多个源代码文件。这样,每个文件长度适中,容易阅读和维护,如果修改了这样,每个文件长度适中,容易阅读和维护,如果修改了某个模块,只需对该模块重新汇编,然后再和其他模块链
44、接,某个模块,只需对该模块重新汇编,然后再和其他模块链接,无须把每个模块重新汇编一次。无须把每个模块重新汇编一次。3.3.许多“子程序”可以重复使用,独立为一个源程序文件便于“子程序库”的管理。73735.4.15.4.1 段的完整定义段的完整定义段名段名SEGMENT对对齐齐方方式式组组合合方方式式使使用用类类型型类类语句语句段名段名ENDS完整的段定义格式如下:SEGMENT伪指令所在行内,方括号括起来的内容称伪指令所在行内,方括号括起来的内容称为为“可选项可选项”,它的出现与否可以由使用者根据需要决定。,它的出现与否可以由使用者根据需要决定。74741.对齐方式对齐方式一一个个新新的的段
45、段开开始始时时,对对齐齐方方式式通通知知连连接接程程序序怎怎样样确确定定这这个个段段的的起始地址。对齐方式可以有下面几种选择:起始地址。对齐方式可以有下面几种选择:BYTE使本段从前面段结束之后的下一个字节地址开始。使本段从前面段结束之后的下一个字节地址开始。WORD使本段从前面段结束之后的下一个字地址开始。使本段从前面段结束之后的下一个字地址开始。DWORD使本段从前面段结束之后的下一个双字地址开始。使本段从前面段结束之后的下一个双字地址开始。PARA使使本本段段从从前前面面段段结结束束之之后后的的下下一一个个节节的的地地址址(16的的整整数数倍倍)开开始始存存放放,是是默默认认的的对对齐齐
46、方方式式。如如果果没没有有出出现现对齐方式说明,自动按照对齐方式说明,自动按照“PARA”来对齐。来对齐。PAGE使使本本段段从从前前面面段段结结束束之之后后的的下下一一个个页页地地址址(256的整数倍)开始。的整数倍)开始。7575假假设设前前面面一一个个段段的的结结束束地地址址为为30204H,用用不不同同对对齐齐方方式式的结果如下表所示。的结果如下表所示。对齐方式对齐方式段基址段基址起始偏移地址起始偏移地址段间空隙(字节)段间空隙(字节)BYTE3020H0005H0WORD3020H0006H1DWORD3020H0008H3PARA3021H0000H11PAGE3030H0000H
47、25176762.组合方式组合方式组组合合方方式式通通知知连连接接程程序序以以何何种种方方式式处处理理不不同同程程序序文文件件中中名名称相同的段。组合方式可以有下面几种选择:称相同的段。组合方式可以有下面几种选择:PRIVATE 这这个个段段不不与与其其他他同同名名段段合合并并,每每个个段段都都有有自自己己的段基地址,是默认的组合方式。的段基地址,是默认的组合方式。PUBLIC将将 该该段段 和和其其 他他名名 称称相相同同,组组合合方方 式式同同为为“PUBLIC”的的段段前前后后连连接接在在一一起起,合合并并成成一一个个段段,产产生生一一个新的段基地址,所有标号的偏移地址都进行调整。个新的
48、段基地址,所有标号的偏移地址都进行调整。STACK将将所所有有的的STACK段段连连接接为为一一个个新新的的STACK段段,类似于类似于PUBLIC。COMMON所所有有同同名名的的段段使使用用相相同同的的起起始始地地址址,这这样样,这些段这些段“共享共享”这一片共同的存储区间。这一片共同的存储区间。AT表达式表达式 段段的的起起始始地地址址由由表表达达式式的的值值来来指指定定,用用于于设设定定一些特殊的段一些特殊的段。77773.使用(使用(USE)类型类型 使用类型仅仅使用于使用使用类型仅仅使用于使用80386以上指令系统的汇编程序以上指令系统的汇编程序(例如,在程序首部使用(例如,在程序
49、首部使用.386说明符)说明符)使用使用16位寻址方式时,段内偏移地址位寻址方式时,段内偏移地址16位,每个段最大位,每个段最大64KB。运行在实模式下的程序使用运行在实模式下的程序使用16位段。位段。使用使用32位寻址方式时,段内偏移地址为位寻址方式时,段内偏移地址为32位,一个段最位,一个段最大可达大可达4GB。运行在保护模式下的程序使用运行在保护模式下的程序使用32位段。位段。使用使用80386以上以上CPU,并且工作在实模式下的程序不能并且工作在实模式下的程序不能遗漏遗漏“USE16”说明。说明。78784.4.类名称类名称一个段除了有一个段名之外,还可以有一个类名一个段除了有一个段名
50、之外,还可以有一个类名称。类名称是以引号引起来的任意字符串。称。类名称是以引号引起来的任意字符串。类名称相同的段被放在安置在一片相邻的存储区类名称相同的段被放在安置在一片相邻的存储区间,但不会合并成同一个段。间,但不会合并成同一个段。7979CSEGSEGMENTCODEASSUMECS:CSEG,DS:DATA1,SS:MYSTACKMAIN PROCMOV AX,DATA1MOV DS,AXMOV AX,SEGVAL2MOV ES,AXMOV AX,VAL1LEASI,VAL2MOV BX,ES:SI例例5.5本例中有两个代码段、两个数据段和一个堆栈段,本例中有两个代码段、两个数据段和一个