《程序篇-子程序结构》由会员分享,可在线阅读,更多相关《程序篇-子程序结构(55页珍藏版)》请在金锄头文库上搜索。
1、第五章 程序篇-子程序结构,子程序又称为过程。它相当于高级语言中的过程和函数。 在一个程序的不同部分,往往要用到类似的程序段,这些程序段的功能和结构形式都相同,只是某些变量的赋值不同,此时就可以把这些程序段写成子程序形式,以便需要时可以调用它。有些程序对于某个用户可能只用到一次,但它是一般用户经常用到的,如十进制数转换成二进制数、二进制数转换成为十六进制数并显示输出等,对于这些常用的特定功能的程序段也经常编成子程序的形式供用户使用。,一、过程定义伪操作,它用在过程(子程序)的前后,使整个过程形成清晰的,具有特定功能的代码块 格式:Procedure name PROC Attribute .
2、. . ret Procedure name ENDP 其中:过程名(标识符),它是子程序的入口符号地址,那么过程的属性如何定义呢? 调用程序和过程在同一个代码段中则使用NEAR属性。 调用程序和过程不在同一个代码段中则使用FAR属性。,例1、调用程序和子程序在统一代码段中:,MAIN PROC FAR CALL SUB RET MAIN ENDP SUB1 PROC NEAR SUB1 ENDP,MAIN PROC FAR CALL SUB RET SUB1 PROC NEAR SUB ENDP MAIN ENDP,或写成:即过程可以嵌套,例2、调用程序和子程序不在同一个代码段中。 SEG1
3、 SEGMENT SUB1 PROC FAR RET SUB1 ENDP CALL SUB1 SEG1 ENDS,在另一段调用:必须用FAR属性,故SUB1过程必须定义为FAR属性,SEG2 SEGMENT CALL SUB1; SEG2 ENDS,注意: 主程序可看成DOS的一个子过程,故用FAR属性。 子程序的调用和返回已包括了返回地址的出、入栈,故在子程序中必须正确使用堆栈,否则将造成运行错误。 在一进入子程序后应该将子程序所需要使用的寄存器内容保存在堆栈中,在退出子程序前把寄存器内容恢复原状。,二、主程序与子程序的参数传送,调用程序在调用子程序时,经常需要传送一些参数给子程序;子程序运
4、行完后也经常要回送一些信息给调用程序。 参数传送(参数传送、过程通信):调用程序和子程序之间的信息传送。,1、通过寄存器传送变量: 利用寄存器在主子程序之间传送数信息。 例、十进制到十六进制转换程序。 程序要求从键盘取得一个十进制数,然后把该数以十六进制形式在屏幕上显示出来。,分析: (1)键盘输入的十进制数(ASCII),要将它转换成二进制数存放:子程序DECIBIN (2)二进制转换成十六进制数并显示:子程序BINIHEX (3)为避免屏幕上的重叠,必须有回车和换行功能:CRLF 用三个子程序联合实现题目要求。 将BX寄存器作为过程之间传送转换数据的“交通员”。,主程序调用如下:,main
5、 proc far repeat: call decibin call crif call binihex call crif jmp repeat main endp,数据在0-9D之间?,Y,N,DECIBIN子程序(注:退出条件) decibin proc near mov bx, 0 newchar: mov ah, 1 int 21h sub al, 30h jl exit cmp al, 9 jg exit cbw,xchg ax, bx ;交换后bx为新数,ax为以前的数 mov cx, 10d mul cx xchg ax, bx ;交换后ax为新数,bx为以前的数*10 ad
6、d bx, ax jmp newchar exit: ret decibin endp,BINIHEX子程序(上一章中的例1已讲过) CRLF子程序: 回车的ASCII 0dH 换行的ASCII 0aH,crlf proc near mov dl, 0dh mov ah, 2 int 21h mov dl, 0ah mov ah, 2 int 21h ret crlf endp,2、直接访问模块中的变量: 如果过程和调用程序在同一源文件(同一程序模块)中,则过程可直接访问模块中的变量。 例、主程序MAIN和过程PROADD在同一源文件中, 要求用过程PROADD累加数组中的所有元素并把和送到指
7、定的存储单元中去(不考虑溢出情况) 注意:合理使用堆栈,data segment ary dw 100 dup(?) count dw 100 sum dw ? data ends,子程序proadd结构 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,Main proc far assume cs:code,ds:dat start: . .
8、. call near ptr proadd . . . Main endp,注意: 如果MAIN中定义了几个不同的数组,要求每次调用PROADD来分别计算每个数组的和,如何计算?数组的内容整个传送给ary,3、通过堆栈传送参数或参数地址 在主程序中把参数地址保存到堆栈,在子程序里从堆栈中取出参数以达到传送参数的目的。,注:在主程序中将数组情况入栈:(将数组的地址入栈) Mov bx, offset ary Push bx Mov bx, offset count Push bx Mov bx, offset sum Push bx,子程序中是直接从堆栈取数进行运算: Mov si, bp+o
9、ah Mov di, bp+8 Mov cx, di Mov di, bp+6 子程序返回时,需用带立即数的RET指令:ret 6 主、子程序分析。堆栈的分析。,parm-seg segment Ary dw 100 dup(?) Count dw 100 Sum dw ? parm-seg ends stack-seg segment dw 100 dup(?) Tos label word stack-seg ends,Codel segment Main proc far assume cs:codel,ds:parm-seg assume ss:stack-seg start: mov
10、 ax, stack-seg mov ss, ax mov sp, offset tos push ds sub ax, ax push ax ;把DS的内容和0作为段地址和偏移地址入栈,以便在程序结束时用RET指令返回DOS,Mov ax, parm-seg Mov ds, ax Mov bx, offset ary Push bx Mov bx, offset count Push bx mov bx, offset sum push bx call far ptr proadd ret Main endp Code1 ends,Code2 segment assume cs:code2
11、Proadd proc far push bp mov bp, sp 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,bp bp ip cs offset sum offset count offset ary,pop di pop si pop cx pop ax pop bp ret 6 Proadd endp Code2 ends end start,三
12、、子程序嵌套,一个子程序作为调用程序去调用另一个子程序,称为子程序嵌套。 嵌套的层次不限,其层数称为嵌套深度。,子程序嵌套的设计应注意: 正确使用CALL和RET指令; 寄存器的保存和恢复; 如使用堆栈来传递参数,则对堆栈的操作要格外小心,避免因使用堆栈而造成子程序不能正确返回.,四、递归子程序,子程序嵌套的情况下,如果一个子程序调用的子程序就是他的自身,则称为递归调用。这样的子程序称为递归子程序。 递归子程序对应于数学中函数的递归定义。它往往能设计出效率较高的程序,可完成相当复杂的计算。因此,它很有用。,以阶乘函数为例说明递归子程序的设计方法 例、要求编制计算N!(N0)的程序。 0!=1
13、其递归定义为: N!=N*(N-1)!(N0),分析: N!本身作为一个子程序,为了计算N*(N-1)!必须递归调用(N-1)!的子程序,但每次调用所使用的参数都不相同,mov dx, 0 mov ax, 1 mov bx, n call powern powern proc mul bx dec bx jz exit call powern exit: ret powern endp,例、HEXIDEC是一个把十六进制数转换成十进制数的程序。 要求把从键盘输入的0FFFFH的十六进制正数转换为十进制数并从屏幕上显示出来。 作出各子程序框图,并说明各框所用的语句。,Display equ 2h
14、 key_in equ 1h Doscall equ 21h Hexidex segment Main proc far assume cs:hexide start: push ds sub ax, ax push ax call hexibin call crif call binidec call crif jmp start ret Main endp,Hexibin proc near mov bx, 0 newchar: mov ah, 1 int 21h sub al, 30h jl exit cmp al, 10d jl add_to sub al, cmp al, 0ah j
15、l exit cmp al, 10h jge exit,add_to: mov cl, 4 shl bx, cl mov ah, 0 add bx, ax jmp newchar exit: ret Hexibin endp,Binidec proc near mov cx, 10000d call dec_div mov cx, 1000d call dec_div mov cx, 100d call dec_div mov cx, 10d call dec_div mov cx, 1d call dec_div ret,dec_div proc near mov ax, bx mov dx, 0 div cx mov bx, dx mov dl, al add dl, 30h mov ah,