			SUBTITLE	"< Console Keyboard Driver, KIDVR/ASM - LS-DOS 6.2 >"
			newpage
			SCOPE

;	TYPBUF+0 = On/Off Flag
;	TYPBUF+1 = Storage pointer
;	TYPBUF+2 = Retrieve pointer
;	TYPBUF+3 = Start of actual buffer

		ORG	driver.KI
KIDVR		JR	KIBGN			;Branch around linkage
		DW	driver.DO-1	;KILAST			;Last byte used
		DB	3,"$KI"
		DW	KIDCB$			;Pointer to DCB
		DW	0			;Spare
KIDATA$		DB	0			;Last key entered
		DB	0			;Repeat time check
RPTINIT		EQU	$-KIDATA$
		DB	22			;22 * 33.3ms = .733 sec
RPTRATE		EQU	$-KIDATA$
		DB	2			;2 x RTC rate
KBROW0		EQU	$-KIDATA$
		DB	-1,-1,-1,-1		;Image of rows 0-3
KBROW4		EQU	$-KIDATA$
		DB	-1,-1			;Image of rows 4-5
KBROW6		EQU	$-KIDATA$
		DB	-1,-1			;Image of rows 6-7

;			Conversion table for keyboard row 7/8

KBTBL		DB	CR.asc,1Dh,1Fh,1Fh	;<ENTER> <CLEAR>
		DB	80h,0,0Bh,1Bh		;<BREAK> <UPARW>
		DB	LF.asc,1Ah,8,18h	;<DNARW> <LTARW>
		DB	9,19h,20h,20h		;<RTARW> <SPACE>
		DB	81h,91h,82h,92h		;<F1> <F2>
		DB	83h,93h			;<F3>

;			Table to generate 5B-5F, 7B-7F

SPCLTB		DB	",/.;",CR.asc

;			Entry to keyboard driver

KIBGN		LD	A,C			;Get the character
		PUSH	AF			;Save flags
		CALL	zKITSK			;Hook for KI task
		POP	AF

;			Screen print (Control-*) processing

		CALL	TYPAHD			;Chain downstream.

		RET	NC			;Ret if not <CONTROL>
		PUSH	AF			;Save flag state
		CP	':'
		JR	Z,$?1			;Go if screen print
		POP	AF
		RET

;			Perform a screen print

$?1		POP	AF			;Clean the stack
zVDPRT		LD	A,(DFLAG$)		;Check on Graphic bit
		RLCA
		LD	A,3Eh			;Init for LD a,'.'
		JR	NC,$+4			;Go if not Graphic
		LD	A,0FEh			;Change to CPR n
		LD	($?4),A			;Stuff cpr or ld
		LD	HL,KFLAG$		;Reset the BREAK bit
		RES	0,(HL)
		PUSH	HL			;Save on stack
		LD	HL,0			;Init for row,col
$?2		LD	B,1			;Get a character at the
		CALL	zVDCTL			;row-H, col-L
		JR	NZ,$?6			;Go on error
		CP	20h
		JR	NC,$+4			;Convert control codes
		ADD	A,40h			;to cap A-Z, +
		CP	80h			;Cvrt anything from X"80"
		JR	C,$?5			;thru X'FF' to a '.'
$?4		LD	A,'.'			;unless graphic bit set
$?5		CALL	zPRT			;Print the char & loop
		JR	NZ,$?6
		INC	L			;Bump column counter
		LD	A,L			;Check for end-of-line
		SUB	80
		JR	NZ,$?2			;Loop if not EOL
		LD	L,A			;Reset to column 0
		DEC	L			;Adj for CR force
		EX	(SP),HL			;Get KFLAG$
		BIT	0,(HL)			;Exit with A=0 on
		EX	(SP),HL			;entrance of BREAK
		JR	NZ,$?6
		INC	H			;Bump row counter
		LD	A,H			;Test for end of screen
		CP	24
		LD	A,CR.asc
		JR	NZ,$?5			;Put the CR & loop
$?6		LD	A,CR.asc		;Close out with CR if
		CALL	zPRT			;BREAK key detected
		POP	HL			;Pop the KFLAG
		RES	0,(HL)			;& reset BREAK bit
		JR	NOCHAR

;	Driver to scan the keyboard

		SCOPE
KISCAN		LD	IX,KIDATA$		;Point to data area
		LD	HL,KIDATA$+KBROW0 	;Load kbd image start
		LD	BC,KB0			;Load start of keyboard
		LD	D,0			;Zero the key counter
$?1		LD	A,(BC)			;Load 1st char from kbd
		LD	E,A
		XOR	(HL)			;XOR with old value
		JR	NZ,$?2			;Go if different
		INC	D			;Bump key counter
		INC	HL			;Bump image pointer
		RLC	C			;Go to next row
		JP	P,$?1			;Loop until end of rows
		LD	A,(BC)			;Get row 7
		AND	078h			;Strip SHIFT, CTL
		LD	E,A
		XOR	(HL)
		JR	NZ,$?2
		LD	A,(IX+0)		;Key down? It"s same as
		OR	A			;the last if so
		JR	Z,NOCHAR		;Ret if no key
		LD	A,(TIMER$)		;Do we repeat the
		SUB	(IX+1)			;same key?
		SUB	(IX+RPTINIT)		;Beyond 0.75 seconds?
		LD	A,(BC)			;Get back row 7
		LD	E,A
		JR	C,$?10			;Go if yes
NOCHAR		OR	1			;Else don"t repeat
		LD	A,0			;Show NZ with A=0
		RET	

;	Found change in key matrix

$?2		LD	(HL),E			;Stuff KB image with new
		AND	E			;KB row value
		JP	Z,NOKEY			;Go if new is none
;
;	Convert the depressed key
;
		LD	E,A			;Save the active bit
		LD	A,D			;Calculate 8 * row
		RLCA
		RLCA
		RLCA
		LD	D,A			;Save 8 * row
		LD	C,1			;Add 8 * row + column
$?3		LD	A,C
		AND	E			;Check if bits match
		JR	NZ,$?6			;Go if match
		INC	D			;else bump value
		RLC	C			;Shift compare bit
		JR	$?3			;Loop to test next

;	Key pressed was not an alpha

$?4		SUB	90h			;Adjust for non-alpha
		JR	NC,$?9			;Go if special key
		ADD	A,40h			;Cvrt to numeric/symbol
		CP	3Ch			;Manipulate to get
		JR	C,$?5			;proper code
		XOR	10h
$?5		BIT	0,E			;Check SHIFT
		JR	Z,$?11			;Go if unshift
		XOR	10h			;else adjust for SHIFT
		JR	$?11

;	Found a key - Set up the function codes

$?6		LD	A,(SHIFT)		;P/u the SHIFT key
		LD	E,A			;Merge RH & LH Shift keys
		AND	2			;Only merge bit 1
		RRCA				;Bit 1 to bit 0
		OR	E			;Merge bits 0 & 1
		LD	E,A			;Value of (RHorLH) shift
		LD	A,D			;Load semi-converted
		ADD	A,60h			;If alpha, convert to
		CP	80h			;correct value
		LD	HL,KFLAG$
		JR	NC,$?4			;Go if not alpha

;	Alpha <z-Z> - If caps lock or <SHIFT>,
;	Convert to caps unless CLEAR

		BIT	2,E			;CTRL key down?
		JR	NZ,CTLA2Z		;CTRL sets <00-1A>
		CP	60h			;Invert z and `
		JR	NZ,$?7
		XOR	20h			;Invert & bypass test
		JR	$?8			;for CAPs lock
$?7		BIT	1,(IX+KBROW6)		;If CLEAR, don"t test
		JR	NZ,$?8			;for CAPs lock
		BIT	5,(HL)			;Caps lock?
		JR	NZ,TGLCASE
$?8		BIT	0,E			;Shift key down?
		JR	Z,$?11			;Bypass if not shifted
		JR	TGLCASE			;Convert to upper case
CTLA2Z		SUB	60h			;Convert CTRL A-Z
		JR	NZ,$?11			;Go on A-Z
		BIT	0,E			;Shifted?
		SCF				;Set C-flag for CTL-z
		RET	Z			;& return if unshifted
		LD	A,1Ch			;else set EOF error
		RET
$?10		LD	A,(TIMER$)		;Advance time check
		ADD	A,(IX+RPTRATE)		;by 0.067 seconds
		JR	$?12			;Go output the key

;	Special keys - rows 6 & 7

$?9		CP	11			;Compress F1-F3 keys
		JR	Z,CAPSKEY		;while checking for CAP
		JR	C,$+4			;F1-F3 to 8-10
		SUB	4
		LD	HL,KBTBL		;Pt to special char table
		RLCA				;Index into table,
		BIT	0,E			;shifted code is +1
		JR	Z,$+3
		INC	A
		LD	C,A			;Index the table
		LD	B,0
		ADD	HL,BC
		LD	A,(HL)			;Load char from table
		JR	$?11			;Bypass restore of char
TGLCASE		XOR	20h			;Toggle the case
$?11		CP	80h			;BREAK key?
		JR	NZ,$?11A		;Ck on <BREAK> disable
		LD	HL,SFLAG$		;Break disabled?
		BIT	4,(HL)
		JR	NZ,$?11B		;Don"t set bit if disabl
		LD	HL,KFLAG$
		SET	0,(HL)			;otherwise set it
		JR	$?11A
$?11B		RLA				;Rotate bit-7 out
$?11A		BIT	1,(IX+KBROW6)		;CLEAR key pressed?
		JR	Z,NOTALPH		;Go if not down
		LD	D,A			;Save code
		RES	5,A			;Set to upper-case for
		SUB	'A'			;test A-Z
		CP	'Z'-'A'+1
		LD	A,D			;Get back actual char
		JR	NC,$+4			;Go if not A-Z
		XOR	20h			;Shift keyboard case
		OR	80h			;Set bit 7 for CLEAR key
NOTALPH		BIT	0,E			;SHIFT key down?
		JR	Z,FIXCLR		;Go if not
GOTSHFT		CP	9Fh			;Shift-clear?
		JR	Z,FIXSCL		;Go if so
TSTSPA		CP	20h			;Shift 0 or shift sp?
		JR	NZ,KEYOK		;Go if not
		BIT	0,(IX+KBROW4)		;Ck zero key
		JR	Z,KEYOK			;Go if not down

;	Toggle the caps lock bit in the KFLAG$

CAPSKEY		LD	A,20h			;CAPs wasn"t 20h
CASHK$		LD	HL,KFLAG$		;Reverse case by
		XOR	(HL)			;flipping bit 5
		LD	(HL),A
		JR	NOKEY
FIXSCL		XOR	80h			;Reset bit 7
FIXCLR		CP	9Fh			;Clear key?
		JR	NZ,KEYOK		;Go if not
NOKEY		XOR	A
KEYOK		LD	(IX+0),A
		LD	BC,0184h		;Delay
TYPHK$		CALL	PAUSEz
		LD	A,(TIMER$)		;Set initialization
DELAY2		ADD	A,(IX+RPTINIT)		;repeat key delay
$?12		LD	(IX+1),A		;Save new repeat time
		LD	A,(IX+0)		;Check if any key
		OR	A			;code was saved
		JP	Z,NOCHAR		;Ret if none
		BIT	2,E			;Shift key down?
		SCF				;Init carry
		JR	NZ,SPECL		;Ret if CTRL
		CCF
DVREXIT		BIT	7,A			;Z-flag set on non-CLEAR
		RET	Z			;Go if not CLEAR+key
SPECL		PUSH	AF			;Save code
$?13		LD	HL,SPCLTB		;Special char table
		RES	7,A			;Turn off "CLEAR"
		LD	BC,5<<8|5Bh		;5 chars, starting char
		JR	NC,$+3			;if not CTRL
		DEC	B			;else only 4
SPCLLP		CP	(HL)			;Is this it?
		JR	Z,HIT			;Go if so
		XOR	10h			;Flip shift state
		CP	(HL)			;Is that it?
		JR	Z,HITWS			;Go if so
		XOR	10h			;Flip back
		INC	HL			;Bump specl table ptr
		INC	C			;Bump "convert to" char
		DJNZ	SPCLLP			;Loop through table
		POP	AF			;Not found in table
		JR	C,CKCTL2		;Ck CTL for C-flag
CKCTL1		CP	A			;Set Z-flag
		RET
HITWS		SET	5,C			;Move to LC set
HIT		POP	AF			;Restore orig char
		LD	A,C			;Load converted one
CKCTL		JR	NC,CKCTL1		;Go if ctl key not down
		AND	1Fh			;Force ctl code
CKCTL2		CP	A			;Set Z-flag
		SCF				;Set C-flag for CTRL
		RET

;	Check the type ahead buffer for any character.

		SCOPE
TYPAHD	;	CALL	ENADIS_DO_RAM		;Bring up Keyboard ram.

		LD	HL,TYPBUF		;P/u start of type buffer
		LD	(HL),0FFh		;Turn off type ahead
		JR	C,GET_kbd_char		;Go on zGET
		JR	Z,TYPON			;No PUT to *KI
		CP	3			;CTL 3 function?
		JP	Z,CLRTYP		;Clear buffer if so
		INC	A
		JR	Z,CTLFF			;Go if CTL 255 function
		XOR	A			;Nothing done, No error
		JR	TYPON

;	Handle CTL-255 - scan keyboard into user rowbuf
;***************************************
CTLFF		LD	HL,KB0			;Start of keyboard image
		LD	B,8			;Do 8 rows
$?0		LD	A,(HL)			;P/u the image
		LD	(IY),A			;and xfer to user buffer
		INC	IY
		RL	L
		DJNZ	$?0
		RET


;	No character in type ahead buffer - get from kbd

GET_kbd_char	PUSH	HL
		SCF
		CALL	U0DVR	;	CALL	KISCAN			;Call keyboard driver
		POP	HL			;Rcvr switch
TYPON		LD	(HL),0			;Type ahead back on
		RET

;	Type ahead task 10 - scans keyboard & saves key

TYPTSK$		DW	$?5			;Task entry for processor
$?5		LD	A,(DFLAG$)		;If type-ahead suppressed
		AND	2h			;then return
		RET	Z
		CALL	ENADIS_DO_RAM		;Bring up the keyboard
		LD	HL,TYPBUF		;P/u type switch
		LD	A,(HL)			;If previous driver is
		OR	A			;currently executing,
		RET	NZ			;do not stack more keys
		INC	HL			;Bump to PUTPTR
		PUSH	HL			;& save it
KIHOOK		CALL	KISCAN			;and scan for a character
		POP	HL
		RET	NZ			;Ret if no char
		PUSH	AF			;else xfer char
		POP	BC			;& flag to BC
		CP	80h			;Check for <BREAK>
		PUSH	AF
		PUSH	HL
		CALL	Z,$?6			;If so clear type buf
		POP	HL			;Restore
		POP	AF
		CP	0C0h			;If CLEAR z, reset keybuf
		JR	Z,$?6
		LD	E,(HL)			;P/u PUTPTR & compare
		LD	A,E			;GETPTR
		INC	HL
		CP	(HL)
		JR	Z,$?8			;Jump if key buffer empty
		LD	A,(TIMER$)		;Check if we expired the
		ADD	A,(IX+RPTRATE)		;time interval between
		CP	(IX+1)			;repeating keys
		JR	NZ,$?7			;Go if time not up
		ADD	A,(IX+RPTRATE)		;Re-adjust time check so
		LD	(IX+1),A		;we don"t repeat in
		RET				;type-ahead task

;	CLEAR z control key entered, clear the buffer

CLRTYP		INC	HL			;Bump to PUT pointer
$?6		XOR	A
		LD	(HL),A			;1st PUT is loc"n 0
		INC	HL			;Pt to GETPTR
		LD	(HL),A			;1st GET is loc"n 0
R7KFLG		LD	HL,KFLAG$		;Show buffer empty
		RES	7,(HL)
		RET

;	Char to stuff - check if buffer will overflow

$?7		LD	A,E			;P/u current PUT pointer
		INC	A			;If the next loc"n wraps
		CP	(HL)			;to the GET loc"n,
		RET	Z			;don"t permit overrun
$?8		PUSH	HL			;Save ptr to GETPTR
		INC	HL			;Pt to start of keybuf
		LD	D,0			;& calculate PUT loc"n
		ADD	HL,DE
		LD	(HL),B			;Store the char
		LD	HL,KFLAG$		;Show type buffer
		SET	7,(HL)			;is not empty
		POP	HL			;Rcvr ptr to GETPTR
		DEC	HL			;Backup to PUTPTR
		INC	(HL)			;Bump past the char
		LD	A,80			;Check for >80
		CP	(HL)
		RET	NC			;Back if not over 80
		LD	(HL),D			;else reset to 1st
		RET				;position in buf (0)

;	Type ahead buffer area


KILAST		EQU	$-1

