● CALL SUBROUT 段内直接调用
  执行操作:① (SP) ← (SP)-2,((SP)) ← (IP)当前
       ② (IP) ← (IP)当前+16位位移量(在指令的第2、3个字节中)
  
  ● CALL DESTIN 段内间接调用
  执行操作:① (SP) ← (SP)-2,((SP)) ← (IP)当前
       ② (IP) ← (EA) ; (EA)为指令寻址方式所确定的有效地址

  ● CALL FAR PTR SUBROUT 段间直接调用
  执行操作:① (SP) ← (SP)-2,((SP)) ← (CS)当前
         (SP) ← (SP)-2,((SP)) ← (IP)当前
       ② (IP) ← 偏移地址(在指令的第2、3个字节中)
         (CS) ← 段地址(在指令的第4、5个字节中)

  ● CALL WORD PTR DESTIN 段间间接调用
  执行操作:① (SP) ← (SP)-2,((SP)) ← (CS)当前
         (SP) ← (SP)-2,((SP)) ← (IP)当前
       ② (IP) ← (EA) ; (EA)为指令寻址方式所确定的有效地址
         (CS) ← (EA+2)

  从CALL指令执行的操作可以看出,第一步是把子程序返回调用程序的地址保存在堆栈中。对段内调用,只需将IP的当前值,即CALL指令的下一条指令的地址存入SP所指示的堆栈字单元中。对段间调用,保存返回地址则意味着要将CS和IP的当前值分别存入堆栈的两个字单元中。

  CALL指令的第二步操作是转子程序,即把子程序的入口地址交给IP(段内调用)或CS:IP(段间调用)。对段内直接方式,调转的位移量,即子程序的入口地址和返回地址之间的差值就在机器指令的2、3字节中。对段间直接方式,子程序的偏移地址和段地址就在操作码之后的两个字中。对间接方式,子程序的入口地址就从寻址方式所确定的有效地址中获得。

  ● RET 段内返回(近返回)
  执行操作:(IP) ← ((SP)),(SP) ← (SP)+2

  ● RET 段间返回(远返回)
  执行操作:(IP) ← ((SP)),(SP) ← (SP)+2
       (CS) ←((SP)),(SP) ← (SP)+2

  ● RET N 带立即数返回
  执行操作:① 返回地址出栈(操作同段内或段间返回)
       ② 修改堆栈指针:(SP) ← (SP)+N

  子程序的最后一条指令必须是RET指令,以返回到主程序。如果是段内返回,只需把保存在堆栈中的偏移地址出栈存入IP即可,如果是段间返回,则要把偏移地址和段地址都从堆栈中取出送到IP和CS寄存器中。

  带立即数返回指令,除完成偏移地址出栈或偏移地址和段地址出栈的操作外,还要再使SP的内容加上一个立即数N,使堆栈指针SP移动到新的位置。指令中的N可以是一个常数,也可以是一个表达式。带立即数返回指令适用于C或PASCAL的调用规则,这些规则在调用过程(子程序)前先把参数压入堆栈,子程序使用这些参数后,如果在返回时丢弃这些已无用的参数,就在RET指令中包含一个数字,它表示压入到堆栈中参数的字节数,这样堆栈指针就恢复到参数入栈前的值。

  CALL指令和RET指令都不影响条件码。

 例3.43 根据下面调用程序和子程序的程序清单,画出RET指令执行前和执行后的堆栈情况。假设初始的SS:SP=A000:1000。
 
    0000  B8 001E   MOV  AX,30
    0003  BB 0028   MOV  BX,40
    0006  50      PUSH AX     ; push data1 into stack
    0007  53      PUSH BX     ; push data2 into stack
    0008  E8 0066   CALL ADDM    ; call subroutine
    000B  B4 02    MOV  AH,2
    …   …      …
    0071  ADDM     PROC NEAR  ; entry point (IP)←0071=000b+0066
    0071  55      PUSH BP     ; save BP
    0072  8B E4    MOV  BP,SP    ; addressing the stack with BP
    0074  8B 46 04  MOV  AX,[BP+4] ; get data2 from stack
    0077  03 46 06   ADD  AX,[BP+6] ; add data1
    007A  CD      POP  BP     ; get back BP
    007B  C2      0004 RET 4   ; return and revert SP
    007E  ADDM     ENDP

图3.12 CALL指令和RET指令对堆栈的影响



  如图3.12所示,主程序中的两条PUSH指令将数据30和40压入堆栈,CALL指令执行后,返回地址000B又压入堆栈,紧接着程序控制转移到子程序ADDM。子程序中的PUSH指令又使BP的值进栈,此时SP指向栈顶0FFA。MOV指令将0FFA传送给BP,使BP作为寻址堆栈数据的指针。(BP+4)指向的是40,(BP+6)指向的是30,取出数据后用POP指令恢复了BP原先的值,此时,(SP)=0FFC,这是RET 4指令执行前的堆栈状态。

  执行RET 4指令时,先使返回地址出栈:(IP)←000B,(SP)←0FFC+2=0FFD,然后,(SP)+4=0FFD+4=1000,结果使SP跳过了堆栈数据而回到了原始位置。