{bt
;---------------------------------------------------------------------
; CE.ASM - Critical Error Handler
;
;       To assemble:    masm /ml ce;
;
; externally accessible functions:
;
;       getCEasker - get address of current asker function
;       setCEasker - set new asker function
;       CE_trap - interrupt 24h handler
;---------------------------------------------------------------------

_DATA   segment word public 'DATA'
_DATA   ends
DGROUP  group   _DATA

_TEXT   segment byte public 'CODE'
        assume cs:_TEXT

AskerAddr       dd      0       ; far ptr to current asker fcn
Action          dw      ?       ; 'C', 'R', 'I' or 'A'
ErrorCode       dw      ?       ; error code in DI
CallerAddr      dd      ?
CallerFlags     dw      ?

;---------------------------------------------------------------------
; setCEasker - change asker function
;
; C usage:
;
;       void far setCEasker(askerfcnptr fcn);
;
; where:
;       fcn is the address of the new asker
;---------------------------------------------------------------------

public  _setCEasker

_setCEasker proc far

        push    bp
        mov     bp, sp

        mov     ax, [bp+6]
        mov     word ptr cs: AskerAddr, ax
        mov     ax, [bp+8];
        mov     word ptr cs: AskerAddr + 2, ax

        pop     bp
        ret

_setCEasker endp

;---------------------------------------------------------------------
; getCEasker - return far ptr to current asker fcn
;
; C usage:
;
;       askerfcnptr far getCEasker(void)
;
;---------------------------------------------------------------------

public  _getCEasker

_getCEasker     proc far

        mov     ax, word ptr cs: AskerAddr
        mov     dx, word ptr cs: AskerAddr + 2
        ret

_getCEasker     endp

;---------------------------------------------------------------------
; CE_trap - Critical Error trap
;
;       This function calls the asker function whenever a critical
;       error occurs.  The asker function must return either 'R' or
;       'C' in ax.
;---------------------------------------------------------------------

public  _CE_trap

_CE_trap        proc far

        sti

        ; First call the asker function to see what to do

        push    ax              ; save entry regs and flags
        push    bx
        push    cx
        push    dx
        push    si
        push    di
        push    bp
        push    ds
        push    es
        pushf

        mov     dx, ax          ; set up DS for asker function
        mov     ax, DGROUP
        mov     ds, ax

        push    si              ; push C arguments
        push    bp       
        push    di
        push    dx              ; ( pushing previous value of ax)

        call    cs: AskerAddr   ; call asker function. On return ax
                                ;  will be 'C', 'R', 'I' or 'A'
        add     sp, 8           ; remove args from stack

        mov     word ptr cs: Action, ax         ; save return val

        popf                    ; restore entry state
        pop     es
        pop     ds
        pop     bp
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx
        pop     ax

        ; Stack and registers are now in state they were upon entry
        ; and Action contains 'C', 'R', 'I' or 'A' for Cancel, Retry
        ; Ignore or Abort

        mov     ax, cs: Action  ; see what asker said to do
        cmp     ax, 'C'
        je      Cancel
        cmp     ax, 'R'
        jne     Ignore
        mov     al, 1           ; Retry operation
        iret

Ignore:
        cmp     ax, 'I'
        jne     Abort
        mov     al, 0           ; Ignore Error
        iret

Abort:
        mov     al, 2           ; Abort
        iret

Cancel: 
                                ; Cancel operation and go directly to
                                ;   application that called 21h in
                                ;   the first place

                                ; convert error code to standard
                                ; return code and save it
        add     di, 13h
        mov     word ptr cs: ErrorCode, di

                                ; Save caller's return address
        push    bp
        mov     ax, [bp+1ah]
        mov     word ptr cs: CallerAddr, ax
        mov     ax, [bp+1ch]
        mov     word ptr cs: CallerAddr+2, ax

                                ; Set caller's CF and save flags
        mov     ax, [bp+1eh]
        or      ax, 1                   ; set cf in flag image
        mov     word ptr cs: CallerFlags, ax

                                ; Place address of label Stabilize
                                ;   on stack in place of caller's
                                ;   return address
        mov     ax, offset cs: Stabilize
        mov     [bp+1ah], ax
        push    cs
        pop     ax
        mov     [bp+1ch], ax

        pop     bp

        add     sp, 6           ; pop DOS IP, CS and flags

        pop     ax              ; restore applications registers
        pop     bx
        pop     cx
        pop     dx
        pop     si
        pop     di
        pop     bp
        pop     ds
        pop     es

        iret                    ; transfer to Stabilize

Stabilize:

        ; When we get here we are officially out of handler and
        ; back into application.

        push    bx              ; Save applications registers
        push    cx
        push    dx
        push    si
        push    di
        push    bp
        push    ds
        push    es

                                ; Call harmless DOS function to
                                ;   stabilize
        mov     ah, 19h         ; Get Current Drive
        int     21h

        pop     es              ; Restore applications registers
        pop     ds
        pop     bp
        pop     di
        pop     si
        pop     dx
        pop     cx
        pop     bx

        ; Set stack up for IRET to appl

                                ; Push applications flags w/ CF set
        mov     ax, word ptr cs: CallerFlags
        push    ax
                                ; Push return address
        mov     ax, word ptr cs: CallerAddr+2
        push    ax
        mov     ax, word ptr cs: CallerAddr
        push    ax
                                ; Put Error Code in ax
        mov     ax, word ptr cs: ErrorCode

        iret                    ; Transfer back to application

_CE_trap        endp

_TEXT   ends

END

{et
