Call指令
作用:调用子程序
实质:流程转移
call指令实现转移得方法和jmp指令的原理相似
格式:call标号
CPU执行call指令,进行两步操作:
(1)将当前的IP或CS和IP压入栈中
(2)转移
call标号
CPU执行此种格式的call指令时,进行如下的操作:
(1)(sp)=(sp)-2
((ss)*16+(sp))=(IP)
(2)(IP)=(IP)+16位位移
16位位移=标号处的地址-call指令后的第一个字节的地址;
16位移位移的范围-32768~32767;
16位位移由编译程序在编译时算出;
之前的博客中我们讲过jmp指令的运转方式
那这里类似于
push IP
jmp near ptr 标号(段内转移)
“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 标号(段间转移)
转移地址在寄存器中的call指令
指令格式:
call 16位reg
功能:
(sp)=(sp)-2
((ss)*16+(sp))=(IP)
(IP)=(16位reg)
相当于:
push IP
jmp 16位 reg
转移地址在内存中的call指令
(1)call word ptr 内存单元地址
用汇编语法来解释此种格式的call指令
CPU执行“call word ptr 内存单元地址“时。相当于进行:
push IP
jmp word ptr 内存单元地址
Ag:
mov sp,10H
mov ax,0123H
mov ds:[0],ax
call word ptr ds:[0]
执行后
IP=0123H
sp=0EH
(2)call dword ptr 内存单元地址
用汇编语法来解释此种格式的call指令
CPU执行“call dword ptr 内存单元地址“时。相当于进行:
push CS
push IP
jmp dword ptr 内存单元地址
Ag:
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
ret和retf
ret指令用栈中的数据,修改IP的内容,从而实现近转移
rerf指令用栈中的数据,修改CS和IP的内容,从而实现远转移
CPU执行ret指令时
(1)(IP)=((ss)*16+(sp))
(2)(sp)=(sp)+2
相当于:
POP IP
Ag
上图中在mov bx,0后少写了
ret这里注意一下
CPU执行retf指令时
(1)(IP)=((ss)*16+(sp))
(2)(sp)=(sp)+2
(3)(CS)=((ss)*16+(sp))
(4)(sp)=(sp)+2
相当于:
POP IP
POP CS
Ag
call和ret的配合使用
具有子程序的源程序的框架如下
assume cs:code
code segment
main:
call sub1 ;调用子程序 sub1
mov ax,4c00h
int 21h
sub1:
call sub2
ret
sub2
ret
code ends
end main
我们通过问题来加深前面的理解
计算2的N次方
我们执行命令后看到IP的值确实返回了
同时算出的值在BX中
但是有个问题
我们观察到这个栈段,使它随机分配的,这是很危险的,可能有时候是运行不了的
正确的程序
我们通过d命令去看一下,执行call指令后栈中的数据
尾部A3 01
的确是压栈了
mul指令
前面的博客中我写了关于div的除法指令
这里mul就是乘法指令
(1)两个相乘的数:两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认放在AL中,另一个放在8位reg或内存字节单元中;如果是16位,一个默认在AX中,另一个放在16位reg或内存子单元中
(2)结果:如果是8位乘法,结果默认放在AX中;如果是16位乘法,结果高位默认在DX中存放,低位在AX中放
格式如下;
mul reg
mul 内存单元
内存单元可以用不同的寻址方式给出
Ag
mul byte ptr ds:[0]
含义:(AX)=(AL)*((ds)*16+0)
mul word ptr [bx+si+8]
含义:(AX)=(AX)((ds)16+(bx)+(si)+8)结果的低16位
(DX)=(AX)((ds)16+(bx)+(si)+8)结果的高16位