汇编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指令的主要内容,如果未能解决你的问题,请参考以下文章