        PAGE    60,132
        TITLE   CRITTER - Critical error handler

;       (C) Copyright 1987 Forest Hill Software, Inc.
;                   All rights reserved
;
;  Note: This program was put together with code from a handler that
;        was integrated with a larger project. Do not be supprised to
;        find added references and code which do not seem pertinent.
;        (it was also ported from Manx Assembler source code)

        DOSSEG
        .MODEL  small

        .STACK  100h

LINE    equ     3                       ;Starting line # of error window
COLUMN  equ     9                       ;Starting column of error window
LINES   equ     4                       ;# of lines in error window
COLUMNS equ     25                      ;# of columns in error window
DISK_ER equ     80h                     ;Disk error flag bit
ALERT_D equ     10                      ;Alert tone duration
ALERT_F equ     300                     ;Alert tone frequency
NOCURS  equ     2000h                   ;Scan lines setting for no cursor

;       Allowed action bits - provided in ah when entering INT 24

QUI_BIT equ     08h                     ;Quit (fail) allowed bit
RET_BIT equ     10h                     ;Retry allowed bit
IGN_BIT equ     20h                     ;Ignore allowed bit

;       Action key scan codes

SCAN_R  equ     19                      ;Key 'R' pressed
SCAN_I  equ     23                      ;Key 'I' pressed

;       Actions which can be supplied back to DOS in al

IGNORE  equ     0                       ;Ignore the error
RETRY   equ     1                       ;Retry the operation
ABORT   equ     2                       ;Abort the program
FAIL    equ     3                       ;Fail the call

;       Video attributes

CO_NORM equ     17h     ;Color text attributes - normal   = white on blue
CO_HIGH equ     1eh     ;                        highlite = yellow on blue
CO_ERRO equ     9ch     ;                        error    = flash red on blue
BW_NORM equ     07h     ;B/W text attributes   - normal   = low intensity
BW_HIGH equ     0fh     ;                        highlite = high intensity
BW_ERRO equ     8fh     ;                        error    = flash high intens
MO_NORM equ     07h     ;Mono text attributes  - normal   = normal
MO_HIGH equ     70h     ;                        highlite = reverse
MO_ERRO equ     87h     ;                        error    = flash reverse

;       Interupts

VIDEO   equ     10h             ;BIOS video services interupt
        SETSIZE equ     01h             ;Set cursor size
        SETPOS  equ     02h             ;Set cursor position
        READCUR equ     03h             ;Read cursor
        READCHR equ     08h             ;Read character and attribute
        WRITCHR equ     09h             ;Write character and attribute
        GETMODE equ     0fh             ;Get video mode
KEYBRD  equ     16h             ;BIOS keyboard services interupt
        READKBD equ     00h             ;Read keypress
        TESTKBD equ     01h             ;Test if keypress waiting
DOS     equ     21h             ;DOS function call interrupt
        DISPSTR equ     09h             ;Display $ terminated string
        SETVECT equ     2500h           ;Set interrupt vector
        KEEPPGM equ     3100h           ;Keep process (TSR)
        GETVECT equ     3500h           ;Get interrupt vector
        EXITPGM equ     4C00h           ;End process
TICK    equ     08h             ;Clock tick interrupt
CRITIC  equ     24h             ;Critical error handler interrupt

        .DATA

logon   db      13,10,'CRITTER - Your hyperactive critical error handler'
        db      13,10,' (C) Copyright 1987 by Forest Hill Software, Inc.'
        db      13,10,'$'

        .CODE

;       Data to be kept in Code segment

        EVEN

top_stk dw      0                       ;Top of local stack
old_cur dw      0                       ;Original cursor scan lines
old_pos dw      0                       ;Original cursor position
err_sta dw      0                       ;Error status information
err_num dw      0                       ;Error number (code)
tic_off dw      0                       ;Saved INT 8 vector
tic_seg dw      0
crt_off dw      0                       ;Saved INT 24 vector
crt_seg dw      0
our_sp  dw      0                       ;Our local stack
our_ss  dw      0
sav_sp  dw      0                       ;Callers stack
sav_ss  dw      0
scr_sav db      LINES*COLUMNS*2 dup (0) ;Storage for used screen area
scr_pag db      0                       ;Current screen page
lin_sav db      0                       ;Temporary line # storage
col_sav db      0                       ;Temporary column # storage
line_c  db      0                       ;Line counter
colum_c db      0                       ;Column counter
att_nor db      0                       ;Video attributes - normal
att_hig db      0                       ;                   highlite
att_err db      0                       ;                   error

;       Error window background

err_top db      213, 8 dup (205), 7 dup (' '), 8 dup (205), 184, '$'
err_mid db      179, 23 dup (' '), 179, '$'
err_bot db      212, 23 dup (205), 190, '$'
err_nam db      'ERROR$'

;       Critical Error Handler Messages

;               |<--   width = 23   --->|       Lower byte of reg di

ERR_SIZ equ     24                      ;Length of each error message inc $

errors  db      '    Write Protected    $'      ;0
        db      '    Unknown Device     $'      ;1
        db      '    Drive Not Ready    $'      ;2
        db      '    Unknown Command    $'      ;3
        db      '   Data Error  (CRC)   $'      ;4
        db      '   Request Structure   $'      ;5
        db      '      Seek Error       $'      ;6
        db      '     Unknown Media     $'      ;7
        db      '   Sector Not Found    $'      ;8
        db      '     Out of Paper      $'      ;9
        db      '      Write Fault      $'      ;a
        db      '      Read Fault       $'      ;b
        db      '    General Failure    $'      ;c
        db      '      (reserved)       $'      ;d
        db      '      (reserved)       $'      ;e
        db      '  Invalid Disk Change  $'      ;f

;       Actions

actions db      'Ignore Retry Abort Fail$'

ignor_c db      'I$'
retry_c db      'R$'
abort_c db      'A$'
fail_c  db      'F$'

I_POS   equ     1                       ;Position of I in Ignore to highlite
R_POS   equ     8                       ;Position of R in Retry to highlite
A_POS   equ     14                      ;Position of A in Abort to highlite
F_POS   equ     20                      ;Position of F in Fail to highlite

;       Install critical error handler and stay resident

start:  mov     ax, @DATA               ;Initialize DS
        mov     ds, ax
        mov     ah, DISPSTR             ;Display logon message
        mov     dx, offset logon
        int     DOS
        call    install                 ;Install handler and tick watch
        mov     ax, sp                  ;Determine paragraphs to keep
        mov     cs:our_sp, ax           ; also save sp for the handler
;fortunately there should be no fatal errors untill this TSR's
        mov     cl, 4                   ;starting with para's in stack
        shr     ax, cl
        mov     bx, ss                  ;Add that to stack segment
        mov     cs:our_ss, bx           ; also save ss for the handler
        add     ax, bx
        mov     bx, es                  ;Then subtract PSP segment
        sub     ax, bx
        inc     ax                      ;Add 1 para for safety
        mov     dx, ax
        mov     ax, KEEPPGM             ;TSR with code 0
        int     DOS

;       Link vector check routine into timer interupt

install proc
        push    ds
        push    es
        push    cs
        pop     ds
        mov     ax, (GETVECT+CRITIC)    ;Read existing INT 24 vector
        int     DOS
        mov     crt_off, bx
        mov     crt_seg, es
        mov     ax, (SETVECT+CRITIC)    ;Set new INT 24 vector
        mov     dx, offset crit_handler
        int     DOS
        mov     ax, (GETVECT+TICK)      ;Read existing INT 8 vector
        int     DOS
        mov     tic_off, bx
        mov     tic_seg, es
        mov     ax, (SETVECT+TICK)      ;Set new INT 8 vector
        mov     dx, offset vect_check
        int     DOS
        pop     es
        pop     ds
        ret
install endp

;       Remove critical error handler from interrupt

remove  proc
        push    ds
        push    cs
        pop     ds
        mov     dx, tic_off
        mov     ax, (SETVECT+TICK)
        int     DOS
        mov     dx, crt_off
        mov     ax, (SETVECT+CRITIC)
        int     DOS
        pop     ds
        ret
remove  endp

;       Vector check

vect_check      proc
        sti
        push    ax
        push    bx
        push    es
        xor     ax, ax                  ;Get current INT 24 Vector
        mov     es, ax
        mov     bx, es:[CRITIC*4]
        mov     ax, es:[CRITIC*4+2]
        cmp     ax, seg crit_handler    ;Does it point to our handler
        jne     revect
        cmp     bx, offset crit_handler
        je      chkend
revect: cmp     ax, seg crit_handler    ;Don't revector programs loaded after
        ja      chkend                  ; this program
        mov     cs:crt_off, bx          ;If not, save current vector
        mov     cs:crt_seg, ax
        cli                             ;then revector it to our handler
        mov     es:[CRITIC*4], offset crit_handler
        mov     es:word ptr [CRITIC*4+2], seg crit_handler
        sti
chkend: pop     es
        pop     bx
        pop     ax
        jmp     dword ptr cs:[tic_off]  ;Chain to previous tick handler
vect_check      endp


;       Critical error interrupt handler

crit_handler    proc
        sti                             ;Enable interupts again
        push    ds                      ;Save caller's data segment
        push    cs                      ;data segment=codeseg
        pop     ds
        mov     sav_ss, ss              ;Save caller's stack
        mov     sav_sp, sp
        cli                             ;Set up local stack
        mov     ss, our_ss
        mov     sp, our_sp
        sti
        push    bx                      ;Save required regs on local stack
        push    cx
        push    dx
        push    bp
        push    si
        push    di
        push    es
;
        mov     err_sta, ax             ;Save error status
        and     di, 00ffh               ;Mask out all but error number
        mov     err_num, di             ;Save error number
        call    vid_mod                 ;Get video mode
        cmp     al, 3                   ;If mode 0-3 it's text
        jle     text
        mov     att_nor, MO_NORM        ;Only remaining text mode is mono
        mov     att_hig, MO_HIGH        ;so set attributes just in case
        mov     att_err, MO_ERRO
        cmp     al, 7                   ;If it is monochrome mode
        je      mode_ok                 ;then all set
;
        call    get_act                 ;Get action - no window in graphics
        jmp     done                    ;Return with action code
;
text:   mov     att_nor, BW_NORM        ;Assume B/W attributes
        mov     att_hig, BW_HIGH
        mov     att_err, BW_ERRO
        test    al, 1                   ;unless it's a color mode
        je      mode_ok
;
color:  mov     att_nor, CO_NORM        ;then use color attributes
        mov     att_hig, CO_HIGH
        mov     att_err, CO_ERRO
;
mode_ok:call    cur_off                 ;Turn cursor off
        call    sav_scr                 ;Save underlying screen
;   display window background and name
        mov     bl, att_nor             ;Use normal attribute for window
        mov     bp, offset err_top      ;Display top line of window
        mov     dh, LINE                ;Top line of window
        mov     dl, COLUMN              ;Left column of window
        call    str_out                 ;Display this line
        mov     bp, offset err_mid      ;Display middle 2 lines (less text)
        inc     dh                      ;on next line
        mov     dl, COLUMN              ;Yes! We have to keep setting it!
        call    str_out
        mov     bp, offset err_mid      ;and the one following
        inc     dh
        mov     dl, COLUMN
        call    str_out
        mov     bp, offset err_bot      ;Display bottom line
        inc     dh
        mov     dl, COLUMN
        call    str_out
        mov     bl, att_err             ;Use highlite attribute
        mov     bp, offset err_nam      ;Display window name - "ERROR"
        mov     dl, COLUMN+10           ;In middle of top line
        mov     dh, LINE
        call    str_out
;   Display appropriate error message
        mov     ax, err_num             ;Calculate start of message
        mov     bl, ERR_SIZ             ;Where each message is ERR_SIZ long
        mul     bl
        add     ax, offset errors       ;and the table base is "errors"
        mov     bp, ax
        mov     bl, att_nor             ;Use normal attributes
        mov     dh, LINE+1              ;Starting on seccond row
        mov     dl, COLUMN+1            ;and the second column
        call    str_out                 ;Display it
;   Display allowed actions
        mov     bl, att_nor             ;Normal attr for all but R & I
        mov     dh, LINE+2              ;Line where actions are displayed
        mov     dl, COLUMN+1            ;Column where actions are displ.
        mov     bp, offset actions      ;Display actions
        call    str_out
        mov     dl, COLUMN+I_POS        ;Overlay highlited "I"
        mov     bp, offset ignor_c
        mov     bl, att_hig
        call    str_out
        mov     dl, COLUMN+R_POS        ;Overlay highlited "R"
        mov     bp, offset retry_c
        mov     bl, att_hig
        call    str_out
        mov     dl, COLUMN+A_POS        ;Overlay highlited "A"
        mov     bp, offset abort_c
        mov     bl, att_hig
        call    str_out
        mov     dl, COLUMN+F_POS        ;Overlay highlited "F"
        mov     bp, offset fail_c
        mov     bl, att_hig
        call    str_out
;   Convert error number to DOS standard
        add     err_num, 19
;   Get desired action from user
        call    get_act                 ;Get desired action from user
        push    ax                      ;Save action code
        call    res_scr                 ;Restore screen under window
        call    cur_on                  ;Restore original cursor
        pop     ax                      ;Restore action code
        mov     bx, err_sta             ;Restore original error status
        mov     bl, al                  ;with new action code
        mov     ax, bx
;
done:   pop     es                      ;Restore registers from local stack
        pop     di
        pop     si
        pop     bp
        pop     dx
        pop     cx
        pop     bx
        cli                             ;int's off...just in case
        mov     ss, sav_ss              ;Restore caller's stack
        mov     sp, sav_sp
        sti
        pop     ds
        iret                            ;At this point al contains action
crit_handler    endp

;       Get video mode

vid_mod proc
        mov     ah, GETMODE             ;Get video page
        int     VIDEO
        mov     scr_pag, bh
        ret
vid_mod endp

;       Turn cursor off

cur_off proc
        mov     bh, scr_pag             ;Get cursor information
        mov     ah, READCUR
        int     VIDEO
        cmp     cx, 0067h               ;Test for BIOS bug
        jne     cur_ok
        mov     cx, 0607h               ;fix it if required
cur_ok: mov     old_cur, cx             ;Save cursor scan lines
        mov     old_pos, dx             ;Save cursor position
        mov     ah, SETSIZE             ;Set cursor off
        mov     cx, NOCURS
        int     VIDEO
        ret
cur_off endp

;   Turn cursor back on

cur_on  proc
        mov     dx, old_pos             ;Restore original cursor position
        mov     bh, scr_pag
        mov     ah, SETPOS
        int     VIDEO
        mov     cx, old_cur             ;Restor original scan lines
        mov     ah, SETSIZE
        int     VIDEO
        ret
cur_on  endp

;   Save the screen area under the window

sav_scr proc
        mov     al, LINE                ;Starting at LINE
        mov     line_c, al
        xor     di, di                  ;Point to start of scr_save
        mov     cx, LINES               ;For each line
lin_loo:push    cx
        mov     cx, COLUMNS             ;For each column
        mov     al, COLUMN              ;Starting at COLUMN
        mov     colum_c, al
col_loo:mov     ah, SETPOS              ;Set position to read
        mov     bh, scr_pag
        mov     dh, line_c
        mov     dl, colum_c
        int     VIDEO
        mov     ah, READCHR             ;Read character & attribute
        mov     bh, scr_pag
        int     VIDEO
        mov     scr_sav[di], ah         ;Place attribute in scr_sav
        inc     di                      ;Bump scr_sav pointer
        mov     scr_sav[di], al         ;Place character in scr_sav
        inc     di
        inc     colum_c                 ;Bump column pointer
        loop    col_loo                 ;Till line is done
        inc     line_c                  ;Bump line pointer
        pop     cx                      ;Till window done
        loop    lin_loo
        ret
sav_scr endp

;       Restore the area under the window

res_scr proc    near
        mov     al, LINE                ;Starting at LINE
        mov     line_c, al
        xor     di, di                  ;Point to start of scr_sav
        mov     cx, LINES               ;For each line
lin_lo2:push    cx
        mov     cx, COLUMNS             ;For each column
        mov     al, COLUMN              ;Point to starting column
        mov     colum_c, al
col_lo2:mov     ah, SETPOS              ;Set position to restore
        mov     bh, scr_pag
        mov     dh, line_c
        mov     dl, colum_c
        int     VIDEO
        push    cx
        mov     ah, WRITCHR             ;Write character & attribute
        mov     bl, scr_sav[di]         ;Get attribute
        inc     di                      ;Bump scr_sav pointer
        mov     al, scr_sav[di]         ;Get character
        inc     di                      ;Bump scr_sav pointer
        mov     bh, scr_pag             ;Ensure we have correct page
        mov     cx, 1                   ;This character just once
        int     VIDEO
        pop     cx
        inc     colum_c                 ;Bump column pointer
        loop    col_lo2                 ;Till line is done
        inc     line_c                  ;Bump line pointer
        pop     cx                      ;Till window done
        loop    lin_lo2
        ret
res_scr endp

;   Display string - till '$' - No Wrap Allowed!

;       call with: bp = pointer to string
;               bl = attribute
;               dh = screen row
;               dl = starting column

str_out proc
        push    di                      ;We must save di reg
        push    ax                      ;and ax reg
        mov     bh, scr_pag             ;Position cursor
        mov     ah, SETPOS
        int     VIDEO
        xor     di, di                  ;Point to start of string
str_loo:mov     al, cs:[bp][di]         ;Get current character
        cmp     al, '$'                 ;Check for end of string
        je      str_end
        mov     cx, 1                   ;Only 1 of each character
        mov     ah, WRITCHR             ;Write character to screen
        int     VIDEO
        inc     dl                      ;Point to next column
        mov     ah, SETPOS              ;Move cursor there
        int     VIDEO
        inc     di                      ;Point to next character in string
        jmp     str_loo                 ;continue
;
str_end:pop     ax                      ;Restore ax reg
        pop     di                      ;and di reg
        ret
str_out endp

;       Obtain response from user - Test for valid action
;       when valid is specified set return code in al reg

get_act proc
        push    cx
action: call    alert                   ;Alert user
;   Clear keyboard buffer
tst_kbd:mov     ah, TESTKBD             ;Any characters in keyboard buffer?
        int     KEYBRD
        jz      buf_clr                 ;If nothing in buffer then it's clr
        mov     ah, READKBD             ;else dump this keypress
        int     KEYBRD
        jmp     tst_kbd
buf_clr:
;   Get a new keypress
key_psd:mov     ah,     TESTKBD         ;Any key pressed yet
        int     KEYBRD
        jz      key_psd                 ;If not then wait
        mov     ah, READKBD             ;else get keypress
        int     KEYBRD
        and     al, 0DFh                ;Convert to lower case
;   Test for valid key
        xor     cx, cx
        cmp     al, 'I'                 ;Test if 'I' pressed
        je      goodkey
        inc     cl
        cmp     al, 'R'                 ;Test if 'R' pressed
        je      goodkey
        inc     cl
        cmp     al, 'A'                 ;Test if 'A' pressed
        je      goodkey
        inc     cl
        cmp     al, 'F'                 ;Test if 'F' pressed
        je      goodkey
        jmp     action
goodkey:mov     al, cl
        pop     cx
        ret
get_act endp

;       Alert user with low beep tone

alert   proc
        push    bx
        push    di
        mov     bx,     ALERT_D         ;Duration of alert tone
        mov     di,     ALERT_F         ;Frequency of alert tone
        call    abeep
        pop     di
        pop     bx
        ret
alert   endp

;       Produce Beep
;               BX = Duration
;               DI = Frequency

abeep   proc
        push    ax
        push    bx
        push    cx
        push    dx
        push    di
        mov     al,     0B6h
        out     43h,    al
        mov     dx,     14h
        mov     ax,     4F38h
        div     di
        out     42h,    al
        mov     al,     ah
        out     42h,    al
        in      al,     61h
        mov     ah,     al
        or      al,     3
        out     61h,    al
delay:  mov     cx,     2801
tone:   loop    tone
        dec     bx
        jnz     delay
        mov     al,     ah
        out     61h,    al
        pop     di
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
abeep   endp

        END     start
