CALL和RET指令
ret和retf
- ret指令用栈的数据,修改IP的内容,实现近转移
简而言之,就是从栈中弹出一个数值给IP
- retf指令用栈的数据,修改CS和IP的内容,实现远转移
简而言之,就是从栈中依次弹出两个分别给CS和IP
call指令
根据位移进行转移
call 标号
- 将当前IP或CS和IP压入栈中
- 转移
相当于:
push IP
jmp nera ptr 标号
在此时栈中存储call指令之后下一条指令的IP,如:
assume cs:code
stack segment
db 16 dup(0)
stack ends
code segment
start: mov ax,0 ;0E25:0000
call s ;0E25:0003
inc ax ;0E25:0006
s: pop ax ;0E25:0007
code ends
end start
pop ax:得到ax=0006 (call下一条指令的IP)
转移目的地址在指令中
call far ptr 标号
实现段间转移
相当于
push CS
push IP
jmp far ptr 标号
此时,栈中存储CS和IP:
assume cs:code
code segment
start: mov ax,0 ;1000:0
call far ptr s ;1000:3
inc ax ;1000:8
s: pop ax ;1000: 9
add ax,ax
pop bx
add ax,bx
code ends
end start
call far ptr s:执行后,push CS,push IP,jmp s => ax=IP=8,bx=CS=1000
转移地址在寄存器中
call 16位 reg
相当于:
push IP
jmp 16位reg(寄存器)
把IP入栈后,跳转到标号寄存器中:
assume cs:code
code segment
start: mov ax,9 ; 1000:0
call ax ; 1000:3
inc ax
inc ax
mov bp,sp
add ax,[bp]
code ends
end start
开始时ax=9,执行call ax后,把IP=3入栈,之后跳转到IP=0009(即ax的偏移地址)处
转移地址在内存中
call word ptr 内存单元地址
相当于:
push IP
jmp word ptr 内存单元地址
转移到内存单元地址:
IP:
assume cs:code
code segment
start: mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]
code ends
end start
执行完毕后:IP=0123 SP=0EH
CS和IP:
assume cs:code
code segment
start: mov sp,10h
mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2],0 ;23 01 00 00
call dword ptr ds:[0] ;双字节,CS=0000 IP=0123
code ends
end start
执行完毕后:CS=0000 IP=0123 SP=0EH
使用call和ret来编写子程序
call指令:可以将IP或者CS和IP压入栈中
ret指令:将IP弹出栈
利用他们入栈出栈的机制可以实现一个子程序的编写,例如:
在主程序中用call去跳转到一个子程序,call保存之后的偏移地址IP,将其压入栈中,以便能够执行完子程序之后返回,call之后,跳转到子程序标号,执行子程序,执行完毕后,使用ret弹出IP栈值,这时的IP指向call之后的地址,因此会跳转回call之后的下一条指令,重新指向主程序。
assume cs:code
code segment
start: mov ax,1
mov cx,3
call s ;1000:0006
mov bx,ax ;1000:0009
mov ax,4c00h
int 21h
s: add ax,ax ;1000:0010
loop s
ret
code ends
end start
分析:主程序call s的IP地址为06,首先将call的下一条指令的IP压入栈中push IP,之后跳转到s标号处,执行cx次add ax,ax 之后ret指令将pop IP,此时的IP为之前压入栈中的IP,IP=09,所以再次执行mov bx,ax,直到程序结束