为什么需要子程序?
汇编没有"函数"关键字,但我们可以用 CALL 和 RET 实现同样的效果。
子程序的基本结构
1
2
3
4
5
6
7
8
9
10
11
12
13
| MyFunc PROC
PUSH BP ; 保存调用者的栈帧
MOV BP, SP ; 建立新栈帧
SUB SP, 4 ; 分配 4 字节局部变量
; 函数体
MOV [BP-2], AX ; 局部变量 1
MOV [BP-4], BX ; 局部变量 2
MOV SP, BP ; 清理局部变量
POP BP ; 恢复调用者栈帧
RET
MyFunc ENDP
|
参数传递方式
1. 寄存器传参(最快)
1
2
3
4
5
6
7
8
9
10
| ; 调用者
MOV AX, 100
MOV BX, 200
CALL AddFunc
; AddFunc
AddFunc PROC
ADD AX, BX ; 结果在 AX 中
RET
AddFunc ENDP
|
2. 栈传参(适合多参数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| ; 调用者
PUSH 200
PUSH 100
CALL AddFunc
ADD SP, 4 ; 清理参数
; AddFunc
AddFunc PROC
PUSH BP
MOV BP, SP
MOV AX, [BP+4] ; 参数 1
MOV BX, [BP+6] ; 参数 2
ADD AX, BX
POP BP
RET
AddFunc ENDP
|
局部变量的作用
局部变量存储在栈中,每个函数调用有独立的副本:
- 不会污染全局数据
- 递归调用时自动隔离
- 函数返回后自动释放
调用约定
| 约定 | 参数传递 | 栈清理 | 寄存器保护 |
|---|
| C 调用 | 栈(右到左) | 调用者 | EAX/ECX/EDX 不保护 |
| Pascal | 栈(左到右) | 被调用者 | 所有寄存器保护 |
| fastcall | 寄存器 + 栈 | 调用者 | 混合 |
总结
子程序是汇编走向工程化的关键。掌握栈帧管理,你就能写出结构清晰、可维护的汇编代码。
下一篇:《宏指令与高级汇编技术:代码复用与元编程》