MS-DOS 裝置驅動程式 (`.SYS` 檔) 採用獨特架構:系統透過 **請求標頭 (Request Header)** 傳遞命令,驅動程式提供 **策略常式 (Strategy)** 暫存請求標頭位址,與 **中斷常式 (Interrupt)** 處理實際命令。HDR.ASM (Frank Whaley, 1985) 定義了完整的驅動程式標頭結構、請求標頭模板、策略/中斷常式框架、命令分派表、本地堆疊管理,是撰寫區塊/字元裝置驅動程式的標準範本。
HDR Proc Far
DD -1 ; 指向下一個驅動程式 (-1 = 串列結尾)
DW 8000H ; 屬性字: Bit 15=1 字元裝置
DW Strategy ; 策略常式入口
DW Interrupt ; 中斷常式入口
DB "MON " ; 裝置名稱 (8 位元組, 字元裝置)
HDR EndP
屬性字關鍵位:Bit 15=字元/區塊, Bit 14=支援 IOCTL, Bit 13=非 IBM 格式 (區塊), Bit 11=支援 Open/Close/Removable (DOS 3+), Bit 3=Clock, Bit 2=NUL, Bit 1=StdOut, Bit 0=StdIn。
DOS 先呼叫 Strategy,將請求標頭位址傳入 ES:BX。Strategy 僅複製指標至本地變數 RHptr,並保存所有暫存器:
Strategy:
MOV Word Ptr CS:RHptr, BX
MOV Word Ptr CS:RHptr+2, ES
PUSHF
PUSH ES, DS, SI, DI, CX, BX
MOV SI, BX
MOV BX, ES
MOV DS, BX ; DS:SI -> 請求標頭
MOV BX, Offset PGROUP:TAIL
MOV CL, 4
SHR BX, CL
MOV CX, CS
ADD BX, CX
MOV ES, BX ; ES = 資料段段值
MOV DI, Offset DGROUP:ReqHdr
CLD
XOR CH, CH
MOV CL, [SI] ; 請求標頭長度
REP MOVSB ; 複製至本地 ReqHdr 緩衝區
POP BX, CX, DI, SI, DS, ES
POPF
RET
關鍵點:REP MOVSB 複製整個請求標頭至本地資料區 ReqHdr,後續中斷常式直接操作此副本,避免競態條件。
Interrupt 以 ReqHdr+2 (命令碼) 索引分派表 FuncTab,呼叫對應處理常式:
Interrupt:
PUSH DS, ES, AX, BX, CX, DX, SI, DI, BP
MOV CS:ssEntry, SS
MOV CS:spEntry, SP
; 切換至本地堆疊 (TAIL 段末尾)
MOV AX, Offset PGROUP:TAIL
SHR AX, CL
ADD AX, CS
MOV BX, Offset DGROUP:MyStack
MOV DS, AX, ES, AX, SS, AX
MOV SP, BX, BP, BX
; 分派
MOV AL, ReqHdr+2
SHL AL, 1
CBW
MOV SI, Offset DGROUP:FuncTab
ADD SI, AX
CALL Word Ptr [SI]
; 複製回原請求標頭
LES DI, RHptr
MOV SI, Offset DGROUP:ReqHdr
MOV CL, [SI]
REP MOVSB
; 恢復堆疊與暫存器
MOV SS, CS:ssEntry
MOV SP, CS:spEntry
POP BP, DI, SI, DX, CX, BX, AX, ES, DS
RET
FuncTab Label Word
DW Offset PGROUP:Init ; 0 初始化
DW Offset PGROUP:MediaChe ; 1 媒體檢查 (區塊)
DW Offset PGROUP:BuildBPB ; 2 建立 BPB (區塊)
DW Offset PGROUP:IoCtlIn ; 3 IOCTL 輸入
DW Offset PGROUP:Input ; 4 讀取
DW Offset PGROUP:ndInput ; 5 非破壞性讀取
DW Offset PGROUP:InputSta ; 6 輸入狀態
DW Offset PGROUP:InputFlu ; 7 清空輸入緩衝
DW Offset PGROUP:Output ; 8 寫入
DW Offset PGROUP:OutVerif ; 9 寫入並驗證
DW Offset PGROUP:OutStatu ; 10 輸出狀態
DW Offset PGROUP:OutFlush ; 11 清空輸出緩衝
DW Offset PGROUP:IoCtlOut ; 12 IOCTL 輸出
DW Offset PGROUP:DevOpen ; 13 開啟裝置 (DOS 3+)
DW Offset PGROUP:DevClose ; 14 關閉裝置 (DOS 3+)
DW Offset PGROUP:RemMedia ; 15 可移動媒體 (DOS 3+)
Request struc
Rlength db ? ; 標頭長度
Unit db ? ; 單元號
Command db ? ; 命令碼
Status dw ? ; 回傳狀態字
Reserve db 8 dup (?) ; 保留
Media db ? ; 媒體描述符
Address dd ? ; 傳輸緩衝區位址
Count dw ? ; 位元組/扇區計數
Sector dw ? ; 起始扇區
Request ends
狀態字:Bit 15=錯誤, Bit 9=忙碌, Bit 8=完成, Bit 0-7=錯誤碼 (0=寫保護, 1=未知單元, 2=未就緒, 3=未知命令, 4=CRC, 5=錯誤長度, 6=尋找錯誤, 7=未知媒體, 8=扇區未找到, 9=缺紙, 10=寫入故障, 11=讀取故障, 12=一般失敗, 15=無效磁碟變更 DOS 3+)。
Init:
PUSH ES, DI
MOV AH, 9
MOV DX, Offset Ident
INT 21h ; 列印簽名訊息
POP DI, ES
MOV Word Ptr ES:[DI.Address], Offset Init
MOV Word Ptr ES:[DI.Address+2], CS
JMP Done ; 回傳完成狀態,記憶體釋放至 Init 之後
初始化常式列印版權訊息,將首個可用記憶體位址設為 Init 自身位址 (即釋放初始化碼佔用的記憶體),並回傳狀態字 Done (AH=1)。
PGROUP Group PROG, TAIL
DGROUP Group DATA, DTAIL
PROG Segment ... EndS
DATA Segment ... EndS
Assume CS:PROG, DS:DATA, ES:DATA, SS:DATA
PROG Segment
Extrn Init, MediaChe, BuildBPB, ...
ORG 0
HDR Proc Far ... EndP
Strategy ...
Interrupt ...
PROG EndS
TAIL Segment ... EndS
DATA Segment
Public ReqHdr
DB StkSiz Dup (?)
MyStack Label Word
FuncTab ...
ReqHdr DB 256 Dup (?)\n />DATA EndS
DTAIL Segment ... EndS
END HDR
MyStack),執行完恢復 — 避免破壞呼叫者 (DOS 核心) 堆疊,現代驅動程式同樣擁有私有堆疊。HDR.ASM 雖為 1985 年產物,其架構哲學 — **請求封裝、無狀態處理、分派表、私有堆疊、屬性協商** — 歷經三十餘年演變,仍清晰可見於 Windows WDM、Linux `file_operations`、FreeBSD `cdevsw`、UEFI Driver Binding Protocol 等現代驅動模型中。研讀此框架代碼,即是窺見作業系統 I/O 子系統設計的基因起源。