;****************************************************************
;**           :
;** Project   : Personal Logic Debugger (PLD)
;** Filename  : KEYS.ASM
;** Author    : Don Lekei
;** Status    : Experimental
;** Date      : 08/27/93
;** Purpose   : Keyboard driver
;**           :
;****************************************************************
;

;**********************************************************************
;* Keyboard Definition Constants from keyscan 
;**********************************************************************
K_0:	=	1	;0
K_1:	=	2	;1
K_2:	=	3	;2
K_3:	=	4	;3
K_4:	=	5	;4
K_5:	=	6	;5
K_6:	=	7	;6
K_7:	=	8	;7
K_8:	=	9	;8
K_9:	=	10	;9
K_PM	=	11	;+/-
K_SQR:	=	12	;root
K_PER:	=	13	;%
K_DIV:	=	14	;ö
K_MRC:	=	15	;MRC
K_MM:	=	16	;M-
K_MP:	=	17	;M+
K_MUL:	=	18	;X
K_SUB:	=	19	;-
K_ADD:	=	20	;+
K_EQ:	=	21	;=
K_DP:	=	22	;.

	.seg	ROMDATA

;**********************************************************************
;* Get a key from KEYBUF.
;* This returns a translated key in W.  Returns Z set if no key
;**********************************************************************

GETKEY:
	movfw	KEYBUF	;get key
	btfsc	Z	;there was none!
	retlw	0	;no key
	movwf	TEMP    ;save key
	addlw	-(MAX_KEY+1) ;limit key
	movfw	TEMP	;re-get key
	clrf	KEYBUF	;clear key. (sets Z but leaves carry)
	skpnc		;skip if key < MAX_KEY
        retlw	0	;no key (Z and CY are set)
	addwf	PCL	;offset into table (high is always set) CLEARS CY&Z

	RETLW	0	;null  (cy is clear)
..tab:
	RETLW	K_1	;1
	RETLW	K_4	;2
	RETLW  	K_8	;3
	RETLW	K_MP	;4
	RETLW	0	;5
	RETLW	K_MM	;6
	RETLW	K_0	;7
	RETLW	K_MRC	;8
	RETLW	K_7	;9
	RETLW	K_EQ	;10
	RETLW	0	;11
	RETLW	K_PER	;12
	RETLW	0	;13
	RETLW	K_SQR	;14
	RETLW	0	;15
	RETLW	K_3	;16
	RETLW	K_2	;17
	RETLW	K_DP	;18
	RETLW	0	;19
	RETLW	K_6	;20
	RETLW	K_5	;21
	RETLW	K_PM	;22
	RETLW	K_SUB	;23
	RETLW	K_9	;24
	RETLW	K_ADD	;25
	RETLW	K_MUL	;26
	RETLW	K_DIV	;27
MAX_KEY = * - ..tab
	retlw	-1	;no key (but Z is set!)
	
	.seg	CODE

;**********************************************************************
;* 
;* Pseudo-interrupt called from the TICKER interrupt process
;*
;* I_KBD_START: called to initiate KBD scan chain
;* I_KBD:       called to do next scan
;* TICKER = 256-KBD State
;*
;* KEYBOARD on RB0,2,3,4,5,6,7. (KEY1-7). Key 1 has no GND connection and
;* must stay as an OUTPUT as it is shared with the LCD bias. KEY1 (clock)
;* must be kept toggling to maintain LCD BIAS.
;* 
;* The keyboard is wired in 2-of-n format for 28 possible keys from a
;* 7 wire matrix. (27 because RB0-GND is not valid).
;* 
;* Must be called when B.KEY1 (B.CLOCK) is 1. B.PWR is always 1.
;*
;*
;**********************************************************************

	.seg	CODE

I_KBD:
	MOVLW	TAINIT		;tristate all segment lines
	TRIS	PORTA		;tristate all segment lines

	;scan for ground keys

	MOVLW	TBKEYS		;port input bits for KEY SCAN
	TRIS	PORTB		;set port to input - weak pullups
	movlw	^B.KEY1|^B.PWR	;(KEY1 is also CLOCK for LCD)
	movwf	PORTB		;clear lcd scan lines of output latch
	CLRF	LCD_TEMP	;we will build the key code here
	MOVLW	$FF		;Key bit mask
	XORWF   PORTB,W		;check inputs (all 1's become 0's)
	BNZ	_kbd_hit0	;hit on line 0 (gnd)

	;scan for keys to KEY1

	movlw	^B.PWR		;drop KEY1 to 0 (KEY1 is also CLOCK for LCD)
	movwf	PORTB		;clear lcd scan lines of output latch
	
	movlw	6		;start code for second scan line
	movwf	LCD_TEMP	;next key starts at 1 more than this

	MOVLW	^B.KEY2|^B.KEY3|^B.KEY4|^B.KEY5|^B.KEY6|^B.KEY7|^B.PWR ;Key bit mask
	XORWF   PORTB,W		;check inputs
	BNZ	_kbd_hit1	;hit on line 1

	;scan for keys to KEY2

	MOVLW	TBKEYS & ~(^B.KEY2) ;output next 0
	TRIS	PORTB		;reveal 1 more 0 by switching to output
	
	movlw	12		;start code for second scan line
	movwf	LCD_TEMP	;next key starts at 1 more than this

	MOVLW	^B.KEY3|^B.KEY4|^B.KEY5|^B.KEY6|^B.KEY7|^B.PWR ;Key bit mask
	XORWF   PORTB,W		;check inputs
	BNZ	_kbd_hit2	;hit on line 2

	;scan for keys to KEY3

	MOVLW	TBKEYS & ~(^B.KEY3) ;output next 0
	TRIS	PORTB		;reveal 1 more 0 by switching to output
	
	movlw	17		;start code for second scan line
	movwf	LCD_TEMP	;next key starts at 1 more than this

	MOVLW	^B.KEY2|^B.KEY4|^B.KEY5|^B.KEY6|^B.KEY7|^B.PWR ;Key bit mask
	XORWF   PORTB,W		;check inputs
	BNZ	_kbd_hit3	;hit on line 3

	;scan for keys to KEY4

	MOVLW	TBKEYS & ~(^B.KEY4) ;output next 0
	TRIS	PORTB		;reveal 1 more 0 by switching to output
	
	movlw	21		;start code for second scan line
	movwf	LCD_TEMP	;next key starts at 1 more than this

	MOVLW	^B.KEY2|^B.KEY3|^B.KEY5|^B.KEY6|^B.KEY7|^B.PWR ;Key bit mask
	XORWF   PORTB,W		;check inputs
	BNZ	_kbd_hit4	;hit on line 4

	;scan for keys to KEY5

	MOVLW	TBKEYS & ~(^B.KEY5) ;output next 0
	TRIS	PORTB		;reveal 1 more 0 by switching to output
	
	movlw	25		;leaves a hole at 25 for last key
	movwf	LCD_TEMP	;next key starts at 1 more than this

	MOVLW	^B.KEY2|^B.KEY3|^B.KEY4|^B.KEY6|^B.KEY7|^B.PWR ;Key bit mask
	XORWF   PORTB,W		;check inputs
	BNZ	_kbd_hit5	;hit on line 5

	;scan for key from KEY6 to KEY7

	MOVLW	TBKEYS & ~(^B.KEY6) ;output next 0
	TRIS	PORTB		;reveal 1 more 0 by switching to output
	DEL2			;2 clock cycle delay
	btfsc	B.KEY7		;last key fills hole at 25

	clrf	LCD_TEMP	;no key
_kbd_scane:
	movlw	TBINIT		;restore port DIR
	TRIS	PORTB
	movfw	LCD_TEMP	;get current key scan
	xorwf	CURKEY		;see if it matches current key
	movwf	CURKEY		;save scan result
	skpz			;same key... continue
	goto	TICK_EXIT	;debounce
	movf	KEYBUF		;check keyboard - Z if empty
	skpz			; 
	goto	TICK_EXIT	;key buf not empty
	xorwf	LASTKEY		;see if it matches last key
	movwf	LASTKEY		;save scan result
	skpz			;if same key... ignore
	movwf	KEYBUF		;set new key
TICK_EXIT:
	TICK_EXIT	;tick interrupt exit code

;**********************************************************************
;* Strip off bits in w one at a time until we find the key that caused
;* the hit. Each miss bumps the key code in LCD_TEMP. Doing it long-hand
;* saves time.
;**********************************************************************
_kbd_hit0:
_kbd_hit1:
	INCF	LCD_TEMP	;next key code
	ANDLW	~^B.KEY2	;mask off 1 bit
	BBS	Z,_kbd_scane	;found it!
_kbd_hit2:
	INCF	LCD_TEMP	;next key code
	ANDLW	~^B.KEY3	;mask off 1 bit
	BBS	Z,_kbd_scane	;found it!
_kbd_hit3:
	INCF	LCD_TEMP	;next key code
	ANDLW	~^B.KEY4	;mask off 1 bit
	BBS	Z,_kbd_scane	;found it!
_kbd_hit4:
	INCF	LCD_TEMP	;next key code
	ANDLW	~^B.KEY5	;mask off 1 bit
	BBS	Z,_kbd_scane	;found it!
_kbd_hit5:
	INCF	LCD_TEMP	;next key code
	ANDLW	~^B.KEY6	;mask off 1 bit
	BTFSS	Z		;found it!
	INCF	LCD_TEMP	;next key code we will assume this must be it!
	goto	_kbd_scane

