	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
