; dos3a/asm
*TITLE	'<DOS+II keyboard drivers>'
;
;	local equates
;
KDCB@	EQU	0		;keyboard DCB number
PDCB@	EQU	2		;printer DCB number
RDKBD	EQU	0FCH		;read keyboard data
;
; the following code is the keyboard driver
;	which will normally run under foreground
;	control.  this is the normal vector of
;	the driver address in the *KI DCB
;
; on entry, C and Z are set to indicate the call type
;	IX => DCB
;
; on exit, B contains the input character from the
;	keyboard.  The Z flag will return the status
;	of the operation.  If NZ, A will contain the
;	error number.  If a call is made via @CTL
;	then service will wait for a key.
;	If call is from @GET, then no waiting
;
KBDCODE BIT	1,(IX+5)	;chaining on?
	JR	NZ,KBCHAIN	;go if yes
;
KI1	CALL	BUFTAKE 	;see if char available
	JR	NC,KBDEMP	;no chars available
	LD	(IX+15),E	;update
	LD	(IX+16),D	;ring
	BIT	0,(IX+5)	;translation on?
	JR	Z,KI2		;nope, return with Z flag
	LD	L,(IX+6)	;get xlate table ptr
	LD	H,(IX+7)
	LD	B,(IX+8)	;table length
	CALL	XLATE		;translate from table
;
KI2	CP	7FH		;ctl--?
	JR	Z,KI3		;if yes
	LD	B,A		;B = key
	XOR	A		;set Z flag (no error)
	RET			;done, B = key, Z flag
;
KI3	LD	A,PDCB@ 	;display device
	CALL	LOCDCB		;locate dcb
	PUSH	IX
	POP	DE		;DE => DCB
	CALL	SCREEN		;screen print
;
KBDEMP	LD	A,2		;char not available
	OR	A		;set NZ
	RET			;NZ = no char
;
;	CHAIN file handler
;
KBCHAIN LD	E,(IX+17)
	LD	D,(IX+18)	;DE => FCB
	CALL	GET		;@GET the char
	RET	Z		;NO ERROR
;
;	reclaim buffer area
;
	RES	1,(IX+5)	;turn off chaining
	EX	DE,HL		;HL => buffer
	DEC	H		;point to data
	LD	BC,256+40	;buff length
	CALL	RECMEM		;reclaim memory
	JR	KBDCODE 	;see if keyboard key
;
*EJECT
;	$KBPUT - PUT CHAR TYPE AHEAD
;
;	ENT	 B =	CHARACTER
;
KBPUT	CALL	REGSAV		;save registers
	LD	A,KDCB@ 	;KBD device
	CALL	LOCDCB		;locate DCB
	LD	A,B		;get char
	DI			;no int
	CALL	KBTASK1 	;to type ahead
	BIT	7,(IX+5)	;all ok?
	LD	A,39		;illegal I/O
	RET	NZ		;not put
	XOR	A		;all ok
	RET
;
*EJECT
; the following is the keyboard driver for the
; vectored interrupt service
;
KBDTASK PUSH	AF		;save reg
	PUSH	IX
	LD	A,KDCB@ 	;KBD device
	CALL	LOCDCB		;locate DCB
	IN	A,(RDKBD)	;read the key
	CALL	KBTASK1 	;to buffer
	POP	IX		;restore
	POP	AF
	RET
;
;	place character into type ahead
;
;	ENT	 A =	CHARACTER
;		IX =>	KBD DCB
;
KBTASK1 PUSH	HL		;save registers
	PUSH	DE
	RES	7,(IX+5)	;assume key handled
	AND	7FH		;ignore high bit
	JR	NZ,NOTHLD	;not hold
	SET	2,(IX+5)	;set hold flag
	BIT	4,(IX+5)	;hold processor on?
	JR	NZ,KBDRET	;yes, ignore!
;
NOTHLD	CP	3		;break?
	JR	NZ,PUTKEY	;nope, go!
	BIT	6,(IX+5)	;break on?
	JR	NZ,KBDRET	;if not
	LD	HL,FLAG1S	;debug int flag
	INC	(HL)		;zero
	DEC	(HL)		;int?
	JR	Z,NOTDBG	;if not
	LD	HL,DEBUG	;debug entry
	JR	KBRET1		;go debug!
;
NOTDBG	BIT	5,(IX+5)	;break processor on?
	JR	Z,PUTKEY	;NO, put in buffer
	LD	L,(IX+19)
	LD	H,(IX+20)	;get processor
KBRET1	POP	DE		;restore reg
	EX	(SP),HL
	EI			;int ok
	RETI			;done!
;
PUTKEY	CALL	BUFFADD 	;room to add the key?
	SET	7,(IX+5)	;assume not added
	JR	NC,KBDRET	;key added
	LD	(IX+13),E	;update
	LD	(IX+14),D	;ring
	RES	7,(IX+5)	;key handled
KBDRET	POP	DE		;restore reg
	POP	HL
	EI			;int ok
	RETI			;done!
;
*EJECT
;	$KBLINE - input line from keyboard
;
; ENTRY:   HL => input buffer
;	    B = max chars to input
;
; EXIT:     B = actual number chars input
;	    C = line termination flag 0/0DH
;	    C flag = BREAK key terminated string
;
; (note) if entry B = 1, then a return is made after
;	the first key pressed.
;	 if entry B <> 1, then no AUTO term is used
;	to terminate the input line
;
CURON	EQU	1		;CURSOR ON
CUROF	EQU	2		;CURSOR OFF
BREAK	EQU	3		;BREAK ON
BS	EQU	8		;BACKSPACE
CR	EQU	13		;CARRIAGE RETURN
ESC	EQU	27		;ESCAPE
;
KBLINE	PUSH	DE		;SAVE
	PUSH	HL		;SAVE
	LD	A,CURON 	;TURN ON
	CALL	LINE0		;CURSOR
	LD	C,B		;C = CHAR COUNT
;
LINE1	PUSH	BC		;SAVE
	CALL	KBDW		;SCAN KBD
	LD	A,B		;GET CHAR
	POP	BC		;RESTORE
	CP	BREAK
	JR	Z,LINE7 	;HANDLE
	CP	CR
	JR	Z,LINE8 	;HANDLE
;
	LD	DE,LINE1	;RET ADD
	PUSH	DE		;TO STACK
	CP	ESC
	JR	Z,LINE4 	;HANDLE
	CP	BS
	JR	Z,LINE5 	;HANDLE
	CP	' '             ;CTL?
	RET	C		;INVALID
;
;	HANDLE CHAR HERE
;
LINE2	INC	B
	DEC	B		;MAX?
	RET	Z
	LD	(HL),A		;TO BUFF
	CALL	LINE0		;OUTPUT
	INC	HL		;NEXT
	DEC	B		;NEXT
	RET	NZ		;NOT FULL
	LD	A,C
	DEC	A		;1 CHAR FIELD?
	RET	NZ		;IF NOT
	POP	DE		;IGNORE CALLER
	JR	LINE8		;EXIT
;
;	HANDLE CAN HERE
;
LINE3	CALL	LINE5		;BKSP CHAR
LINE4	LD	A,B		;GET COUNT
	CP	C		;BEG LINE?
	JR	NZ,LINE3	;IF NOT
	RET
;
;	HANDLE BKSP HERE
;
LINE5	LD	A,B		;GET COUNT
	CP	C		;AT BEG?
	RET	Z		;IF NOT
	DEC	HL		;BKSP
	LD	A,BS
	CALL	LINE0		;TO VIDEO
	INC	B		;NEXT
	RET
;
;	HANDLE CR/BREAK HERE
;
LINE7	SCF			;HAVE BREAK
LINE8	PUSH	AF		;SAVE STAT
	LD	A,C		;GET COUNT
	SUB	B		;CHAR INPUT
	LD	B,A		;B = CHAR INPUT
	SUB	C		;FULL FIELD?
	JR	Z,LINE9 	;IF NOT
	INC	B		;INCL C/R
	LD	A,CR		;C/R
	LD	(HL),A		;TO BUFF
;
LINE9	LD	C,A		;C = TERM CHAR
	LD	A,CR		;C/R
	CALL	LINE0		;OUTPUT
	LD	A,CUROF 	;TURN OFF
	CALL	LINE0		;CURSOR
	POP	AF		;GET STAT
	POP	HL		;RESTORE
	POP	DE
	LD	A,0		;ZERO
	RET
;
LINE0	PUSH	BC		;SAVE
	LD	B,A		;B = CHAR
	CALL	DSP		;OUTPUT
	POP	BC		;RESTORE
	RET
 