call和ret指令都是转移指令,它们都修改IP,或同时修改CS和IP。它们经常被共同用来实现子程序的设计。
ret和retf
ret指令用栈中的数据,修改IP的内容,从而实现近转移。
retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移。
当CPU执行ret指令时,相当于进行:pop IP
当CPU执行retf指令时,相当于进行:
pop ip
pop cs
call指令
CPU执行call指令时,进行两步操作:
- 将当前的IP或CS和IP压入栈中。
- 转移
call指令不能实现短转移,call指令实现转移方法和jmp指令的原理相同。
依据位移进行转移的call指令
call 标号(将当前的IP压栈后,转到标号处执行指令)
CPU执行这种格式的call指令进行的操作:
-
(sp)=(sp)-2
((ss)*16+(sp))=(ip)
-
(ip)=(ip)+16位位移
转移的目的地址在指令中的call指令
call far ptr 标号
实现的是段间转移。
CPU执行"call far ptr 标号"时,相当于进行:
push cs
push ip
jmp far ptr 标号
转移地址在寄存器中的call指令
指令格式:call 16位reg
相当于:
push ip
jmp 16位reg
转移地址在内存中的call指令
转移地址在内存中的call指令有两种格式:
-
call word ptr 内存单元地址
相当于进行:
push ip jmp word ptr 内存单元地址
-
call dword ptr 内存单元地址
相当于进行:
push cs push ip jmp dword ptr 内存单元地址
call和ret配合使用
写一个具有一定功能的程序段,称其为子程序,在需要的时候,用call指令转去执行。call指令转去执行子程序之前,call指令后面的指令的地址将存储在栈中,所以可在子程序的后面使用ret指令,用栈中的数据设置IP的值,从而转到call指令后面的代码处继续执行。
具有子程序的源程序框架:
assumce cs:code
code segment
main: ……
call sub1 ;调用子程序sub1
……
mov ax,4c00h
int 21h
sub1: …… ;sub1开始
call sub2 ;调用子程序sub2
……
ret ;子程序返回
sub2: …… ;sub2开始
ret ;子程序返回
code ends
end main
mul指令
mul是乘法指令。
- 两个相乘的数,要么都是8位,要么都是16位。如果是8位,一个默认在AL中,另一个放在8位reg或内存字节单元中;如果是16位,一个默认在AX中,另一个在16位reg或内存字单元中。
- 如果是8位惩罚,结果默认放在AX中;如果是16位乘法,结果高位默认在DX中存放,低位在AX中放。
格式:
mul reg
mul 内存单元
内存单元可以用不同的寻址方式给出:
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位
参数和结果传递的问题
子程序一般都要根据提供的参数处理一定的事务,处理后,将结果提供给调用者。
程序的注释应包含对子程序功能、参数和结果的说明。
调用者将参数送入参数寄存器,从结果寄存器中取到返回值;子程序从参数寄存器中取到参数,将返回值送入结果寄存器。
寄存器冲突的问题
子程序中使用的寄存器,很可能在主程序中也要使用,造成了寄存器使用上的冲突。
简捷方法是,在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来,在子程序返回前再恢复。可以用栈来保存寄存器中的内容。