在組合語言中,子程序(或稱過程)相當於高階語言的函式/程序。將重複使用的指令序列封裝為獨立模組,帶來四大效益:
PROC_NAME PROC [NEAR | FAR]
; 子程序本體指令
PROC_NAME ENDP
| 屬性 | 說明 | |||
|---|---|---|---|---|
| 段屬性 | 子程序所在邏輯段的段基址(CS) | |||
| 偏移屬性 | 子程序在段內的起始位移(IP) | |||
| 類型/距離屬性 | NEAR(段內調用,僅壓 IP)或 FAR(跨段調用,壓 CS:IP) |
| 類型 | 調用指令 | 返回指令 | 堆疊變化 | 適用場景 |
|---|---|---|---|---|
| NEAR | CALL PROC_NAME | RET | 壓入 IP (2 bytes),彈出 IP | 主程式與子程序在同一邏輯段 |
| FAR | CALL PROC_NAME | RET | 壓入 CS:IP (4 bytes),彈出 CS:IP | 跨段調用,或同段但聲明 FAR |
特點:速度最快、無堆疊開銷、參數數量受限於可用暫存器(AX、BX、CX、DX、SI、DI、BP)。
; 入口參數:
; DX = 待轉換數值(高位在前)
; AX = 位元數(8 或 16)
; DI = ASCII 輸出緩衝區首址
; 出口:緩衝區填入 ASCII 字元
BINASC PROC
MOV CX, AX ; 迴圈計數器 = 位元數
LOP:
ROL DX, 1 ; 最高位循環移至最低位
MOV AL, DL
AND AL, 1 ; 只保留最低位
ADD AL, '0' ; 轉為 '0' 或 '1'
MOV [DI], AL ; 存入緩衝區
INC DI ; 指標前移
LOOP LOP
RET
BINASC ENDP
; 主程式調用
MOV DH, BIN1 ; 8-bit 數值
MOV AX, 8
LEA DI, ASCBUF
CALL BINASC
MOV DX, BIN2 ; 16-bit 數值
MOV AX, 16
LEA DI, ASCBUF+8
CALL BINASC
特點:參數數量無暫存器限制、支援遞迴、符合高階語言呼叫慣例(CDECL/STDCALL)。
關鍵技巧:使用 BP(基址指針)建立堆疊幀,固定偏移存取參數,不受 PUSH/POP 影響。
; 主程式:壓入參數(右至左順序,符合 CDECL)
PUSH AX ; 位元數
PUSH DX ; 待轉數值
LEA DI, ASCBUF
PUSH DI ; 輸出緩衝區首址
CALL BINASC_STACK
ADD SP, 6 ; 清理堆疊 (CDECL: 呼叫端清理)
BINASC_STACK PROC
PUSH BP
MOV BP, SP ; 建立堆疊幀
PUSH AX ; 保存暫存器
PUSH CX
PUSH DX
PUSH DI
MOV DI, [BP+10] ; 取參數:緩衝區首址 (BP+2+2+2+4=10)
MOV CX, [BP+12] ; 位元數
MOV DX, [BP+14] ; 待轉數值
LOP:
ROL DX, 1
MOV AL, DL
AND AL, 1
ADD AL, '0'
MOV [DI], AL
INC DI
LOOP LOP
POP DI ; 恢復暫存器
POP DX
POP CX
POP AX
POP BP
RET 6 ; 返回並彈出 6 bytes 參數 (STDCALL 慣例)
BINASC_STACK ENDP
高位址
+------------------+
| 返回地址 (IP) | ← BP+2 (NEAR) 或 BP+2/BP+4 (FAR)
+------------------+
| 參數3: DI (緩衝區) | ← BP+10
+------------------+
| 參數2: CX (位元數) | ← BP+12
+------------------+
| 參數1: DX (數值) | ← BP+14
+------------------+
| 舊 BP | ← BP (PUSH BP 後 SP 位置)
+------------------+
| 暫存器保存區 |
| ... |
低位址
特點:參數極多時優雅、主程式建立參數位址陣列,子程序透過基址間接存取。
PARA_TABLE DW PARAM1, PARAM2, PARAM3, ...MOV AX, [SI]、MOV BX, [SI+2] 等間接定址取得參數位址,再存取資料。DATA SEGMENT
BIN1 DB 35H
BIN2 DW 0AB48H
ASCBUF DB 20H DUP(?)
PARA_TABLE DW 3 DUP(?) ; 位址表範例
DATA ENDS
STACK SEGMENT PARA STACK
DW 100 DUP(0)
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA, SS:STACK
START:
MOV AX, DATA
MOV DS, AX
; ... 初始化 ...
; 調用子程序
MOV AX, 4C00H
INT 21H
; 子程序定義區
BINASC PROC
; ...
BINASC ENDP
BINASC_STACK PROC
; ...
BINASC_STACK ENDP
CODE ENDS
END START
| 錯誤 | 現象 | 解決 |
|---|---|---|
| 忘記保存/恢復暫存器 | 返回主程式後暫存器值錯亂,邏輯失效 | 入口 PUSH 所有將使用的暫存器,出口逆序 POP |
| 堆疊不平衡 | RET 後 SP 錯誤,程式崩潰 | 檢查 PUSH/POP 次數、RET n 參數、呼叫端 ADD SP |
| NEAR/FAR 不匹配 | 跨段調用 NEAR 導致 CS 錯誤 | 跨段必須 FAR,同段可 NEAR/FAR 皆可 |
| 參數順序錯誤 | 子程序取到錯誤值 | 統一協定(右至左壓棧),文件化介面 |
| 修改呼叫端不該改的資料 | 副作用難追蹤 | 明確標註輸入/輸出/輸入輸出參數,遵守 const 慣例 |
組合語言子程序設計的核心在於:明確介面協定(參數位置/暫存器約定)、嚴格暫存器保護、堆疊平衡、文件化。掌握暫存器、堆疊、位址表三大傳參模式,並能根據參數數量、效能需求、遞迴要求選擇最適模式,是編寫高品質組合語言模組的關鍵。