过程调用与算术指令

callret 实现了子程序(函数/过程)的调用和返回,它们本质上是操作 的转移指令。此外还有乘法、除法、进位加减法等算术指令。

前置知识

需要先理解 06-栈(push/pop)和 07-转移指令与循环(jmp 的各种形式)。

ret 指令:返回

ret 用栈中的数据修改 IP,实现段内近返回

执行过程:

  1. IP = SS:SP 处的字(pop IP)
  2. SP = SP + 2
; ret 相当于:
pop IP

retf 指令:远返回

retf 用栈中的数据同时修改 CS 和 IP,实现段间远返回

执行过程:

  1. IP = SS:SP 处的字SP = SP + 2
  2. CS = SS:SP 处的字SP = SP + 2
; retf 相当于:
pop IP
pop CS

call 指令:调用子程序

call 执行两步操作:

  1. 将当前的 IP(或 CS 和 IP)压入栈(保存返回地址)
  2. 转移到目标位置执行

call 标号(段内近调用)

call s        ; 段内调用标号 s 处的子程序

相当于:

push IP       ; 压入 call 指令下一条指令的 IP
jmp near ptr s

call far ptr 标号(段间远调用)

call far ptr s

相当于:

push CS
push IP
jmp far ptr s

call 16位寄存器

call ax       ; IP 压栈,然后 IP = AX

相当于:

push IP
jmp ax

call word ptr 内存单元(段内间接调用)

call word ptr ds:[0]

相当于:

push IP
jmp word ptr ds:[0]

call dword ptr 内存单元(段间间接调用)

call dword ptr ds:[0]

相当于:

push CS
push IP
jmp dword ptr ds:[0]

子程序的标准写法

; 子程序:计算 ax 的平方
; 参数:ax = 要计算的数
; 返回:ax = 结果
square:
    mul ax          ; ax = ax * ax(mul 指令见下文)
    ret             ; 返回调用点

; 调用示例
code segment
start:
    mov ax, 5
    call square     ; 调用 square 子程序
                    ; 返回后 ax = 25
    mov ax, 4c00H
    int 21H

square:
    mul ax
    ret
code ends

call 和 ret 的配合

call 压入返回地址,ret 弹出返回地址,两者必须配对使用。如果栈不平衡(比如 call 了但没 ret,或者 ret 前多 push/少 pop),程序会跳转到错误地址崩溃。

mul 指令:乘法

mul 是乘法指令,规则和 div 除法 类似:

乘数位数被乘数位置积的位置
8位(字节乘法)AL(隐含)AX(16位结果)
16位(字乘法)AX(隐含)DX(高16位) + AX(低16位)(32位结果)
; 8位乘法:AL × 8位操作数 → AX
mov al, 100
mov bl, 20
mul bl           ; AX = 100 × 20 = 2000

; 16位乘法:AX × 16位操作数 → DX:AX
mov ax, 1000
mov bx, 100
mul bx           ; DX:AX = 1000 × 100 = 100000
                 ; AX = 低位,DX = 高位

格式:

  • mul reg(寄存器)
  • mul 内存单元mul byte ptr [bx]mul word ptr [bx]

两个乘数必须同位数

要么都是8位(其中一个在AL),要么都是16位(其中一个在AX),不能一个8位一个16位。

div 指令:除法

02-内存寻址 中已经介绍过,这里快速回顾:

除数位数被除数位置余数
8位AXALAH
16位DX(高) + AX(低)AXDX
; 16位除法
mov dx, 0
mov ax, 1000
mov bx, 10
div bx           ; AX = 100(商),DX = 0(余数)

adc 指令:带进位加法

adc(Add with Carry)在加法时加上 CF(进位标志)位的值:

adc 操作对象1, 操作对象2  →  操作对象1 = 操作对象1 + 操作对象2 + CF

用途:大数加法(超过16位的数相加)。

; 计算 12345678H + 87654321H(32位加法)
; 低16位用 add,高16位用 adc(加上低位相加产生的进位)
mov ax, 5678H     ; 低16位
mov bx, 1234H     ; 高16位
add ax, 4321H     ; 低16位相加,可能产生进位到 CF
adc bx, 8765H     ; 高16位相加 + CF(来自低位的进位)
; 结果在 BX:AX = 99999999H

sbb 指令:带借位减法

sbb(Subtract with Borrow)在减法时减去 CF(借位标志)位的值:

sbb 操作对象1, 操作对象2  →  操作对象1 = 操作对象1 - 操作对象2 - CF

用途:大数减法,和 adc 配合使用。

; 32位减法:BX:AX - CX:DX
sub ax, dx        ; 低16位相减
sbb bx, cx        ; 高16位相减 - CF(借位)

相关