过程调用与算术指令
call 和 ret 实现了子程序(函数/过程)的调用和返回,它们本质上是操作 栈 的转移指令。此外还有乘法、除法、进位加减法等算术指令。
前置知识
需要先理解 06-栈(push/pop)和 07-转移指令与循环(jmp 的各种形式)。
ret 指令:返回
ret 用栈中的数据修改 IP,实现段内近返回。
执行过程:
IP = SS:SP 处的字(pop IP)SP = SP + 2
; ret 相当于:
pop IP
retf 指令:远返回
retf 用栈中的数据同时修改 CS 和 IP,实现段间远返回。
执行过程:
IP = SS:SP 处的字,SP = SP + 2CS = SS:SP 处的字,SP = SP + 2
; retf 相当于:
pop IP
pop CS
call 指令:调用子程序
call 执行两步操作:
- 将当前的 IP(或 CS 和 IP)压入栈(保存返回地址)
- 转移到目标位置执行
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位 | AX | AL | AH |
| 16位 | DX(高) + AX(低) | AX | DX |
; 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(借位)
相关
- 06-栈 — call/ret 的底层机制就是栈
- 07-转移指令与循环 — jmp 和 loop 是 call 的基础
- 09-标志寄存器 — CF 标志位是 adc/sbb 的关键
- 02-内存寻址 — div 除法的详细说明