call和ret都是转移指令。
它们修改IP或者同时修改CS和IP。
ret用栈中的数据修改IP实现近转移。
retf用栈中的数据修改CS和IP的内容实现远转移。
ret实际执行的步骤为:
(IP)=((SS)×16+(SP))
(SP)+=2
retf实际执行的步骤为:
(IP)=((SS)×16+(SP))
(SP)+=2
(CS)=((SS)×16+(SP))
(SP)+=2
由于往高地处移动和出栈的操作方向相同,因为出栈也是站定指针向高地址处移动,并且它们用的都是SS和SP。
所以ret相当于POP SP,retf相当于POP IP,POP CS。
分两步:
1、将当前的IP或CS和IP压入栈中。
2、转移。
它不能实现短转移。
call标号
将当前的IP压栈后,再转移到标号处执行指令。
具体步骤如下:
(SP)-=2
((SS)×16+(SP))=(IP)
(IP)+=(-215~215-1)
IP的位移实际为标号处地址减去call指令后第一个字节的地址。
用汇编语言解释如下:
PUSH IP
JMP NEAR PTR标号
CALL FAR PTR标号
它实现段间转移,实际步骤如下:
1、(SP)-=2
((SS)16+(SP))=(CS)
(SP)-=2
((SS)16+(SP))=(IP)
2、(CS)表示标号所在段的段地址,(IP)表示在段中的偏移地址。
用汇编语言解释如下:
PUSH CS
PUSH IP
JMP FAR PTR标号
用法:
call16位寄存器
作用为:
(SP)-=2
((SS)×16+(SP))=(IP)
IP是16位寄存器
汇编语言解释如下:
PUSH IP
JMP16位寄存器
格式1:
call word ptr内存单元地址
用汇编语言解释如下:
PUSH IP
JMP WORD PTR内存单元地址
格式2:
CALL DWORD PTR内存单元地址
汇编语言解释如下:
PUSH CS
PUSH IP
JMP DWORD PTR内存单元地址
在这里可以总结一下call和ret。
call就是调用,它可以调用子程序。
ret就是return,即,返回,返回到调用处。
call是压栈,ret是出栈。
call和ret搭配使用可以调用子程序,书写的框架如下:
标号:
指令
ret
P196~P197之间的程序段是个很好的例子。
P198给出了完整的程序调用架构,这就类似于函数调用。
mul:multiplication,乘法。
1、它要求乘数和被乘数位数相同,即,都是8位或者都是16位。
如果是8位,一个默认放在AL中无需指定,另一个放在8位寄存器或者内存字节单元中。
如果是16位,一个默认放在AX中无需指定,另一个放在16位寄存器或者内存字单元中。
2、结果。8位乘法结果放在AX中。16位乘法高位放在DX中,低位放在AX中。
格式如下:
mul寄存器/内存单元
编写子程序的标准框架:
子程序开始:子程序中使用的寄存器入栈
子程序内容
子程序中使用的寄存器出栈
返回(ret,retf)
这一框架能够解决程序中不同部分对寄存器使用冲突的问题。
div做除法运算是可能溢出的。
解决这一问题的办法是使用公式,在P208有关于它的一个简单说明。
它可以将一个可能产生溢出的除法运算变为多个不会产生溢出的除法运算。