		page	64,132










Comment @
    This is a patch to the ROM BIOS keyboard routine.  When the patch is
    loaded, the Alt, Ctrl, Left Shift and Right Shift keys toggle between
    their shifted and unshifted states.

    There are two main entry points.

    1.  'Keyloc' sets up the keyboard interupt vector to point to the patch 
	instead of the normal BIOS routine.  After Keyloc executes, it 
	leaves the new interupt handler resident in memory. 
   
    2.  'Ikeyloc' is the new keyboard interupt handler.  The first section 
	is a duplication of the ROM BIOS routine until a point where tests 
	for the input of a Alt, Ctrl, Left Shift or Right Shift key can be 
	made.  If it is one of those keys, the appropriate bits in the 
	keyboard flag byte are twiddled. 

    John Black
    5225 Pooks Hill Rd. #1715 N.
    Bethesda MD. 20814
@

port_b		equ	60h	; 8255 port a addr
timer		equ	40h
kb_data		equ	60h	; keyboard scan code port
kb_ctl		equ	61h	; control bits for keyboard sense data

abso		segment at 0
		org	24h
kb_vector	label	dword
abso		ends

bioscode 	segment at 0F000h
;		...locations in ROM BIOS used to return from interupt
		org	0E9AFh
K16		label	far		; resume code if not shift

		org	0EC24h
K62		label	far

		org	0EA23h
K23		label	far		; break for shift key

		org	0EA5Eh
K26		label	far		; return from interupt
bioscode 	ends

biosdata	segment	at 40h

		org	17h
kb_flag		label	byte
;	...bit definitions for kb_flag
ins_state	equ	80h		; insert state is active
caps_state	equ	40h		; caps lock state has been toggled
num_state	equ	20h		; num lock state has been toggled
scroll_state	equ	10h		; scroll lock state has been toggled
alt_shift	equ	08h		; alternate shift key depressed
ctl_shift	equ	04h		; control shift key depressed
left_shift	equ	02h		; left shift key depressed
right_shift	equ	01h		; right shift key depressed

;	...scan codes
num_key		equ	69
scroll_key	equ	70
alt_key		equ	56
ctl_key		equ	29
caps_key	equ	58
left_key	equ	42
right_key	equ	54
ins_key		equ	82
del_key		equ	83
biosdata	ends

keylocs	segment	para public 'CODE'
		assume	cs:keylocs

keyloc_ident	db	'KEYLOC'
ident_length	equ	6
ikeyloc		proc	far		; interupt entry point 
		sti			; begin copy of ROM BIOS
		push	ax
		push	bx
		push	cx
		push	dx
		push	si
		push	di
		push	ds
		push	es
		cld	
		mov	ax,biosdata
		mov	ds,ax
		in	al,kb_data
		push	ax
		in	al,kb_ctl
		mov	ah,al
		or	al,80h
		out	kb_ctl,al
		xchg	ah,al
		out	kb_ctl,al
		pop	ax
		mov	ah,al
		cmp	al,0FFh
		jnz	keylocpatch
		jmp	K62
					; End of rom code copy
keylocpatch:
		push	ax
		assume	ds:biosdata

		and	al,01111111b	; mask out break indicator
		cmp	al,alt_key
		je	alt_in
		cmp	al,ctl_key
		je	ctrl_in
		cmp	al,left_key
		je	leftshift_in
		cmp	al,right_key
		je	rightshift_in
;		...return to normal bios keyboard routine
retbioskey:	pop	ax
		jmp	K16


alt_in:
		mov	al,alt_shift
		call	Depress_lock
;		... if this is a break, enter bios at K23
		pop	ax
		mov	ah,alt_shift
		test	al,80h			;  if
		jz	fi			;  .
		test	kb_flag,alt_shift	;  .
		jnz	fi			;  .
						;  / break and not depressed
	    					;  .
		or	kb_flag,alt_shift	;  .  Reset depressed flag
		jmp	K23			;  .  Return in case using
						;  .  alt keypad entry
fi:						;  fi
		jmp	K26

ctrl_in:
		mov	al,ctl_shift
		call	Depress_lock
		jmp	retdone
leftshift_in:
		mov	al,left_shift
		call	Depress_lock
		jmp	retdone
rightshift_in:
		mov	al,right_shift
		call	Depress_lock
		jmp	retdone

retdone:	pop	ax
		jmp	K26
ikeyloc		endp

;---------------------------------------------
;
;   Depress_lock
;
;	For keys that normally are depressed and held down
;	while another key is depressed. (Cntl for example)
;
;	ah	input key, with break indicator (80h)
;	al	contains depressed indicator bit mask
;
Down		db	?	; indicates the key is held down
Depress_lock	proc	near
		push	dx
		test	ah,80h			; if1
		jnz	fi1			; .
		test	Down,al			; .
		jnz	fi1			; .
						; / make and not down
		test	kb_flag,al		; .   if2
		je	else2			; .   .
						; .   / depressed
		call	high_beep		; .   .
		call	delay			; .   .
		call	low_beep		; .   .
		mov	dl,al			; .   . dl:=depressed bit
		not	dl			; .   .
		and	kb_flag,dl		; .   . key := not depressed
		jmp	fi1			; .   .
else2:						; .   / not depressed
		call	low_beep		; .   .
		call	delay			; .   .
		call	high_beep		; .   .
		or	kb_flag,al		; .   . key := depressed
fi1:						; fi1 fi2

		test	ah,80h			; if5
		jnz	else5			; .
						; / make
		or	Down,al			; .  down := true
		jmp	fi5			; .
else5:						; / break
		mov	dl,al			; .
		not	dl			; .
		and	Down,dl			; .  down := false
fi5:						; fi5
		pop	dx
		ret
Depress_lock	endp


;---------------------------------
h_cycles	equ	20
h_half		equ	300
l_cycles	equ	h_cycles / 3
l_half		equ	h_half * 3

low_beep	proc
		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
		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

;--------------------------------
delay		proc	near
		push	cx
		mov	cx,15000
		loop	$
		pop	cx
		ret
delay		endp

ikeylast	equ	$	; last location + 1 to remain in memory

stacks		segment	stack 'STACK'
		db	64 dup('stack   ')
stacks		ends

;-------------------------------------
loaded_mess	db	'KEYLOC loaded$'
err_mess	db	'error - KEYLOC is already loaded$'
keyloc		proc	far
		push	ds
		sub	ax,ax
		push	ax
		push	ds			; save program seg

;		...Check to see if a copy of KEYLOC has
;		   already been loaded by comparing Keyloc_ident
;		   with the same locations relative the the current
;		   keyboard interupt routine ( it is pointed to by the
;		   the keyboard interupt vector)
		cld					; increment
		assume	ds:abso
		mov	ax,abso
		mov	ds,ax
		mov	si,offset keyloc_ident
		mov	ax,kb_vector+2			; segment
		mov	di,kb_vector			; offset to ident
		sub	di,ident_length
		mov	es,ax
		assume	ds:keylocs
		mov	ax,keylocs
		mov	ds,ax
		mov	cx,ident_length
		repne	cmpsb				; ds:[si],es:[di]
		cmp	cx,0				; if
		je	NotLoaded			; .
							; / already loaded
		mov	ah,9h				; .  print string
		mov	dx,offset err_mess		; .
		int	21h				; .
		pop	ds				; . get rid of pseg
		ret					; . return to DOS
NotLoaded:						; fi

;		...set up keyboard interupt vector
		mov	ah,9h
		mov	dx,offset loaded_mess
		int	21h
		assume	ds:abso
		mov	ax,abso
		mov	ds,ax
		mov	ax,offset ikeyloc		; ip
		mov	kb_vector,ax
		mov	ax,seg ikeyloc			; cs
		mov	kb_vector+2,ax
;		...return to DOS, but leave ikeyloc resident
		pop	es				; get program segment
		mov	es:byte ptr 1,27h		; change int 20 to 27
		mov	dx,offset ikeylast+100h		; dx:=lastaddr+1+prfx
		ret
keyloc		endp

keylocs		ends

		end	keyloc
