当前位置:
文档之家› 第6章(龚尚福) 子程序结构
第6章(龚尚福) 子程序结构
MOV
MOV CALL RET
AX,DATA
DS,AX NEAR PTR PROADD
MAIN ENDP
第6章
子程序结构
NEAR AX CX SI SI,ARY CX,COUNT AX,AX AX,[SI] SI,2 NEXT SUM,AX PUSH PUSH PUSH LEA MOV XOR
PROADD PROC
RET SUBR1 ENDP
主子程序 编写在同 一个程序 段中。
MAIN ENDP
第6章
子程序结构
【例6-2】 调用程序和子程序不在同一个代码段内。 CODE SEGMENT SUBT PROC FAR RET SUBT ENDP
SUBT是一个子程序,它在 两处被调用,一处是与 CODE同在段内,另一处是 在 SEGY 段 内 。 因 此 , SUBT必须具有FAR属性以 适应SEGY段调用的需要。 既然SUBT有FAR属性,则 不 论 在 CODE 段 中 还 是 SEGY 段 中 , 对 SUBT 的 调 用就都具有FAR属性,这样 不会发生什么错误;反之, 如 果 这 里 的 SUBT 使 用 了 NEAR 属 性 , 则 在 SEGY 段 内对它的调用就要出错了。
文件中,要求用子程序PROADD累加数组中的所有元素,
并把和(不考虑溢出的可能性)送到指定的存储单元中去。 在这里子程序PROADD直接访问模块的数据区。
程序如下:
第6章
子程序结构
DATA SEGMENT ARY SUM DATA ENDS CODE SEGMENT MAIN PROC FAR ASSUME CS:CODE,DS:DATA START:PUSH DS SUB PUSH AX,AX AX DW DW 100 100 ? DUP(?) COUNT DW
第6章 分析:
子程序结构
1、用一个子程序DECIBIN实现从键盘取得十进制数并把它转换 为二进制数;
2、另一个子程序BINIHEX把此二进制数以十六进制数的形式在
屏幕上显示出来。 3、用CRLF子程序取得回车和换行效果。
整个程序结构如图6.2所示。在这里,各个子程序之间用BX寄存 器来传送信息。在子程序DECIBIN中取得的输入数据转换为二进
21H DL,0AH ;执行换行
MOV
INT RET
AH,2
21H
CRLF
DECIHEX
ENDP
ENDS END MAIN
第6章
子程序结构
6.3.2 直接参数传递 若子程序和调用程序在同一源文件(同一程序模块)中, 则子程序可直接访问模块中的变量(局部变量),进行参 数传递。 【例6-5】 主程序MAIN和子程序PROADD在同一源
6.3 子程序的参数传送
主程序在调用子程序时,经常需要传送一些参数给子 程序,子程序运行完后也经常要回送一些信息给主程序。 这种调用程序和被调用程序之间的信息传送称为主—子程 序参数传送(或称变量传送和过程通信)。
6.3.1 通过寄存器传送参数
通过寄存器传送是最常用的一种参数传送方式,使用 方便,但参数太多时不宜使用此法。 【例6-4】 十进制数到十六进制数转换程序,要求从 键盘取得一个十进制数,然后把该数以十六进制形式在 屏幕上显示出来。
结束
图6.2 十进制数到十六进制数转换的程序结构
第6章
子程序结构
程序如下:
DECIHEX SEGMENT ASSUME CS: DECIHEX
MAIN
REPEAT:
PROC FAR
CALL DECIBIN CALL CRLF ;调用键入子程序 ;调用回车换行子程序
CALL BINIHEX
CALL CRLF JMP
BINIHEX PROC ROTATE:
PRINTIT:
MOV
MOV
INT DEC JNZ
AH,2
21H CH ;是第四个字符? ROTATE ;不,继续
RET
BINIHEX ENDP
;是,返回
第6章
子程序结构
CRLF
PROC NEAR MOV DL,0DH ;执行回车
MOV
INT MOV
AH,2
为使子程序具有可递归和可重入性,应当利用堆栈 和寄存器作为中间结果的暂存器,而不能用固定的存储 单元作暂存器。
第6章
子程序结构
6.2 子程序结构形式与操作
子程序定义伪操作用在子程序的前后,使整个子程序 形成清晰的、具有特定功能的代码块。其格式为 <PROCEDURE NAME> PROC ATTRIBUTE (子程序体) <PROCEDURE NAME> ENDP
由于执行CALL时已使返回地址入栈,所以执行RET时
应该使返回地址出栈,如果子程序中不能正确使用堆 栈而造成执行RET前SP并未指向进入子程序时的返回
地址,则必然会导致运行出错,因此子程序中对堆栈
的使用应该特别小心,以免发生错误。
第6章
子程序结构
6.2.3 现场保护与现场恢复 主程序和子程序通常是分别编制的,所以它们所 使用的寄存器往往会发生冲突。如果主程序在调用子 程序之前的某个寄存器内容在从子程序返回后还有用, 而子程序又恰好使用了同一个寄存器,这就破坏了该
NEXT: ADD ADD LOOP MOV
POP
POP POP RET
SI
CX AX
PROADD ENDP
CODE ENDS END START
第6章
子程序结构
如果数据段中有如下两个数组: DATA SEGMENT ARY COUNT SUM NUM N DW DW DW DW DW 100 DUP(?) 100 ? 100 DUP(?) 100
其中,子程序名为标识符,它又是子程序入口的符号地址。
它的写法与标号的写法相同。属性(Attribute)是指子程序 的类型属性,可以是NEAR或FAR。段内调用使用NEAR 属性,但可以隐含;段间调用使用FAR属性。
第6章
子程序结构
子程序是属性决定对它的调用(CALL)和返回(RET)
属性的。这样,用户只需在定义子程序时考虑它的属
第6章
子程序结构
第6章 子程序结构
6.1 子程序的概念 6.2 子程序结构形式与操作 6.3 子程序的参数传送 6.4 子程序的嵌套与递归 习题6
第6章
子程序结构
6.1 子程序的概念
6.1.1 子程序概念
把可以多次调用、能够完 成特定操作功能的程序段编写成 独立的程序模块,该程序模块称 为子程序,又称为过程。调用这 些子程序的程序称为主程序。在 主程序中,如果调用到子程序, 就需要把控制转移到子程序,这 个过程称为转子。子程序执行完 了,要把控制再返回到主程序, 这个过程称为返主。主程序与子 程序之间的关系如图6.1所示。
============ RET
MAIN ENDP
SUBR1 RET SUBR1
PROC NEAR
;子程序(NEAR可省略)
ENDP
第6章
子程序结构
例6-1的情况也可以写成如下的程序:
MAIN PROC FAR CALL SUBR1
==========
RET SUBR1 PROC NEAR
========
是用来向主程序回送结果的寄存器,就更不应该因保存
和恢复寄存器而破坏了应该向主程序传送的信息。 从 80286 CPU 开 始 可 用 的 PUSHA/POPA 指 令 和 从
80386 CPU开始的高档微机可用的PUSHAD/POPAD指令
为子程序中保存和恢复寄存器内容提供了有力的支持。
第6章
子程序结构
;取下一个字符
EXIT:
DECIBIN
RET
ENDP
第6章
子程序结构
NEAR MOV CH,4 CL,4 BX,CL AL,BL AL,0FH AL,30H AL,3AH PRINTIT AL,7H ;数大于9吗? ;否,显示 ;大于加7成为‘A’~‘F’ DL,AL ;显示字符 MOV ROL MOV AND ADD CMP JL ADD ;转换成16进制数 ;字数送CH ;位数送CL ;BX值循环左移 ;组装ASCII字符
CALL SUBT
CODE ENDS SEGY SEGMENT CALL SUBT SEGY ENDS
第6章
子程序结构
6.2.2 子程序的调用和返回 子程序的正确执行是由子程序的正确调用和正确 返回保证的。80x86的CALL和RET指令完成的就是调 用和返回的功能。为保证其正确性,除PROC的属性要 正确选择外,还应该注意子程序运行期间的堆栈状态。
主程序 1 调用子程序 2 3 调用子程序 1 调用子程序 2 3 返回 子程序
图6.1 主程序与子程序之间的关系
第6章
子程序结构
子程序具有如下4个特性: (1) 重复性。一个子程序只占用一段存储区域,但 可以多次被调用,避免了编程人员的重复劳动,又节 省程序的存储空间。 (2) 通用性。只能完成特定功能的子程序用处不大, 例如,只能实现5个字节加法运算的多字节加法子程序 和只能在定长字符串上查找某一固定字符的子程序都 没有通用性,因而用处也就不大。要能够得到广泛应 用的通用的多字节加法子程序,字节数应该是任意的, 字符查找子程序、字符串的长度和查找的字符都应是 任意的。
PUSH PUSH PUSH PUSH <子程序体> POP POP POP POP RET SUBT ENDP
DX CX BX AX
;现场恢复
第6章
子程序结构
在子程序设计时,应仔细考虑哪些寄存器必须保护, 哪些不必保护。一般说来,子程序中用到的寄存器都必 须保护。但是,如果使用寄存器在主程序和子程序之间 传送参数的话,则这种寄存器就不一定需要保护,特别