;======================================================================
;	NABU KEYBOARD DRIVER
;
;	CREATED BY: LES BIRD
;
;======================================================================
;
; NABU KEYBOARD CODES:
;
;  $00-$7F	STANDARD ASCII CODES
;  $80-$8F	JOYSTICK PREFIXES ($80 = JS1, $81 = JS2)
;  $90-$9F	KEYBOARD ERROR CODES
;  $A0-$BF	JOYSTICK DATA
;  $C0-$DF	UNUSED
;  $E0-$EF	SPECIAL KEYS
;
; NOTE THAT THE ERROR CODE $94 IS A WATCHDOG TIMER THAT WILL BE
; SENT BY THE KEYBOARD EVERY 3.7 SECONDS.
;
; THE CODE BELOW WILL IGNORE (SWALLOW) THE ERROR CODES ($90-$9F) AND
; WILL TRANSLATE SPECIAL KEYS ($E0-$FF) TO ROMWBW EQUIVALENTS.  ALL
; OTHER KEYS WILL BE PASSED THROUGH AS IS.
;
NABUKB_IODAT	.EQU	$90		; KEYBOARD DATA (READ)
NABUKB_IOSTAT	.EQU	$91		; STATUS (READ), CMD (WRITE)
;
;
NABUKB_BUFSZ	.EQU	16		; RECEIVE RING BUFFER SIZE
;
	DEVECHO	"NABUKB: IO="
	DEVECHO	NABUKB_IODAT
	DEVECHO	"\n"
;
; INITIALZIZE THE KEYBOARD CONTROLLER.
;
NABUKB_INIT:
	CALL	NEWLINE
	PRTS("NABUKB: IO=0x$")
	LD	A,NABUKB_IODAT
	CALL	PRTHEXBYTE
;
	XOR	A
	CALL	NABUKB_PUT
	CALL	NABUKB_PUT
	CALL	NABUKB_PUT
	CALL	NABUKB_PUT
	CALL	NABUKB_PUT
	LD	A,$40		; RESET 8251
	CALL	NABUKB_PUT
	LD	A,$4E		; 1 STOP BIT, 8 BITS, 64X CLK
	CALL	NABUKB_PUT
	LD	A,$04		; ENABLE RECV
	CALL	NABUKB_PUT
;
#IF (INTMODE == 1)
	; ADD TO INTERRUPT CHAIN
	LD	HL,NABUKB_INT
	CALL	HB_ADDIM1		; ADD TO IM1 CALL LIST
#ENDIF
;
#IF (INTMODE == 2)
	; INSTALL VECTOR
	LD	HL,NABUKB_INT
	LD	(IVT(INT_NABUKB)),HL	; IVT INDEX
#ENDIF
;
#IF (INTMODE > 0)
	; ENABLE KEYBOARD INTERRUPTS ON NABU INTERRUPT CONTROLLER
	LD	A,14			; PSG R14 (PORT A DATA)
	OUT	(NABU_RSEL),A		; SELECT IT
	LD	A,(NABU_CTLVAL)		; GET NABU CTL PORT SHADOW REG
	SET	5,A			; ENABLE VDP INTERRUPTS
	LD	(NABU_CTLVAL),A		; UPDATE SHADOW REG
	OUT	(NABU_RDAT),A		; WRITE TO HARDWARE
#ENDIF
;
	XOR	A
	RET
;
#IF (INTMODE > 0)
;
; INTERRUPT HANDLER FOR NABU KEYBOARD.  HANDLES INTERRUPTS FOR EITHER
; INT MODE 1 OR INT MODE 2.  THE KEYBOARD BUFFER IS JUST A SINGLE CHAR
; AT THIS POINT.  NEW CHARACTERS ARRIVING WHEN THE BUFFER IS FULL WILL
; BE DISCARDED.
;
NABUKB_INT:
	IN	A,(NABUKB_IOSTAT)	; GET KBD STATUS
	AND	$02			; CHECK DATA RDY BIT
	RET	Z			; ABORT W/ Z (INT NOT HANDLED)
;
	;CALL	PC_LT			; *DEBUG*
	IN	A,(NABUKB_IODAT)	; GET THE KEY
	;CALL	PRTHEXBYTE		; *DEBUG*
	;CALL	PC_COMMA		; *DEBUG*
	CALL	NABUKB_XB		; TRANSLATE THE KEY
	;CALL	PRTHEXBYTE		; *DEBUG*
	;CALL	PC_GT			; *DEBUG*
	JR	C,NABUKB_INTRCV4	; ABORT IF UNUSABLE KEY
	LD	B,A			; SAVE BYTE READ
	LD	HL,NABUKB_RCVBUF	; SET HL TO START OF BUF STRUCT
	LD	A,(HL)			; GET COUNT
	CP	NABUKB_BUFSZ		; COMPARE TO BUFFER SIZE
	JR	Z,NABUKB_INTRCV4	; BAIL OUT IF BUFFER FULL, RCV BYTE DISCARDED
	INC	A			; INCREMENT THE COUNT
	LD	(HL),A			; AND SAVE IT
	INC	HL			; HL NOW HAS ADR OF HEAD PTR
	PUSH	HL			; SAVE ADR OF HEAD PTR
	LD	A,(HL)			; DEREFERENCE HL
	INC	HL
	LD	H,(HL)
	LD	L,A			; HL IS NOW ACTUAL HEAD PTR
	LD	(HL),B			; SAVE CHARACTER RECEIVED IN BUFFER AT HEAD
	INC	HL			; BUMP HEAD POINTER
	POP	DE			; RECOVER ADR OF HEAD PTR
	LD	A,L			; GET LOW BYTE OF HEAD PTR
	SUB	NABUKB_BUFSZ+4		; SUBTRACT SIZE OF BUFFER AND POINTER
	CP	E			; IF EQUAL TO START, HEAD PTR IS PAST BUF END
	JR	NZ,NABUKB_INTRCV3	; IF NOT, BYPASS
	LD	H,D			; SET HL TO
	LD	L,E			; ... HEAD PTR ADR
	INC	HL			; BUMP PAST HEAD PTR
	INC	HL
	INC	HL
	INC	HL			; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START
NABUKB_INTRCV3:
	EX	DE,HL			; DE := HEAD PTR VAL, HL := ADR OF HEAD PTR
	LD	(HL),E			; SAVE UPDATED HEAD PTR
	INC	HL
	LD	(HL),D
NABUKB_INTRCV4:
	OR	$FF			; NZ SET TO INDICATE INT HANDLED
	RET				; AND RETURN
;
#ENDIF
;
; NORMAL HBIOS CHAR INPUT STATUS.  IF INTERRUPTS ARE NOT ACTIVE, THEN
; KEYBOARD POLLING IS IMPLEMENTED HERE.
;
#IF (INTMODE == 0)
NABUKB_STAT:
	LD	A,(NABUKB_KSTAT)	; GET KEY WAITING STATUS
	OR	A			; SET FLAGS
	RET	NZ			; KEY WAITING, ALL SET
	IN	A,(NABUKB_IOSTAT)	; GET KBD STATUS
	AND	$02			; CHECK DATA RDY BIT
	JR	Z,NABUKB_STATX		; BAIL OUT W/ Z (NO KEY)
	IN	A,(NABUKB_IODAT)	; GET THE KEY
	CALL	NABUKB_XB		; TRANSLATE/FILTER
	JR	C,NABUKB_STATX		; IF CF SET, IGNORE
	LD	(NABUKB_KEY),A		; BUFFER IT
	LD	A,1			; SIGNAL KEY WAITING
	LD	(NABUKB_KSTAT),A	; SAVE IT
	OR	A			; SET FLAGS
	RET				; DONE
;
NABUKB_STATX:
	XOR	A			; SIGNAL NO CHAR READY
	JP	CIO_IDLE		; RETURN VIA IDLE PROCESSOR
;
#ELSE
NABUKB_STAT:
	LD	A,(NABUKB_BUFCNT)	; GET BUFFER UTILIZATION COUNT
	OR	A			; SET FLAGS
	JP	Z,CIO_IDLE		; NOT READY, RETURN VIA IDLE PROCESSING
	RET				; DONE
#ENDIF
;
; ROUTINE TO FILTER AND TRANSLATE INCOMING NABU KEYBOARD KEYCODES
; ENTER WITH RAW KEY CODE IN A
; RESULT IN A, CF SET IF THE KEY SHOULD BE DISCARDED
;
NABUKB_XB:
	; $00 - $7F ARE NORMAL ASCII CHARS (PASS THROUGH)
	BIT	7,A			; HIGH BIT IS SPECIAL CHAR
	JR	Z,NABUKB_XB2		; IF NORMAL CHAR, RETURN W/ CF CLEAR
	; $80-$8F ARE JOSYSTICK PREFIXES (PASS THESE THROUGH)
	CP	$90			; END OF JOYSTICK PREFIXES
	JR	C,NABUKB_XB2		; IF JOY PRE, RETURN W/ CF CLEAR
	; $90-$9F ARE KEYBOARD ERROR CODES (IGNORE THESE)
	CP	$A0			; END OF ERR CODES
	JR	C,NABUKB_XB1		; IF ERR CODE, RETURN W/ CF SET
	; $A0-$BF ARE JOYSTICK DATA (PASS THESE THROUGH)
	CP	$C0			; END OF JOYSTICK DATA
	JR	C,NABUKB_XB2		; IF JOY DATA, RETURN W/ CF CLEAR
	; $C0-$DF ARE UNUSED CODES (IGNORE THESE)
	CP	$E0			; END OF UNUSED CODES
	JR	C,NABUKB_XB1		; IF UNUSED CODE, RETURN W/ CF SET
	; NABU KEYBOARD USES $E0-$FF FOR SPECIAL KEYS
	; HERE WE TRANSLATE TO ROMWBW SPECIAL KEYS AS BEST WE CAN
	SUB	$E0			; ZERO OFFSET
	LD	HL,NABUKB_XTBL		; POINT TO XLAT TABLE
	CALL	ADDHLA			; OFFSET BY SPECIAL KEY VAL
	LD	A,(HL)			; GET TRANSLATED VALUE
	OR	A			; CHECK FOR N/A (0)
	RET	NZ			; XLAT OK, RET W/ CF CLEAR
NABUKB_XB1:
	; RETURN W/ CF SET (DISCARD RESULT)
	SCF				; ELSE SIGNAL INVALID
	RET				; DONE
NABUKB_XB2:
	; RETURN W/ CF CLEAR (KEEP RESULT)
	OR	A			; CLEAR CF
	RET				; RETURN, CF CLEAR
;
; FLUSH KEYBOARD BUFFER
;
NABUKB_FLUSH:
#IF (INTMODE == 0)
	XOR	A
	LD	(NABUKB_KSTAT),A
#ELSE
	; RESET THE RECEIVE BUFFER
	LD	DE,NABUKB_RCVBUF	; DE := CNT
	XOR	A			; A := 0
	LD	(DE),A			; _CNT = 0
	INC	DE			; DE := ADR OF _HD
	PUSH	DE			; SAVE IT
	INC	DE
	INC	DE
	INC	DE
	INC	DE			; DE := ADR OF _BUF
	POP	HL			; HL := ADR OF _HD
	LD	(HL),E
	INC	HL
	LD	(HL),D			; _HD := _BUF
	INC	HL
	LD	(HL),E
	INC	HL
	LD	(HL),D			; _TL := _BUF
#ENDIF
	RET
;
; WAIT FOR A KEY TO BE READY AND RETURN IT.
;
#IF (INTMODE == 0)
NABUKB_READ:
	CALL	NABUKB_STAT		; CHECK FOR KEY READY
	JR	Z,NABUKB_READ		; LOOP TIL ONE IS READY
	LD	A,(NABUKB_KEY)		; GET THE BUFFERED KEY
	LD	E,A			; PUT IN E FOR RETURN
	XOR	A			; ZERO TO ACCUM
	LD	C,A			; NO SCANCODE
	LD	D,A			; NO KEYSTATE
	LD	(NABUKB_KSTAT),A	; CLEAR KEY WAITING STATUS
	RET				; AND RETURN
#ELSE
;
NABUKB_READ:
	CALL	NABUKB_STAT		; SEE IF CHAR AVAILABLE
	JR	Z,NABUKB_READ		; LOOP UNTIL SO
	HB_DI				; AVOID COLLISION WITH INT HANDLER
	LD	HL,NABUKB_RCVBUF	; SET HL TO START OF BUF STRUCT
	LD	A,(HL)			; GET COUNT
	DEC	A			; DECREMENT COUNT
	LD	(HL),A			; SAVE UPDATED COUNT
	INC	HL			; HL := ADR OF TAIL PTR
	INC	HL			; "
	INC	HL			; "
	PUSH	HL			; SAVE ADR OF TAIL PTR
	LD	A,(HL)			; DEREFERENCE HL
	INC	HL
	LD	H,(HL)
	LD	L,A			; HL IS NOW ACTUAL TAIL PTR
	LD	C,(HL)			; C := CHAR TO BE RETURNED
	INC	HL			; BUMP TAIL PTR
	POP	DE			; RECOVER ADR OF TAIL PTR
	LD	A,L			; GET LOW BYTE OF TAIL PTR
	SUB	NABUKB_BUFSZ+2		; SUBTRACT SIZE OF BUFFER AND POINTER
	CP	E			; IF EQUAL TO START, TAIL PTR IS PAST BUF END
	JR	NZ,NABUKB_READ2		; IF NOT, BYPASS
	LD	H,D			; SET HL TO
	LD	L,E			; ... TAIL PTR ADR
	INC	HL			; BUMP PAST TAIL PTR
	INC	HL			; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START
NABUKB_READ2:
	EX	DE,HL			; DE := TAIL PTR VAL, HL := ADR OF TAIL PTR
	LD	(HL),E			; SAVE UPDATED TAIL PTR
	INC	HL			; "
	LD	(HL),D			; "
	LD	E,C			; MOVE CHAR TO RETURN TO E
	HB_EI				; INTERRUPTS OK AGAIN
	XOR	A			; SIGNAL SUCCESS
	RET				; AND DONE
;
#ENDIF
;
; HELPER ROUTINE TO WRITE 
;
NABUKB_PUT:
	OUT	(NABUKB_IOSTAT),A
	NOP
	NOP
	NOP
	NOP
	NOP
	RET
;
;
;
#IF (INTMODE == 0)
NABUKB_KSTAT	.DB	0		; KEY STATUS
NABUKB_KEY	.DB	0		; KEY BUFFER
#ELSE
;
; RECEIVE BUFFER
;
NABUKB_RCVBUF:
NABUKB_BUFCNT	.DB	0		; CHARACTERS IN RING BUFFER
NABUKB_HD	.DW	NABUKB_BUF	; BUFFER HEAD POINTER
NABUKB_TL	.DW	NABUKB_BUF	; BUFFER TAIL POINTER
NABUKB_BUF	.FILL	NABUKB_BUFSZ,0	; RECEIVE RING BUFFER
NABUKB_BUFEND	.EQU	$		; END OF BUFFER
#ENDIF
;
; THIS TABLE TRANSLATES THE NABU KEYBOARD SPECIAL CHARS INTO
; ANALOGOUS ROMWBW STANDARD SPECIAL CHARACTERS.  THE TABLE STARTS WITH
; NABU KEY CODE $E0 AND HANDLES $20 POSSIBLE VALUES ($E0-$FF)
; THE SPECIAL KEYS SEND A SPECIFIC KEYCODE TO INDICATE DOWN (KEY
; PRESSED) AND UP (KEY RELEASED).  WE WILL ARBITRARILY CHOOSE TO
; RESPOND TO KEY PRESSED.  a TRANSLATION VALUE OF $00 MEANS THAT THE
; KEY CODE SHOULD BE DISCARDED.
;
NABUKB_XTBL:
	.DB	$F9		; $E0, RIGHT ARROW (DN) -> RIGHT ARROW
	.DB	$F8		; $E1, LEFT ARROW (DN) -> LEFT ARROW
	.DB	$F6		; $E2, UP ARROW (DN) -> UP ARROW
	.DB	$F7		; $E3, DOWN ARROW (DN) -> DOWN ARROW
	.DB	$F5		; $E4, PAGE RIGHT (DN) -> PAGE DOWN
	.DB	$F4		; $E5, PAGE LEFT (DN) -> PAGE UP
	.DB	$F3		; $E6, NO (DN) -> END
	.DB	$F2		; $E7, YES (DN) -> HOME
	.DB	$EE		; $E8, SYM (DN) -> SYSRQ
	.DB	$EF		; $E9, PAUSE (DN) -> PAUSE
	.DB	$00		; $EA, TV/NABU (DN) -> APP
	.DB	$00		; $EB, N/A
	.DB	$00		; $EC, N/A
	.DB	$00		; $ED, N/A
	.DB	$00		; $EE, N/A
	.DB	$00		; $EF, N/A
	.DB	$00		; $F0, RIGHT ARROW (UP)
	.DB	$00		; $F1, LEFT ARROW (UP)
	.DB	$00		; $F2, UP ARROW (UP)
	.DB	$00		; $F3, DOWN ARROW (UP)
	.DB	$00		; $F4, PAGE RIGHT (UP)
	.DB	$00		; $F5, PAGE LEFT (UP)
	.DB	$00		; $F6, NO (UP)
	.DB	$00		; $F7, YES (UP)
	.DB	$00		; $F8, SYM (UP)
	.DB	$00		; $F9, PAUSE (UP)
	.DB	$00		; $FA, TV/NABU (UP)
	.DB	$00		; $FB, N/A
	.DB	$00		; $FC, N/A
	.DB	$00		; $FD, N/A
	.DB	$00		; $FE, N/A
	.DB	$00		; $FF, N/A
