汇编——子程序调用参数传递的三种方式(示例程序:三个数累加求和)

一、子程序定义

子程序名      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

这个例子没有我们常见的数据段,二是把数据定义在了代码段,子程序在执行时,需要用数据时,访问存放数据的内存单元,并且在子程序执行要结束时,改变原来的断点地址,跳过代码段中的数据存储区,到下一片段的执行代码区。

你可能感兴趣的:(汇编)