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

DOS 列印程式實作:從命令列讀取檔案並輸出至印表機

     編輯:組合語言

前言:DOS 時代的列印自動化

在無圖形介面、無背景列印服務的 DOS 環境下,將文字檔內容導向列印機 (LPT1) 是常見需求。這份 PRINT FILE PROGRAM 完整展示了如何:從 PSP (Program Segment Prefix) 解析命令列檔名、以 Handle 方式開啟/讀取/關閉檔案、呼叫 BIOS INT 17h 逐字元輸出至並行埠、並在列印過程中輪詢鍵盤允許使用者按 Esc 中斷。程式結構清晰,錯誤處理完善 (檔案開啟失敗、讀取錯誤、印表機離線/錯誤),是學習 DOS 檔案 I/O 與硬體級列印的經典範例。

程式架構與流程

  1. 初始化:取得 PSP 位址 (DS/ES)、解析命令列長度 (DS:[80h])、檢查是否有參數。
  2. 參數提取:從 PSP:82h 複製檔名至資料區 filename 緩衝區,遇空白/控制字元 (≤20h) 結束,補 0 成 ASCIIZ。
  3. 開啟檔案:INT 21h, AH=3Dh, AL=0 (唯讀),取得 Handle 存入 file_handle。
  4. 顯示啟動訊息:提示使用者按 Esc 停止。
  5. 讀取-列印迴圈:
    • INT 21h, AH=3Fh 讀取 1024 位元組至 file_buffer
    • 逐字元:INT 16h, AH=1 檢查鍵盤緩衝區 → 若有鍵再 AH=0 取碼 → 比對 Esc (1Bh)
    • INT 17h, AH=0 輸出字元至 LPT1 (DX=0),檢查狀態位 (AH bit 0=Timeout, bit 3=I/O Error, bit 5=Not Selected) → 錯誤則跳轉錯誤處理
    • 緩衝區耗盡 → 回讀取下一塊;讀取到 EOF (AX=0) → 關閉檔案、結束

關鍵技術細節

1. PSP 命令列解析

mov al, [DS:80H]      ; 命令列長度
cmp al, 1
ja get_PSP_filename
; 無參數 → 顯示錯誤訊息並結束
get_PSP_filename:
mov ds, psp_seg       ; DS 指向 PSP
mov si, 82H           ; 命令列起始
lea di, filename
cld
get_PSP_data_1:
lodsb
cmp al, 21H           ; 控制字元/空白?
jb got_PSP_filename
stosb
jmp get_PSP_data_1
got_PSP_filename:
mov al, 0
stosb                 ; ASCIIZ 結尾

2. 檔案區塊讀取與指標管理

file_read:
mov ah, 3FH
lea dx, file_buffer
mov cx, 1024
mov bx, file_handle
int 21H
jnc file_read_ok
; 錯誤處理:關閉檔案、顯示訊息、結束
file_read_ok:
cmp ax, 0
je close_file
mov file_count, ax          ; 實際讀取位元組數
lea bx, file_buffer
mov file_pointer, bx        ; 當前處理指標

3. BIOS 列印中斷 (INT 17h) 與狀態檢查

print_data_block:
mov ah, 1
int 16H                  ; 檢查鍵盤
jz print_data_block_1
mov ah, 0
int 16H                  ; 取鍵碼
cmp al, 01BH             ; ESC?
je close_file
print_data_block_1:
mov si, file_pointer
mov al, [si]
mov ah, 0
mov dx, 0                ; LPT1
int 17H                  ; BIOS 列印
test ah, 25H             ; 檢查錯誤位 (Timeout/Error/Not Selected)
jnz printer_error
inc si
mov file_pointer, si
dec file_count
jnz print_data_block
jmp file_read

INT 17h, AH=0 回傳狀態於 AH:Bit 0=Time Out, Bit 3=I/O Error, Bit 4=Selected, Bit 5=Out of Paper, Bit 6=Acknowledge, Bit 7=Not Busy。程式以 test ah, 25h (00100101b) 同時檢查 Timeout (bit 0) 與 I/O Error (bit 3) / Not Selected (bit 5) — 實際上 25h = bit 0 + bit 3 + bit 5,涵蓋主要錯誤狀態。

4. 錯誤處理與資源釋放

所有錯誤路徑 (檔案開啟失敗、讀取錯誤、印表機錯誤) 均:

  1. 關閉檔案 Handle (INT 21h, AH=3Eh)
  2. 載入對應錯誤訊息位址至 DX
  3. 呼叫 terminate_display (INT 21h, AH=09h 列印 $ 結尾字串)
  4. 呼叫 terminate_program (INT 21h, AH=4Ch 結束)

確保無 Handle 洩漏、無記憶體殘留。

完整原始碼

.MODEL small
.STACK 500
.DATA
psp_seg dw 0
no_cl_mess db "This routine requires that a "
db "filename be on the command line for printing."
db 0dh,0ah,"Please try with a filename.",0dh,0ah,"$"
file_bad_open db "Bad file open",0dh,0ah,"$"
file_bad_read db "Bad file read",0dh,0ah,"$"
printer_bad_mess db "!! Printer Error !!!!",0dh,0ah,"$"
printing_mess db "A file is being printed,",0dh,0ah
db "To stop printing, Press ESC key",0dh,0ah,"$"
filename db 128 dup(0)
file_handle dw 0
file_count dw 0
file_pointer dw offset file_buffer
file_buffer db 1024 dup(0)
.CODE
start proc near
mov al,[DS:80H]
mov dx,@data
mov ds,dx
mov psp_seg,ES
cmp al,1
ja get_PSP_filename
lea dx,no_cl_mess
terminate_display:
mov ah,09
int 21H
terminate_program:
mov ah,4CH
mov al,00
int 21H
get_PSP_filename:
mov ax,ds
mov es,ax
mov ds,psp_seg
mov si,82H
lea di,filename
cld
get_PSP_data_1:
lodsb
cmp al,21H
jb got_PSP_filename
stosb
jmp get_PSP_data_1
got_PSP_filename:
mov al,0
stosb
push es
pop ds
mov ah,3DH
lea dx,filename
mov al,0
int 21H
jnc file_open_ok
lea dx,file_bad_open
jmp terminate_display
file_open_ok:
mov file_handle,ax
lea dx,printing_mess
mov ah,09
int 21H
file_read:
mov ah,3FH
lea dx,file_buffer
mov cx,1024
mov bx,file_handle
int 21H
jnc file_read_ok
mov ah,3EH
mov bx,file_handle
int 21H
lea dx,file_bad_read
jmp terminate_display
file_read_ok:
cmp ax,0
je close_file
mov file_count,ax
lea bx,file_buffer
mov file_pointer,bx
print_data_block:
mov ah,1
int 16H
jz print_data_block_1
mov ah,0
int 16H
cmp al,01BH
je close_file
print_data_block_1:
mov si,file_pointer
mov al,[si]
mov ah,0
mov dx,0
int 17H
test ah,25H
jnz printer_error
inc si
mov file_pointer,si
dec file_count
jnz print_data_block
jmp file_read
close_file:
mov ah,3EH
mov bx,file_handle
int 21H
jmp terminate_program
printer_error:
lea dx,printer_bad_mess
jmp terminate_display
start endp
end start

教學價值與現代映照

  • PSP 結構解析:命令列緩衝區位於 PSP:80h (長度) + 82h (內容),是 DOS 程式啟動參數傳遞的核心機制。
  • Handle vs FCB:程式使用 Handle API (3Dh/3Fh/3Eh) 而非舊式 FCB,展示 DOS 2.0+ 現代化檔案 I/O 模型。
  • BIOS vs DOS 列印:選擇 INT 17h 直接驅動並行埠,而非 DOS INT 21h, AH=05h (列印字元至 LST:),前者可精細控制狀態檢查與輪詢,後者由 DOS 管理列印佇列但無中斷機制。
  • 非阻塞鍵盤輪詢:INT 16h, AH=1 (檢查狀態) + AH=0 (讀取) 模式,實現「長時間 I/O 操作中可響應使用者中斷」的交互模式,現代非同步 I/O / 事件迴圈的雛形。
  • 錯誤碼驅動流程:所有系統呼叫均檢查 Carry Flag (CF),統一錯誤處理路徑,符合結構化錯誤處理原則。

這份程式碼雖屬 DOS 時代產物,但其「資源獲取即初始化 (RAII) 前身」、「非阻塞輪詢」、「分層錯誤處理」等設計思想,在現代嵌入式韌體、裝置驅動程式、高效能網路伺服器中依然閃耀著工程智慧的光芒。

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