	page	60,132
comment	*  PARINT.COM - (C) 1984 by David G Hunter.
	   This program may be freely copied but may not be sold for profit.

	   (Necessary modifications for the early model (1981) IBM PC
    	   made by Lawrence B. Afrin, 12/30/84.  These modifications
	   are documented at the appropriate point in this program.
	   Modifications are Copyright (c) 1984 by Lawrence B. Afrin.
	   Along with Mr. Hunter's code, these modifications may be
	   freely copied but may not be sold for profit.)

	   PARINT.COM detects parity errors, logs them on the printer if
	   possible, beeps, and returns to program execution.  The system does
	   not halt when this program is resident.  Parity error checking is
	   terminated after first error is detected.  The error message
	   includes the time of the event.  If the printer is not available,
	   errors are logged to the screen.
*
;***************** Addresses of Interrupt handlers *******************

Book	segment	at 0h		;this is where the interrupt address books is
	org	2h*4		;
Int_2	label	dword		;address of NMI handler
Book	ends

;***************** Beginning of PARINT Instructions *******************
cseg	segment
	assume	cs:cseg
	org	100h

Start:	jmp	newvec ;install new NMI handler in RAM


;************************** NMI handler ********************************

newint	proc	near		;new interrupt handler
	assume	ds:cseg, es:cseg
	sti			;this flag was cleared when interrupt was issued
	jmp	go		;jump over more data

go:	push	cx
	push	ds
	pushf
	push	dx
	push	bx
	push	ax
	pushf
	mov	al, 00h
	out	0A0h, al	;turn off parity checking for now
	xor	cx, cx	;cx=error flag: cx=1 if parity OK
	in	al,62h	;get data in port C

; - - find origin of NMI - - - - - -
	test	al, 40h
	jz	mother
	mov	dx, offset par2 ;if bit 6 set, error is on expansion board
	jmp	out		; print message and exit

mother:	test	al, 80h
	jz	other
	mov	dx, offset par1 ;if bit 7 set, error is on main board
	jmp	out		;print message and exit

other:	or	cx, 1h	;If neither bit set, no parity error occurred,
	mov	al, 80h	;so set parity OK flag
	out	0A0h, al	;turn parity checking back on
	jmp	noprt		;Don't print message if not parity error

; - - print error message and exit.  Type of exit depends on type of error --
out:	mov	ax,cs		;establish proper data segment to
	mov	ds,ax		; allow access to messages
	call	whatime	;get time of error
	mov	bx, device1
	cmp	bx,0004	;if output is to printer, make sure it's ready
	jne	f1
	call	testprt
f1:	call	print
	mov	dx, offset time ;"time of error" message
	call	print
	mov	dx, offset paroff
	call	print

noprt:	popf
	pop	ax
	pop	bx
	pop	dx
	popf
	pop	ds
	test	cx, 1h	;special exit if parity OK
	jnz	oth
	pop	cx
	iret			;message printed; let execution continue

;     If no parity error, turn control over to pre-existing NMI handler.
; This is done by restoring DS and jumping to the instruction that
; specifies the address of the old interrupt routine.  This unusual exit
; is necessary because otherwise, once DS was restored, the address of the
; old interrupt routine would not be accessible.

oth:	cli
	pop	cx
	jmp	GetOut

newint	endp


;**************************** SUBROUTINES ******************************

Testprt	proc	near
	push	dx
	push	ax
	xor	dx,dx		;print # 0
	mov	ah,2
	int	17h		;read printer status
	test	ah,00101001b	;error bits
	jz	aok
	mov	bx, device2	;if not ok, try other device
aok:	pop	ax
	pop	dx
	ret
testprt	endp

Print	proc	near ;output to file named in bx; if error try another file
	push	cx
	mov	cx,34		;34 characters to print
	mov	ah,40h	;DOS function call
	int	21h
	pop	cx
	ret
print	endp

Whatime	proc	near		;what time is it?  put result in hour/min
	push	cx
	push	dx
	mov	ah,2Ch	;DOS time
	int	21h
	mov	al,ch		;hours (0-23)
	call	convt		;put tens in ah, ones in al
	mov	hour,"00"
	add	hour,ax
	mov	al,cl
	call	convt		;do same for minutes (0-59)
	mov	min,"00"
	add	min,ax
	pop	dx
	pop	cx
	ret
Whatime	endp

Convt	proc	near		;convert number (<100) to ASCII.  Number is in
				;al.  result: tens in ah, ones in al.
	xor	ah,ah		;input is less than 100 anyway, so clear it
	push	cx
	mov	cl,10
	div	cl		;divide ax by 10
	pop	cx
little:	ret
Convt	endp

;------------------------------- DATA ----------------------------------

GetOut:	nop
Instruc	db	0EAh		;this is the jump-immediate instruction!
				;it jumps to the address stored in oldint
Oldint	dd	?		;address of old interrupt 2 routine -
Device1	dw	0004		;printer
Device2	dw	0001		;standard output device
par1	db	0Dh,0Ah,"Parity Error: Main Board     ",07h,0Dh,0Ah
par2	db	0Dh,0Ah,"Parity Error: Expansion Board",07h,0Dh,0Ah
time	db	"    TIME:  "
hour	dw	"00"
	db	":"
min	dw	"00"
	db	"                ",0Dh,0Ah
paroff	db	"PARITY CHECKING NOW DISABLED   ",0Dh,0Ah,0Ah

;---------------------------- MAIN PROGRAM -----------------------------

;************************* INSTALL NMI HANDLER **************************

Newvec	proc	near
	jmp	past

loaded	db	0Dh,0Ah,"PARITY ERROR INTERCEPTOR v2.00 by David Hunter IS NOW"
	db	" INSTALLED",0Dh,0Ah,"$"

past:	mov	dx, offset loaded
	mov	ah,9
	int	21h
	assume	ds:Book	;interrupt address book area
	push	ds		;save old ds for future use
	mov	ax,Book
	mov	ds,ax

; NOTE: The following modifications and their associated documentation
; are Copyright (c) 1984 by Lawrence B. Afrin.  Further copyright
; information is included in this program's header comment block.
;
;       The following two instructions have been removed from David
; Hunter's version by Lawrence B. Afrin because Mr. Afrin found that
; on the original model of the IBM PC (and possibly later models, too),
; these instructions cause the machine to go into the Twilight Zone
; later on when NMI interrupts are re-enabled.  For some reason in the
; early IBM PC models, when NMI interrupts are re-enabled after having
; been disabled, the reenabling operation causes the system to signal
; an NMI interrupt.  At the point in this program at which that occurs,
; the program is not ready to handle that interrupt and consequently
; dies.  Therefore, by preventing the initial disabling of NMI
; interrupts, the problem is take care of.  -- LBA, 12/30/84

;	mov	al, 00h
;	out	0A0h, al 	;turn off parity checking
	mov	ax,int_2	;get the address
	mov	oldint,ax	;save it for some future use
	mov	ax,int_2[2]	;second part of double word
	mov	oldint[2],ax
	mov	int_2, offset newint	;now load the new address
	mov	int_2[2],cs		;cs is ds in com program
	mov	al, 80h
;
; NOTE: It is at the following instruction that the early model PC would
; ordinarily enter the Twilight Zone if the NMI-disabling instructions
; noted above were to be left in the program.  Because those instructions
; were removed, the following instruction, which re-enables an already
; enabled NMI mask, has no effect on the machine and does not cause a
; spurious NMI interrupt.
;
	out	0A0h, al		;turn parity checking back on
	mov	dx, offset newvec	;leave new interrupt routine resident
	int	27h			;don't need "newvec" or beyond
newvec	endp
cseg	ends
	end	start
