子程序名 PROC NEAR|FAR
.
.
RET
子程序名 ENDP
子程序名相当于标号,表示本过程的符号地址。过程有NEAR和FAR两种类型,FAR型的过程可供段间调用,NEAR型过程仅供段内调用。
在一个过程中,至少要有一条返回指令RET,它可以书写在过程中的任何位置,但是过程执行的的最后一条指令一定是RET。
格式:CALL [NEAR PTR] 子程序名
CALL指令的两个作用,1.将断点地址(CALL指令的下一条指令地址)压栈保存,2.转去子程序执行。
[NEAR PTR]可以省略。
格式:RET [N]
指令作用:实现子程序执行完后返回主程序的指令。从堆栈栈顶弹出一个字数据(段内调用)送入IP作为返回地址。N是立即数,执行完RET之后,再将SP增加N,也叫“平栈”。
程序示例:假设:N1=1234H,N2=2345H,N3=3456H,计算并显示这3个数的累加和,并用二进制形式显示结果
1.通过寄存器传递参数:调用子程序前,调用程序把入口参数放在约定的寄存器中,子程序执行时,通过约定的寄存器取得入口参数;返回时,子程序把出口参数存放在约定的寄存器中,调用程序通过约定的寄存器中取得出口参数。
DATA SEGMENT
NUM DW 1234H
DW 2345H
DW 3456H
DATA ENDS
STACKS SEGMENT STACK
DB 100 DUP(?)
STACKS ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACKS
BEG: MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET NUM
CALL COMPUTE
XYZ: CALL DISP
MOV AH,4CH
INT 21H
COMPUTE PROC
MOV BX,0
ADD BX,[SI+0]
ADD BX,[SI+2]
ADD BX,[SI+4]
RET
COMPUTE ENDP
DISP PROC
MOV CX,16
LAST: MOV DL,'0'
RCL BX,1
JNC NEXT
MOV DL,'1'
NEXT: MOV AH,2
INT 21H
LOOP LAST
RET
DISP ENDP
CODE ENDS
END BEG
本例程序中调用程序把2号功能入口参数放在DL寄存器中,显示子程序在执行时从DL中取得参数。
2.通过堆栈传递参数:子程序调用前,调用程序把参数依次压入堆栈,构成一个堆栈参数表,当子程序调用时,子程序从堆栈中取出各参数。子程序返回时,要使用 RET n 指令调整SP指针,其中n是堆栈参数表的大小,即使用完堆栈之后删除堆栈参数表,使堆栈恢复到原始状态,也就是文章开始提到的“平栈”。
DATA SEGMENT
NUM DW 1234H
DW 2345H
DW 3456H
DATA ENDS
STACKS SEGMENT STACK 'STACK'
DB 100 DUP(?)
STACKS ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,SS:STACKS
BEG: MOV AX,DATA
MOV DS,AX
MOV SI,OFFSET NUM
PUSH WORD PTR [SI+0]
PUSH WORD PTR [SI+2]
PUSH WORD PTR [SI+4]
CALL COMPUTE ;注意CALL指令隐含的堆栈操作
XYZ: CALL DISP
MOV AH,4CH
INT 21H
COMPUTE PROC
MOV BP,SP
MOV BX,0
ADD BX,[BP+2] ;栈底处在高地址段
ADD BX,[BP+4]
ADD BX,[BP+6]
RET 6
COMPUTE ENDP
DISP PROC
MOV CX,16
LAST: MOV DL,'0'
RCL BX,1
ADC DL,0
NEXT: MOV AH,2
INT 21H
LOOP LAST
RET
DISP ENDP
CODE ENDS
END BEG
3.通过变量传递参数:如果子程序和调用程序都在同一个源文件中,则双方可以直接访问模块中的变量,从而实现参数传递。
STACKS SEGMENT STACK 'STACK’
DB 100 DUP(?)
STACKS ENDS
CODE SEGMENT
ASSUME CS:CODE,SS:STACKS
BEG: CALL COMPUTE
NUM DW 1234H
DW 2345H
DW 3456H
XYZ: CALL DISP
EXIT: MOV AH,4CH
INT 21H
COMPUTE PROC
MOV BP,SP
MOV SI,[BP+0] ; [BP+0]为断点地址
MOV BX,0
ADD BX,CS:[SI+0]
ADD BX,CS:[SI+2]
ADD BX,CS:[SI+4]
POP AX ;弹出原来的断点地址
MOV AX,OFFSET XYZ ;改变栈顶内容,从而使返回地址改变
PUSH AX
RET ;返回断点XYZ
COMPUTE ENDP
DISP PROC
MOV CX,16
LAST: MOV AL,'0'
RCL BX,1
ADC AL,0
NEXT: MOV AH,0EH ;BIOS功能调用显示一个字符
INT 10H
LOOP LAST
RET
DISP ENDP
CODE ENDS
END BEG
这个例子没有我们常见的数据段,二是把数据定义在了代码段,子程序在执行时,需要用数据时,访问存放数据的内存单元,并且在子程序执行要结束时,改变原来的断点地址,跳过代码段中的数据存储区,到下一片段的执行代码区。