汇编10:CALL和RET指令

Posted yinyunmoyi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了汇编10:CALL和RET指令相关的知识,希望对你有一定的参考价值。

CALL和RET指令

call和ret指令都是转移指令,它们经常被共同用来实现子程序的设计。

ret和retf

ret指令用栈中的数据实现修改IP的内容,从而完成近转移,执行ret指令时相当于执行:

pop IP

retf指令用栈中的数据实现修改CS和IP的内容,从而完成远转移,执行retf指令相当于执行:

pop IP
POP CS

在程序中就可以使用这两个指令来转移:

code segment
	mov ax,4c00h
	int 21h
start:	...
	...
	ret
code ends
end start

这样由于ret指令的执行,就会让CS:IP指向代码段的第一条指令,完成返回。

call指令

call指令执行后进行两步操作:

1、将当前的IP或CS和IP压入栈中

2、转移

转到标号处

call指令可以转到标号处,同时将当前的IP压入栈中:

call 标号

相当于执行:

push IP
jmp near ptr 标号

这个指令对应的机器码中有相对于当前IP的转移位移。

对应的段间转移指令:

call far ptr 标号

相当于执行:

push CS
push IP
jmp far ptr 标号

这个指令对应的机器码中有转移的目的地址。

使用内存地址和寄存器

call指令后也可以跟寄存器:

call 16位reg

相当于执行:

push IP
jmp 16位reg

call指令后也可以加内存地址,一般有两种形式:

1、段内转移:

call word ptr 内存单元地址

相当于执行:

push IP
jmp word ptr 内存单元地址

2、段间转移:

call dword ptr 内存单元地址

相当于执行:

push CS
push IP
jmp dword ptr 内存单元地址

实现子程序机制

结合ret和call指令我们可以总结一个实现子程序的框架:

assume cs:code
code segment
	main:	...
		...
		call sub1					调用了子程序sub1
		...
		...
		mov ax,4c00h
		int 21h
	sub1:	...
		...
		call sub2					调用了子程序sub2
		...
		ret						sub1返回
	sub2:	...
		...
		ret						sub2返回
			
code ends
end main

乘法指令mul

使用格式:

mul reg
mul 内存单元

涉及内存单元时,可以中间加word ptr或byte ptr来指定处理的数据长度。

乘法指令中两个相乘的数要么都是8位,要么都是16位;如果都是8位,则一个默认放在AL寄存器中;如果都是16位,则一个默认放在AX寄存器中。

如果是8位乘法那么结果默认放在AX中,如果是16位乘法,那么高位默认放在DX中,低位放在AX中。

模块化程序设计

有了子程序我们就可以实现汇编语言编程的模块化设计。

参数和结果传递的问题

在使用子程序时有两个问题:参数存在哪里?结果存在哪里?

最常见的方法是使用寄存器,调用子程序者将参数送入参数寄存器,从结果寄存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。

多个参数和多个结果的传递

如果我们要传递更多的参数怎么办?我们不可能都用寄存器来完成传递,因为寄存器的个数终究是有限的。

这个时候常用的做法是将批量数据放到内存中,然后将他们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于多个返回结果,我们也可以用同样的方法。

栈也可以用来完成这一类任务。

寄存器冲突问题

有时程序中会出现寄存器冲突问题,如在一个循环中调用某个子程序,子程序中也有循环,这些循环共用一个cx寄存器来计数,就会导致循环次数的混乱。此时正确的做法应该是在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前都恢复。

案例需求:把数据段中的所有字符串都转换为大写:

assume cs:code
data segment
	db ‘word‘,0
	db ‘unix‘,0
	db ‘wind‘,0
	db ‘good‘,0
data ends

此时我们需要写一个子程序来处理单个字符串转换为大写:(ds:si指向字符串的首地址)

capital:mov cl,[si]
	mov ch,0
	jcxz ok							    如果cx为0,就结束
	and byte ptr [si],11011111b				    否则就将该字母转换为大写,然后再将si自增
	inc si
	jmp short capital
ok:	ret

但是这个子程序没有完成环境保存,所以需要再改动一下,加上数据恢复和取用:

capital:push cx
	push si
change:	mov cl,[si]
	mov ch,0
	jcxz ok								如果cx为0,就结束
	and byte ptr [si],11011111b				        否则就将该字母转换为大写,然后再将si自增
	inc si
	jmp short change
ok:	pop si
	pop cx
	ret

然后再调用该子程序即可:

code segment
	start:	mov ax,data
		mov ds,ax
		mov bx,0						指定总的循环次数
			
		mov cx,4
	s:	mov si,bx						处理字符串,然后将指针加5,处理下一个字符串
		call capital
		add bx,5
		loop s
			
		mov ax,4c00h
		int 21h
			
	capital:push cx
		push si
	change:	mov cl,[si]
		mov ch,0
		jcxz ok							如果cx为0,就结束
		and byte ptr [si],11011111b				否则就将该字母转换为大写,然后再将si自增
		inc si
		jmp short change
	ok:	pop si
		pop cx
		ret
			
code ends
end start

以上是关于汇编10:CALL和RET指令的主要内容,如果未能解决你的问题,请参考以下文章

汇编-10.0-CALL和RET指令

汇编入门学习笔记 —— call和ret

关于汇编指令call和ret的具体细节操作!

汇编语言——call 和 ret 指令

汇编_ret_retf_call指令使用说明

8086汇编之 CALL 和 RET指令