第十章 CALL和RET指令
call和ret都是转移指令,它们都修改IP,或同时修改CS和IP,常用于子程序的设计。
10.1 ret 和 retf
ret用栈中的数据,修改IP的内容,从而实现近转移
retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
ret指令
1) (IP) = ((ss)*16 + (sp))
2) (sp) = (sp) + 2
相当于cpu执行来pop IP
retf指令
1) (ip) = ((ss)*16 + (sp))
2) (sp) = (sp) + 2
3) (cs) = ((ss) * 16 + (sp))
4) (sp) = (sp) + 2
相当于cpu执行来pop IP , pop CS
以下代码执行以后将跳转到代码段第一条指令
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
mov ax, 4c00h
int 21h
start: mov ax, stack
mov ss, ax
mov sp, 10h
mov ax, 0
push ax
mov bx, 0
ret
code ends
end start
将代码跳转至1000:0000H处
assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
start: mov ax, stack
mov ss, ax
mov sp, 10h
mov ax, 1000h
push ax
mov ax, 0
push ax
retf ; equal pop ip, pop cs
code ends
end start
10.2 call指令
call执行
1)将当前的IP或cs和IP压入栈
2)转移
call不能实现短转移,转移方法和jmp指令的原理相同。
10.3 根据位移进行转移的call指令
call 标号(将当前的IP压栈后,转移到标号处执行指令
1) (sp) = (sp) - 2
((ss)* 16 + (sp)) = (ip)
2) (ip) = (ip) + 16位位移
16位位移=标号处的地址-call指令后的第一个字节的地址;
16位位移的范围为-32768~32767,用补码表示
16位位移由编译时算出
即
push IP
jmp near ptr 标号
10.4 转移的目的地址在指令中的call指令
call far ptr 标号 实现段间转移
1) (sp) = (sp) -2
((ss)*16 + (sp)) = (cs)
(sp) = (sp) - 2
((ss)*16 + (sp)) = (ip)
2) (cs) = 标号所在的段地址
(IP) = 标号所在的偏移地址
相当于
push cs
push ip
jmp far ptr 标号
10.5 转移地址在寄存器中的call指令
call 16位 reg
(sp) = (sp) - 2
((ss)*16 + (sp)) = (ip)
(ip) = (16位reg)
即
push IP
jum 16位reg
10.6 转移地址在内存中的call指令
两种格式
1) call word ptr 内存单元地址
push IP
jmp word ptr 内存单元地址
2) call dword ptr 内存单元地址
push CS
push IP
jmp dword ptr 内存单元地址
监测点10.5
1)
assume cs:code
stack segment
dw 8 dup (0)
stack ends
code segment
start: mov ax, stack
mov ss, ax
mov sp, 10h
mov ds, ax
mov ax, 0
call word ptr ds:[0eh]
inc ax
inc ax
inc ax
mov ax, 4c00h
int 21h
code ends
end start
该代码执行到 call word ptr ds:[0eh]
的时候
首先
a. 执行push ip (此时的ip是指向下一条inc ax的地址这里称为A) sp = 10h -2 = 0eh ((ss)*16 + (sp)) = A
b. jmp word ptr ds:[0eh] 这里的[0eh]就是ss:sp指向的地址A 因此直接跳转至A处执行call语句后面的inc ax
然后inc 3次 ax
最后ax = 3
使用debug单步调试的时候debug程序也会使用相同的stack因此会产生奇怪的问题。
2)ax = 1, bx = 0
注意call dword ptr 会产生的几个push pop 会改变栈的值和sp的值
10.7 call 和 ret 的配合使用
利用call 和 ret 实现的子程序框架如下
assume cs:code
stack segment
dw 8 dup (0)
stack ends
code segment
main: ;
;
; call sub1
;
;
mov ax, 4c00h
int 21h
sub1: ;
;
; call sub2
;
ret
sub2: ;
;
ret
code ends
end main
10.8 mul指令
1) 两个相乘的数,要么都是8位。要么都是16位。 如果是8位一个默认在AL 另一个存放在8位reg 或内存字节单元中
如果是16位,一个默认中AX中,另一个放在16位reg或者内存字单元中
2)结果,如果是8位乘法,结果默认放在AX中; 如果是16位乘法,结果高位默认在DX,低位在AX中存放
mul reg
mul 内存单元
内存单元可以用不同的寻址方式
mul byte ptr ds:[0]
含义: (ax) = (al) * ((ds)*16 + 0)
mul word ptr [bx + si + 8]