這是一個經典的 DOS 時期組語專案——Mixer Volume Ctrler V1.0。程式採用記憶體常駐(TSR, Terminate and Stay Resident)技術,攔截鍵盤中斷(INT 09h),在背景監聽 Ctrl+F1~F8 熱鍵,直接操作聲卡混音器埠(Port 224h/225h)調整 Master、CD Audio、FM Synthesis、Voice 四組音量。雖然未實作鍵盤解鎖(Unhook)功能,但完整展示了 TSR 程式的核心架構:中斷向量替換、鍵盤掃描碼判斷、I/O 埠操作與常駐記憶體管理。
程式啟動時(init 標籤),先以 INT 21h, AH=35h 取得原 INT 09h 向量並保存至 int9 變數,再以 AH=25h 設定新向量指向 new_int。此為標準 TSR 攔截流程。值得注意的是,程式在記憶體中留下特徵碼 'zd'(位於 mark),重複執行時可偵測是否已安裝,避免多重常駐。
new_intSTI 開啟中斷,推入所有通用暫存器與段暫存器。60h 讀取掃描碼;若第 7 位為 1(Key Up 事件)則直接跳離。0040:0017h,測試 Ctrl 鍵是否按下(bit 4)。59h~66h(F1~F8 掃描碼),符合則跳轉 ok 標籤處理。61h 寫入 80h 再還原,發送 20h 至 PIC(20h 埠)發出 EOI(中斷結束)信號。call my 執行音量調整子程序。jmp cs:int9 跳轉原 INT 09h 處理程序,確保鍵盤正常運作。my、up、down聲卡混音器採用索引/資料埠對(224h 索引、225h 資料)。my 根據掃描碼決定控制哪一組音量(對應索引值:Master=32h, CD=38h, FM=36h, Voice=14h),再呼叫 up 或 down:
up:讀取目前值,加 11h,上限飽和至 FFh。down:讀取目前值,減 11h,下限飽和至 00h。寫入時需先輸出索引至 224h,再輸出資料至 225h。
初始化完成後,以 INT 27h(或 INT 21h, AH=31h)終止並常駐,保留 init 之前的程式碼與資料區塊。此為 DOS TSR 程式的標準收尾方式。
code segment
assume cs:code,ds:code
org 100h
begin:jmp init
r1 equ 224h
r2 equ 225h
int9 dd ?
mark db 'zd'
new_int proc far
sti
push ax
push es
xor ax,ax
mov es,ax
in al,60h
test al,80h
jnz exit1
test byte ptr es:[0417h],100b
jz exit1
cmp al,59 ;F1
jz ok
cmp al,60 ;F2
jz ok
cmp al,61 ;F3
jz ok
cmp al,62 ;F4
jz ok
cmp al,63 ;F5
jz ok
cmp al,64 ;F6
jz ok
cmp al,65 ;F7
jz ok
cmp al,66 ;F8
jz ok
jmp exit1
ok:
push ax
push bx
push cx
push dx
push bp
push di
push si
push ds
mov bl,al
push cs
pop ds
in al,61h
push ax
or al,80h
out 61h,al
pop ax
out 61h,al
mov al,20h
out 20h,al
call my
pop ds
pop si
pop di
pop bp
pop dx
pop cx
pop bx
pop ax
exit1:
pop es
pop ax
jmp cs:int9
new_int endp
my proc near
o: cmp bl,59
jnz o1
mov cl,32h
call up
jmp quit
o1:cmp bl,61
jnz o2
mov cl,38h
call up
jmp quit
o2:cmp bl,63
jnz o3
mov cl,36h
call up
jmp quit
o3:cmp bl,65
jnz o4
mov cl,14h
call up
jmp quit
;down
o4: cmp bl,60
jnz o5
mov cl,32h
call down
jmp quit
o5:cmp bl,62
jnz o6
mov cl,38h
call down
jmp quit
o6:cmp bl,64
jnz o7
mov cl,36h
call down
jmp quit
o7:cmp bl,66
jnz o8
mov cl,14h
call down
jmp quit
o8:
quit:ret
my endp
up proc near
xor ax,ax
mov al,cl
mov dx,r1
out dx,al
mov dx,r2
in al,dx
add ax,11h
cmp ax,0ffh
jle goon
mov ax,0ffh
goon:
push ax
mov al,cl
mov dx,r1
out dx,al
pop ax
mov dx,r2
out dx,al
ret
up endp
down proc near
xor ax,ax
mov al,cl
mov dx,r1
out dx,al
mov dx,r2
in al,dx
sub ax,11h
cmp ax,0h
jge go
mov ax,0h
go:
push ax
mov al,cl
mov dx,r1
out dx,al
pop ax
mov dx,r2
out dx,al
ret
down endp
init :
cli
push cs
pop ds
mov dx,offset zd
mov ah,9
int 21h
mov ax,3509h
int 21h
cmp word ptr es:[bx-2],'dz'
jnz install
mov dx,offset mes1
mov ah,9
int 21h
mov ah,4ch
int 21h
install:
mov di,offset int9
mov [di],bx
mov [di+2],es
push cs
pop ds
mov dx,offset new_int
mov ax,2509h
int 21h
sti
mov dx,offset mes
mov ah,9
int 21h
mov dx,offset init
int 27h
zd db 'MIXER VOLUME CTRL V1.0 ZDsoft 1995',0dh,0ah,'$'
mes db 'CTRL-F1 -- TURN MASTER VOL UP',0dh,0ah
mes2 db 'CTRL-F2 -- TURN MASTER VOL DOWN ',0dh,0ah
mes3 db 'CTRL-F3 -- TURN CD VOL UP ',0dh,0ah
mes4 db 'CTRL-F4 -- TURN CD VOL DOWN ',0dh,0ah
mes5 db 'CTRL-F5 -- TURN FM VOL UP ',0dh,0ah
mes6 db 'CTRL-F6 -- TURN FM VOL DOWN ',0dh,0ah
mes7 db 'CTRL-F7 -- TURN VOICE VOL UP ',0dh,0ah
mes8 db 'CTRL-F8 -- TURN VOICE VOL DOWN ',0dh,0ah,'$'
mes1 db 'Already Install!$'
code ends
end begin
雖然現代作業系統已禁止使用者程式直接存取硬體埠,但此程式完美示範了中斷鏈結、狀態機設計、資源佔用與釋放等系統程式設計核心觀念。若移植至現代環境,可對應至鍵盤鉤子、音訊端點音量 API (IAudioEndpointVolume) 等機制。