;18kbd3.asm
;WWW.MWFTR.COM/book.html
;Charles Kim
;CAPS LOCK is included
;
;18kbd2.asm does not accommodate the CAPS LOCK Key
;
;This program is to:
;1. Read At or PS/2 Type Keyboard
;2. Display them, as characters, on PC monitor
;
;3. Note that all Category 2 keys (E0 keys) are ignored
;
;   Baud rate for this is set as 19200 for Monitor display
;   
;
;Terminal set up: 8N1 19200
;
;Asynchronouse mode
;


        list P = 16F877
PCL	EQU	0x02		;For Key Table Calling
PCLATH	EQU	0x0A		;upper part of PC
STATUS	EQU	0x03
CARRY	EQU	0x00
ZERO	EQU	0x02
TRISB	EQU	0x86
PORTB	EQU	0x06
TXSTA	EQU	0x98		;TX status and control
RCSTA	EQU	0x18		;RX status and control
SPBRG	EQU	0x99		;Baud Rate assignment
TXREG	EQU	0x19		;USART TX Register
RCREG	EQU	0x1A		;USART RX Register
PIR1	EQU	0x0C		;USART RX/TX buffer status (empty or full)
RCIF	EQU	0x05		;PIR1<5>: RX Buffer 1-Full  0-Empty
TXIF	EQU	0x04		;PIR1<4>: TX Buffer 1-empty 0-full
TXMODE	EQU	0x20		;TXSTA=00100000 : 8-bit, Async
RXMODE	EQU	0x90		;RCSTA=10010000 : 8-bit, enable port, enable RX
BAUD	EQU	0x0F		;0x0F (19200), 0x1F (9600)
CARRY	EQU	0x00
ZERO	EQU	0x02
MSB	EQU	0x07
CLOCK	EQU	0x07		;from Keyboard
KDATA	EQU	0x06		;from Keyboard
	
;
;RAM AREA
       
	CBLOCK	0x20
		TXtemp
		KSTAT
		DATAreg
		Kount100us
		Kount10ms
		Kount1s
		Bitcount	;data bit count
	ENDC
	


;=========================================================
        org 	0x0000      	
	GOTO	START		
;======================================================
	org	0x05
;start of the program from $0005
START	
	
	banksel	TRISB
; 1100 0000
	
	movlw	B'11000000'	;RB7 for CLOCK and RB6 for DATA as inputs
	movwf	TRISB
	call	ASYNC_mode
	call	delay1s		;Give Keyboard to send STATUS to the host
;KBD initial set-up by itself
;BAT(Basic Assurance TEst) code
;typematic/make/break coding

BEGIN	
	banksel	TXREG
	clrf	TXREG
	banksel	DATAreg
	clrf	DATAreg
	
; CHECK IF THE CLCOK is HIGH at least for 10mS

	banksel	PORTB
	btfss	PORTB, CLOCK
	goto	BEGIN		;if CLOCK is LOW, start again
	call	Delay10ms	;10mS delays

;check again for CLCOK
	btfss	PORTB, CLOCK
	goto	BEGIN
;READY FOR CLOCK PULSES	
;KSTAT
;KSTAT<0>: Parity Bit Value
;KSTAT<1>:Break detection
;KSTAT<2>: Kbd error
	clrf	KSTAT
KEYIN
;X reading
	call	RX11bit			;
	clrf	STATUS
	movf	DATAreg,0	;Break Code?
	xorlw	0xF0
	btfss	STATUS,ZERO
	goto	CAT
;BREAK is detected. Abort It. Resume It
	goto	BEGIN
;Category detection
CAT	clrf	STATUS
	movf	DATAreg,0
	xorlw	0xE0
	btfsc	STATUS,ZERO
	goto	Begin		;E0 keys (CAT2) are ignored
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0x12
	btfsc	STATUS,ZERO
	goto	LRSHIFT
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0x59
	btfsc	STATUS,ZERO
	goto	LRSHIFT
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0x58		;CAPS LOCK
	btfsc	STATUS,ZERO
	goto	CAPS
	
					;L Shift ===>12 | F0 12
					;R Shift ===>59 | F0 59
;CAT1 has the format of (X)|(F0)(X)
CAT1	movf	DATAreg,0
	call	NoShiftKeyTable		;(X) display
	call	TXCALL			
;(F0) detection
	call	RX11bit
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0xF0
	btfss	STATUS,ZERO
;Key is not broken. Still pressed,
	goto	CAT1
;Key is broken
;Last (X) reading
	call	RX11bit			;(X) after F0 

	goto	BEGIN

;L-SHIT and R-SHIFT has the form
;L-SHIFT and a character 12 X | F0 X |F0 12
;R-SHIFT and a character 59 X | F0 X |F0 59

LRSHIFT					;12 or 59 entered

;(F0) detection
	call	RX11bit
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0xF0
	btfsc	STATUS,ZERO
	goto	BEGIN
	
					;X	

	clrf	STATUS		;if (12) do not display
	movf	DATAreg,0
	xorlw	0x12
	btfsc	STATUS, ZERO
	goto	LRSHIFT

	clrf	STATUS		;if (59) do not display
	movf	DATAreg,0
	xorlw	0x59
	btfsc	STATUS, ZERO
	goto	LRSHIFT

				;a Key (X) is entered
	movf	DATAreg,0
	call	ShiftKeyTable
	call	TXCALL

;(F0) detection
	call	RX11bit
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0xF0
	btfss	STATUS,ZERO
	goto	LRSHIFT
;Last (X) reading
	call	RX11bit			
	movf	DATAreg,0
	clrf	STATUS			;check if (X) or (12) entered after F0
	xorlw	0x12
	btfsc	STATUS,ZERO
	goto	BEGIN
	goto	LRSHIFT			
;
CAPS					;caps lock (58) entered

;(F0) detection
	call	RX11bit			;this must be F0
	call	RX11bit			;this must be (58) again

CAPnext
	call	RX11bit			;Check if (58) or other
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0x58
	btfss	STATUS,ZERO
	goto	CAPtwo			;End of CAP session
	call	RX11bit			;F0
	call	RX11bit			;(58)
	goto	BEGIN
					;a Key (X) is entered
CAPtwo	movf	DATAreg,0
	call	CAPKeyTable
	call	TXCALL

;(F0) detection
	call	RX11bit			;this
	clrf	STATUS
	movf	DATAreg,0
	xorlw	0xF0
	btfss	STATUS,ZERO
	goto	CAPtwo


;Last (X) reading			;F0 is read
	call	RX11bit			;(X) again and ignore
	goto	CAPnext
		
	

;SUBROUTINE RX11bit =========================================================
;RX Routine for 11-bit read
;1 Start
;8 Data (LSB first)
;1 Parity (Odd)
;1 Stop (HIGH)
;KSTAT Bit Info
; KSTAT<0> : parity
; KSTAT<2>:kBD Error
RX11bit	
	clrf	DATAreg
	banksel	PORTB
;Let it have at least 500us CLOCK high period
	btfss	PORTB, CLOCK
	goto	RX11bit		;if CLOCK is LOW, start again
	call	Delay100us	;200uS delays
	call	DElay100us
;check again for CLCOK
	btfss	PORTB, CLOCK
	goto	RX11bit	
;Clock Check
Scheck	btfsc	PORTB,CLOCK
	goto	Scheck
	call	delay5us	;wait for 5us	for data stabilization
	btfsc	PORTB, KDATA
	goto	KERROR			;if START BIT is not Zero ERROR
;START Detected
;8-bit Data Check
	movlw	0x08		
	movwf	Bitcount	;8 data bits	
RXNEXT	bcf	STATUS, CARRY	;Clear the Carry Bit
	rrf	DATAreg		;rotate to the right
CKHIGH	btfss	PORTB, CLOCK	;Wait for CLOCK to back to High
	goto	CKHIGH
CKLOW	btfsc	PORTB, CLOCK	;wait for CLOCK now to LOW
	goto	CKLOW
	call	delay5us	;5us delay
	btfsc	PORTB, KDATA	;0 or 1
	bsf	DATAreg, MSB	;1? Then set the MSB
	decfsz	Bitcount
	goto	RXNEXT
;Check for Parity Bit
;Wait for CLOCK bacj to High
CKHIGH2	btfss	PORTB, CLOCK	;Wait for CLOCK to back to High
	goto	CKHIGH2
CKLOW2	btfsc	PORTB, CLOCK	;wait for CLOCK now to LOW
	goto	CKLOW2
	call	delay5us	;5us delay
	btfsc	PORTB, KDATA	;Parity Bit
	goto	OneP		;Pbit=1
	bcf	Kstat,0x00	;Pbit=0
	goto	Stopcheck
Onep	bsf	Kstat, 0x00	;Pbit=1
Stopcheck
;wait for CLOCK back to High
CKHIGH3	btfss	PORTB, CLOCK	;Wait for CLOCK to back to High
	goto	CKHIGH3
CKLOW3	btfsc	PORTB, CLOCK	;wait for CLOCK now to LOW
	goto	CKLOW3
	call	delay5us	;5us delay
	btfss	PORTB, KDATA	;STOP bit
	goto	KERROR		;if STOP=0 , ERROR
	return
KERROR
	bsf	KSTAT, 0x02
	return			
;========================================================
;RX TX Initialization with Asyc Mode
;Async_mode Subroutine
Async_mode
	banksel	SPBRG 
	movlw	baud		;B'00001111' (19200)
	movwf	SPBRG	
	banksel	TXSTA	
	movlw	TXMODE		;B'00100000' Async Mode
	movwf	TXSTA
	banksel	RCSTA
	movlw	RXMODE		;B'10010000' Enable Port
	movwf	RCSTA
	return
;======================================================================
TXCALL
;slight change so that CR make CR and LF together
	banksel	TXtemp
	movwf	TXtemp
	clrf 	STATUS
	movf	TXtemp,0
	xorlw	0x0D
	btfsc	STATUS,ZERO
	goto	CRNLF
Keymain
	banksel	PIR1
	btfss	PIR1, TXIF	; Check if TX buffer is empty
	goto	Keymain
	banksel	TXREG
	movf	Txtemp,0
	movwf	TXREG		; Place the character to TX buffer
	return
CRNLF	call	CRLF
;	call	CRLF
	return
;===
CRLF	
	banksel	PIR1
	btfss	PIR1, TXIF
	goto	CRLF
	banksel	TXREG
	movlw	H'0d'		;CR
	movwf	TXREG
LFkey
	banksel	PIR1
	btfss	PIR1, TXIF
	goto	LFkey
	banksel	TXREG
	movlw	H'0A'		;LF
	movwf	TXREG
	return
;================================================
;==========================================================
;DELAY SUBROUTINES 

delay5us
;need total 10 insturctions
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	return
;==========================================

Delay100us
	banksel	Kount100us
	movlw	H'A4'
	movwf	Kount100us
R100us	decfsz	Kount100us
	goto	R100us
	return

;
;10ms delay
; call 100 times of 100 us delay  (with some time discrepancy)
Delay10ms 
	banksel	Kount10ms
	movlw	H'64'	;100
	movwf	Kount10ms
R10ms	call	delay100us
	decfsz	Kount10ms
	goto	R10ms
	return
;
	
;1 sec delay
;call 100 times of 10ms delay
Delay1s
	banksel	Kount1s
	movlw	0x6A
	movwf	Kount1s
R1s	call	Delay10ms
	decfsz	Kount1s
	goto	R1s
	return
;

;======================================================
	org	0x0100				;So that all the table
						;range has the same bit=1
						;for the bit8 of PC
;Without Shift (or CAPs Lock) Key Table
NoshiftKeyTable

	bsf	PCLATH, 0x00
	bcf	PCLATH,0x01
	addwf	PCL	
;Note that writing to PCL also brings the content of lower 5 bits of PCLATH
;to PC.
;In this code, this Table starts from 0134h
;While the main part of program which calls this Table is somewhere in
;0045h.
;for 0045, the PCLATH part is 00
;Therefore we have to manually set the PCLATH part so that it
;can point inside this table
	retlw	0	;PC+0
	retlw	0	;PC+1
	retlw	0	;+2
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0	;+0D
	retlw	0x60	;+0E MAKE/BREAK= 0E ---->ASCII = 0x60 Apostrophe 
	retlw	0	;+0F
	retlw	0
	retlw	0
	retlw	0
	retlw	0	;+13
	retlw	0	;+14
	DT	"q1"	;+15, 16
	retlw	0	;+17
	retlw	0
	retlw	0
	DT	"zsaw2"	;+1A, 1B, 1C, 1D, 1E
	retlw	0	;+1F
	retlw	0	;+20
	DT	"cxde43"	;+21, 22, 23, 24, 25, 26
	retlw	0	;+27
	retlw	0	;+28
	retlw	' '	;+29 Space
	DT	"vftr5"	;+2A, 2B, 2C, 2D, 2E
	retlw	0	;+2F
	retlw	0	;+30
	DT	"nbhgy6"	;+31, 32, 33, 34,35,36
	retlw	0	;+37
	retlw	0	;+38
	retlw	0	;+39
	DT	"mju78"	;+3A, 3B, 3C, 3D, 3E
	retlw	0	;+3F
	retlw	0	;+40
	DT	",kio09"	;+41, 42,43,44,45,46
	retlw	0	;+47
	retlw	0	;+48
	DT	"./l;p-"	;+49, 4A, 4B, 4C, 4D, 4E
	retlw	0	;+4F
	retlw	0	;+50
	retlw	0	;+51
	retlw	0x27	;+52	single quote
	retlw	0	;+53
	DT	"[=" 	;+54, 55
	retlw	0	;+56
	retlw	0	;+57
	retlw	0	;+58
	retlw	0	;+59
	retlw	0x0D	;+5A	Return
	retlw	']'	;+5B
	retlw	0	;+5C
	retlw	0x5C	;+5D	\
	retlw	0	;+5E
	retlw	0	;+5F
	retlw	0	;+60
	retlw	0	;+61
	retlw	0	;+62
	retlw	0	;+63
	retlw	0	;+64
	retlw	0	;+65
	retlw	0x08	;+66  Backspace
	
		
;With Shift  Key Table
shiftKeyTable
	bsf	PCLATH, 0x00
	bcf	PCLATH,0x01
	addwf	PCL	
;Note that writing to PCL also brings the content of lower 5 bits of PCLATH
;to PC.
;In this code, this Table starts from 0134h
;While the main part of program which calls this Table is somewhere in
;0045h.
;for 0045, the PCLATH part is 00
;Therefore we have to manually set the PCLATH part so that it
;can point inside this table
	retlw	0	;PC+0
	retlw	0	;PC+1
	retlw	0	;+2
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0	;+0D
	retlw	0x7E	;+0E MAKE/BREAK= 0E ---->ASCII 7E (~) 
	retlw	0	;+0F
	retlw	0
	retlw	0
	retlw	0
	retlw	0	;+13
	retlw	0	;+14
	DT	"Q!"	;+15, 16
	retlw	0	;+17
	retlw	0
	retlw	0
	DT	"ZSAW@"	;+1A, 1B, 1C, 1D, 1E
	retlw	0	;+1F
	retlw	0	;+20
	DT	"CXDE$#"	;+21, 22, 23, 24, 25, 26
	retlw	0	;+27
	retlw	0	;+28
	retlw	' '	;+29 Space
	DT	"VFTR%"	;+2A, 2B, 2C, 2D, 2E
	retlw	0	;+2F
	retlw	0	;+30
	DT	"NBHGY^"	;+31, 32, 33, 34,35,36
	retlw	0	;+37
	retlw	0	;+38
	retlw	0	;+39
	DT	"MJU&*"	;+3A, 3B, 3C, 3D, 3E
	retlw	0	;+3F
	retlw	0	;+40
	DT	"<KIO)("	;+41, 42,43,44,45,46
	retlw	0	;+47
	retlw	0	;+48
	DT	">?L:P_"	;+49, 4A, 4B, 4C, 4D, 4E
	retlw	0	;+4F
	retlw	0	;+50
	retlw	0	;+51
	retlw	0x22	;+52 double quote
	retlw	0	;+53
	DT	"{+" 	;+54, 55
	retlw	0	;+56
	retlw	0	;+57
	retlw	0	;+58
	retlw	0	;+59
	retlw	0x0D	;+5A	Return
	retlw	'}'	;+5B
	retlw	0	;+5C
	retlw	0x7C	;+5D	|
	retlw	0	;+5E
	retlw	0	;+5F
	retlw	0	;+60
	retlw	0	;+61
	retlw	0	;+62
	retlw	0	;+63
	retlw	0	;+64
	retlw	0	;+65
	retlw	0x08	;+66  Backspace

;CAPs Lock Key Table
	org	0x0200
CAPKeyTable
	bsf	PCLATH, 0x01
	bcf	PCLATH, 0x00
	addwf	PCL	
;Note that writing to PCL also brings the content of lower 5 bits of PCLATH
;to PC.
;In this code, this Table starts from 0134h
;While the main part of program which calls this Table is somewhere in
;0045h.
;for 0045, the PCLATH part is 00
;Therefore we have to manually set the PCLATH part so that it
;can point inside this table
	retlw	0	;PC+0
	retlw	0	;PC+1
	retlw	0	;+2
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0
	retlw	0	;+0D
	retlw	0x60	;+0E MAKE/BREAK= 0E ---->ASCII = 0x60 Apostrophe 
	retlw	0	;+0F
	retlw	0
	retlw	0
	retlw	0
	retlw	0	;+13
	retlw	0	;+14
	DT	"Q1"	;+15, 16
	retlw	0	;+17
	retlw	0
	retlw	0
	DT	"ZSAW2"	;+1A, 1B, 1C, 1D, 1E
	retlw	0	;+1F
	retlw	0	;+20
	DT	"CXDE43"	;+21, 22, 23, 24, 25, 26
	retlw	0	;+27
	retlw	0	;+28
	retlw	' '	;+29 Space
	DT	"VFTR5"	;+2A, 2B, 2C, 2D, 2E
	retlw	0	;+2F
	retlw	0	;+30
	DT	"NBHGY6"	;+31, 32, 33, 34,35,36
	retlw	0	;+37
	retlw	0	;+38
	retlw	0	;+39
	DT	"MJU78"	;+3A, 3B, 3C, 3D, 3E
	retlw	0	;+3F
	retlw	0	;+40
	DT	",KIO09"	;+41, 42,43,44,45,46
	retlw	0	;+47
	retlw	0	;+48
	DT	"./L;P-"	;+49, 4A, 4B, 4C, 4D, 4E
	retlw	0	;+4F
	retlw	0	;+50
	retlw	0	;+51
	retlw	0x27	;+52	single quote
	retlw	0	;+53
	DT	"[=" 	;+54, 55
	retlw	0	;+56
	retlw	0	;+57
	retlw	0	;+58
	retlw	0	;+59
	retlw	0x0D	;+5A	Return
	retlw	']'	;+5B
	retlw	0	;+5C
	retlw	0x5C	;+5D	\
	retlw	0	;+5E
	retlw	0	;+5F
	retlw	0	;+60
	retlw	0	;+61
	retlw	0	;+62
	retlw	0	;+63
	retlw	0	;+64
	retlw	0	;+65
	retlw	0x08	;+66  Backspace


;END OF CODE

        END

