首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 組合語言

組合語言編寫 DOS 記憶體駐留程式(TSR):基礎架構與中斷鏈接(四)

     編輯:組合語言

四、基本駐留程式:從 COM 框架到 TSR 核心

4.1 最小 COM 程式框架

DOS 可執行檔分 .COM(單段、≤64KB、起始 100H、載入快)與 .EXE(多段、無大小限制、載入慢、TSR 較麻煩)。

cseg segment
assume cs:cseg, ds:cseg
org 100h
start:
    ret
cseg ends
end start
  • 三段式:段定義(org 100h)、可執行代碼(此處僅 RET)、結束標記(end start 指定入口)。
  • 編譯聯結後僅 1 位元組(RET 機器碼 C3),因 .COM 無程式段前綴(PSP),DOS 載入時自動建立 PSP(0000H 起 256 位元組)。

4.2 最小 TSR:INT 27H 終止並駐留

cseg segment
assume cs:cseg, ds:cseg
org 100h
start:
    nop
done:
    mov dx, offset done
    int 27h
cseg ends
end start
  • INT 27H:以 DX 指向可用記憶體起始位置(相對 DS),DOS 更新載入基準指標,DX 之前區域永久駐留。
  • 對比 INT 21H AH=31H:需指定保留段落數並回傳退出碼,較不直觀。
  • DS=CS 為 .COM 載入預設,assume ds:cseg 僅告知編譯器,不產生代碼;執行時必須確保 DS=CS,否則失敗。

4.3 改良 TSR:分離初始化與駐留主體

cseg segment
assume cs:cseg, ds:cseg
org 100h
start:
    jmp initialize
app_start:
    nop                 ; 駐留程式主體放此處
initialize:
    mov dx, offset initialize
    int 27h
cseg ends
end start
  • start(100H)跳轉至 initialize,駐留主體置於 app_start~initialize 之間,僅初始化執行一次,主體常駐記憶體等待喚醒。
  • 入口必須是 start(100H),若將 initialize 放在 end 後會無法用 EXE2BIN 生成 .COM。

4.4 移除 PSP 節省 256 位元組?得不償失

TSR 駐留時 PSP 也被保留。理論上可在初始化時將整個程式下移 256 位元組覆蓋 PSP,但需處理:首次執行檢測、多 TSR 共存、長期未執行狀態。代碼超過 256 位元組即虧損。多數 TSR 不值得為此增加複雜度,保持簡潔優先。

4.5 使用駐留程式:中斷鏈接是關鍵

TSR 必須掛載到事件源:鍵盤(INT 16H/硬體中斷)、時鐘(INT 1CH)、系統呼叫等。必須在安裝時建立喚醒管道,否則永遠無法被使用。IBM PC 事件驅動,攔截中斷向量讓我們的程式優先執行,成為應用程式與作業系統間的橋樑。

掛載 INT 16H 範例

cseg segment
assume cs:cseg, ds:cseg
org 100h
start:
    jmp Initialize
new_keyboard_io proc far
    sti
    nop
    iret
new_keyboard_io endp
Initialize:
    mov dx, offset new_keyboard_io
    mov al, 16h
    mov ah, 25h
    int 21h             ; 設定中斷向量
    mov dx, offset Initialize
    int 27h
cseg ends
end start
  • STI 開中斷,IRET 正確返回(恢復旗標)。雖此例多餘,但實戰中斷處理常式必備。
  • AH=25H AL=中斷號 DS:DX=新處理器位址 設定向量。

4.6 連接中斷處理鏈:呼叫舊處理器

單純攔截不呼叫舊處理器會導致鍵盤輸入卡死(硬體中斷仍填滿隊列,但軟體中斷 INT 16H 讀不出)。必須模擬 INT 呼叫舊處理器:

Old_Keyboard_IO dd ?    ; 雙字儲存舊向量(段:偏移)

new_keyboard_io proc far
    sti
    pushf               ; 模擬 INT 壓入旗標
    assume ds:nothing   ; 告知編譯器 DS 不可靠
    call Old_Keyboard_IO; 呼叫舊處理器
    nop
    iret
new_keyboard_io endp

Initialize:
    assume cs:cseg, ds:cseg
    mov bx, cs
    mov ds, bx
    mov al, 16h
    mov ah, 35h
    int 21h             ; 取得舊向量 → ES:BX
    mov word ptr Old_Keyboard_IO, bx
    mov word ptr Old_Keyboard_IO[2], es
    ; 設定新向量...
    int 27h
  • PUSHF + CALL 模擬 INT:舊處理器以 IRET 返回,控制權回我們程式,再 IRET 返回原調用者。
  • assume ds:nothing 防止編譯器誤用 DS 產生錯誤定址,確保 call Old_Keyboard_IO 產生正確遠呼叫碼。
  • 舊向量儲存雙字(段+偏移),因舊處理器可能在不同段。

4.7 驗證攔截:大小寫互換測試

修改 AH=0(讀字元)路徑:呼叫舊處理器取字元後,若 AL='y' 改 'Y',若 'Y' 改 'y',其他功能(AH=1,2)直接 JMP 舊處理器。執行後鍵盤輸入 y/Y 即互換,驗證攔截鏈路暢通。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved