PIC Microcontroller based Keyboard IO

3x4 Keyboard decoder

by Chris Camerlin [camerlin at ovis.net] and Micheal Rigby Jones

Chris Camerlin says:

...a keyless entry system where you use a 4x4 keypad to unlock an electric door strike on the door to your house. I built this and it works like a charm so the decoding of the keypad section is correct. I have the data sheet of the keypad with truth table if you want that too. I used a keypad from grayhill. I got it through Jameco. This program is a little different from the original that Micheal Rigby Jones sent me, I changed it some to fit my needs. I like to give programming credit when credit is do. The program is heavily commented but if you would like more info on the project and how exactly it works let me know.

See:

;                 ***************************
;	          * 	                    *
;                 *  Keyless Entry Program  *
;	          *                         *
;                 ***************************
;
;
;DIRECTIVES

	list	p=16f84
	ifndef __16f84
	messg "processor-header file mismatch.  verify selected processor."
	endif

;REGISTER EQU
w	equ	0x0000
f	equ	0x0001

;the following equates are used for reference

;REGISTER FILES BANK 0
indf		equ	0x0000	;uses contents of FSR to address
tmr0		equ	0x0001	;8-bit realtime clock/counter
pcl		equ	0x0002	;low order 8 bits of program counter
status		equ	0x0003	;arithmetic status of ALU
fsr		equ	0x0004	;indirect data memory address pointer
porta		equ	0x0005	;
portb 		equ	0x0006	;
eedata		equ	0x0008	;eeprom data register
eeadr		equ	0x0009	;eeprom address register
pclath		equ	0x000a	;13 program counter
intcon		equ	0x000b	;RD/WR register contains enable bits Int
;-------------------------------------------------
;REGISTER FILES BANK 1

option_reg	equ	0x0081	;enables bits for TMR0
trisa		equ	0x0085	;1=input
trisb		equ	0x0086
eecon1		equ	0x0088	;control register for read writes
eecon2		equ	0x0089
;--------------------------------------------------
;STATUS BITS
;Arithmetic status of the ALU (w)

irp		equ	0x0007	;register bank select (indirect)
rp1		equ	0x0006	;register bank select (direct)
rp0		equ	0x0005	;
not_to		equ	0x0004	;time out bit
not_pd		equ	0x0003	;power down bit
z		equ	0x0002	;zero
dc		equ	0x0001	;digit carry
c		equ	0x0000	;carry
;---------------------------------------------------
;INTCON BITS
;Enable bits for all interupt sources
gie		equ	0x0007	;global interrupt enable bit
eeie		equ	0x0006	;ee write complete inter enable
t0ie		equ	0x0005	;TMR0 overflow inter enable
inte		equ	0x0004	;RB0/INT Enable bit
rbie		equ	0x0003	;RB port change
t0if		equ	0x0002
intf		equ	0x0001
rbif		equ	0x0000
;---------------------------------------------------
;OPTION BITS
;Configures TMRO/WDT/esternal INT/weak pull-ups on PortB
not_rbpu	equ	0x0007	;PortB pull up enable bit   (1-disable)
intedg		equ	0x0006	;interrupt edge select bit
t0cs		equ	0x0005	;TMR0 clock source select bit
t0se		equ	0x0004	;TMR0 source edge
psa		equ	0x0003	;prescalars
ps2		equ	0x0002
ps1		equ	0x0001
ps0		equ	0x0000
;---------------------------------------------------
;EECON1 BITS
;Control Register
eeif		equ	0x0004	;interrupt flag bit
wrerr		equ	0x0003	;a write operation is interrupted
wren		equ	0x0002	;allow a write operation
wr		equ	0x0001	;initiate write
rd		equ	0x0000	;initiate read
				;upper 3 bits are defaulted to 000
;---------------------------------------------------
;RAM
	__maxram 0xaf
	__badram 0x07, 0x50-0x7f, 0x87
;---------------------------------------------------
;CONFIGURATION BITS
;_cp = code protection bits
;_pwrite = power up timer enable bit
;_wdt = watch dog timer
;_lp,_xt,_hs,_rc = oscillator modes
_cp_on		equ	0x3fef	;11111111101111	;code protection bits
_cp_off		equ	0x3fff	;11111111111111
_pwrte_on	equ	0x3fff	;11111111111111	;power up timer enable
_pwrte_off	equ	0x3ff7	;11111111110111
_wdt_on		equ	0x3fff	;11111111111111	;watch dog timer
_wdt_off	equ	0x3ffb	;11111111111011
_lp_osc		equ	0x3ffc	;11111111111100	;RC=11
_xt_osc		equ	0x3ffd	;11111111111101	;HS=10
_hs_osc		equ	0x3ffe	;11111111111110	;XT=01
_rc_osc		equ	0x3fff	;11111111111111	;LP=00
;setting the congiguration bits
	__config	_xt_osc & _wdt_off & _pwrte_on  & _cp_off

;---------------------------------------------------------
xtal_freq	=	d'4000000'	;crystal frequency
clock		=	xtal_freq/4	;base operating freq.

;PORTA ASSIGNMENTS

n_red		equ	0	;low on ra0 turns red led on
n_green		equ	1	;low on ra1 turns green led on
relay		equ	2	;high on ra2 turns relay on
ra3		equ	3	;not used
ra4		equ	4	;not used

;PIN OUTS FOR KEYPAD
;
;  	       <5>     <6>     <7>     <8>
;	<1>	1	2	3	A
;	<2>	4	5	6	B
;	<3>	7	8	9	C
;	<4>	*	0	#	D

;decoding of keypad (truth table from Grayhill)


xload	=	b'11100111'	;* - LOAD
yload	=	'l'
x0	=	b'11010111'	;0
y0	=	'0'
xclr	=	b'10110111'	;# - CLEAR
yclr	=	'c'
xent	=	b'01110111'	;D - ENTER
yent	=	'e'
x1	=	b'11101110'	;1
y1	=	'1'
x2	=	b'11011110'	;2
y2	=	'2'
x3	=	b'10111110'	;3
y3	=	'3'
xrun	=	b'01111011'	;C - RUN
yrun	=	'r'
x4	=	b'11101101'	;4
y4	=	'4'
x5	=	b'11011101'	;5
y5	=	'5'
x6	=	b'10111101'	;6
y6	=	'6'
xdown	=	b'01111101'	;B
ydown	=	'd'
x7	=	b'11101011'	;7
y7	=	'7'
x8	=	b'11011011'	;8
y8	=	'8'
x9	=	b'10111011'	;9
y9	=	'9'
xup	=	b'01111110'	;A
yup	=	'u'
xx	=	b'00000000'	;allows to test z

;eeprom data
;area is 64x8 bytes
	org	0x2100

usercode0	de  xx,  xx,  xx,  xx,  xx,  xx,  xx,  xx	
usercode1	de  xx,  xx,  xx,  xx,  xx,  xx,  xx,  xx
usercode2 	de  xx,  xx,  xx,  xx,  xx,  xx,  xx,  xx
usercode3	de  xx,  xx,  xx,  xx,  xx,  xx,  xx,  xx
usercode4	de  xx,  xx,  xx,  xx,  xx,  xx,  xx,  xx
usercode5	de  xx,  xx,  xx,  xx,  xx,  xx,  xx,  xx
umastercode	de  y1,  y2,  y3,  y4,  y5,  y6,  y7,  y8
mastercode	de  y5,	 y5,  y5,  y5,  y5,  y5,  y5,  y5	

;usercode can be changed which is represented by the xx
;mastercode can not be changed (55555555)

; **************************************************************
; * file register ram starts at 0ch and ends at 4fh (68 bytes) *
; **************************************************************

	cblock	h'0c'
	
keycode		;stores uncoded or decoded keycode
savew		;saves w for the interupt handler
savestatus	;saves statis register for the int. handler
savefsr		;saves the interrupt pointer
eeadr1		;storage for eeadr1
xmillisec	;first loop counter for waits in msec
ymillisec	;second loop counter for waits in msec
keycounter1	;first loop counter
keycounter2	;second loop counter
ok		;non-zero means ok
blinks		;indicates number of blinks to perform
exitkey		;holds value of key used to terminate entry
usercounter1	;user key entry
user0		;user0-7 holds the code entered by the keypad
user1		;for usercode and mastercode
user2
user3
user4
user5
user6
user7
onetime		;one time use code

	endc

	org	h'0000'	;sets origin in rom
	goto	setup	;gets around the interrupt vector
	org	h'0004'	;sets the interrupt vector



; **************************************************************
; *                Interupt Service Routine                    *
; **************************************************************

inthandler
	movwf	savew		;saves w register
	swapf	status,w	;twist status so its not effected
	movwf	savestatus	;save status register
	movf	fsr,w		;saves the interrupt pointer
	movwf	savefsr		;saves the fsr now in savefsr
;the following loops are used to waste 25msecs for debounce
;doing 65000 instructions in the loops will waste 25msec of time
;65000/400nsec = 26msec

	clrf	keycode		;clears keycode
	clrf	keycounter1	;initialize secound counter
loop256x256
	decfsz	keycounter1,f	;256 loops
				;since keycounter1=h'00' it will
				;decrement to h'FF'
	goto	more256x256
	goto	nogood

more256x256
	clrf	keycounter2	;initialize first counter
loopinner256
	decfsz	keycounter2,f	;256fist loops per second loop
	goto	innertest
	goto	loop256x256
innertest
	comf	portb,w		;compliments portb
	andlw	b'11110000'	;tests for any key hit
	btfsc	status,z	;tests z flag/skips next if 0
	goto	loopinner256	;try it again
	;if we are here their was probably a key hit
scankey0
	movlw	b'11111110'	;loads mask
	movwf	portb		;set outputs and inputs
	nop			;waste time to settle
	movf	portb,w		;read current value into keycode
	movwf	keycode		;through the w register
	comf	keycode,w	;reverse bits to match truthtab
	andlw	b'11110000'	;look at the high bites
	btfss	status,z	;no hit sets z flag
	goto	decode
scankey1
	movlw	b'11111101'	;loads mask
	movwf	portb		;set outputs and inputs
	nop			;waste time to settle
	movf	portb,w		;read current value into keycode
	movwf	keycode		;through the w register
	comf	keycode,w	;reverse bits to match truthtab
	andlw	b'11110000'	;look at the high bites
	btfss	status,z	;no hit sets z flag
	goto	decode
scankey2
	movlw	b'11111011'	;loads mask
	movwf	portb		;set outputs and inputs
	nop			;waste time to settle
	movf	portb,w		;read current value into keycode
	movwf	keycode		;through the w register
	comf	keycode,w	;reverse bits to match truthtab
	andlw	b'11110000'	;look at the high bites
	btfss	status,z	;no hit sets z flag
	goto	decode
scankey3
	movlw	b'11110111'	;loads mask
	movwf	portb		;set outputs and inputs
	nop			;waste time to settle
	movf	portb,w		;read current value into keycode
	movwf	keycode		;through the w register
	comf	keycode,w	;reverse bits to match truthtab
	andlw	b'11110000'	;look at the high bites
	btfss	status,z	;no hit sets z flag
	goto	decode
	goto	loopinner256	;no keys were hit yet
	clrf	keycode
nogood
	clrf	keycode		;reset keycode
decode
	movf	keycode,w
	btfsc	status,z
	goto	fillercode
decode0
	movf	keycode,w	;reloads keycode
	sublw	x0		;subs w from x0
	btfss	status,z	;
	goto	decode1		;checks next code
	movlw	y0		;loads the ascii 
	goto	savecode	;saves the ascii code
decode1
	movf	keycode,w	
	sublw	x1
	btfss	status,z
	goto	decode2
	movlw	y1
	goto	savecode
decode2
	movf	keycode,w
	sublw	x2
	btfss	status,z
	goto	decode3
	movlw	y2
	goto	savecode
decode3
	movf	keycode,w
	sublw	x3
	btfss	status,z
	goto 	decode4
	movlw	y3
	goto	savecode
decode4
	movf	keycode,w
	sublw	x4
	btfss	status,z
	goto	decode5
	movlw	y4
	goto	savecode
decode5
	movf	keycode,w
	sublw	x5
	btfss	status,z
	goto	decode6
	movlw	y5
	goto	savecode
decode6
	movf	keycode,w
	sublw	x6
	btfss	status,z
	goto	decode7
	movlw	y6
	goto	savecode
decode7
	movf	keycode,w
	sublw	x7
	btfss	status,z
	goto	decode8
	movlw	y7
	goto	savecode
decode8
	movf	keycode,w
	sublw	x8
	btfss	status,z
	goto	decode9
	movlw	y8
	goto	savecode
decode9
	movf	keycode,w
	sublw	x9
	btfss	status,z
	goto	decodeload
	movlw	y9
	goto	savecode
decodeload
	movf	keycode,w
	sublw	xload
	btfss	status,z
	goto	decodeclr
	movlw	yload
	goto	savecode
decodeclr
	movf	keycode,w
	sublw	xclr
	btfss	status,z
	goto	decodeent
	movlw	yclr
	goto	savecode
decodeent
	movf	keycode,w
	sublw	xent
	btfss	status,z
	goto	decoderun
	movlw	yent
	goto	savecode
decoderun
	movf	keycode,w
	sublw	xrun
	btfss	status,z
	goto	decodedown
	movlw	yrun
	goto	savecode
decodedown
	movf	keycode,w
	sublw	xdown
	btfss	status,z
	goto	decodeup
	movlw	ydown
	goto	savecode
decodeup
	movf	keycode,w
	sublw	xup
	btfss	status,z
	goto	fillercode
	movlw	yup
	goto	savecode
fillercode
	clrf	keycode		;set up filler code
	goto	intreturn	;skips delay
savecode
	movwf	keycode		;store keycode
	bsf	porta,n_red	;turn red off
	movlw	b'11110000'
	movwf	portb
	nop
release
	comf	portb,w		;wait for key to be released
	andlw	b'11110000'	;only check upper 4 bits
	btfss	status,z
	goto	release		;if key is down...wait
	movlw	d'25'		;set debounce for 25msec
	call	wmillisec
	comf	portb,w
	andlw	b'11110000'
	btfss	status,z
	goto	release		;if key is still down
	movlw	d'25'
	call 	wmillisec
intreturn
	movlw	b'11110000'
	movwf	portb
	bcf	porta,n_red	;turn red on after keypress
	bcf	intcon,rbif	;clear interrupt flag
				;1 when at least 1 of 7-4 has
				; changed
				;0- nothing has changed
	;restoring everything saved at beginning
	movf	savefsr,w
	movwf	fsr
	swapf	savestatus,w
	movwf	status
	swapf	savew,f
	swapf	savew,w
	retfie		;return from interupt
	;keycode now contains valid keycode
	;anything else gives a 00000000 in keycode
	
;===============================================================
;THE SETUP SECTION IS USED TO CONFIGURE THE REGISTERS AND PORTS
;===============================================================
setup
;PORTA and PORTB
	bsf	status,rp0		;sets rp0 in status register to 1
	movlw	b'00000000'		;laods w with 00h to set porta I/O direction
					;w is cleared so trisa reg. can be used to configure
	movwf	trisa			;moves a copy of w into tris
					;0=output, 1=input, tris is setup for all outputs
					;that completes configuration for porta
	movlw	b'11110000'		;moves copy of w into tris register
	movwf 	trisb			;0Fh in the trisb register sets RB7-RB4 as outputs
					;and RB3-0 as inputs since they are used for the keypad
					;that completes configuration for portb
;OPTION
	bcf	option_reg,not_rbpu	;the not rbpu is cleared so that internal 
					;pull-ups are enabled
	bcf	option_reg,intedg	;clears bit6 of option to make interupt on
					;on falling edge
	bcf	option_reg,t0cs		;enables internal clkout
	bcf	option_reg,t0se		;inc on low to high transistion
	bcf 	option_reg,psa		;enables the watch dog timer
	bcf 	option_reg,ps2		;a 000 on these last 3 bits sets the rate at
	bcf	option_reg,ps1		;a 1:1 ratio
	bcf	option_reg,ps0
;STATUS
	bcf	status,rp0		;a 0 at rp0 selects bank 0 
;INTCON (interupt configuration)
	clrf	tmr0			;resets tmr0 since I am not using it
	clrf	intcon			;clears any interupts that may be present
	bsf	intcon,gie		;sets the GIE bit so all interupts are enabled
	bsf	intcon,rbie		;enables RB port change on pins 7,6,5,4
	clrf	keycode			;clears keycode so that no code currently exists
	bcf	porta,relay		;relay is equ. so in turns the relay off
;END OF SETUP FOR I/O



;**************************************************************************
;                           MAIN PROGRAM                                  *
;**************************************************************************
;enter notes on how program works here 

mainprog
	movlw	b'11110000'	;scans all 4 lines from keypad at once
	movwf	portb		;initializes keypad scanning
	bcf	porta,relay	;relay was equ to 02h, a high on ra2 turns the relay on
				;this instruction clrs the relay bit so relay is off
	bcf	porta,n_red	;n_red was equ to 0h (ra0)
				;low on ra0 turns red LED on
				;red is usually on unless a key is depressed
				;the interupt will turn it back off
	bsf	porta,n_green	;n_green was equated to 01h (ra1)
				; low on ra1 turns on green light
				;bsf sets this bit high so green light is off
	movf	keycode,w	;checks to see if CLR (*) button was hit
	sublw	yclr		;subtracts w from '10110111', result in w
	btfsc	status,z	;tests the z flag in STATUS register if it is '0' 
				;it skips next instruction
				;1=result of arithmetic is zero,0=result in not zero
				;if ystr was hit z flag will be a '1'
	goto	gotyclr		;if ystr was hit then i have to handle that separetely
	bsf	porta,n_green	;turn green off
	clrf	keycode
mainloop
	movf	keycode,w	;check to see if keycode is there
	btfsc	status,z	;check the z flag
	goto	mainloop	;loop until there is a code in keycode
				;this was handled by the interrupt
	sublw	yclr		;check for ystr
	btfsc	status,z	;
	goto	gotyclr
	goto	mainloop

;-----------------------------------------------------------------------------------

gotyclr
	bcf	porta,n_green	;turns on green led to indicate that ystr was hit
	movlw	d'20'		;d'20' is used as a counter for the msec wait
	
	call	wmillisec	;sub to create the 1msec on time for the green led
	
	bsf	porta,n_green	;turns green led off
	clrf	keycode		;clears keycode and begins to wait for next code
gotyclr1
	movf	keycode,w	;moves contents of keycode into w so that it can be checked
	btfsc	status,z	;skips next inst. if the z flag is zero	
	goto 	gotyclr1	;loops while empty
	movf	keycode,w	;the next 4 lines checks for ystr again. if it was we go back 
	sublw	yclr
	btfsc	status,z	;to gotystr to start over. if it is not another ystr then it
	goto	gotyclr		;skips the goto gotystr inst. and calls 'getuser'
	
	call	getuser		;gets a set of inputs
	
	movf	exitkey,w	;check for exitmode
	sublw	0		;was it an error
	btfsc	status,z
	goto	mainprog	;if it was an error then start over
	movf	exitkey,w
	sublw	yent		;checks for yd
	btfsc	status,z	
	goto	wasent		;if it was yd we handle it here
	movf	exitkey,w
	sublw	yrun		;check for yc
	btfsc	status,z
	goto	wasrun
	goto	mainprog	;anything else was an error


wasent				;compare with code
	movlw	0*8		;usercode check
	movwf	eeadr
	call	compare8
	movf	ok,w		;if ok is not zero then we have a match
	btfss	status,z
	goto	entok
	
	movlw	1*8		;mastercode check
	movwf	eeadr	
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	entok

	movlw	2*8
	movwf	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	entok

	movlw	3*8
	movwf	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	entok

	movlw	4*8
	movwf	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	entok

	movlw	5*8
	movwf	eeadr
	call 	compare8
	movf	ok,w
	btfss	status,z
	goto	entok1

	movlw	6*8
	movwf	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	entok

	movlw	7*8
	movwf	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	entok
	goto	mainprog	;if no sets were matched then it is wrong code
				;if we are here then code was correct
nogood2
	clrf	onetime
	decf	onetime,1
	goto	mainprog
entok1
	incfsz	onetime,1		;if already used goes back to mainprog
	goto	entok
	goto	nogood2
	
entok
	decf	onetime,1
	decf	onetime,1
	bsf	porta,relay	;turn relay on
	movlw	d'100'		;for 100*100 msec = 1 sec
	movwf	usercounter1
	clrf	keycode		;clear keycode to allow new key
ldelay
	movf	keycode,w	;any key hit will terminate
	btfss	status,z
	goto	mainprog
	bcf	porta,n_red	;turn red on
	bsf	porta,n_green	;turn green led off
	movlw	d'50'		
	call 	wmillisec	;delay 50msec
	bsf 	porta,n_red	;turn red off
	bcf	porta,n_green	;turn green on
	movlw	d'50'
	call	wmillisec	;delay 50msec
	decfsz	usercounter1,f	;update counter
	goto	ldelay
	goto	mainprog

wasrun				;compare user set with mastercode
				;if ok, then allow new entry set
				;turn on green led
				;1st key is pointer, next 8 max
				;get entered at pointer in eeprom
				;then goto mainprog
	movlw	6*8		;compares with mastercode
	movwf 	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	runok
	
	movlw	7*8
	movwf	eeadr
	call	compare8
	movf	ok,w
	btfss	status,z
	goto	runok
	goto	mainprog
runok
	clrf	keycode		;clears any old key data
	bcf	porta,n_green	;turn green led on

runloop1
	bcf	porta,n_red	;turn red on
	movlw	d'35'
	call	wmillisec	;delay for 35msec
	bsf	porta,n_red	;red off
	movlw	d'35'
	call	wmillisec	;delay for 35msec
	movf	keycode,w	;interrupt will load it
	btfsc	status,z
	goto	runloop1	;wait for key
	bcf	porta,n_red	;once key is found turn red on
	bcf	porta,n_green	;turn green on
	sublw	yclr
	btfsc	status,z
	goto	mainprog	;handle ystr
	movf	keycode,w	;look at code again
	sublw 	'6'		;allow only stuff less than 6
	btfss	status,c	;if w<6 c is set, if w>6 c is cleared
	goto 	mainprog
	movf	keycode,w	;look at code once again
	andlw	b'00000111'	;convert from ascii to binary
	movwf	eeadr1		;save it
	decf	eeadr1,f	;reduce address by 1
	btfss	eeadr1,7	;0-1 would give us 11111111
	goto	rollem		;if msb=0 handle it
	movlw	b'00000110'	;convert 0 to 6
	movwf	eeadr1		;so 

rollem
	bcf	status,c	;initial carry in must be 0
	rlf	eeadr1,f	;*2
	rlf	eeadr1,f	;*4
	rlf	eeadr1,f	;*8
	;we now have proper ee address set in eeadr1

eeadrok
	clrf	keycode		;get ready for next key
	
loadloop	
	movf	keycode,w	;interrupt will load it
	btfsc	status,z	;
	goto	loadloop	;wait for key
	sublw	yload		;must be the 'load' key	
	btfss	status,z	
	goto	mainprog	;if not loose it
	clrf	keycode
	call	getuser		;get user inputs
	movf	exitkey,w	;check for exitmode
	sublw	yent		;check for ent
	btfss	status,z	;
	goto 	mainprog	;on error or ystr start all over

ent2stuff
	movlw	d'8'		;eight digits to be moved
	movwf	usercounter1	;used as a counter
	movf	eeadr1,w	;recover starting address
	movwf	eeadr		;set up ee address
	movlw	user0		;point to user set in ram
	movwf	fsr		;make it indirect pointer

nextent2
	movf	indf,w		;use indirect pointer
	movwf	eedata		;copy ram to eedata
	call	writeeedata	;use sub to do it
	decfsz	usercounter1,f	;update loop
	goto	next1ent2	;
	goto	mainprog	;when done start over
next1ent2
	incf	eeadr,f		;update ee address
	incf	fsr,f		;update indirect pointer
	goto	nextent2	;do all 8 digits

;-----------------------------------------------------------------------------------

getuser
	clrf	exitkey		;get ready for next key
	movf	keycode,w	;checks for ystr again
	sublw	yclr	
	btfss	status,z	
	goto	get8keys	;if it wasn't ystr then go to get8keys
				;if it was then go to handleystr
handleyclr
	movlw	h'00'
	movwf	exitkey		;exitkey still has the ystr so it can be used here
	goto	checkret	;handle ystr special

get8keys
	clrf	user0		;clears the key holders
	clrf	user1
	clrf	user2
	clrf	user3
	clrf	user4
	clrf	user5
	clrf	user6
	clrf	user7		;clears master key holder
	movlw	user0
	movwf	fsr		;sets interupt vector so (allows indirect addressing)
	clrf	usercounter1	;we handle 8 keys, 1-8

get8wait
	movf	keycode,w
	btfsc	status,z
	goto 	get8wait	;we loop until we find the key and then we check for ystr
	sublw	yclr
	btfsc	status,z
	goto	handleyclr	;ystr will synchronize everything
	movf	keycode,w	;put the current key input into w
	sublw	yent		;yd means the end of the entry
	btfsc	status,z	
	goto    checkent		;if it was yd we'll handle it at checkent
	movf	keycode,w	;check for yc
	sublw	yrun
	btfsc	status,z
	goto	checkrun	;if yc, handle it at checkrun
	;if we get this far we know that the key is not a special purpose key (CLR,ENT,RUN)
	movf	keycode,w
	movwf	indf		;indirect keycode to user elecments
	clrf	keycode		;resets keycode to allow for next key entry
	incf	fsr,f		;increments the interupt pointer (fsr)
	incf	usercounter1,f	;update counter
	movf	usercounter1,w	;moves the incremented usercounter into w
	sublw	9		;9 is subtracted since their is only a max of 8 digits
	btfss	status,z	;tests the z flag for a 1, if it is it skips next inst.
	goto	get8wait	;if not 8 yet get next entry
				;if the z flag was set then the ninth key was not a special
				;purpose key so it was an error
	goto	checkret	;return with exitkey=0 to indicate error
;-------------------------------------------------------------------------

checkent
	movlw	yent
	movwf	exitkey
	goto	checkret	;returns with ystr in exitkey

checkrun
	movlw	yrun
	movwf	exitkey

checkret
	return			;return with yc in exitkey

compare8
	clrf	ok		;assume not ok at first
	movf	user0,w		;look at first key entry
	btfsc	status,z	;
	goto	compareret	;return with error set if 0 set				
				;this prevents a null set from being ok
	movlw	1		;set ok for now
	movwf	ok		;
	movlw	8
	movwf	usercounter1	;initialize counter to 8
	movlw	user0	
	movwf	fsr		;allow indirect addressing

compareloop
	movf	indf,w		;recover user data
	bsf	status,rp0	;bank select to 1 so eecon register can be asseccesed
	bsf	eecon1,rd	;set up read, initiates eeprom read
	bcf	status,rp0	;back to bank 0
	subwf	eedata,w	;compare with eeprom data
	btfss	status,z	;
	goto 	compareerror	;if different then error
	incf	eeadr,f		;addresses up to 256 bytes of eeprom data
				;direct addressing
	incf	fsr,f		;indirect addressing
	decfsz	usercounter1,f	;update counter
	goto	compareloop	;if not all 8 done do more
	goto	compareret	;if all 8 matched, all done

compareerror
	clrf	ok

compareret
	return
;-------------------------------------------------------------------------

;EEPROM 
;eecon1 register in bank 1 will be used
;eeadr is used to hold the address of the desired data
;eecon1 and eecon2 are eeprom_control registers to make sure that there
;  can never be an accidental write to the eeprom
;to read:  load eeadr with the desired address
;clear eecon1 and the rd bit to 1, read eedata.

writeeedata
	bsf	status,rp0	;selecting bank 1
	bcf	eecon1,eeif	;clear eeif(eeprom write operation interupt flag)
	bsf	eecon1,wren	;set wren (eeprom wirte enable bit)
	bcf	intcon,gie	;disable all interrupts	
	movlw	h'55'
	movwf	eecon2
	movlw	h'aa'
	movwf	eecon2
	bsf	eecon1,wr

eewait
	btfss	eecon1,eeif	;tests the eeif reg. for a 1
	goto	eewait		; if a 0 we loop
	bcf	eecon1,eeif	;clears eeif
	bcf	eecon1,wren	;disables writes
	bsf	intcon,gie	;enable gie interrupts
	bcf	status,rp0	;select bank 0
	return
;-------------------------------------------------------------------------

;wblinks is a subroutine that will let us enter the number of blinks for green
wblinks
	movwf	blinks		;save no. of blinks
	incf	blinks,f	;addjust for initial decrement

wblinksloop
	decfsz	blinks,f
	goto	blinkit		;on or off once
	return

blinkit
	bcf	porta,n_green	;on
	call	xmillisecs	;delay for light on time
	bsf	porta,n_green	;off
	call	xmillisecs	;delay again for off time
	goto	wblinksloop	;do more blinks

;-------------------------------------------------------------------------
;the following loops are used to waste some time for led on time (1msec)
xmillisecs
	movlw	d'200'		;delay
wmillisec
	movwf	xmillisec	;saves the no. of msec in w to delay the led 
	incf	xmillisec,f	;adjust to account for initial decrement
wmloop1				;first outer loop for the wait
	decfsz	xmillisec,f	;decrements xmillisec, if it is a 0 it skips next instruction
				;so if there is something in xmill.. we will loop more
	goto	wmloopa
	return			;if it is all done then return to gotystr
wmloopa	
	clrf	ymillisec	;second loop
wmloopb
	decfsz	ymillisec,f	;decrements ymillisec if zero skip next step
	goto	wmloopb		;takes 3usec per loop
	movlw	d'75'+1		
	movwf	ymillisec	;75 loops for third loop
wmloopc
	decfsz	ymillisec,f	;update inner loop
	goto	wmloopc		;another 3usec per loop
	goto 	wmloop1		;continue with last outer loop
				;total time for the loop is 3*(256+75) = 993usec = 1msec
;-----------------------------------------------------------------------------------

;**************************************************************
;*                    END OF PROGRAM                          *
;**************************************************************

Questions: