.386p
page    58,132
;******************************************************************************
        TITLE   ErrHndlr - Error Handler
;******************************************************************************
;   (C) Copyright IBM Corp. 1986-1991
;   (C) Copyright COMPAQ Computer Corp. 1986-1991
;
;    Title:     EMM386.EXE - IBM Expanded Memory Manager 386 Driver
;
;    Module:    ErrHndlr - Recover from exception and priveledged operation errors
;
;    Version:   2.00
;
;    Date:      June 6,1986
;
;    Authors:   Brad Tate
;
;******************************************************************************
;
;  CHANGES:
;
;    DATE     REVISION                  DESCRIPTION
;  --------   --------   ------------------------------------------------------
;  06/06/86   Original
;  06/28/86   0.02      Name changed from CEMM386 to CEMM (SBP).
;  06/28/86   0.02      Removed STI at end of ErrHndlr (SBP).
;  06/28/86   0.02      Changed error # display to leading zeroes (SBP).
;  07/06/86   0.04      Changed assume to _DATA (SBP).
;  01/12/87   0.09      Added defines BOOT_KEY and CONTINUE to define
;                       key make code for error handler input (SBP).
;  05/13/87   2.00      Moved to R_CODE segment (SBP).
;  03/29/93   ELM1214   Included file mriemm38.inc to define equates
;                       for BOOT_KEY and CONTINUE_KEY.
;
;******************************************************************************
        page
;******************************************************************************
;
;  Functional description:
;
;       This module contains the code that displays an error message and
;       asks the user to continue or reboot.
;
;******************************************************************************
;
        include vdmseg.inc
        include vdmsel.inc
        include emm386.inc
        include kbd.inc
        include emmfunct.inc
        include emmdata.inc
        MRI_ERRHNDLR_ASM=1      ; Included mri messages      ;ELM1214;
        include mriemm38.inc    ;   MRI message include file ;ELM1214;
        include oemdep.inc
        include vm386.inc

;******************************************************************************
;       Public Declarations
;******************************************************************************
;
        public  ErrHndlr                ; Display message and continue or reboot
        public  Error_Flag
        public  EHReturnFar

;******************************************************************************
;       Externs
;******************************************************************************
_DATA   segment
        extrn   pLastVMTF:word
        extrn   POE_Mess:byte           ; privileged operation error
        extrn   POE_Num:byte            ; where to put error code
        extrn   POE_Len:abs             ; length of message
        extrn   PEN_Mess:byte           ; unrecoverable privileged error
        extrn   PEN_Num:byte            ; where to put error code
        extrn   PEN_Len:abs             ; length of message
        extrn   EXCPE_Mess:byte         ; exception error message
        extrn   EXCPE_Num:byte          ; where to put error code
        extrn   EXCPE_CSIP:byte         ; where to put CS:IP
        extrn   EXCPE_Len:abs           ; length of message
        extrn   DMASIZE_Mess:byte       ; exception error message
        extrn   DMASIZE_Num:byte        ; where to put error code
        extrn   DMASIZE_Len:abs         ; length of message
        extrn   DMAMODE_Mess:byte       ; exception error message
        extrn   DMAMODE_Num:byte        ; where to put error code
        extrn   DMAMODE_Len:abs         ; length of message
_DATA   ends

R_CODE  segment
        extrn   ExitVirtual:far         ; exit virtual mode
        extrn   RetReal:near            ; return to real mode
;LEO    extrn   JumpReal:near           ; continue in real mode

        Extrn   GoRealMode:near
        extrn   GoVirtualMode:near
        extrn   EHFarReturn:dword

ErrGoVirtualMode        proc    far
        call    GoVirtualMode
        jmp     dword ptr cs:[EHFarReturn]
ErrGoVirtualMode        endp


R_CODE  ends

R1_CODE segment

        extrn   egetc:near              ; get keyboard character
        extrn   WaitKBD:near            ; wait for keyboard ready
        extrn   EHMsgBuf:byte

R1_CODE ends

ATTR            EQU     07              ; attribute for write string
WRSTR           EQU     1301h           ; write string function code (format 1)
CPOSN           EQU     5*256+0         ; cursor position to write

;
R1_CODE segment
        ASSUME  CS:R1_CODE, DS:R1_CODE, ES:R1_CODE, GS:R1_CODE
;
;******************************************************************************
;               LOCAL DATA
;******************************************************************************

EUserDS         dw      0
EUserES         dw      0
EUserFS         dw      0
EUserGS         dw      0
EUserSS         dw      0
EUserSP         dw      0
EUserCS         dw      0
EUserIP         dw      0

Error_Flag      dw      0               ; flags for Instruction Prefixes
masterp         db      0               ; save master port value
slavep          db      0               ; save slave port value
mode            db      0               ; save mode
boot            db      0               ; value to reboot
continue        db      0               ; value to continue
count           db      0
R1_CODE ends


_TEXT   segment
        ASSUME  CS:_TEXT, DS:_DATA, GS:R_CODE
;******************************************************************************
;
;       ErrHndlr - displays the appropriate error message and prompts the
;                   user for a character to continue or reboot.  The screen
;                   is cleared by this routine.  If the user chooses to
;                   continue, the system is in real mode.
;
;       *** This routine must be entered by a JUMP ***
;
;       entry:  ax = 0 => Privileged operation error
;               ax = 1 => Exception error
;               ax = 2 => Fatal DMA errors
;               bx = error number to display
;               STACK:
;               SS:[BP] -> points to saved EBP on GP fault stack frame
;               SS:[SP] -> bottom of pushad
;
;                       => to unwind:
;                               popad
;                               pop     ebp     ; entry BP pts to here
;                               add     sp,4    ; throw away error code
;                               iret
;
;
;       note:   Only privilegded operation errors are allowed to continue.
;               All other error types must reboot.
;
;
;       exit:   either reboot, or continue the system in real mode
;               via the JumpReal routine.
;
;       used:   none
;
;       stack:
;
;******************************************************************************
ErrHndlr        proc    near
;
        push    VDMD_GSEL       ;LEO  access _DATA data
        pop     ds              ;LEO
        push    RCODEA_GSEL     ;LEO  access R_CODE data
        pop     gs              ;LEO
        push    R1CODEA_GSEL
        pop     fs

;
;  If a privilige error occurs while CEMM is nested more than one level deep
;  during interrupt processing, the user may not continue by turning CEMM off.
;  Thus, the following will change the privilige error to an exception which
;  will force a reboot.  This condition needs an error message which needs to
;  be documented.
;

        cmp     ax,PrivErr              ; Q: Privilige error?
        jne     short EHcont            ; N: continue
        cmp     [pLastVMTF],-1          ; Q: Is CEMM nested more than one
                                        ; level?
        jne     short EHunrec           ; Y: unrecoverable privileged error

        cmp     gs:[UMBHMA], TRUE       ; Q: are we providing UMBs
        jne     short EHcont            ; N: continue

EHunrec:
        mov     ax,UnRecPrivErr         ; Y: unrecoverable privileged error

EHcont:

;
;  Save error type and number
;
        mov     gs:[ErrType],ax ;LEO
        mov     gs:[ErrNum],bx  ;LEO

;
;  Setup RSS_GSEL in GDT to have same base as user stack
;
        mov     ax,GDTD_GSEL            ; ES access to GDT
        mov     es,ax

        movzx   ebx,[bp][VTFOE].VMTF_SS ; get base address of user stack
        mov     fs:[EUserSS],bx         ; save user stack segment
        shl     ebx,4
        mov     es:[RSS_GSEL][2],bx     ; bits 0-15 of base address
        shr     ebx,16
        mov     es:[RSS_GSEL][4],bl     ; bits 16-23
        mov     es:[RSS_GSEL][7],bh     ; bits 24-31
;
;  Setup user stack with proper return address and CS:IP & flags inorder
;  to return to the user code.
;
        mov     bx,[bp][VTFOE].VMTF_ESP ; get offset for user stack

        cmp     gs:[ErrType], PrivErr   ; only setup stack if user can continue
        jne     short EHno_stack        ;   else this may fault if ss:sp is bad
                                        ;   (for ex when except is stack fault)
        mov     ax,RSS_GSEL             ; access user stack via ES
        mov     es,ax

        mov     ax,[bp][VTFOE].VMTF_EFLAGS ; get user flags
        sub     bx,2                       ; and place it on the user stack
        mov     es:[bx],ax

        mov     ax,[bp][VTFOE].VMTF_CS  ; get code segment
        sub     bx,2                    ; and place it on the user stack
        mov     es:[bx],ax

        mov     ax,[bp][VTFOE].VMTF_EIP; get instruction pointer
        sub     bx,2                    ; and place it on the user stack
        mov     es:[bx],ax

EHno_stack:
        movzx   esp,sp                  ; access only 64K
;
;  Save all segment registers for real mode
;
        mov     ax,[bp][VTFOE].VMTF_DS  ; get user DS
        mov     fs:[EUserDS],ax

        mov     ax,[bp][VTFOE].VMTF_ES  ; get user ES
        mov     fs:[EUserES],ax

        mov     ax,[bp][VTFOE].VMTF_FS  ; get user FS
        mov     fs:[EUserFS],ax

        mov     ax,[bp][VTFOE].VMTF_GS  ; get user GS
        mov     fs:[EUserGS],ax

        mov     ax, [bp][VTFOE].VMTF_CS ; get user CS
        mov     fs:[EUserCS], ax

        mov     ax, [bp][VTFOE].VMTF_EIP ; get faulting IP
        mov     fs:[EUserIP], ax
;
;  Save user stack values
;
        mov     fs:[EUserSP],bx
;
        mov     ax,gs:[ErrType]    ;LEO
        mov     bx,gs:[ErrNum]     ;LEO

        cmp     ax,PrivErr              ;Q: Privileged error?
        jne     short excep             ; N: try exception error
        mov     si,offset POE_Mess      ; Y: privileged error
        mov     cx,POE_Len
        mov     di,offset POE_Num       ; error number location
        mov     fs:[boot],BOOT_KEY      ; key to boot
        mov     fs:[continue],CONTINUE_KEY ; key to continue
        jmp     short copy_msg
excep:
        cmp     ax,ExcpErr              ;Q: Exception error?
        jne     short DMAsize           ; N: try fatal DMA error

        ; Add CS:IP of faulting instruction to exception msg.

        mov     di, offset EXCPE_CSIP
        mov     ax, fs:[EUserCS]        ; faulting CS value
        call    HexPrintAX              ;   format CS
        inc     di                      ; skip over ':'
        mov     ax, fs:[EUserIP]        ; faulting IP value
        call    HexPrintAX              ;   format IP

        mov     si,offset EXCPE_Mess    ; Y: load up exception error
        mov     cx,EXCPE_Len            ; length of msg
        mov     di,offset EXCPE_Num     ; error number location
        jmp     short must_reboot

DMAsize:
        cmp     ax,DMASizeErr           ;Q: DMA buffer too small?
        jne     short DMAmode           ; N: DMA mode
        mov     si,offset DMASIZE_Mess  ; Y: load up DMASIZE error
        mov     cx,DMASIZE_Len          ; length of msg
        mov     di,offset DMASIZE_Num   ; error number location
        mov     ax,bx                   ; print new D parameter
        call    PrintAX
        mov     di,offset DMAMODE_Num   ; fake b2asc
        jmp     short must_reboot
DMAmode:
        cmp     ax,DMAModeErr           ;Q: DMA mode error?
        jne     short UnRecPriv         ; N: Unrecoverable privileged
        mov     si,offset DMAMODE_Mess  ; Y: load up DMAMODE error
        mov     cx,DMAMODE_Len          ; length of msg
        mov     di,offset DMAMODE_Num   ; fake b2asc
        jmp     short must_reboot
UnRecPriv:
        mov     si,offset PEN_Mess      ; load up message
        mov     cx,PEN_Len              ; length of msg
        mov     di,offset PEN_Num       ; error number location

must_reboot:
        mov     fs:[boot],ENTER_KEY     ; key to reboot
        mov     fs:[continue],0ffh      ; can't continue

copy_msg:
        mov     ax,bx                   ; error number in ax
        call    b2asc                   ; convert to ascii
        push    fs
        pop     es
        mov     count,cl
        mov     di,offset R1_CODE:EHMsgBuf
        cld
        rep     movsb

        ;
        ; change to emm386's real mode stack.
        ;

        mov     ax,GDTD_GSEL            ; ES access to GDT
        mov     es,ax

        xor     ebx,ebx
        mov     bx, seg R_STACK         ; get emm386's real stack

        shl     ebx,4
        mov     es:[RSS_GSEL][2],bx     ; bits 0-15 of base address
        shr     ebx,16
        mov     es:[RSS_GSEL][4],bl     ; bits 16-23
        mov     es:[RSS_GSEL][7],bh     ; bits 24-31

        lea     bx, R_STACK:RealStackTop
;
;  Switch to EMM386's stack
;
        mov     ax, RSS_GSEL
        mov     ss, ax
        mov     sp, bx
;
;  Return to virtual mode
;

        PJmp    RCODE_GSEL, R_CODE:ErrGoVirtualMode

ErrHndlr        endp

page
;******************************************************************************
;
;       b2asc - converts binary to ascii decimal and store at DS:DI.
;               Stores 2 ascii chars.
;               Decimal # is right justified and filled on left with 0s.
;
;       entry:  ax = binary number
;               ds:di = place to store ascii chars.
;
;       exit:   ASCII decimal representation of number stored at R_CODE:DI
;
;       used:   none
;
;       stack:
;
;******************************************************************************
;
b2asc   proc    near
        push    ax
        push    cx
        push    dx
;
        xor     dx,dx                   ; clear word extension
        mov     cx,10
        div     cx                      ; divide by power of 10
        add     al,'0'                  ; put into ascii format
        mov     ds:[di],al              ; put ascii number into string
        add     dl,'0'
        mov     ds:[di+1],dl
;
        pop     dx
        pop     cx
        pop     ax
        ret                             ; *** return ***
b2asc   endp

;==============================================================================
;==
;==  PrintAX: Output the value in AX
;==
;==  Enter: AX      = Binary number to convert to decimal
;==         DS:[DI] = place to store ascii chars.
;==
;==  Exit:  DS:[DI] = next character position
;==
;==============================================================================
PrintAX proc    near
        push    ax
        push    cx
        push    dx

        mov     cx,10           ; divide value
        xor     dx,dx           ; clear dx
        div     cx              ; ax/10 remainder in dx
        or      ax,ax           ;Q: Was AX < 10?
        jz      short AXPrint   ; Y: print decimal value
        push    dx              ; N: Save result
        call    PrintAX         ; divide again
        pop     dx              ; get previous result
AXPrint:                        ;
        add     dl,'0'          ; make it printable
        mov     ds:[di],dl              ; place in buffer
        inc     di              ; increment cursor position

        pop     dx
        pop     cx
        pop     ax
        ret
PrintAX endp

;==============================================================================
;==
;==  HexPrintAX: Output the value in AX in hex
;==
;==  Enter: AX      = Binary number to convert to hexadecimal
;==         DS:[DI] = place to store hex ascii chars.
;==
;==  Exit:  DS:[DI] = next character position
;==
;==============================================================================

hextab          db      "0123456789ABCDEF"

HexPrintAX proc near

        push    ax
        push    bx
        push    cx
        push    dx

        cld                             ;forward,increment
        mov     dx,ax
        mov     cl,12
b2hex_loop:
        mov     bx,dx
        shr     bx,cl                   ; get highest nibble
        and     bx,0Fh                  ; nibble only
        mov     al,hextab[bx]           ; get ASCII for it
        mov     byte ptr ds:[di], al
        inc     di

        sub     cl,4
        jge     short b2hex_loop

        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret

HexPrintAX endp
_TEXT   ends


R1_CODE segment
        assume  CS:R1_CODE

EHReturnFar:

        pushad                  ;LEO
        push    ds              ;LEO
        push    es              ;LEO

        push    seg R_CODE
        pop     es
        assume  es:R_CODE

        push    cs
        pop     ds
        assume  ds:R1_CODE


;
;  CEMM is OFF now
;
        mov     es:[Current_Mode],MODE_OFF
;

        push    bp                      ; save entry BP

        in      al,MASTER               ; get value of master interrupt port
        mov     [masterp],al            ; save it
        in      al,SLAVE                ; get value of slave interrupt port
        mov     [slavep],al             ; save it
        mov     al,DIS_MSTSLV           ; value to disable master/slave int
        out     MASTER,al               ; disable master
        out     SLAVE,al                ; disable slave
        mov     al,DISABLE_NMI          ; value to disable NMI
        out     NMI_CMD,al
kbdbusy:
        call    egetc                   ; q: is there stuff in keyboard buffer?
        jnz     short kbdbusy           ; y: get it and pitch it
                                        ; n: continue
print_msg:
        mov     ah,0fh                  ; read video state
        int     10h
        mov     [mode],al               ; save mode
        mov     ax,3                    ; set to mode 3
        int     10h                     ; standard 80 x 25 color
        push    cs
        pop     es
        mov     bp,offset EHMsgBuf
        movzx   cx,count
        mov     dx,CPOSN                ; cursor position
        mov     bl,ATTR                 ; attribute
        mov     ax,WRSTR                ; write string function code
        int     10h                     ; do it
        cli                             ; make sure int 10 didn't enable
key_loop:
        call    egetc                   ; get a character
        jz      short key_loop          ; nothing there yet

        cmp     al,[continue]           ; q: continue?
        je      short err_cont          ; y
        cmp     al,[boot]               ; q: boot?
        jne     short key_loop          ; n: try again
;******************************************************************************
;
;               Reboot system
;
;******************************************************************************
        assume  ds:romdata
        mov     ax,romdata
        mov     ds,ax                   ; ds = romdata segment
        mov     [freset],0              ; cold restart
        mov     al,0fh or DISABLE_NMI           ; shutdown byte address/disable NMI
        out     NMI_CMD,al                      ; write CMOS address
        jmp     short $+2               ; delay
        mov     al,0h                   ; shutdown code 0 = processor reset
        out     71h,al                  ; write shutdown code to shutdown byte
        call    WaitKBD                 ; wait for 8042 to accept command
        mov     al,0feh                 ; feh = pulse output bit 0 (reset)
        out     KbStatus,al             ; reset processor
        db      0eah
        dw      0ffffh
        dw      0f000h
        hlt

        assume  ds:R1_CODE
;
;   here if USER elects to continue in real mode
;
err_cont:
        xor     ah,ah                   ; ah = 0 to set video mode
        mov     al,[mode]               ; restore their mode
        int     10h
        cli                             ; turn them off...
;
;       restore master, slave, and NMI
;
        mov     al,[masterp]            ; get value of master interrupt port
        out     MASTER,al               ; restore it
        mov     al,[slavep]             ; get value of slave interrupt port
        out     SLAVE,al                ; restore it
        mov     al,ENABLE_NMI           ; value to enable NMI
        out     NMI_CMD,al
;
        pop     bp                      ; restore entry BP

        pop     es      ;LEO
        pop     ds      ;LEO
        popad           ;LEO

        mov     al,0Fh
        out     84h,al          ; port 84/85 return to real sequence
        mov     al,00h
        out     85h,al
        jmp     $+2             ; clear prefetch/avoid race cond

;
;  Restore segment resiters
;
        mov     ds,cs:[EUserDS]
        mov     es,cs:[EUserES]
        mov     fs,cs:[EUserFS]
        mov     gs,cs:[EUserGS]

        ;
;  Switch to user stack
;
        mov     ss,cs:[EUserSS]
        mov     sp,cs:[EUserSP]

        iret            ;LEO

;LEO    jmp     JumpReal

R1_CODE ENDS
        END
