[汇编语言]子程序结构--五种子程序的参数传送[重点]

1.过程定义伪操作

过程名 PROC 属性

过程名 ENDP
其中过程名为标识符,它又是子程序入口的符号地址。属性是指类型属性们可以为NEAR/FAR

(1)NEAR属性:调用程序和子程序在同一个代码段中,(段内调用)
(2)FAR属性,调用程序和子程序不在同一个代码段中(段间调用)

2.子程序的参数传送

2.1通过寄存器传送参数

在例1与例2中,都是通过bx寄存器,在不同的过程中进行传递数值

例1:十六进制到十进制的转换。要求从键盘输入的0-FFFFH的正整数转换为十进制数并在屏幕上显示出来(通过寄存器传送参数)
详细解读

hexidec segment
	assume cs:hexidec
main proc far
	 push ds
	 mov ax,0
	 mov ds,ax
repeat:
	call hexibin
	call crlf
	call binidec
	call crlf
	jmp repeat
	ret
main endp
hexibin proc near
	mov bx,0
newchar:
	mov ah,2
	int 21h
	sub al,30h
	jl exit
	cmp al,10
	jl add_to
	sub al,27h
	jl exit
	cmp al,10h
	jge exit
add_to:
;这里得注意先左移还是先加,应该是先左移,再相加,实际上是左移三次,加法四次,如果左移四次,那么第一次的结果就被移出了。所以需要损耗一次
	mov cl,4
	shl bx,cl
	mov ah,0
	add bx,al
	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
binidec endp
dec_div proc near
	mov ax,bx
	mov dx,0
	div cx
	mov bx,dx
	mov dl,al
	add dl,30h;即使我们知道商在低位AX当中,但是输出的时候需要ascll码,因此还要加上30h
	mov ah,2
	int 21h
	ret
dec_div endp	
crlf proc far
	mov al,0dh
	mov ah,2
	int 21h
	mov dl,0ah
	mov ah,2
	int 21h
	ret
crlf endp
hexidec ends
	end main

例2:十进制到十六进制的转换程序,程序要求从键盘取得一个十进制数,然后该数以十六进制形式在屏幕上显示出来

decihex segment
	assume cs:decihex
main proc far
repeat:	
	call decibin
	call crlf
	call binihex
	call crlf
	jmp repeat
main endp
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
	mov cx,10
	mul cx
	xchg ax,bx
	add bx,ax
	jmp newchar
exit:
	ret
decibin endp
binihex proc near
	mov ch,4
	mov cl,4
again:
	rol bx,cl
	mov al,bl
	and al,0fh
	add al,30h
	cmp 3ah
	jl prinit
	add al,7
prinit:
	mov dl,al
	mov ah,2
	int 21h
	dec ch
	jnz again
	ret
binihex endp
crlf proc far
	mov al,0dh
	mov ah,2
	int 21h
	mov dl,0ah
	mov ah,2
	int 21h
	ret
crlf endp
decihex ends
	end main

2.2 通过存储器传送参数

在例3中过程PROADD直接访问模块的数据 区

例3:主程序MAIN和过程PROADD在同一源文件当中,要求用过程累加数组中的所有元素,并把和(不考虑溢出的可能性)送到指定的存储单元中去。

data segment
	ary dw 1,2,3,4,5,6,7,8,9,10
	count dw 10
	sum dw ?
data ends
code segment
main proc far
	assume cs:code,ds:data
	mov ax,data
	mov ds,ax
	
	call proadd
	
	mov ax,4c00h
	int 21h
main endp
proadd proc near
	push ax
	push cx
	push si
	lea si,ary
	mov cx,count
	mov ax,0
add_to:	
	add ax,ary[si]
	add si,2
	loop add_to
	mov sum,ax
	pop si
	pop cx
	pop ax
	ret
proadd endp
code ends
	end main 

然而在过程proadd中,使用了lea si,ary
因此直接访问内存变量,那么如果此时需要累加数组ary1
则会出现累加ary和数组ary1不能用同一个子程序proadd

2.3通过地址表传送参数地址

依然是刚刚的例题,累加数组中的元素

data segment
	ary dw 10,20,30,40,50,60,70,80,90,100
	count dw 10
	sum dw ?
	table dw 3 dup(?);地址表
data ends
code segment
main proc far
	assume cs:code,ds:data
	push ds
	sub ax,ax
	push ax
	mov ax,data
	mov ds,ax
	mov table,offset ary
	mov table+2,offset count
	mov table+4,offset sum
	mov bx,offset table
	call proadd
	ret
main endp
proadd proc near
	push ax
	push cx
	push si
	push di
	mov si,[bx];值得注意的是这是寄存器间接寻址方式,bx寄存器存放的是存储单元的地址,因此是将bx寄存器里地址指向的存储单元赋值给si
	;而这个存储单元里面存储的依旧是地址,因此后面出现[si]进一步寻址
	mov di,[bx+2]
	mov cx,[di]
	mov di,[bx+4]
	xor ax,ax
next:	
	add ax,[si]
	add si,2
	loop next
	mov [di],ax
	pop di
	pop si
	pop cx
	pop ax
	ret
proadd endp
code ends
	end main

在这里插入图片描述

2.4通过堆栈传送参数地址

data segment
	ary dw 10,20,30,40,50,60,70,80,90,100
	count dw 10
	sum dw ?
data ends
stack segment
	dw 100 dup(?)
	tos label word
stack ends

在这里:dup为重复的意思,在堆栈段中开辟了200个字节(100个字)。tos(top of stack),并非关键字,tos label word 是伪指令,label的意义是指向下一个要被赋予地址的地方,并且并不分配空间。LABEL 伪指令可以插入一个标号,并定义它的大小属性,但是不为这个标号分配存储空间。
在这里插入图片描述在这里插入图片描述

如图,会提示没有堆栈段,为了解决这个问题,使用stack关键字:stack segment stack(后面的是关键字),有了这个关键字,也不需要手动为ss和sp赋初值了。
在这里插入图片描述

code1 segment
main proc far
	assume cs:code1,ds:data,ss:stack
start:
	mov ax,stack
	mov ss,ax
	mov sp,offset tos
	;这里需要初始化ss寄存器和sp寄存器(也就是栈顶指针)
	mov ax,data
	mov ds,ax
	;初始化data段
	mov bx,offset ary
	push bx
	mov bx,offset count
	push bx
	mov bx,offset sum
	push bx
	;压入堆栈
	call far ptr proadd
	;在分析堆栈情况时,call会调用push,并且由于这里是段间直接远调用
	;因此会先后push cs;push ip
	
	mov ax,4c00h
	int 21h
main endp

通常用一个叫做栈基址(bp)的寄存器来保存正在运行函数栈帧的开始地址,由于栈指针(sp)始终保存的是栈顶的地址,所以栈指针保存的也就是正在运行函数栈帧的结束地址。

code2 segment
	assume cs:code2
proadd proc far
	push bp
	mov bp,sp;sp会随着push和pop指令自动的增加与减少
	
	push ax
	push cx
	push si
	push di

	mov si,[bp+0ah];这里依然得注意这里si里的内容是array的偏移地址
	mov di,[bp+8]
	mov cx,[di]
	mov di,[bp+6]
	
	xor ax,ax;相当于初始化一样,这个还会把标志位初始化
	;XOR/AND/OR
	;CF/OF清零,其余根据结果进行判定
next:
	add ax,[si]
	add si,2;注意这里是2,字
	loop next
	mov [di],ax
	pop di
	pop si
	pop cx
	pop ax
	pop bx
	ret 6
proadd endp
code2 segment

其实最后的代码主体加法部分已经并不复杂了,但是这里得注意返回的立即数
在这里插入图片描述
在这里插入图片描述
最后的sp应该指向最下面的tos所指的存储单元

2.5多个模块之间的参数传递

局部符号:在本模块中定义,在本模块中引用的符号
外部符号:在某一模块中定义,在另一模块中引用的符号
public 符号 extern 符号:类型
在这里插入图片描述

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值