CALL和RET指令

call和ret属于转移指令,都修改IP或者CS,IP。进程用来设计子程序,也就是函数

ret和retf

ret指令用栈中的数据来修改IP,实现近转移

retf指令用栈中的数据来修改cs和ip,实现远跳转

ret

在汇编中执行ret的指令时,需要进行两步操作

(ip)=((ss)*16+(sp))

(sp)=(sp+2)

意思就是把当前栈的东西送给ip,然后再栈顶指针下移,相当于就是pop ip

retf

执行retf指令时分为四步操作:

1 (ip)=((ss)*16+(sp))

2 (sp)=(sp)+2

3 (CS)=((ss)*16+(sp))

4 (sp)=(sp)+2

相当于:

pop ip

pop cs

例:

assume cs:code,ss:stack

stack segment
db 16 dup (0)
stack ends

code segment
mov ax,4c00h
int 21h

start:
mov ax,stack
mov ss,ax
mov sp,16
;init stack

mov ax,0
push ax
mov bx,0
ret
code ends

end start

这个程序表示的意思就是ret执行后,cs:ip跳转到第一条指令来执行

assume cs:code,ss:stack

stack segment
db 16 dup (0)
stack ends

code segment
mov ax,4c00h
int 21h

start:
mov ax,stack
mov ss,ax
mov sp,16
; init stack

mov ax,0
push cs
push ax
mov bx,0
retf
code ends

end start

执行retf后,回到了第一条指令

 

实现从内存1000:0000开始执行指令

stack segment
db 16 dup (0)
stack ends

code segment
assume cs:code,ss:stack
start:
mov ax,stack
mov ss,ax
mov sp,16
;init stack
mov ax,1000h
push ax
mov ax,0000h
push ax
retf
code ends

end start

CALL指令

CPU执行call指令时要进行两步操作

1 将当前的ip或者cs:ip入栈

2 转移

call指令不能断跳转,除此之外call指针实现转移的原理和jmp相同

依据位移进程转移的call指令

call标号(将当前的IP压栈后跳转到标号)

CPU执行CALL指令后进行的操作:

(sp)=(sp)-2

((ss)*16+(sp))=(ip)

(ip)=(ip)+16位位移

相当于进行了

push ip

jmp near ptr 标号

call标号

16位位移=“标号”最小的地址-call指令后的第一个字节的地址

16位范围

-32768~32767

例:

1000:0  mov ax,0
1000:3 call s
1000:6 inc ax
1000:7s: pop ax

这个程序执行后ax等于6

转移的目的地址在指令中的call指令

call far ptr 标号实现的是段间转移

CPU执行此种格式的call指令时,执行以下操作:

(sp)=(sp)-2

((ss)*16+(sp))=(cs)

(sp)=(sp)-2

((ss)*16+(sp))=(IP)

(CS)=标号的段地址

(IP)=标号的便宜偏移地址

执行call far ptr标号就相当于

push CS

push IP

jmp far ptr标号

例:

1000:0  mov ax,0
1000:3 call far ptr s
1000:8 inc ax
1000:9s:
pop ax
add ax,ax
pop bx
add ax,bx

执行完之后 ax=10h bx=1010h

转移地址在寄存器中的call指令

格式:call reg16

功能:

(sp)=(sp)-2

((ss)*16+(sp))=(ip)

(ip)=reg16

用汇编来描述就是

push IP

jmp reg16

例:

1000:0  mov ax,6    ;ax=6
1000:3 call ax
1000:5 inc ax
1000:6 mov bp,sp ;跳转到这里ip=5
;相当于push 5
add ax,[bp] ;6+5=11

ax=0bh

转移地址在内存中的call指令

转移地址在内存中有两种格式:

第一种格式

1 call word ptr 内存单元地址

用汇编来解释就是

push IP

jmp word ptr 内存单元地址

;例
mov sp,10h
mov ax,0123h
mov ds:[0],ax
call word ptr ds:[0]

执行后(IP)=0123h,(sp)=0Eh

第二种格式

call dword ptr

相当于执行

push CS

push IP

jmp dword ptr 内存单元地址

CS

;例
mov sp,10h
mov ax,0123h
mov ds:[0],ax
mov word ptr ds:[2],0
call dword ptr ds:[0]

(cs)=0,(IP)=0123h,(sp)=0CH

CS在高位,IP在低位

stack segment
dw 8 dup (0)
stack ends

code segment
assume cs:code,ss:stack
start:
mov ax,stack
mov ss,ax
mov sp,16
;init stack
mov ds,ax
mov ax,0
call word ptr ds:[0EH] ;跳转到栈顶修改IP的值
;push ip=inc ax的地址
inc ax
inc ax
inc ax

mov ax,4c00h
int 21h

code ends

end start

call和ret的配合使用

分析第一个简单程序

code segment
assume cs:code
start:
mov ax,1
mov cx,3 ;表示循环3次

call s

mov bx,ax ;bx=8
mov ax,4c00h
int 21h

s: add ax,ax
loop s
ret ;返回时ax=8

code ends

end start

分析第二个程序

assume cs:code

stack segment
db 8 dup (0)
db 8 dup (0)
stack ends

code segment

start:
mov ax,stack
mov ss,ax
mov sp,16
;init stack

mov ax,1000
call s

mov ax,4c00h
int 21h

s:
add ax,ax
ret
code ends
end start

其实就和设计函数差不多

模块化程序设计

利用call和ret来进行各种函数子进程的编写来达成模块化的程序设计

参数和结果传递的问题

通常利用寄存器和栈来存储函数的各种内容,比如参数,返回值等等