page	,132
title  kbfix - 127 character buffer & screen display of ibm-pc shift status
;****************************************************************
;* module name = kbfix
;*
;* copyright(C) 1984	Skip Gilbrech
;*			90 Lexington Ave. #10-G
;*			New York, NY 10016
;*			212-685-0551
;*
;* This program may be freely copied/altered for any non-commercial
;* purpose but may not be sold or used in any way as part of any
;* profit-making venture without permission of the author.
;*
;* author = skip gilbrech
;* date written = 01/84
;*
;* environment:
;*  system = ibm pc (dos 2.0 - but should work on any version)
;*  processor = microsoft 8086 macro assembler
;*
;* This program will print out the status of the caps-, num-, and scroll-
;* lock keys on the upper right-hand corner of the ibm-pc screen in reverse-
;* video, and will beep from low to high when one of these keys is toggled on,
;* and from high to low when it is toggled off.  The display is updated after
;* every keystroke.  The program writes directly to the video ram to avoid the
;* overhead of rom-bios calls:	I found this approach resulted in a much
;* cleaner display.  The program should work with both monochrome and color 80
;* column monitors.
;*
;* The program is invoked only once and is installed through the dos
;* 'terminate but stay resident' function.
;*
;* I have incorporated an optional 127-character keyboard buffer to replace
;* the standard 15 provided by ibm.  The buffer is installed only if a '/b'
;* switch is encountered on the command line.
;*
;* Installation of the buffer also allows fixing a problem which I found
;* irritating:	control-s (pause output) and control-c (abort) only worked
;* if they were the only key in the buffer, i.e., if you typed ahead while
;* something else was happening, then changed your mind, you had to use
;* control-numlock or control-break to pause or abort.	This is understandable:
;* since bios doesn't treat control-s or control-c in any special way, dos
;* would have to scan the whole buffer to detect these keys in any but the
;* first position.  Now, if either of these keys is detected, the buffer
;* is cleared and the character is placed in the first buffer position.
;*
;* The program permanently occupies 1152 bytes when installed with the buffer;
;* without the buffer, it uses 672 bytes.
;*
;* The program should be independent of any particular rom-bios version, as
;* the keyboard interrupt routine gains control only after calling the vector
;* at the original int 9 (keyboard interrupt) location.  I have no idea about
;* this program's compatibility with other keyboard enhancement programs, as
;* I don't have any of them (although a friend tells me that Smartkey works
;* fine if it is installed AFTER this program).
;*
;* I have been using various versions of this program for several months,
;* now, and haven't run into any destructive bugs, but I can't accept
;* responsibility for any damage it may cause.	I would appreciate hearing
;* about any problems, however, at the number or address above, or on
;* Compuserve (71445,534).
;*
;* This ideas (and some of the code) for this program come from a variety
;* of public-domain sources, and the program evolved slowly over several
;* months, so if I miss giving credit to someone, please accept my apologies.
;*
;* The ideas of printing out the key status on the upper-right hand corner of
;* the screen and intercepting the interrupt routine after it has executed
;* come from Tony A. Rhea's KEYSTAT program.  His copyright notice is
;* reproduced below:
;*
;*	KEYSTAT -- Copyright (C) 1983 Tony A. Rhea
;*	This program may be copied for non-commercial use.
;*	Adapted from KEYFLAGS (PC-World, Oct. 83, page 266) by Morton Kaplon
;*
;* The bios beep routines were taken from the KEYLOC program by:
;*
;*	John Black
;*	5225 Pooks Hill Rd. #1715 N.
;*	Bethesda MD. 20814
;*
;* The basic idea for the enlarged keyboard buffer came from a program
;* on the Compuserve IBM-PC SIG called KEYBOARD.ASM.  There was no name
;* on the file I downloaded.
;*
;****************************************************************
	page
;****************************************************************
;*
;*	constants
;*
;****************************************************************

; general equates:

false		equ	0
true		equ	not false

cr		equ	0dh	; carriage return
lf		equ	0ah	; line feed
bell		equ	7	; ascii bell
ctrl_s		equ	13h	; control-s
ctrl_c		equ	3	; control-c
spc		equ	' '     ; space, blank
tab		equ	9	; tab

; program equates:

bytes_per_row	equ	160	; num. bytes per row in video ram
rowcaps 	equ	0	; row for cap symbol
colcaps 	equ	77	; column for cap symbol
rownums 	equ	0	; row for num symbol
colnums 	equ	78	; column for num symbol
rowscroll	equ	0	; row for scroll symbol
colscroll	equ	79	; column for scroll symbol
blank		equ	' '     ; display this if a mode is not active
capschar	equ	'C'     ; character to signify capslock state
numchar 	equ	'N'     ; character to signify numlock state
scrollchar	equ	'S'     ; character to signify scroll lock state
normal		equ	7	; display attribute (7=normal white on black)
reverse 	equ	112	; display attr. (112=reverse video - sg)
blankword	equ	((normal * 256) + blank)	; words for video ram
capsword	equ	((reverse * 256) + capschar)	;
numword 	equ	((reverse * 256) + numchar)	;
scrollword	equ	((reverse * 256) + scrollchar)	;

; dos and bios equates:

dosint		equ	21h	; interrupt number for dos functions
print_string	equ	9	; dos print console string function
get_vers	equ	30h	; dos get version number function
get_int_vec	equ	35h	; dos 2.0 get interrupt vector function
set_int_vec	equ	25h	; dos set interrupt vector function

kb_int_no	equ	9	; interrupt number for keyboard interrupt
kb_io_int_no	equ	16h	; interrupt number for keyboard i/o
capsmask	equ	40h	; mask for kb_flag - capslock on
nummask 	equ	20h	; mask for kb_flag - numlock on
scrollmask	equ	10h	; mask for kb_flag - scrolllock on

; hardware specific equates:

inta01		equ	21h	; port adr. for 8259 ocw 1 and icw's 2, 3 & 4
ena_irq1	equ	11111101b	; mask-enable irq1 (keyboard) interrupt
dis_irq1	equ	00000010b	; mask-disable irq1 interrupt
stat_6845	equ	03dah		; 6845 status register

; for beep routine:

h_cycles	equ	30
h_half		equ	300
l_cycles	equ	h_cycles / 3
l_half		equ	h_half * 3
kb_ctl		equ	61h

page

;**************************************************************************

int_vecs	segment at 0
int_vecs	ends

;**************************************************************************

bios_data segment at 40h

	org	17h
kb_flag 	db	?		; holds state of caps- and numlock, etc.

	org	1ah			; keyboard buffer data area
bios_buffer_head	dw	?	; ptr to next char to be gotten
bios_buffer_tail	dw	?	; ptr to last char entered in buffer
bios_kb_buffer	dw	16 dup (?)	; keyboard buffer
bios_kb_buffer_end	label word	; end of buffer

	org	49h
crt_mode	db	?		; crt mode - mono/color, etc.

	org	80h
bios_buffer_start	dw	?	; in new bios, ptr to beginning of buf
bios_buffer_end 	dw	?	; in new bios, ptr to end of buf

bios_data ends

;**************************************************************************

monoram segment at 0b000h		; monochrome ram
monoram ends

;**************************************************************************

colorram segment at 0b800h		; color/graphics ram
colorram ends

	page
;**************************************************************************

cseg	segment
	assume cs:cseg,ds:cseg,es:nothing

	org	80h			; command line parms
cmd_ct	label	byte			; number of chars. in command line

	org	100h			; for .COM file
entry:
	jmp	init			; ck for residency, init if not resident

last_kb_flag	db	?		; kb_flag when last entered routine
buf_flag	db	false		; set to true when/if new keyboard buffer
					; is installed
orig_kb_int_vec dd	?		; pointer to orig. kb_int service routine

;**************************************************************************
;* This is the start of the new keyboard interrupt handler code.
;*
;* The numlock and capslock keys tend to bounce when hit quickly, and
;* this can cause another keyboard interrupt before the beeping is done
;* (resulting in unpleasant beeps) since the bios has already sent out an
;* eoi to the 8259.
;* To solve the problem, and to simplify the keyboard buffer manipulations
;* (assuming the buffer is installed), interrupts will not be re-enabled
;* before pushing the flags and calling the original routine.  When we regain
;* control, interrupts will still be disabled, so we can mask off the keyboard
;* interrupt before re-enabling other interrupts.

kb_int_hdlr proc			; entry point of keyboard (hardware)
					;  interrupt handler

	pushf				; (IRET will pop 3 words off the stack)
	call	cs:orig_kb_int_vec	; kb_flag will be updated on return

; interrupts are disabled -- mask off just the keyboard interrupt and re-enable

	push	ax			; save AX before using
	in	al,inta01		; get ocw1 from 8259
	or	al,dis_irq1		; turn on the mask bit
	out	inta01,al		; write modified byte to 8259
	sti				; reenable ints

	push	bx			; save other regs. used
	push	cx			;
	push	dx			;
	push	si			;
	push	ds			;
	push	es			;

	mov	ax,cs			; now establish data addressability
	mov	ds,ax			;  off of DS
	mov	ax,bios_data		; and set ES to bios_data segment
	mov	es,ax			;

	cmp	buf_flag,false		; see if our keyboard buffer is in use
	je	kb_int1 		; if not, continue
	call	get_from_bios_buf	; else, check for char to move

kb_int1:
	mov	al,es:kb_flag		; get bios keyboard status flag

; check capslock state - if on, show reverse-video 'C' else show blank

	mov	dx,blankword		; assume blank to be sent
	push	dx			; save for later
	push	dx			;  and again..
	test	al,capsmask		; al contains <kb_flag>
	jz	cl1			; if bit not set, nothing to do
	mov	dx,capsword		; else send out the caps symbol
cl1:
	mov	bh,rowcaps		; row for cap symbol
	mov	bl,colcaps		; column for cap symbol
	call	dispchar		;

; check numlock state - if on, show reverse-video 'N' else show blank

	pop	dx			; restore blankword
	test	al,nummask		; check the flag
	jz	nl1			; if bit not set, nothing to do
	mov	dx,numword		; else, send out the numlock symbol
nl1:
	mov	bh,rownums		; row for num symbol
	mov	bl,colnums		; column for num symbol
	call	dispchar		;

; check scroll lock state - if on, show reverse-video 'S' else show blank

	pop	dx			; restore blankword
	test	al,scrollmask		; check the flag
	jz	sl1			; if bit not set, nothing to do
	mov	dx,scrollword		; else, send out the scroll lock symbol
sl1:
	mov	bh,rowscroll		; row for scroll symbol
	mov	bl,colscroll		; column for scroll symbol
	call	dispchar		;

	mov	bl,last_kb_flag 	; get last kb_flag
	cmp	al,bl			; see if anything's changed
	je	int_exit		; if not, go home

; else, check if caps-, num- or scroll-lock has changed state

	mov	dl,capsmask		; capslock state changed?
	call	chk_chg 		;
	jnz	got_diff		; if difference, check it out

	mov	dl,nummask		; numlock state changed?
	call	chk_chg 		;
	jnz	got_diff		; if difference, check it out

	mov	dl,scrollmask		; scroll lock state changed?
	call	chk_chg 		;
	jz	int_exit		; if not, return

got_diff:
	jc	beep_dn 		; carry set if transition was from on to off
	call	low_beep		; otherwise, beep low then high
	call	high_beep		;
	jmp short int_exit		;

beep_dn:
	call	high_beep		; beep high then low
	call	low_beep		;

int_exit:
	mov	last_kb_flag,al 	; al has current flag - store as last flag

	pop	es			; restore saved registers
	pop	ds			;
	pop	si			;
	pop	dx			;
	pop	cx			;
	pop	bx			;

; now disable ints and turn the keyboard back on -- interrupts will be
; re-enabled when the flags are popped during our IRET

	cli				;
	in	al,inta01		; get ocw1 from 8259
	and	al,ena_irq1		; shut off the mask bit
	out	inta01,al		; write modified byte to 8259

	pop	ax			;
	iret				;

kb_int_hdlr	endp

page
;**************************************************************************
;* compare current flag (in <al>) with last flag (in <bl>) using mask in <dl>
;* set zero flag if no change -- set carry flag if bit changed from 1 to 0

chk_chg proc near

	push	ax
	push	bx
	and	al,dl			; isolate bit in al
	and	bl,dl			; do same for bl
	cmp	al,bl			; carry will be set if curr. flag (al) is 0
					; while last flag (bl) was 1
	pop	bx
	pop	ax
	ret

chk_chg endp

;**************************************************************************
;* display char/attrib. in dl/dh at row/column in bh/bl of video ram page 0
;* supports b/w or color monitors, but row/column won't be right on 40
;* column displays
;* on entry, ES points to bios_data seg - all registers preserved

dispchar proc near

	push	ax			; save registers used
	push	bx			;
	push	di			;
	push	es			; save extra seg

	mov	ax,bytes_per_row	; num. bytes per row in video ram
	mul	bh			; calc. row offset
	sub	bh,bh			; make BH = 0
	shl	bl,1			; col * 2 = column offset
	add	ax,bx			; AX = offset into video ram
	mov	di,ax			; move offset into DI

	cmp	es:crt_mode,7		; 7 = mono. display
	jne	co_80			; no, then jump to color routine
	mov	ax,monoram		; set ES to mono. ram segment
	mov	es,ax			;
	mov	ax,dx			; get char/attrib in AX
	stosw				; place in video ram

dc_exit:
	pop	es			; restore extra segment
	pop	di			;
	pop	bx			;
	pop	ax			;
	ret

; -- display char. on color monitor.  DX has char/attrib., DI points to
;    offset in video ram.  Must wait for horizontal retrace to avoid screwing
;    up the display.

co_80:
	mov	ax,colorram		; set ES to color ram segment
	mov	es,ax			;
	mov	bx,dx			; save char/attrib. in BX
	mov	dx,stat_6845		; 6845 status reg port in DX

co_80_1:
	in	al,dx			; get 6845 status port
	test	al,1			; test bit 0 (on = horiz. retrace active)
	jnz	co_80_1 		; loop until inactive
	cli				; no ints while waiting or writing

co_80_2:
	in	al,dx			; get status again
	test	al,1			; test for retrace active
	jz	co_80_2 		; no? loop to wait until active

	mov	ax,bx			; get char/attrib. into AX
	stosw				; place in video ram
	sti				; re-enable interrupts

	mov	dx,bx			; restore entering value of DX
	jmp	dc_exit 		; and return

dispchar endp

;**************************************************************************
;* from KEYLOC

low_beep proc near

	push	dx
	push	bx
	mov	bx,l_cycles
	mov	dx,l_half
	call	beep
	pop	bx
	pop	dx
	ret

low_beep endp

high_beep proc near

	push	dx
	push	bx
	mov	bx,h_cycles
	mov	dx,h_half
	call	beep
	pop	bx
	pop	dx
	ret

high_beep endp

;**************************************************************************
;* Adapted from BIOS Beep routine
;
;  bx = # of cycles
;  dx = length of half cycle

beep proc near

	push	ax
	push	cx
	in	al,kb_ctl
	push	ax
k65:
	and	al,0FCh
	out	kb_ctl,al
	mov	cx,dx
k66:	loop	k66
	or	al,2
	out	kb_ctl,al
	mov	cx,dx
k67:	loop	k67
	dec	bx
	jnz	k65
	pop	ax
	out	kb_ctl,al
	pop	cx
	pop	ax
	ret

beep endp

;**************************************************************************
;* this is the end of the code which will be installed if the keyboard
;* buffer handler is NOT used -- kb_int_len will be used as the length
;* of code for the residency test in any case, since it is sufficient to
;* determine if the program has been installed in some form.

kb_int_end label near			; area after this usable by dos if
					;  buffer not installed

kb_int_len equ	(offset kb_int_end - offset kb_int_hdlr)

	page
;**************************************************************************
;* the following data locations will be used for our replacement keyboard
;* buffer if it is installed

our_buffer_head 	dw	?		; ptr to next char to be gotten
our_buffer_tail 	dw	?		; ptr to last char entered in buffer
our_kb_buffer		dw	128 dup (?)	; keyboard buffer
our_kb_buffer_end	label word		; end of buffer

; This is essentially a copy of the bios keyboard i/o interrupt handler
; modified to check our buffer for chars rather than the bios buffer
; As in the bios routine, only AX and flags are changed.

kb_io_int_hdlr	proc far

	sti					; reenable ints
	push	bx				;
	push	si				;
	push	ds				;
	push	es				;

	mov	bx,cs				; est. data addressability off of DS
	mov	ds,bx				;
	mov	bx,bios_data			; and set ES to bios_data segment
	mov	es,bx				;

	or	ah,ah				; ah=0
	jz	k1				; ascii read
	dec	ah				; ah=1
	jz	k2				; ascii status
	dec	ah				; ah=2
	jz	k3				; shift status
	jmp	short int10_end 		; invalid parameter, exit

;----- read the key to figure out what to do

k1:						; ascii read
	sti					; interrupts back on during loop
	nop					; allow an interrupt to occur
	cli					; interrupts back off
	call	ck_clr_bios_buf 		; buf cleared (by other than kb_int)?
	mov	bx,our_buffer_head		; get pointer to head of buffer
	cmp	bx,our_buffer_tail		; test end of buffer
	jz	k1				; loop until something in buffer
	mov	ax,[bx] 			; get scan code and ascii code
	inc	bx				; move pointer to next position
	inc	bx				;
	cmp	bx,offset our_kb_buffer_end	; at end of buffer?
	jne	k5				; no, continue
	mov	bx,offset our_kb_buffer 	; yes, reset to buf beginning
k5:
	mov	our_buffer_head,bx		; store value in variable
	jmp	short int10_end 		; return

;----- ascii status

k2:
	cli					; interrupts off
	call	ck_clr_bios_buf 		; buf cleared (by other than kb_int)?
	mov	bx,our_buffer_head		; get head pointer
	cmp	bx,our_buffer_tail		; if equal (z=1) then nothing there
	mov	ax,[bx] 			;
	sti					; interrupts back on
	pop	es				; recover registers
	pop	ds				;
	pop	si				;
	pop	bx				;
	ret	2				; throw away flags

;----- shift status

k3:
	mov	al,es:kb_flag			; get the shift status flags

int10_end:
	pop	es				;
	pop	ds				;
	pop	si				;
	pop	bx				;
	iret					;

kb_io_int_hdlr	endp

;**************************************************************************
;* Called from our keyboard interrupt handler after the keystroke has been
;* processed by the bios (or by whomever is handling keyboard input).
;*
;* At this point, the keyboard interrupt is disabled, so no need to disable
;* interrupts when manipulating keyboard data.
;*
;* If there is a new character available, it will be checked for control-s and
;* control-c, and if either is found, our buffer will be cleared and that char.
;* will become the 1st and only char in it.  Dos apparently checks only the
;* first position in the buffer for these two characters, and will not pause
;* or abort a command if anything else has been entered.  Of course, ctrl-break
;* and ctrl-numlock will still work, but for those who came from a cp/m
;* background, it's an annoying feature.
;*
;* On entry:  DS is set to our CS, ES points to the <bios_data> segment
;* AX,BX,SI changed.

get_from_bios_buf	proc near

	call	ck_clr_bios_buf 		;
	mov	bx,offset bios_kb_buffer + 2	; point past dummy entry
	cmp	bx,es:bios_buffer_tail		; is there a char?
	je	get_from_exit			; no, just exit
	mov	es:bios_buffer_tail,bx		; set tail = head
	mov	ax,es:[bx]			; get the char.
	cmp	al,ctrl_s			; see if control-s
	jne	get_from_0			; no, continue
	call	init_our_buf			; clear our buf
get_from_0:
	cmp	al,ctrl_c			; see if control-c
	jne	get_from_1			; no, continue
	call	init_our_buf			; clear our buf
get_from_1:
	mov	si,our_buffer_tail		; get ptr to next pos in buffer
	mov	bx,si				; save the value
	inc	si				; bump to next position
	inc	si				;
	cmp	si,offset our_kb_buffer_end	; wrapped?
	jne	get_from_2			; no, continue
	mov	si,offset our_kb_buffer 	; else, set ptr to beginning of buf

get_from_2:
	cmp	si,our_buffer_head		; see if there's room in the buf
	jne	get_from_3			; continue if so
	call	err_beep			; if not, beep, discard data
	ret					;

get_from_3:
	mov	[bx],ax 			; store the value
	mov	our_buffer_tail,si		; store ptr to next pos in buf

get_from_exit:
	ret

get_from_bios_buf	endp

;* Check if the bios buffer has been cleared by seeing if tail is set
;* to the beginning of the buffer.  Normally, tail is equal to either
;* beginning + 2 (if no data in buf) or beginning + 4 (if data).
;* If cleared, clear our buffer and reset the bios buffer to its normal
;* condition.  At this point, interrupts are either disabled entirely,
;* or the keyboard interrupt has been masked off.

ck_clr_bios_buf 	proc near

	cmp	es:bios_buffer_head,offset bios_kb_buffer  ; same?
	ja	ck_clr_exit			; if not, buf not cleared
	call	init_bios_buf			; else, reset bios buffer
	call	init_our_buf			; and reset our buffer
ck_clr_exit:
	ret

ck_clr_bios_buf 	endp

; set bios buffer to start 1 position higher than normal -- can then check
; for reset by seeing if head has been moved to normal starting point.

init_bios_buf proc near

	mov	bx,offset bios_kb_buffer + 2	;
	mov	es:bios_buffer_head,bx		;
	mov	es:bios_buffer_tail,bx		;
	ret

init_bios_buf endp

; initialize our keyboard buffer

init_our_buf	proc near

	mov	si,offset our_kb_buffer 	; and reset our buffer
	mov	our_buffer_head,si		;
	mov	our_buffer_tail,si		;
	ret

init_our_buf	endp

; buffer-full beep -- essentially the same as the bios routine

err_beep	proc near

	push	bx
	push	dx
	mov	bx,80h			; num. cycles for 1/12 second tone
	mov	dx,48h			; half cycle time for tone
	call	beep
	pop	dx
	pop	bx
	ret

err_beep	endp

kb_io_int_end label near	; area after this usable by dos if
				;  keyboard buffer installed
	page
;**************************************************************************

resident_msg		db	bell,'KBFIX already resident',cr,lf,'$'
buf_err_msg		db	bell,'Original buffer in unknown state...',cr,lf,'$'
kbfix_install_msg	db	'KBFIX installed with','$'
buf_not_install_msg	db	'out'
buf_install_msg 	db	cr,lf,'127-byte keyboard buffer',cr,lf,'$'
usage_msg		db	'Usage: kbfix /b (or) /n',cr,lf
			db	'/b = install 127 char keyboard buffer',cr,lf
			db	'/n = no buffer desired',cr,lf,'$'
buf_desired		db	false

;**************************************************************************
;* Initialization procedure:
;*
;*	1. Check whether the routine is already resident; if it is, then
;*	   just print message and exit.
;*	2. Check command line to see if enlarged buffer installation
;*	   desired.  If no parms or unknown parms, print usage message.
;*	2. Get current kb_int vector and store, then change vector to point
;*	   to new interrupt handler and print message.
;*	3. If buffer installation desired, check the status of the existing
;*	   keyboard buffer.
;*	4. If ok, install new keyboard i/o interrupt handler;
;*	   otherwise, do NOT change the vector.
;*	5. Print message showing status of keyboard buffer.
;*	6. Execute dos 'terminate but stay resident' interrupt, installing
;*	   only the code which will actually be used.

init	proc	near

	call	testresident		; see if we are already servicing kb_int
	jz	dont_install		; ZF set = already resident -- print msg and exit

	call	get_parms		; see if keyboard buffer desired
	jc	print_usage		; if parm error, print usage message

	; shut off the keyboard interrupt while initializing

	cli				; shut off interrupts while messing with 8259
	in	al,inta01		; get ocw1 from 8259
	or	al,dis_irq1		; turn on the mask bit
	out	inta01,al		; write modified byte to 8259
	sti				; reenable ints

	call	install_kb_int_hdlr	; install new keyboard interrupt handler

	cmp	buf_desired,true	; see if buffer installation desired
	je	install_buf		; if so, install it

	mov	dx,offset kbfix_install_msg	; print kbfix install message
	call	msg			;
	jmp	short buf_not_installed ;

install_buf:
	call	install_kb_io_int_hdlr	; install new buffer handler if ok
	pushf				; save result flags
	mov	dx,offset kbfix_install_msg	; print kbfix install message
	call	msg			;
	popf				; restore flags
	jc	buf_not_installed	; buffer not installed if carry set
	mov	dx,offset buf_install_msg ; else, show buffer installation
	call	msg			;
	mov	dx,offset kb_io_int_end ; set up to install everything...
	jmp	short make_res		; and install it

buf_not_installed:
	mov	dx,offset buf_not_install_msg	; else, print msg showing not installed
	call	msg			;
	mov	dx,offset kb_int_end	; install only kb_int_hdlr

make_res:
	cli				; re-enable keyboard interrupt
	in	al,inta01		; get ocw1 from 8259
	and	al,ena_irq1		; shut off the mask bit
	out	inta01,al		; write modified byte to 8259
	sti				;
	int	27h			; dos terminate but stay resident func.

dont_install:
	mov	dx,offset resident_msg	; print msg showing already installed
	call	msg			;
	int	20h			; return to dos (nothing made resident)

print_usage:
	mov	dx,offset usage_msg	;
	call	msg			;
	int	20h			; return to dos (nothing made resident)

init	endp

;**************************************************************************
;* see if KBFIX already servicing kb_int - AX,BX,CX,SI,DI changed

testresident proc near

	push	es			;
	mov	al,kb_int_no		; interrupt number in AL
	call	get_vec 		; interrupt vector returned in ES:BX
	mov	orig_kb_int_vec,bx	; store IP of current servicer
	mov	orig_kb_int_vec[2],es	; store CS of current servicer
	mov	cx,kb_int_len		; number of bytes to match
	mov	si,offset kb_int_hdlr	; point DS:SI to our code
	mov	di,bx			; make ES:DI point to current kb_int servicer
	cld				; auto increment
	repe	cmpsb			; compare while equal - zero flag set on exit
					; if all bytes matched (already resident)
	pop	es			;
	ret

testresident endp

;**************************************************************************
;* install new keyboard interrupt handler  - AX & DX changed

install_kb_int_hdlr	proc near

	push	es			;
	mov	ax,bios_data		;
	mov	es,ax			; getflag expects ES = bios_data seg
	mov	al,es:kb_flag		; get bios keyboard status flag
	mov	last_kb_flag,al 	; store for comparison

	mov	al,kb_int_no		; interrupt number in AL
	mov	ah,set_int_vec		; funct. number for set int. vector
	mov	dx,offset kb_int_hdlr	; offset of resident code
	int	dosint			; set the vector
	pop	es			;
	ret

install_kb_int_hdlr	endp

;**************************************************************************
;* check status of bios keyboard buffer, install new buffer handler if ok -
;* set carry and return if not.  AX,BX,DX,SI changed.
;*
;* Buffer_start & buffer_end were not used in the original pc rom bios --
;* the offsets of kb_buffer & kb_buffer_end were hard-coded, preventing
;* easy expansion of the buffer.  The newer tech-reference manual (v2.02),
;* however, shows these variables starting at offset 80h, and bios uses them
;* as pointers to the actual keyboard buffer area, making it rather easy
;* to expand.  In my system (original bios), these locations always (as far
;* as I know) contain 0, so I will use this fact to test for unknown
;* buffer conditions.  If the locations contain 0's, a standard (16 char)
;* buffer will be assumed; if they do not, their contents will be checked
;* against the offsets of kb_buffer & kb_buffer_end.  If the values don't match,
;* the buffer has been changed somehow, and this program will not mess with it.

install_kb_io_int_hdlr	proc near

	push	es
	mov	ax,bios_data			; set ES to bios_data segment
	mov	es,ax				;

	mov	ax,es:bios_buffer_start 	; check contents of pointer
	or	ax,ax				; zero?
	jz	inst_io_1			; if so, probably old rom -
						;  check next word
	cmp	ax,offset bios_kb_buffer	; if they match, ok so far
	jne	inst_io_err			; if not, who knows?

inst_io_1:
	mov	ax,es:bios_buffer_end		; check contents of next pointer
	or	ax,ax				; zero?
	jz	inst_io_2			; if so, definitely old rom -
						;  ok to install
	cmp	ax,offset bios_kb_buffer_end	; match?  ok to install
						;  (new rom but default buffer)
	jne	inst_io_err			; if not, who knows?

inst_io_2:					; initialize our keyboard buffer

	mov	si,offset our_kb_buffer 	; point SI & head to start
	mov	our_buffer_head,si		;  of new keyboard buffer

	call	movem				; get chars & setup bios buf

	mov	al,kb_io_int_no 		; int number for keyboard i/o
	mov	ah,set_int_vec			; dos set int. vector function
	mov	dx,offset kb_io_int_hdlr	; offset of new i/o handler
	int	dosint				; set the vector

	mov	buf_flag,true			; tell kb_int_hdlr it's ok to
						;  use the buffer now..
	clc					; clear CF to show no error
	jmp short inst_io_exit			; return

inst_io_err:					; unknown buffer condition found
						;  - don't install
	mov	dx,offset buf_err_msg		; print informational messages..
	call	msg				;
	stc					; set CF to show error

inst_io_exit:
	pop	es
	ret

; get existing chars into our buffer - SI points to beginning of our buffer

movem:
	mov	bx,es:bios_buffer_head		; get ptr to 1st char in buf
movem1:
	cmp	bx,es:bios_buffer_tail		; if tail = head,
	je	movem_exit			;  nothing there - setup buf
	mov	ax,es:[bx]			; else, get a word of data
	mov	[si],ax 			; put it in our buffer
	inc	bx				; bump both ptrs to next word
	inc	bx				;
	inc	si				;
	inc	si				;
	cmp	bx,offset bios_kb_buffer_end	; at end of bios buffer?
	jne	movem2				; no, continue
	mov	bx,offset bios_kb_buffer	; set to beginning of buffer
movem2:
	jmp	movem1				; loop while chars available

movem_exit:
	mov	our_buffer_tail,si		; store ptr to next pos. avail.
	call	init_bios_buf			; set keyboard buf to empty
	ret

install_kb_io_int_hdlr	endp


;**************************************************************************
;* print msg (offset in dx)

msg proc near

	push	ax				;
	mov	ah,print_string 		; dos function number
	int	dosint				;
	pop	ax				;
	ret

msg endp

;**************************************************************************
;*
;*	GET_VEC 	get interrupt vector
;*
;*	Entry:		AL = interrupt number
;*
;*	Exit:		ES:BX contain CS:IP for interrupt vector
;*
;*	(Only ES and BX changed)
;*
;**************************************************************************

get_vec proc near

	push	ax			; save entering value
	push	ax			; save interrupt number requested
	mov	ah,get_vers		; dos get version function
	int	dosint			; major version returned in AL, minor in AH
	pop	bx			; restore int. number requested
	cmp	al,2			; less than 2 = pre dos 2.0
	jb	gv_direct		; get vector directly

	mov	al,bl			; move requested interrupt vector into AL
	mov	ah,get_int_vec		; dos get int. vector function
	int	dosint			; CS:IP returned in ES:BX
	pop	ax			;
	ret

gv_direct:				; get vector directly
					; first, convert number in BL to offset in BX
	xor	bh,bh			; make sure BH = 0
	shl	bx,1			; multiply by 4
	shl	bx,1			;
	mov	ax,int_vecs		; point ES to segment for int. vectors
	mov	es,ax			;
	mov	ax,es:[bx+2]		; get CS value into AX
	mov	bx,es:[bx]		; get IP value into BX
	mov	es,ax			; mov CS value into ES
	pop	ax			;
	ret

get_vec endp

;****************************************************************
;*
;*	get_parms	scan command line for /b or /n parm
;*
;*	exit:	carry set on error
;*
;*	AX,BX,SI changed
;*
;****************************************************************

get_parms	proc near

	mov	bx,offset cmd_ct	; BX pts to number of cmd line chars
	mov	si,[bx] 		; get count
	inc	bx			; point BX to 1st char
	and	si,0ffh 		; max. 127 chars. - set flags
	jz	gp_err_exit		; no parms, skip the rest
	mov	byte ptr [bx][si],0	; make command line null terminated

gp_top_loop:
	mov	al,[bx] 		; get the char
	or	al,al			; null?
	je	gp_err_exit		; if so, return error
	cmp	al,spc			; space?
	je	gp_bot_loop		; yes, get another char
	cmp	al,tab			; tab?
	je	gp_bot_loop		; yes, get another char
	cmp	al,'/'                  ; see if possible /b or /n switch
	jne	gp_err_exit		; no, return error
	call	ck_switches		; check parm, carry set on error
	jc	gp_err_exit		; carry set, return error
	jmp	gp_exit 		;

gp_bot_loop:
	inc	bx			; point to next char
	jmp	gp_top_loop		; go get it

gp_err_exit:
	stc				; set carry to show error
gp_exit:				; if got here by jump, carry is reset
	ret

ck_switches:				; ck parms ( BX pts to '/' )
	inc	bx			; point to possible switch
	mov	al,[bx] 		; get char
	cmp	al,'Z'                  ; check if possibly upper-case
	jbe	ck_sw1			; no? continue
	sub	al, 'a' - 'A'           ; make upper-case
ck_sw1:
	cmp	al,'B'                  ; buffer desired ?
	jne	ck_sw2			; no? continue
	mov	buf_desired,true	; else, set flag
	jmp	ck_sw_exit		; and exit
ck_sw2:
	cmp	al,'N'                  ; no buffer desired ?
	je	ck_sw_exit		; yes, return ok
	stc				; set carry to show error
ck_sw_exit:
	ret

get_parms	endp

cseg	ends

end	entry
ESS.TXT file will have the follow