|
下面给出一个用于演示中断处理的实例。该实例的逻辑功能是,在屏幕的左上角以倒计时方式显示秒为单位的时间,在时间用完后结束。该实例演示内容包括:外部中断处理程序和陷阱处理程序。
1.源程序组织和清单
本实例由如下几部分组成:
(1)全局描述符表GDT。GDT中除了含有常见的几个描述符外,还含有描述时钟中断处理程序所使用的代码段和数据段描述符,以及描述显示程序所使用的代码段和数据段描述符。
(2)中断描述符表IDT。为了在保护模式下响应中断和处理异常,必须有IDT。IDT含有256个门描述符。8号安排的是一个通向时钟中断处理程序的中断门,0FEH号安排的是通向显示处理程序的陷阱门,其它均安排成通向其它中断或异常处理程序的陷阱门。
(3)时钟中断处理程序的代码段和数据段。
(4)实现直接写显示缓冲区进行显示的程序代码段和数据段。
(5)处理其它中断或异常的处理程序的代码段。
(6)演示程序的代码段、数据段和堆栈段。
(7)实模式下执行的启动和结束程序代码段和数据段。
源程序清单如下:
INCLUDE 386SCD.INC
EOICOM = 20h
ICREGP = 20h
IMREGP = 21h
GDTSeg SEGMENT PARA USE16
GDT LABEL BYTE
DUMMY Desc <>
Normal Desc <0ffffh,,,ATDW,,>
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,,>
EchoCode Desc <EchoCodeLen-1,EchoCodeSeg,,ATCE,,>
EchoData Desc <EchoDataLen-1,EchoDataSeg,,ATDW,,>
TICode Desc <TICodeLen-1,TICodeSeg,,ATCE,,>
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
EchoData_Sel = EchoData-GDT
TICode_Sel = TICode-GDT
TIData_Sel = TIData-GDT
Other_Sel = Other-GDT
GDTSeg ENDS
IDTSeg SEGMENT PARA USE16
IDT LABEL BYTE
REPT 8
Gate <OtherBegin,Other_Sel,,AT386TGate,>
ENDM
Gate <TIBegin,TICode_Sel,,AT386IGate,>
REPT 245
Gate <OtherBegin,Other_Sel,,AT386TGate,>
ENDM
Gate <EchoBegin,EchoCode_Sel,,AT386TGate,>
Gate <OtherBegin,Other_Sel,,AT386TGate,>
IDTLen = $-IDT
IDTSeg ENDS
OtherCodeSeg SEGMENT PARA USE16
ASSUME 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
TIDataSeg SEGMENT PARA USE16
Count DB 0
TIDataLen = $
TIDataSeg ENDS
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
mov Count,18
int 0feh
cmp BYTE PTR fs:Mess,'0'
jnz TI1
mov BYTE PTR gs:Flag,1
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
EchoDataSeg SEGMENT PARA USE16
Mess DB '8',4eh
EchoDataLen = $
EchoDataSeg ENDS
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
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,>
VIDTR PDesc <IDTLen-1,>
NORVIDTR PDesc <3ffh,>
SPVar DW ?
SSVar DW ?
IMaskRegV DB ?
RDataSeg ENDS
RCodeSeg SEGMENT PARA USE16
ASSUME CS:RCodeSeg,DS:RDataSeg
Start PROC
mov ax,RDataSeg
mov ds,ax
cld
call InitGDT
call InitIDT
mov SSVar,ss
mov SPVar,sp
sidt QWORD PTR NORVIDTR
in al,IMREGP
mov BYTE PTR IMaskRegV,al
lgdt QWORD PTR VGDTR
cli
lidt QWORD PTR VIDTR
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指令作详细说明。 |