过程调用
8086 处理器支持四种调用方式
16 位相对近调用
操作数是当前 call 指令相对于目标过程的偏移量。计算过程如下:用目标过程的汇编地址减去当前 call 指令的汇编地址,再减去当前 call 指令以字节为单位的长度(3),保留 16 (signed int)位的结果。
例:
call near proc_1
近调用的特征是在指令中使用关键字“near”。在编译阶段,编译器用标号 proc_1 处的汇编地址减去本指令的汇编地址,再减去 3,作为机器指令的操作数。
关键字“near”不是必需的,如果 call 指令中没有提供任何关键字,则编译器认为该指令是近 调用。因此,上面的指令与这条指令等效:
call proc_1
被调用过程的首地 址必须位于距离当前 call 指令-32768~32767 字节的地方。
在 call 指令后跟一个数值,只是帮了编译器的忙,帮它省了一个转化步骤,它依然会 用这个数值减去当前指令的汇编地址,来得到一个偏移量。
16 位间接绝对近调用
这种调用也是近调用,只能调用当前代码段内的过程,指令 中的操作数不是偏移量,而是被调用过程的真实偏移地址,故称为绝对地址。不过,这个偏移地址 不是直接出现在指令中,而是由 16 位的通用寄存器或者 16 位的内存单元给出。
例:
call cx ;目标地址在 CX 中。省略了关键字“near”,下同 call [0x3000] ;要先访问内存才能取得目标偏移地址 call [bx] ;要先访问内存才能取得目标偏移地址 call [bx+si+0x02] ;要先访问内存才能取得目标偏移地址
由于间接绝对近调用的机器指令操作数是 16 位的绝对地址,因此,它可以调用当前代码段任 何位置处的过程
16 位直接绝对远调用
这种调用属于段间调用,即调用另一个代码段内的过程,所 以称为远调用(Far Call)。很容易想到,远调用既需要被调用过程所在的段地址,也需要该过程在 段内的偏移地址
“16 位”是针对偏移地址来说的,而不用于限定段地址;“直接”的意思是,段地址和偏移地址 直接在 call 指令中给出了。当然,这里的地址也是绝对地址。比如:
call 0x2000:0x0030
16 位间接绝对远调用
这也属于段间调用,被调用过程位于另一个代码段内,而且, 被调用过程所在的段地址和偏移地址是间接给出的。还有,这里的“16 位”同样是用来限定偏移地 址的。下面是这种调用方式的几个例子:
call far [0x2000] call far [proc_1] call far [bx] call far [bx+si]
间接远调用必须使用关键字“far”,这一点务必牢记。 因为是远调用,也就是段间调用,所以,必须给出被调用过程的段地址和偏移地址。但是,段 地址和偏移地址在内存中的其他位置,指令中仅仅给出的是该位置的偏移地址,需要处理器在执行 指令的时候自行按图索骥,找到它们。 以上,前两条指令是等效的,不同之处仅仅在于,第一条指令直接给出的是数值,而第二条指 令用的是标号。但这无关紧要,在编译后,标号也会变成数值。 为了进一步说清间接远调用是怎么发生的,下面是一个实例。 假如在数据段内声明了标号 proc_1 并初始化了两个字:
proc_1 dw 0x0102, 0x2000
这两个字分别是某个过程的段地址和偏移地址。按处理器的要求,偏移地址在前,段地址在后。 也就是说,0x0102 是偏移地址; 0x2000 是段地址。 那么,为了调用该过程,可以在代码段内使用这条指令:
call far [proc_1]
当这条指令执行时,处理器访问由段寄存器 DS 指向的数据段,从指令中指定的偏移地址处取 得两个字(分别是段地址 0x2000 和偏移地址 0x0102);接着,将代码段寄存器 CS 和指令指针寄存 器 IP 的当前内容分别压栈;最后,用刚才取得的段地址和偏移地址分别取代 CS 和 IP 的原值。 至于后面的两条指令 call far [bx]和 call far [bx+si],仅仅是寻址方式上有所区别,指令执行过程 大体上是一样的。
call 指令在执行过程调用时不影响任何标志位