在早期 PC 開發環境中,BASIC (特別是編譯型 BASIC 如 QuickBASIC) 常需載入並執行外部 .EXE/.COM 程式。但 BASIC 的執行期庫未直接暴露 DOS INT 21h, AH=4Bh (EXEC) 功能。BASLOAD.ASM 正是為了解決此問題而生:它是一個 FAR PROC,供 BASIC 以 CALL ABSOLUTE 或連結時期 PUBLIC 方式呼叫,透過系統參數區 (SYSPARM) 傳遞檔名與命令列參數,最終跳轉至 DOS LOAD 功能。
BASIC 傳遞 4 個字串描述子 (String Descriptor) 至堆疊:
; 堆疊佈局 (高位址 → 低位址) BP+12 : File Spec 1 描述子位址 (要載入的程式完整路徑) BP+10 : Parameter 1 描述子位址 (命令列參數) BP+8 : File Spec 2 描述子位址 (保留/次要程式) BP+6 : Parameter 2 描述子位址每個描述子結構為:
[長度:WORD][位址:DWORD](編譯型 BASIC 通常為近指標,段值在 DS)。核心流程:搬移字串至 SYSPARM 區
程式先透過
INT 77h, DX='dk'取得 SYSPARM 區段地址 (回傳於 AX),此為專屬通訊緩衝區。接著依序將 4 組字串搬移至固定偏移:
| 內容 | SYSPARM 偏移 | 格式 |
|---|---|---|
| File Spec 1 | 0 | ASCIIZ (以 0 結尾) |
| Parameter 1 | 81h | 長度前綴 + 內容 + CR (0Dh) |
| File Spec 2 | 163 (0A3h) | ASCIIZ |
| Parameter 2 | 244 (0F4h) | 長度前綴 + 內容 + CR |
搬移使用 REP MOVSB 高效完成,並妥善處理長度調整與結尾字元。
CODE SEGMENT 'CODE'
ASSUME CS:CODE
PUBLIC BASLOAD
BASLOAD PROC FAR
PUSH BP
MOV BP,SP
PUSH DS
PUSH ES
MOV DX,'dk'
INT 77H ; 取得 SYSPARM 區段 → AX
MOV ES,AX ; ES 指向 SYSPARM
CLD ; 正向搬移\n /> ; --- File Spec 1 ---\n /> MOV BX,SS:[BP+12] ; 取得描述子位址\n /> MOV CX,DS:[BX] ; 長度\n /> MOV SI,DS:[BX+2] ; 字串位址\n /> MOV DI,0 ; SYSPARM 偏移 0\n /> REP MOVSB ; 搬移\n /> MOV BYTE PTR ES:[DI],0 ; ASCIIZ 結尾\n /> ; --- Parameter 1 ---\n /> MOV BX,SS:[BP+10]\n /> MOV CX,DS:[BX]\n /> MOV SI,DS:[BX+2]\n /> MOV DI,81\n /> INC CL ; 預留 CR 空間\n /> MOV BYTE PTR ES:[DI],CL ; 長度前綴\n /> DEC CL\n /> INC DI\n /> REP MOVSB\n /> MOV BYTE PTR ES:[DI],13 ; 加入 CR\n /> ... (File Spec 2, Parameter 2 同理) ...\n /> POP ES\n /> POP DS\n /> POP BP
RET 8 ; 清除 4 個參數 (8 位元組)\n />BASLOAD ENDP
CODE ENDS
END BASLOAD
RET 8 自動彈出 4 個 WORD 參數,符合 Pascal 呼叫慣例 (BASIC 預設)。BASLOAD 屬於「語言邊界互操作」的典型產物。現代開發中,P/Invoke (C#)、JNI (Java)、FFI (Rust/Go) 解決同類問題,但核心挑戰不變:
研讀這段 1984 年的程式碼,能深刻體會「薄介面層」設計哲學:最小化組合語言邏輯,僅做資料重排與中斷跳轉,將複雜性留給高階語言與作業系統。