演示异常处理之实例七
2009-02-16 09:36:51 来源:WEB开发网----------------------------------------------------------------------------
通用保护故障处理程序代码段
----------------------------------------------------------------------------
GPCodeSeg SEGMENT PARA USE16
ASSUME CS:GPCodeSeg
----------------------------------------------------------------------------
GPBegin PROC FAR
push ebp
mov ebp,esp
push eax
push esi
push edi 保护现场
mov si,OFFSET MessD
mov di,0
int 0feh 显示提示信息
mov eax,[bp+4] 从堆栈中取出出错代码
CALL16 SubCode_Sel,SubBegin 显示出错代码
pop edi
pop esi
pop eax 恢复部分现场
add DWORD PTR [ebp+8],2 按模拟的故障指令调整返回
pop ebp 地址
add esp,4 废除堆栈中的出错代码
iretd
GPBegin ENDP
----------------------------------------------------------------------------
显示出错码过程代码段
----------------------------------------------------------------------------
SubCodeSeg SEGMENT PARA USE16
ASSUME CS:SubCodeSeG
----------------------------------------------------------------------------
SubBegin PROC AX中含出错代码
push ax 保护现场
push cx
push dx
push si
push di
mov si,OFFSET ErrCode
mov dx,ax
mov cx,4
SubR1: rol dx,4 把16位出错代码转换成4位
mov al,dl 十六进制数的ASCII码并保存
and al,0fh
add al,30h
cmp al,'9'
jbe SubR2
add al,7
SubR2: mov [si],al
inc si
loop SubR1
mov si,OFFSET ErrMess
Mov di,80*2 从第二行行首开始
int 0feh 显示出错码
pop di 恢复现场
pop si
pop dx
pop cx
pop ax
retf 返回
SubBegin ENDP
----------------------------------------------------------------------------
SubCodeLen = $
SubCodeSeg ENDS
----------------------------------------------------------------------------
GPCodeLen = $
GPCodeSeg ENDS
----------------------------------------------------------------------------
实现显示的陷阱处理程序代码段
入口参数--DS:SI指向显示信息串,ES:DI指向显示缓冲区
----------------------------------------------------------------------------
EchoCodeSeg SEGMENT PARA USE16
ASSUME CS:EchoCodeSeg
----------------------------------------------------------------------------
EchoBegin PROC FAR
pushad 保护现场
cld
mov ah,7
mov al,20h
mov cx,80
push di
rep stosw 清所在显示行
pop di
Echo1: lodsb
or al,al
jz Echo2
stosw 显示指定信息串
jmp Echo1
Echo2: popad 恢复现场
iretd
EchoBegin ENDP
----------------------------------------------------------------------------
EchoCodeLen = $
EchoCodeSeg ENDS
----------------------------------------------------------------------------
缓冲区数据段
----------------------------------------------------------------------------
BufferSeg SEGMENT PARA USE16
KeyASCII DB ?
Buffer DB 128 DUP(?)
BufferLen = $
BufferSeg ENDS
----------------------------------------------------------------------------
演示任务局部描述符表段
----------------------------------------------------------------------------
DemoLDTSeg SEGMENT PARA USE16
----------------------------------------------------------------------------
DLDT LABEL BYTE
演示任务TSS段作为数据段的描述符及选择子
ToDemoTSS Desc
ToDemoTSS_Sel = ToDemoTSS-DLDT+TIL
演示任务堆栈段描述符及选择子
DemoStack Desc
DemoStack_Sel = DemoStack-DLDT+TIL
演示任务数据段描述符及选择子
DemoData Desc
DemoData_Sel = DemoData-DLDT+TIL
除法出错故障处理程序代码段描述符及选择子
Divide Desc
Divide_Sel = Divide-DLDT+TIL
溢出陷阱处理程序代码段描述符及选择子
OverFlow Desc
OF_Sel = OverFlow-DLDT+TIL
段不存在故障处理程序代码段描述符及选择子
SNPCode Desc
SNP_Sel = SNPCode-DLDT+TIL
堆栈段出错故障处理程序代码段描述符及选择子
SSECode Desc
SSE_Sel = SSECode-DLDT+TIL
通用保护故障处理程序代码段描述符及选择子
GPCode Desc
GP_Sel = GPCode-DLDT+TIL
为模拟段不存在故障而安排的数据段描述符及选择子
TestNPS Desc <0ffffh,,,ATDW-80h,,>
TestNPS_Sel = TestNPS-DLDT+TIL
----------------------------------------------------------------------------
DemoLDNum = ($-DLDT)/(SIZE Desc) LDT描述符个数
DemoLDTLen =$
----------------------------------------------------------------------------
DemoLDTSeg ENDS
----------------------------------------------------------------------------
演示任务TSS段
----------------------------------------------------------------------------
DemoTSSSeg SEGMENT PARA USE16
DemoTaskSS TSS <>
DB 0ffh
DemoTSSLen = $
DemoTSSSeg ENDS
----------------------------------------------------------------------------
演示任务的堆栈段
----------------------------------------------------------------------------
DemoStackSeg SEGMENT PARA USE16
DemoStackLen = 1024
DB DemoStackLen DUP(0)
DemoStackSeg ENDS
----------------------------------------------------------------------------
演示任务的数据段
----------------------------------------------------------------------------
DemoDataSeg SEGMENT PARA USE16
Mess0 DB 'Divide Error (Exception 0)',0
Mess4 DB 'Overflow (Exception 4)',0
MessB DB 'Segment Not Present (Exception 11)',0
MessC DB 'Stack Segment (Exception 12)',0
MessD DB 'General Protection (Exception 13)',0
MessOther DB 'Other Exception',0
ErrMess DB 'Error Code = '
ErrCode DB 4 DUP(0),'H',0
DemoDataLen = $
DemoDataSeg ENDS
----------------------------------------------------------------------------
演示任务的代码段
----------------------------------------------------------------------------
DemoCodeSeg SEGMENT PARA USE16
ASSUME CS:DemoCodeSeg,DS:DemoDataSeg
----------------------------------------------------------------------------
DemoBegin PROC FAR
mov ax,DemoLDT_Sel
lldt ax 装载LDTR
mov ax,DemoStack_Sel 置堆栈
mov ss,ax
mov esp,DemoStackLen
mov ax,ToDemoTSS_Sel
mov gs,ax 把演示任务LDT选择子填入TSS
mov WORD PTR gs:DemoTaskSS.TRLDTR,DemoLDT_Sel
mov ax,DemoTSS_Sel
ltr ax 装载TR
mov ax,DemoData_Sel
mov ds,ax 装载其它数据段寄存器
mov ax,VideoBuf_Sel
mov es,ax
mov ax,XBuffer_Sel
mov fs,ax
mov ax,XBuffer_Sel
mov gs,ax
int 0ffh 接收要模拟的异常类型号
mov al,BYTE PTR fs:KeyASCII 按接收的字符模拟异常号
cmp al,'0'
jnz Demo4
mov ax,2000
mov cl,2 模拟除法出错故障
div cl 该指令长2字节
jmp Over
Demo4: cmp al,'4'
jnz Demo11
mov al,100
add al,50
into 模拟溢出陷阱
JMP OVER
Demo11: cmp al,'B'
jnz Demo12
mov ax,TestNPS_Sel 模拟段不存在故障
mov gs,ax 该指令长2字节
JMP Over
Demo12: cmp al,'C'
jnz Demo13
mov ebp,esp 模拟堆栈出错故障
mov al,[ebp] 该指令长4字节
jmp Over
Demo13: mov ax,DemoTSS_Sel 模拟通用保护故障
mov gs,ax 该指令长2字节
Over: 转临时代码段
JUMP16 TempCode_Sel,
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 ,
Virtual ENDP
----------------------------------------------------------------------------
TempCodeSeg ENDS
============================================================================
RDataSeg SEGMENT PARA USE16 实方式数据段
VGDTR PDesc GDT伪描述符
VIDTR PDesc IDT伪描述符
NORVIDTR PDesc <3ffh,> 用于保存原IDTR值
SPVar DW ? 用于保存实方式下的SP
SSVar DW ? 用于保存实方式下的SS
Mess DB 'Press a key[0,4,B,C,D]:$'提示信息
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 ax,GKeyLDTSeg
mov fs,ax
mov cx,GKeyLDNum
mov si,OFFSET GLDT
CALL InitLDT
mov ax,DemoLDTSeg
mov fs,ax
mov cx,DemoLDNum
mov si,OFFSET DLDT
CALL InitLDT
mov SSVar,ss 保存堆栈指针
mov SPVar,sp
lgdt QWORD PTR VGDTR 装载GDTR
sidt QWORD PTR NORVIDTR 保存IDTR
cli 关中断
lidt QWORD PTR VIDTR 装载IDTR
mov eax,cr0
or al,1
mov cr0,eax
JUMP16 ,
Real: mov ax,RDataSeg
mov ds,ax
lss sp,DWORD PTR SPVar 又回到实方式
lidt QWORD PTR NORVIDTR
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
----------------------------------------------------------------------------
入口参数:FS:SI=第一个要初始化的描述符,CX=要初始化的描述符数
----------------------------------------------------------------------------
InitLDT PROC
ILDT: mov ax,WORD PTR FS:[si].BaseL
movzx eax,ax
shl eax,4
shld edx,eax,16
mov WORD PTR fs:[si].BaseL,ax
mov BYTE PTR fs:[si].BaseM,dl
mov BYTE PTR fs:[si].BaseH,dh
add si,SIZE Desc
loop ILDT
ret
InitLDT ENDP
----------------------------------------------------------------------------
RCodeSeg ENDS
END Start
2.关于实例七的说明
上述模拟与演示程序的许多内容与实例六相同,下面就各异常处理程序和读键盘任务的实现作些说明:
(1)除法出错故障处理程序的实现
从源程序可见,除法出错是在执行故意安排的被除数为2000,而除数为2的无符号除指令时引起的。作为演示,除法出错故障处理程序先显示一条提示信息,然后把存放被除数AX的内容右移一位,然后就返回。由于除法出错为故障类异常,所以在故障处理结束后,仍执行该无符号除指令。显然将再次引起同样的故障,仍把被除数右移一位。由于每次处理时都把被除数减半,所以几次故障后就不发生该故障了。
(2)溢出陷阱处理程序的实现
作为演示的溢出陷阱处理程序较简单。先显示一条提示信息,然后就返回。因为溢出异常为陷阱类异常,所以在陷阱处理结束后,就直接返回到引起陷阱指令的下一条指令。
(3)段不存在故障处理程序的实现
从源程序可见,段不存在故障是在执行故意安排的把一个选择子送段寄存器GS的指令时引起的。该选择子索引的描述符中的存在位P被置为0,表示对应段不在内存。在正常情况下,段不存在故障处理程序要把对应的段装入内存,再把描述符内的P位修改为1,于是,在故障处理结束后,引起故障的指令可得到顺序执行。为了简单,这里安排的故障处理程序先显示一条提示信息,然后显示出错码,最后调整堆栈中的返回地址并返回。段不存在故障提供一个出错码,该故障处理程序利用POP指令把它用堆栈中弹出,这样堆栈指针就指向返回地址。由于段不存在异常属于故障类异常,所以返回点仍是引起故障的指令。因此,演示程序调整了堆栈中的返回地址,使其返回到引起故障的指令的下一条指令。
(4)堆栈段出错故障处理程序的实现
引起堆栈出错故障的原因有多种,实例通过执行故意安排的偏移超过段界限的堆栈段访问指令来模拟堆栈段出错故障的产生。作为演示的堆栈出错故障处理程序比较简单,先显示一条信息,然后显示出错码,最后调整堆栈中的返回地址并返回。
(5)通用保护障处理程序的实现
引起通用保护故障处理程序的原因有多种,实例通过把一个指向系统段描述符的选择子装入数据段寄存器GS来模拟通用保护故障的产生。作为演示的通用保护故障处理程序,象上述两个故障处理程序一样比较简单,先显示一条提示信息,然后显示出错码,最后调整堆栈中的返回地址并返回,但在废除堆栈中的出错码和调整堆栈中的返回地址时采用了其它方法。
(6)异常处理程序的一般说明
在实例中,通向上述各种异常处理程序的门都是陷阱门。所以,在发生异常而转入这些异常处理程序时,都不发生任务切换。于是,这些异常处理仍作为演示任务的一部分。
正常情况下,异常处理程序应该注意现场的保护和恢复,但为了简单,作为演示的异常处理程序没有能够切实地保护现场。注意,这些异常处理程序所采用的处理方法与所模拟的指令有关,不适用于一般情况。
(7)显示出错代码的过程
实例采用一个过程用于显示出错代码,该过程的入口参数是AX含出错码。利用该过程不仅缩短程序,而且也用于表现异常处理程序的实现。
(8)读键盘任务的实现
在实例的IDT中,0FFH号门描述符是任务门,指向一个独立的任务。该任务的功能是读键盘,接收一个指定范围内的字符。演示任务通过指令“INT 0FFH”来调用它,接收一个代表需要模拟异常的字符。
为了简单,该任务在实模式下读键盘,接收指定范围内的字符。为此,该任务每次经历如下步骤:(1)转到实模式。此前要作必要的准备,转到实模式后,要恢复必须的实模式下的部分现场。(2)接收指定的字符。调用DOS功能显示提示信息,调用BIOS中断读键盘,如果在指定范围内,那么就显示,并保存在约定的数据段中。(3)转回到保护模式,此前也要作必要的准备。
尽管在任务切换时,自动利用TSS保护和恢复现场,但由于该任务相当于一个读键盘的过程,所以在开始任务时,还通过堆栈保护必要的现场,在结束任务时恢复现场。请特别注意,安排在该任务代码段中的IRETD指令之后的转移指令的作用。当执行IRETD指令时,由于NT位为1,所以按反向链进行任务切换,同时保存各寄存器到当前的TSS,为了下次进入时仍能从头开始执行此任务,所以在IRETD后加一条转移到该任务开头的指令。
更多精彩
赞助商链接