;----------------------------------------------------------------------;
; COMBO4M.ASM              A combination lock  4 MHz                   ;
;----------------------------------------------------------------------;

; Hold both buttons down when power applied to enter code set routine 
; A total of 8 button presses makes up the combination. If the correct   
; combination is entered, all four LEDs will flash 6 times else once.

;                            .-----------.
;                           -|RA2     RA1|-       V+ = 4.5 or 5V
;                           -|RA3     RA0|-       X = 4 MHz cer. res.
;                           -|RA4    OSC1|--|X|___ gnd
;   -{r}- = 470 ohm    V+ ---|MCLR   OSC2|--|X|  
;   -|<- = LED        gnd ---|Vss     Vdd|--- V+
;            gnd ---|<--{r}--|RB0     RB7|-         
;            gnd ---|<--{r}--|RB1     RB6|-         
;            gnd ---|<--{r}--|RB2     RB5|---[PB]--- gnd       
;            gnd ---|<--{r}--|RB3     RB4|---[PB]--- gnd        
;                            '-----------'         
;                               PIC16F84      -[PB]- pushbutton

        LIST P=16F84           ;  tells which processor is used
        INCLUDE "p16f84.inc"   ;  defines various registers etc. Look
        ERRORLEVEL -224        ;  supress annoying message from tris
        ERRORLEVEL -302        ;    "       "       "       page change
        __CONFIG _PWRTE_ON & _XT_OSC & _WDT_OFF   ;  config.switches

    #define PB1 PORTB, 4       ; Pushbutton #1, generates a '0' bit 
    #define PB2 PORTB, 5       ; Pushbutton #2, generates a '1' bit 
    #define LED1 PORTB, 3      ; LED #1
    #define LED2 PORTB, 0      ; LED #2

; register usage:
           CBLOCK H'C'
               combo            ; holds combination
               count            ; general count register
               codevalue        ; holds code value
               cntmsec          ; used to count milliseconds
           ENDC

           ORG 0              ; start a program memory location zero

           goto main          ; jump over the interrupt routine

;----------------------------------------------------------------------;
;                        Initialize the ports etc.                     ;
;----------------------------------------------------------------------;
init:    movlw B'00000000'    ; all bits low in W
         tris PORTA           ; contents of W copied to PORT A ...
         movlw B'00110000'    ; Rb4,RB5 inputs, all other output
         tris PORTB           ; into PORT B
         clrf PORTB           ; LEDs blanked
         movlw 0
         option               ; Port B pullups enabled
         return
;----------------------------------------------------------------------;
;          Read a byte from the data EEPROM at address given in W      ;
;----------------------------------------------------------------------;
readee      movwf EEADR                ; set up eeprom address from W
            bsf STATUS,RP0             ; change to page 1
            bsf EECON1,RD              ; set the read bit
            bcf STATUS,RP0             ; back to page 0
            movf EEDATA,W              ; return value in W
            return                     

;----------------------------------------------------------------------;
;              This routine writes a byte to data EEPROM               ;
;                set up EEADR and EEDATA before entry                  ;
;----------------------------------------------------------------------;
writee:     bsf STATUS,RP0             ; enter page 1
            clrf EECON1                
            bsf EECON1,WREN            ; enable write
            movlw H'55'                ; magic sequence
            movwf EECON2               
            movlw H'AA'                  
            movwf EECON2               
            bsf EECON1,WR              
eeloop:     btfsc EECON1,WR            ; wait for WR to go low
            goto eeloop                ; not yet
            bsf EECON1,WREN            
            bcf EECON1,EEIF            ; clear the interrupt flag
            bcf STATUS,RP0             ; return to page 0
            movlw D'10'                ; delay to 'burn' in
            call nmsec
            return

;----------------------------------------------------------------------;
;                              Enter Code                              ;
;----------------------------------------------------------------------;
entercode:
            movlw 1                 ; flash once
            call flashleds
            movlw 8                 ; set up to count 8 presses
            movwf count
waitboth:
            btfss PB1               ; wait until pushbutton 1 up
            goto waitboth
            btfss PB2               ; and pushbutton 2 up
            goto waitboth
            call msec20             ; bypass any bouncing
            bcf LED1
            bcf LED2
eitherbutton:                       ; wait until either button pressed
            comf PORTB, W           ; compliment of Port B in W
            andlw B'00110000'       ; look at buttons, ( 0's if up)
            btfsc STATUS, Z         ; skip if either pressed
            goto eitherbutton       ; try again
            call msec20             ; debounce press (not in 32 kHz ver)
            bcf STATUS, C           ; clear carry
            btfss PB1               ; if one pressed leave carry clear
            goto PB1pressed
            bsf STATUS, C           ; else set carry
            goto $ + 3              ; skip over
PB1pressed  bsf LED1                ; turn LED1 on
            goto $ + 2              ; skip over
            bsf LED2                ; turn LED2 on
            rrf combo, f            ; roll result into combo high bit
            decfsz count, f         ; more presses necessary
            goto waitboth           ; next press
            movf combo, W           ; transfer result to W
            return

;----------------------------------------------------------------------;
;                        time delay routines                           ;
;----------------------------------------------------------------------;

micro4      addlw H'FF'                ;  subtract 1 from 'W'
            btfss STATUS,Z             ;  skip when you reach zero
            goto micro4                ;  more loops
            return                     

onesecond:                         ; a subroutine that delays for 1 sec
            call msec250
            call msec250
            call msec250
            call msec250
            return

msec20      movlw D'20'                ; 20 msec delay entry point
            goto nmsec
msec250:                               ; a subroutine to delay 250 msec
            movlw D'250'          
                ;*** N millisecond delay routine ***
nmsec:      movwf cntmsec              ;  delay for N (in W) millisec
msecloop:   movlw D'248'               ;  1 usec for load
            call micro4                ;  this instruction is 995 usec
            nop                        ;  1 usec 
            decfsz cntmsec,f           ;  1 usec, (2 if skip taken)
            goto msecloop              ;  2 usec, loop = 995+5 = 1 msec
            return 

;----------------------------------------------------------------------;
;            Flash the LEDs, number of flashes in W on entry           ;
;----------------------------------------------------------------------;
flashleds:
         movwf count
flashloop:
         movlw H'F'
         movwf PORTB 
         call onesecond + 2     ; 1/2 sec on
         movlw 0
         movwf PORTB
         call onesecond + 2     ; 1/2 sec off
         decfsz count, f
         goto flashloop
         return

;----------------------------------------------------------------------;
;          Get combo value and place it in EEPROM location 0           ;
;----------------------------------------------------------------------;
setcombo:
         call entercode          ; get desired press combination
         movwf EEDATA            ; set up data and address
         movlw 0
         movwf EEADR
         call writee             ; save combo in EEPROM
         movlw 2                 ; flash leds 2 times
         call flashleds          ; (one additional when entering main)
         return

;----------------------------------------------------------------------;
;                          The Main routine                            ;
;----------------------------------------------------------------------;
main:
         call init               ; initialize registers etc.
         movf PORTB, W           ; check both buttons pressed on pwr-up
         andlw B'00110000'       ; look at buttons, (0's if down)
         btfsc STATUS, Z         ; skip if both not presssed
         call setcombo           ; if both press set the combination  
         movlw 0                 ; get combo from EEPROM address 0
         call readee
         movwf codevalue         ; save it 
mainloop:
         call entercode
         subwf codevalue, W      ; is it same as code?
         btfss STATUS, Z         ; zero set if yes
         goto mainloop
         movlw 5                 ; sucess, unlocking procedure would ...
         call flashleds          ; be placed here.
         goto mainloop


         org 2100                ; this is location of EEPROM data
         dw H'33'                ; the initial value of location 0 ...
                                 ; is set to B'00110011'

         end                     ; end of program