当前位置:文档之家› 第6章 子程序结构

第6章 子程序结构


例6.5 三个模块中的外部符号定义。 ; source module 1 extrn var2:word,lab2:far public var1,var4,lab1 data1 segment var1 db ? var3 dw ? var4 dw ? data1 ends code1 segment assume cs:code1,ds:data1 main proc far start: mov ax,data1 mov ds,ax ; ...... lab1: ; ...... mov ax,4c00h int 21h main endp code1 ends end start
6.1.3 保存与恢复寄存器 由于调用程序(又称主程序)和 子程序经常是分别编制的,所以 它们所使用的寄存器往往会发生 冲突。 如果主程序在调用子程序以前的 某个寄存器内容在从子程序返回 后还有用,而子程序又恰好使用 了同一寄存器,造成破坏了寄存 器的原有内容,那就会造成程序 运行错误,这是不允许的。
1.通过寄存器传送变量
开始 从键盘收得十进制 数保存在BX中
例6.3十进制到十六进 制数转换程序。程序 要求从键盘取得一个 十进制数,然后把该 数以十六进制形式在 屏幕上显示出来。
程序结构
通用DECIBIN
通用CRLF 显示回车换行 通用BINIHEX 用十六制数形式 显示BX中的数
通用CRLF
结束
pop
ret proadd endp
ax
;恢复寄存器
;功能:将BX的内容按4位十六进制数格式 输出
;入口条件:BX中存放待输出的数据
binihex proc push push push mov rotate: mov rol mov and near ax cx dx ch,4 cl,4 bx,cl al,bl al,0fh ;数据位数 ;循环移位次数 ;向左循环移位,将高四位移到低四位 ;取低字节到AL ;屏蔽高四位,截取低四位 ;保护寄存器
al,0fh ;屏蔽高四位,截取低四位 al,30h ;加30H,转换成十进制数的ASCII
cmp jl add print: mov mov
al,3ah ; print al,7h dl,al ah,2 ;如果是十六进制数A-F, 则再加7 ;转存到DL ;显示功能调用
int
dec jnz
21h
main
endp
proadd proc push push push lea mov xor next: add add loop mov pop pop
near ax cx si si,ary cx,count ax,ax ax,[si] si,2 next sum,ax si cx ;相加 ;SI指向下一数 ;未完继续 ;存和 ;SI置为数组首址 ;CX为数组长度 ;累加器清0 ;保护寄存器
;DECIHEX----主程序 ; 将键盘输入的十进制数转换成十六进制数送屏幕输出 decihex segment assume cs:decihex ;程序的主要内容,包括过程
main
proc
far
decibin ;将键盘所送的十进制数转换成二进制数存入BX crlf ;屏幕输出回车换行
repeat: call call
AND
JZ call call jmp DONE: MOV INT main endp
BX,BX
DONE binihex crlf repeat AX,4C00H 21H ;将BX中的二进制数转换成十六进制数送屏幕输出 ;屏幕输出回车换行
;过程名:DECIBIN ;功能 ;出口 :将键盘所送的十进制数转换成二进制数 :转换结果在BX寄存器中 near bx,0 ;BX赋初值
exit:
ret
decibin endp ;过程名:BINIHEX ;功能 ;入口 :将BX中的二进制数转换成十六进制数送屏幕输出 :BX中有待转换的二进制数 near ch,4 cl,4 bx,cl al,bl ;数据位数 ;循环移位次数 ;向左循环移位,将高四位移到低四位 ;取低字节到AL
binihex proc mov rotate: mov rol mov and add
dx cx
;非0则转换完,继续
pop
ret binihex endp code ends end
ax
;恢复寄存器
;返回 ;
start
除此以外,还可以通过地址表传送参数地址和通过堆栈传送参数或参数地址。
3.多个模块之间的参数传送问题 多个模块和程序之间的参数连接时,并不一定要把所有的代码段或数据段放在一起 形成一个大代码段。通常,各程序模块可有各自的分段,通过模块之间的调用来进行工 作。 (1)外部符号——在某一个模块中定义,而在另一个模块中引用的符号称为外部符号, 包括变量,标号,过程名等。
第6章 子程序结构
子程序又称为过程,相当于高级 语言中的过程和函数,引入过程的目 的是为了减少相同功能的程序段在程 序中的重复出现,提高代码的可重用 性,也是模块化的程序设计的基础。
6.1子程序的设计方法
6.1.1 过程定义伪操作 过程定义的一般形式: 过程名 PROC 属性 … 过程名 其中: 过程名为标识符,它又是子程序的入口地址,它的写法和标识符的写法相同。 属性是指类型属性,它可以是NEAR或FAR 说明: 1.过程是位于代码段中的子程序,由调用程序通过CALL指令调用。 2.过程的最后一个语句是RET语句。 ENDP
6.1.4 子程序的变量传送 调用程序在调用子程序时经常需要传送一些参数给子程序; 子程序运行完成后也经常要回送一些信息给调用程序。这种调用 程序和子程序之间的信息传送称为变量传送(或称为参数传送或 过程通信)。变量传送方式有以下几种: 1. 通过寄存器传送参数; 2. 如果过程和调用程序在同一程序模块中,则直接访问模块中 的变量; 3. 通过地址表传送参数地址; 4. 通过堆栈传送参数或参数地址; 5. 多个模块之间的参数传送问题可通过对引用符号的说明后在 连接过程中解决。
end main
2.如子程序和调用程序在同一个源文件中,则可以直接访问模块中的变量
例6.4 主程序MAIN和过程PROADD在同一源文件中,要求用过程 PROADD累加数组中的所有元素,并把和(不考虑溢出的可能性) 送到指定存储单元中去。
data ary count sum data code main segment dw dw dw ends segment proc far ;定义代码段 ;定义数据段 100,200,300,400,9000 ;其和为十进制数10000十六进制数2710H ($-ary)/2 ? ;数据个数 ;和存放单元
SUBT
PROC … RET
FAR
SUBT为一个过程,它 有两处被调用,一处是与它 在同一段的SEGX段内,另 一处是在另一段SEGY段内, 为此它必须声明具有FAR属 性以适应SEGY段调用的需 要。SUBT既有FAR属性, 则不 论 在 SEGX和 SEGY段 对 SUBT 的 调 用 就 都 具 有 FAR属性了,这样不会发生 什么错误,反之,如果这里 的SUBT使用了NEAR属性, 则SEGY段能者多劳它调用 就会出错了。
SUBT PROC NEAR PUSH AX PUSH BX PUSH CX PUSH DX

POP POP DX CX
为了避免这种错误的产生,在一 进入子程序后就应该把子程序所 需要用到的寄存器内容保存在堆 栈中,而在退出子程序前把寄存 器内容恢复原状。例如:
POP
POP RET
BX
AX
SUBT ENDP
相对而言,在本模块中定义又在本模块中引用的符号称为局部符号。
有两个伪操作与外部符号有关。 • PUBLIC伪操作:
ห้องสมุดไป่ตู้
格式:PUBLIC symbol[, ….]
作用:用于说明本模供其他模块调用的符号。 • EXTRN伪操作: 格式:EXTRN symbol name:type [, ….] 其中,type 对变量而言可以是byte,word,dword等, 而对标号而言,可以是near或far。 作用:用于说明本模供要引用的在其他模块定义的符号。 这两个伪操作就提供了模块之间相互访问的可能性。使用时,要注意两者的匹配。
3.当调用程序和过程在同一个代码段中时,定义过程为NEAR属性
4.当调用程序和过程不在同一个代码段中时,定义过程为FAR属性
例6.1调用程序和子程序在同一代码段中。 MAIN … CALL … SUBR1 PROC FAR
或定义成 MAIN … CALL … SUBR1 PROC FAR
RET
MAIN ; ENDP ; SUB1 PROC
assume cs:code,ds:data start: push ds
sub push mov mov call
ax,ax ax ax,data ds,ax near ptr proadd
;建立返回堆栈
;DS指向数据段
;调用proadd子程序
mov
call ret
bx,sum
binihex ;将和输出显示
ch rotate
;
;位数减1 ;非0则转换完,继续
ret
binihex endp ;过程名:CRLF
;返回
;功能 :输出回车换行
crlf proc mov mov int near dl,0dh ah,2 21h crlf
mov mov int ret endp
dl,0ah ah,2 21h
decihex ends
在子程序设计时,应该仔细考虑哪些寄存器是必须保存的, 哪些寄存器是不必要或不应该保存的。 一般说来,子程序中用到的寄存器是应该保存的。但是, 如果使用寄存器来在主程序和子程序之间传送参数的话, 这种寄存器就不一定需要保存,特别是用来向主程序回送 结果的寄存器,就更不应该保存和恢复寄存器而破坏了应 该向主程序传送的信息。
相关主题