WEB开发网
开发学院软件开发汇编语言 演示异常处理之实例七 阅读

演示异常处理之实例七

 2009-02-16 09:36:51 来源:WEB开发网   
核心提示:通用保护故障处理程序代码段GPCodeSeg SEGMENT PARA USE16ASSUMECS:GPCodeSegGPBegin PROCFARpushebpmov ebp,esppusheaxpushesipushedi 保护现场mov si,OFFSET MessDmov di,0int 0feh显示提示信息m

----------------------------------------------------------------------------
通用保护故障处理程序代码段
----------------------------------------------------------------------------
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后加一条转移到该任务开头的指令。

上一页  1 2 

Tags:演示 异常 处理

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接