;KIDVR/ASM - LS-DOS 6.2
	SUBTTL	'<Keyboard Driver>'
	PAGE	OFF
*MOD
;
LF	EQU	10
CR	EQU	13
KB0	EQU	0F401H		;Row 0 RAM address
KB6	EQU	0F440H		;Row 6 RAM address
SHIFT	EQU	0F480H		;Row 7 RAM address
;
KIDVR	JR	KIBGN		;Branch around linkage
	DW	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,1DH,1FH,1FH	;<ENTER> <CLEAR>
	DB	80H,0,0BH,1BH	;<BREAK> <UPARW>
	DB	LF,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
;
;	Entry to keyboard driver
;
KIBGN	LD	A,C		;Get the character
	PUSH	AF		;Save flags
	CALL	@KITSK		;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
@VDPRT	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	@VDCTL		;  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	@PRT		;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
	JR	NZ,$?5		;Put the CR & loop
$?6	LD	A,CR		;Close out with CR if
	CALL	@PRT		;  BREAK key detected
	POP	HL		;Pop the KFLAG
	RES	0,(HL)		;  & reset BREAK bit
	JR	NOCHAR
;
;	Driver to scan the keyboard
;
*MOD
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> - 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 @ 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-@
	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	PAUSE@
	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
;
*MOD
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,$?1		;Go on @GET
	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
;
$?1	PUSH	HL
	INC	HL		;Bump to PUT pointer
	LD	A,(HL)		;  & pick it up
	INC	HL		;Bump to GET pointer
	CP	(HL)		;The same?
	JR	Z,$?4		;Go if so
	PUSH	HL		;Save pointer to GETPTR
	LD	E,(HL)		;P/u offset to buffer
	INC	HL		;Pt to buffer start
	LD	D,0		;Add offset to start
	ADD	HL,DE		;  to point to char posn
	LD	B,(HL)		;GET the stored char
	POP	HL		;Rcvr GETPTR
	INC	(HL)		;Bump by 1 for char
	LD	A,80		;Check for >80
	CP	(HL)		;  after INC
	JR	NC,$?2		;Go if not at end
	LD	(HL),0		;Reset to start of buf
$?2	LD	A,(HL)		;If we emptied the
	DEC	HL		;  type-ahead buffer,
	CP	(HL)		;  update KFLAG$
	CALL	Z,R7KFLG	;Reset bit-7 if empty
	POP	HL		;Pointed to & get switch
	LD	(HL),0		;Turn type back on
	LD	A,B		;Transfer char/flag
	CP	A		;Set flag "Z"
	RET
;
;	No character in type ahead buffer - get from kbd
;
$?4	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 @, 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 @ 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
;
TYPBUF	EQU	0FF80H
;
;	TYPBUF+0 = On/Off Flag
;	TYPBUF+1 = Storage pointer
;	TYPBUF+2 = Retrieve pointer
;	TYPBUF+3 = Start of actual buffer
;
KILAST	EQU	$-1
