第十章 CALL和RET指令
# 第十章 CALL 和 RET 指令
call 和 ret 都是转移指令,它们都修改 IP,或同时修改 CS 和 IP。它们经常被共同用来实现子程序的设计。
# 10.1 ret 和 retf
这个两个指令可以理解为高级语言中的 return 关键字,表示程序返回。
ret 用栈中的数据,修改 IP 的内容,从而实现近转移;
retf 指令用栈中的数据,修改 CS 和 IP 的内容,从而实现远转移。
CPU 执行 ret 指令时,进行下面两步操作:
(1)(IP) = ((SS)*16+(SP))
(2)(sp) = (sp)+2
以上步骤相当于进行:
pop IP
CPU 执行 retf 指令时,进行下面 4 步操作:
(1)(IP) = ((SS)*16+(SP))
(2)(sp) = (sp)+2
(3)(CS) = ((SS)*16+(SP))
(4)(sp) = (sp)+2
以上步骤相当于进行:
pop IP pop CS
# 10.2 call 指令
call 指令可以理解为高级语言中的方法(函数)调用功能。
CPU 指令 call 指令时,进行两步操作:
(1)将当前的 IP 或 CS 和 IP 压入栈中。 (保存现场) (2)转移。
call 指令不能实现短转移,除此之外,call 指令实现转移的方法和 jmp 指令的原理相同。
# 10.3 依据位移进行转移的 call 指令
指令格式:
call 标号
CPU 执行该指令时相当于进行:
push IP jmp near ptr 标号
# 10.4 转移的目的地址在指令中的 call 指令
指令格式:
call far ptr 标号
CPU 执行该指令时相当于进行:
push CS push IP jmp far ptr 标号
该指令编译的机器指令中包含了转移的目的地址。包括段地址 CS 的值及偏移地址 IP 的值。
# 10.5 转移地址在寄存器中的 call 指令
指令格式:
call 16 位 reg
CPU 执行该指令时相当于进行:
push IP jmp 16 位 reg
# 10.6 转移地址在内存中的 call 指令
转移地址在内存中的 call 指令有两种格式。
(1)第一种指令格式:
call word ptr 内存单元地址
CPU 执行该指令时相当于进行:
push IP jmp word ptr 内存单元地址
(2)第二种指令格式:
call dword ptr 内存单元地址
CPU 执行该指令时相当于进行:
push CS push IP jmp dword ptr 内存单元地址
# 10.7 call 和 ret 的配合使用
call 和 ret 的配合使用可以用来实现子程序的机制。call 指令在转去执行子程序之前,会将当前指令下一条指令的位置保持在栈中,当子程序执行 ret 或 retf 指令后,会用栈中的数据设置 ip 或 cs 和 ip 的值,从而转到 call 指令后面的代码处继续执行。
# 10.8 mul 指令
(1)两个相乘的数:练歌相乘的数,要么都是 8 位,要么都是 16 位。如果是 8 位,一个默认放在 AL 中,另一个放在 8 位 reg 或内存字节单元中;如果是 16 位,一个默认放在 AX 中,另一个放在 16 位 reg 或内存字单元中。
(2)结果:如果是 8 位乘法,结果默认放在 AX 中;如果是 16 位乘法,结果高位默认在 DX 中存放,低位在 AX 中存放。
# 10.9 模块化程序设计
现实问题比较复杂,对现实问题进行分析时,把它转化为相互联系、不同层次的子问题,是必须的解决方法。
在高级语言中的函数或者方法就是这种思想的体现。在汇编语言中我们将高级语言中的方法或函数称之为子程序。
# 10.10 参数和结果传递的问题
当我们设计子程序时面临两个问题:
(1)参数存放的位置?
(2)计算结果存放的位置?
实际上,我们可以将参数及结果存放于任何可以存储数据的地方。一般情况下,我们可以将参数存储在寄存器中,也可以存储在普通内存单元中。更一般的做法我们将其存储在栈中进行传递。
# 10.11 寄存器冲突的问题
寄存器数量是有限的,子程序中使用的寄存器,很可能在主程序中也要使用,造成了寄存器使用上的冲突。解决这个问题的简捷方法是,在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前再恢复。 可以用栈来保存寄存器中的内容。
栈是临时保存数据的一个比较理想的数据结构。