下面給出一個用於演示中斷處理的實例。該實例的邏輯功能是,在屏幕的左上角以倒計時方式顯示秒為單位的時間,在時間用完後結束。該實例演示內容包括:外部中斷處理程序和陷阱處理程序。
1.源程序組織和清單
本實例由如下幾部分組成:
(1)全局描述符表GDT。GDT中除了含有常見的幾個描述符外,還含有描述時鐘中斷處理程序所使用的代碼段和數據段描述符,以及描述顯示程序所使用的代碼段和數據段描述符。
(2)中斷描述符表IDT。為了在保護模式下響應中斷和處理異常,必須有IDT。IDT含有256個門描述符。8號安排的是一個通向時鐘中斷處理程序的中斷門,0FEH號安排的是通向顯示處理程序的陷阱門,其它均安排成通向其它中斷或異常處理程序的陷阱門。
(3)時鐘中斷處理程序的代碼段和數據段。
(4)實現直接寫顯示緩沖區進行顯示的程序代碼段和數據段。
(5)處理其它中斷或異常的處理程序的代碼段。
(6)演示程序的代碼段、數據段和堆棧段。
(7)實模式下執行的啟動和結束程序代碼段和數據段。
源程序清單如下:
;名稱:ASM6.ASM
;功能:演示中斷處理的實現
;編譯:TASM ASM6.ASM
;連接:TLINK ASM6.OBJ
;----------------------------------------------------------------------------
INCLUDE 386SCD.INC
;----------------------------------------------------------------------------
;部分常量定義
;----------------------------------------------------------------------------
EOICOM = 20h ;外部中斷處理結束命令
ICREGP = 20h ;中斷控制寄存器端口地址
IMREGP = 21h ;中斷屏蔽寄存器端口地址
;----------------------------------------------------------------------------
GDTSeg SEGMENT PARA USE16 ;全局描述符表數據段(16位)
;----------------------------------------------------------------------------
;全局描述符表GDT
GDT LABEL BYTE
;空描述符
DUMMY Desc <>
;規范段描述符
Normal Desc <0ffffh,,,ATDW,,>
;視頻緩沖區段描述符(DPL=3)
VideoBuf Desc <0ffffh,8000h,0bh,ATDW,,>
;----------------------------------------------------------------------------
EFFGDT LABEL BYTE
;臨時代碼段描述符
TempCode Desc <0ffffh,TempCodeSeg,,ATCE,,>
;演示代碼段描述符
DemoCode Desc <DemoCodeLen-1,DemoCodeSeg,,ATCE,,>
;演示數據段描述符
DemoData Desc <DemoDataLen-1,DemoDataSeg,,ATDW,,>
;演示堆棧段描述符
DemoStack Desc <DemoStackLen-1,DemoStackSeg,,ATDWA,,>
;0feh號中斷處理程序(顯示程序)代碼段描述符
EchoCode Desc <EchoCodeLen-1,EchoCodeSeg,,ATCE,,>
;0feh號中斷處理程序(顯示程序)數據段描述符
EchoData Desc <EchoDataLen-1,EchoDataSeg,,ATDW,,>
;8號中斷處理程序代碼段描述符
TICode Desc <TICodeLen-1,TICodeSeg,,ATCE,,>
;8號中斷處理程序數據段描述符
TIData Desc <TIDataLen-1,TIDataSeg,,ATDW,,>
;其它中斷或異常處理程序代碼段描述符
Other Desc <OtherCodeLen-1,OtherCodeSeg,,ATCE,,>
;----------------------------------------------------------------------------
GDTLen = $-GDT ;全局描述符表長度
GDNum = ($-EFFGDT)/(SIZE Desc) ;需特殊處理的描述符數
;----------------------------------------------------------------------------
Normal_Sel = Normal-GDT ;規范段描述符選擇子
Video_Sel = VideoBuf-GDT ;視頻緩沖區段描述符選擇子
;----------------------------------------------------------------------------
TempCode_Sel = TempCode-GDT ;臨時代碼段的選擇子
DemoCode_Sel = DemoCode-GDT ;演示代碼段的選擇子
DemoData_Sel = DemoData-GDT ;演示數據段的選擇子
DemoStack_Sel = DemoStack-GDT ;演示堆棧段的選擇子
EchoCode_Sel = EchoCode-GDT ;0feh號中斷程序代碼段選擇子
EchoData_Sel = EchoData-GDT ;0feh號中斷程序數據段選擇子
TICode_Sel = TICode-GDT ;8號中斷程序代碼段選擇子
TIData_Sel = TIData-GDT ;8號中斷程序數據段選擇子
Other_Sel = Other-GDT ;其它中斷或異常代碼段選擇子
;----------------------------------------------------------------------------
GDTSeg ENDS ;全局描述符表段定義結束
;----------------------------------------------------------------------------
IDTSeg SEGMENT PARA USE16 ;中斷描述符表數據段(16位)
;----------------------------------------------------------------------------
IDT LABEL BYTE ;中斷描述符表
;0--7的8個陷阱門描述符
REPT 8
Gate <OtherBegin,Other_Sel,,AT386TGate,>
ENDM
;對應8號(時鐘)中斷處理程序的門描述符
Gate <TIBegin,TICode_Sel,,AT386IGate,>
;從9--0fdh的245個陷阱門描述符
REPT 245
Gate <OtherBegin,Other_Sel,,AT386TGate,>
ENDM
;對應0feh號中斷處理程序的陷阱門描述符
Gate <EchoBegin,EchoCode_Sel,,AT386TGate,>
;對應0ffh號中斷處理程序的陷阱門描述符
Gate <OtherBegin,Other_Sel,,AT386TGate,>
;----------------------------------------------------------------------------
IDTLen = $-IDT
;----------------------------------------------------------------------------
IDTSeg ENDS ;中斷描述符表段定義結束
;----------------------------------------------------------------------------
;其它中斷或異常處理程序的代碼段
;----------------------------------------------------------------------------
OtherCodeSeg SEGMENT PARA USE16
ASSUMEGISter>CS:OtherCodeSeg
;----------------------------------------------------------------------------
OtherBegin PROC FAR
mov ax,Video_Sel
mov es,ax
mov ah,17h ;在屏幕左上角顯示蘭底白字
mov al,'!' ;符號"!"
mov WORD PTR es:[0],ax
jmp $ ;無限循環
OtherBegin ENDP
;----------------------------------------------------------------------------
OtherCodeLen = $
OtherCodeSeg ENDS
;----------------------------------------------------------------------------
;8號中斷處理程序的數據段
;----------------------------------------------------------------------------
TIDataSeg SEGMENT PARA USE16
Count DB 0 ;中斷發生的計數器
TIDataLen = $
TIDataSeg ENDS
;----------------------------------------------------------------------------
;8號中斷處理程序的代碼段
;----------------------------------------------------------------------------
TICodeSeg SEGMENT PARA USE16
ASSUME CS:TICodeSeg,DS:TIDataSeg
;----------------------------------------------------------------------------
TIBegin PROC FAR
push eax ;保護現場
push ds
push fs
push gs
mov ax,TIData_Sel ;置中斷處理程序數據段
mov ds,ax
mov ax,EchoData_Sel ;置顯示過程數據段
mov fs,ax
mov ax,DemoData_Sel ;置演示程序數據段
mov gs,ax
cmp Count,0
jnz TI2 ;計數非0表示未到1秒
mov Count,18 ;每秒約18次
int 0feh ;調用0FEH號中斷處理程序顯示
cmp BYTE PTR fs:Mess,'0'
jnz TI1
mov BYTE PTR gs:Flag,1 ;顯示符號'0'時置標記
TI1: dec BYTE PTR fs:Mess ;調整顯示符號
TI2: dec Count ;調整計數
pop gs ;恢復現場
pop fs
pop ds
mov al,EOICOM ;通知中斷控制器中斷處理結束
out ICREGP,al
pop eax
iretd ;中斷返回
TIBegin ENDP
;----------------------------------------------------------------------------
TICodeLen = $
TICodeSeg ENDS
;----------------------------------------------------------------------------
;0FEH號中斷處理程序數據段
;----------------------------------------------------------------------------
EchoDataSeg SEGMENT PARA USE16
Mess DB '8',4eh
EchoDataLen = $
EchoDataSeg ENDS
;----------------------------------------------------------------------------
;0FEH號中斷處理程序(顯示程序)的代碼段
;----------------------------------------------------------------------------
EchoCodeSeg SEGMENT PARA USE16
ASSUME CS:EchoCodeSeg,DS:EchoDataSeg
;----------------------------------------------------------------------------
EchoBegin PROC FAR
push ax ;保護現場
push ds
push es
mov ax,EchoData_Sel ;置顯示過程數據段
mov ds,ax
mov ax,Video_Sel ;置視頻緩沖區數據段
mov es,ax
mov ax,WORD PTR Mess
mov WORD PTR es:[0],ax
pop es
pop ds
pop ax
iretd
EchoBegin ENDP
;----------------------------------------------------------------------------
EchoCodeLen = $
EchoCodeSeg ENDS
;----------------------------------------------------------------------------
;演示任務的堆棧段
;----------------------------------------------------------------------------
DemoStackSeg SEGMENT PARA USE16
DemoStackLen = 1024
DB DemoStackLen DUP(0)
DemoStackSeg ENDS
;----------------------------------------------------------------------------
;演示任務的數據段
;----------------------------------------------------------------------------
DemoDataSeg SEGMENT PARA USE16
Flag DB 0
DemoDataLen = $
DemoDataSeg ENDS
;----------------------------------------------------------------------------
;演示任務的代碼段
;----------------------------------------------------------------------------
DemoCodeSeg SEGMENT PARA USE16
ASSUME CS:DemoCodeSeg,DS:DemoDataSeg
;----------------------------------------------------------------------------
DemoBegin PROC FAR
mov ax,DemoStack_Sel ;置堆棧
mov ss,ax
mov sp,DemoStackLen ;置數據段
mov ax,DemoData_Sel
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov al,11111110b ;置中斷屏蔽字
out IMREGP,al ;只開發時鐘中斷
sti ;開中斷
DemoConti: cmp BYTE PTR Flag,0 ;判標志
jz DemoConti ;直到不為0
cli ;關中斷
;轉回臨時代碼段,准備回實方式
JUMP16 TempCode_Sel,<OFFSET ToDos>
DemoBegin ENDP
;----------------------------------------------------------------------------
DemoCodeLen = $
DemoCodeSeg ENDS
;----------------------------------------------------------------------------
TempCodeSeg SEGMENT PARA USE16 ;臨時任務的代碼段
ASSUME CS:TempCodeSeg
;----------------------------------------------------------------------------
Virtual PROC FAR
JUMP16 DemoCode_Sel,DemoBegin ;轉演示任務
ToDos: mov ax,Normal_Sel ;恢復實方式段描述符高速緩存
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov eax,cr0 ;准備返回實模式
and al,11111110b
mov cr0,eax
JUMP16 <SEG Real>,<OFFSET Real>
Virtual ENDP
;----------------------------------------------------------------------------
TempCodeSeg ENDS
;============================================================================
RDataSeg SEGMENT PARA USE16 ;實方式數據段
VGDTR PDesc <GDTLen-1,> ;GDT偽描述符
VIDTR PDesc <IDTLen-1,> ;IDT偽描述符
NORVIDTR PDesc <3ffh,> ;用於保存原IDTR值
SPVar DW ? ;用於保存實方式下的SP
SSVar DW ? ;用於保存實方式下的SS
IMaskRegV DB ? ;用於保存原中斷屏蔽寄存器值
RDataSeg ENDS
;----------------------------------------------------------------------------
RCodeSeg SEGMENT PARA USE16 ;實方式代碼段
ASSUME CS:RCodeSeg,DS:RDataSeg
;----------------------------------------------------------------------------
Start PROC
mov ax,RDataSeg
mov ds,ax
cld
call InitGDT ;初始化全局描述符表GDT
call InitIDT ;初始化中斷描述符表IDT
mov SSVar,ss ;保存堆棧指針
mov SPVar,sp
sidt QWORD PTR NORVIDTR ;保存IDTR
in al,IMREGP
mov BYTE PTR IMaskRegV,al
lgdt QWORD PTR VGDTR ;裝載GDTR
cli ;關中斷
lidt QWORD PTR VIDTR ;裝載IDTR
mov eax,cr0
or al,1
mov cr0,eax
JUMP16 <TempCode_Sel>,<OFFSET Virtual>
Real: mov ax,RDataSeg
mov ds,ax
lss sp,DWORD PTR SPVar ;又回到實方式
lidt QWORD PTR NORVIDTR
mov al,IMaskRegV
out IMREGP,al
sti
mov ax,4c00h
int 21h
Start ENDP
;----------------------------------------------------------------------------
InitGDT PROC
push ds
mov ax,GDTSeg
mov ds,ax
mov cx,GDNum
mov si,OFFSET EFFGDT
InitG: mov ax,[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR [si].BaseL,ax
mov BYTE PTR [si].BaseM,dl
mov BYTE PTR [si].BaseH,dh
add si,SIZE Desc
loop InitG
pop ds
mov bx,16
mov ax,GDTSeg
mul bx
mov WORD PTR VGDTR.Base,ax
mov WORD PTR VGDTR.Base+2,dx
ret
InitGDT ENDP
;----------------------------------------------------------------------------
InitIDT PROC
mov bx,16
mov ax,IDTSeg
mul bx
mov WORD PTR VIDTR.Base,ax
mov WORD PTR VIDTR.Base+2,dx
ret
InitIDT ENDP
;----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
2.關於實例六的說明
(1)時鐘中斷仍使用8H號中斷向量
為了即簡單又清楚地演示在保護模式下響應外部中斷並進行處理,實例使用了時鐘中斷源,但沒有通過重新設置中斷控制器的方法改變對應的中斷向量。所以,時鐘中斷使用的8H號中斷向量號就與雙重故障異常使用的中斷向量號發生沖突。但實例僅是演示程序,所以只要保證不發生雙重故障異常,就可避免沖突,從而不會影響演示。
設置中斷屏蔽寄存器,僅開放時鐘中斷。所以,在開中斷狀態下,也只可能發生時鐘中斷,而不會發生其它外部中斷。
(2)時鐘中斷處理程序的設計
由於通過中斷門轉時鐘中斷處理程序,所以在控制轉移時不發生任務切換。但外部中斷隨時可能發生,因此中斷處理程序必須采取保護現場等措施。作為演示程序,該中斷處理程序檢查和調整在其數據段中的計數器,滿18次後,就認為已滿一秒,再調整用於顯示的倒計數信息。如果倒計數信息為0,那麼就設置演示程序數據段中的時間為0標志。該中斷處理程序通過約定的數據區與顯示程序及演示程序交換信息。
(3)利用一個軟中斷(陷阱處理)程序實現顯示
為了演示陷阱及其處理,把顯示過程安排成陷阱處理程序。上述時鐘中斷處理程序通過軟中斷調用指令INT調用該顯示程序,以顯示倒計數。在控制轉移時,也沒有任務切換。該陷阱處理程序相當於一個“軟中斷”處理程序,類似實模式下的BIOS中斷INT 10H。
(4)對其它中斷或異常的響應
為了簡單,除了8H號和0FEH號外,IDT中其它的門均通向一個處理程序。該處理程序用於處理其它中斷或異常。處理過程也極其簡單,在屏幕左上角顯示藍底白字的符號“!”,然後進入無限循環。實際上,按演示程序現在的安排,不可能發生這種情況。
(5)沒有特權級變換
為了簡單,實例涉及的中斷處理程序和異常處理程序都保持特權級0。所以,控制轉移時不發生特權級變換。因此,沒有使用其它堆棧。
(6)對IDT的初始化
由於IDT中門描述符沒有32位段基地址,並且入口點偏移較小,所以就直接填寫門描述符結構變量,沒有額外再初始化。過程InitIDT只是設置IDT偽描述符。
(7)裝載和保存IDTR寄存器
再使IDT發揮作用之前,還要裝載中斷描述符表寄存器IDTR;但為了回到實模式後,恢復原來的IDTR之內容,所以先保存IDTR的內容。實例使用如下指令保存IDTR:
sidt QWORD PTR NORVIDTR
該指令的功能是把IDTR的內容保存到存儲器中的偽描述符NORVIDTR中。該偽描述符的結構如前文所述的結構類型PDESC所示,低字是以字節為單位的界限,高雙字是基地址。在後面的文章中將對SIDT指令作詳細說明。
本實例使用如下指令裝載IDTR寄存器:
lidt QWORD PTR VIDTR lidt QWORD PTR NORVIDTR
LIDT指令類似於LGDT指令,在後面的文章中將對LIDT指令作詳細說明。