編譯型 BASIC (QuickBASIC 等) 內建 `SHELL` 指令功能有限,無法精細控制子程式參數、FCB、環境區、錯誤回傳。EXECSUB.ASM 實作一個 FAR PROC 供 BASIC 以 `CALL EXECSUB(PROG$, PARM$, FCB1$, FCB2$, RETCD%)` 呼叫,完整封裝 DOS INT 21h, AH=4Bh (EXEC) 功能:釋放記憶體、保存/恢復中斷向量 (INT 1Bh/1Ch/24h 影子向量)、建立參數區塊、執行子程式、恢復堆疊與中斷向量、回傳錯誤碼。程式展示了 BASIC 字串描述子解析、記憶體管理、中斷向量影子備份、參數區塊構建、堆疊切換等高階混合語言技巧。
CALL EXECSUB(PROG$, PARM$, FCB1$, FCB2$, RETCD%)
PROG$ : 程式檔名 (如 "COMMAND.COM")
PARM$ : 命令列參數 (如 "/C DIR *.BAS")
FCB1$ : 第一個 FCB (某些程式需求)
FCB2$ : 第二個 FCB
RETCD% : 錯誤碼返回 (0=成功, 1=記憶體不足, 2=檔案不存在...)
BASICDS (位於絕對位址 0:510h)INT 1Bh (Ctrl-Break), 1Ch (Timer Tick), 24h (Critical Error) 的實模式向量與 BASIC 影子向量 (在 0:512h 起)。子程式執行可能修改這些向量,返回前必須恢復。0:4F2h 處儲存目前 PSP 段值),計算頂端記憶體,呼叫 INT 21h, AH=4Ah 保留最小空間給載入器。SS:[BP+n]),描述子結構 `[長度:WORD][位址:DWORD]`。提取 `PROG$` (DX), `PARM$` (命令列), `FCB1$`, `FCB2$` 位址/長度,填入參數區塊 (prm1..prm4)。cli / MOV CS:[stak], SP / MOV CS:[stak+2], SS / sti。MOV AH, 4Bh / MOV AL, 0 / MOV DX, PROG$位址 / MOV BX, 參數區塊位址 / INT 21h。MOV SI, [BP+6] / MOV [SI], AX)。編譯型 BASIC 執行期會 Hook INT 1Bh/1Ch/24h 並在 0:512h 區域維護影子副本。子程式執行期間 (特別是 `COMMAND.COM /C ...`) 可能覆寫這些向量。EXECSUB 採用「雙重備份」策略:
push ds:[i24ip] ; 實向量 IP
push ds:[b24ip] ; 影子向量 IP
push ds:[b24ip]
pop ds:[i24ip] ; 實向量 ← 影子向量 (確保實向量指向 BASIC 處理常式)
push ds:[i24cs]
push ds:[b24cs]
push ds:[b24cs]
pop ds:[i24cs] ; 同理 CS
... (INT 1Bh, 1Ch 同理) ...
返回時逆序恢復,確保 BASIC 執行期環境完整。
prm1 equ this byte
dw 0 ; 環境區段 (0=繼承父程式)
prm2 equ this word
dw 0 ; 命令列緩衝區位址 (IP)
dw 0 ; 命令列緩衝區段值 (CS)
prm3 equ this byte
dw 0 ; FCB1 位址
dw 0
prm4 equ this byte
dw 0 ; FCB2 位址
dw 0
從 BASIC 字串描述子提取位址/段值填入。命令列緩衝區格式:長度位元組 + 內容 + CR (0Dh)。
cli
mov sp, cs:[stak]
mov ss, cs:[stak+2]
sti
在關中斷下切換 SS:SP,避免中斷發生時堆疊不一致導致崩潰。
skip equ 2
cseg segment para public 'code'
public execsub
execsub proc far
assume cs:cseg,ds:cseg,ss:nothing,es:nothing
basicds equ 510h
i24ip equ 90h
i24cs equ 92h
i1bip equ 6ch
i1bcs equ 6eh
i1cip equ 70h
i1ccs equ 72h
b24ip equ 51ah
b24cs equ 51ch
b1bip equ 516h
b1bcs equ 518h
b1cip equ 512h
b1ccs equ 514h
push bp
mov bp,sp
jmp p010
stak equ this byte
dw 0
dw 0
prm1 equ this byte
dw 0
prm2 equ this word
dw 0
dw 0
prm3 equ this byte
dw 0
dw 0
prm4 equ this byte
dw 0
dw 0
p010:
xor ax,ax
mov es,ax
mov bx,ds
mov ds,ax
cli
push ds:[basicds]
push ds:[i24ip]
push ds:[b24ip]
push ds:[b24ip]
pop ds:[i24ip]
push ds:[i24cs]
push ds:[b24cs]
push ds:[b24cs]
pop ds:[i24cs]
push ds:[i1bip]
push ds:[b1bip]
push ds:[b1bip]
pop ds:[i1bip]
push ds:[i1bcs]
push ds:[b1bcs]
push ds:[b1bcs]
pop ds:[i1bcs]
push ds:[i1cip]
push ds:[b1cip]
push ds:[b1cip]
pop ds:[i1cip]
push ds:[i1ccs]
push ds:[b1ccs]
push ds:[b1ccs]
pop ds:[i1ccs]
sti
mov ds,bx
mov di,4f2h
mov ax,es:[di]
mov es,ax
mov di,2
mov bx,es:[di]
sub bx,ax
mov ah,4ah
int 21h
jnc p020
mov ah,0
jmp p090
p020:
mov si,[bp+12]
add si,skip
mov ax,[si]
mov si,offset prm2
mov cs:[si],ax
mov ax,ds
mov cs:[si+2],ax
mov si,[bp+10]
add si,skip
mov ax,[si]
mov si,offset prm3
mov cs:[si],ax
mov ax,ds
mov cs:[si+2],ax
mov si,[bp+8]
add si,skip
mov ax,[si]
mov si,offset prm4
mov cs:[si],ax
mov ax,ds
mov cs:[si+2],ax
push bp
push ds
push es
pushf
mov si,offset stak
mov cs:[si],sp
mov cs:[si+2],ss
mov ah,4bh
mov al,0
mov si,[bp+14]
add si,skip
mov dx,[si]
push cs
pop es
mov bx,offset prm1
int 21h
jnc p050
mov ah,1
jmp p060
p050: mov ax,0
p060:
mov bx,cs
mov ds,bx
mov si,offset stak
cli
mov sp,cs:[si]
mov ss,cs:[si+2]
sti
popf
pop es
pop ds
pop bp
mov cx,ds
xor bx,bx
mov ds,bx
cli
pop ds:[b1ccs]
pop ds:[i1ccs]
pop ds:[b1cip]
pop ds:[i1cip]
pop ds:[b1bcs]
pop ds:[i1bcs]
pop ds:[b1bip]
pop ds:[i1bip]
pop ds:[b24cs]
pop ds:[i24cs]
pop ds:[b24ip]
pop ds:[i24ip]
pop ds:[basicds]
sti
mov ds,cx
p090:
mov si,[bp+6]
mov [si],ax
pop bp
ret 10
execsub endp
cseg ends
end
EXECSUB 以約 200 行程式碼,解決了「高階語言執行期環境保護 + 系統載入器參數封裝 + 子程式執行狀態隔離」三大難題,是混合語言系統程式設計、執行期庫擴充、嵌入式多階段啟動的進階教材。