;-----------------------------------------------------------------------;
; BINCLOCK.ASM          A clock that displays in bcd numbers            ;
;-----------------------------------------------------------------------;

        LIST P=16F87           ;  tells which processor is used
        INCLUDE "p16f87.inc"   ;  defines various registers etc. Look it over.
        ERRORLEVEL -224        ;  supress annoying message because of tris
        
		;Program Configuration Register 1
		__CONFIG    _CONFIG1, _CP_OFF & _CCP1_RB0 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_ON & _BODEN_ON & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _LP_OSC

		;Program Configuration Register 2
		__CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

		;__CONFIG _WDT_OFF & _PWRTE_ON & _XT_OSC   ;  configuration switches EREDETI CONFIG

            CBLOCK     0CH
                sec             ; seconds digit
                sec10           ; 10's of second digit
                mins            ; minutes digit
                min10           ; 10's of minutes digit
                hr              ; hours digit
                hr10            ; 10's of hours digit
                w_temp          ; holds W during interrupt
                status_temp     ; holds STATUS during interrupt
                fsr_temp        ; holds FSR during interrupt
                button          ; holds mask for pushbuttons
              ENDC
          
;-----------------------------------------------------------------------;
;  Here are some DEFINEs which give 'names' to pushbutton port bits     ;
;-----------------------------------------------------------------------;
            #DEFINE SETPB PORTB, 4
            #DEFINE SELECTPB PORTB, 5
            #DEFINE SHOWPB PORTB, 6

            ORG 0               ; start at location 0

            goto main           ; jump over to main routine       

            ORG 4
             
            goto isr            ; jump to interrupt routine

;-----------------------------------------------------------------------;
;                  High limit + 1 of digits at position W               ;
;-----------------------------------------------------------------------;
sethi:
            addwf PCL, f
         dt H'A',H'6',H'A',H'6',H'A',H'3'


;-----------------------------------------------------------------------;
;                           Delay routines                              ;
;-----------------------------------------------------------------------;
msec250:                       ; enter here to delay for 250 milliseconds
         movlw D'250'          
nmsec:                         ; delay for # msec in W on entry
         nop                   ; each nop is 0.122 milliseconds
         nop
         nop                   ; each total loop is 8 X 0.122 = 0.976 msec
         nop
         addlw H'FF'           ; same as subtracting 1 from W
         btfss STATUS, Z       ; skip if result is zero
         goto nmsec            ; this is  2 X 0.122 msec  
         return                ; back to calling point

;-----------------------------------------------------------------------;
;                        Delay for one second                           ;
;-----------------------------------------------------------------------;
onesecond:                     ; a subroutine that delays for 1 seconds
         call msec250
         call msec250
         call msec250
         call msec250
         return

;-----------------------------------------------------------------------;
;                   Put value in W on LEDs for 1 second                 ;
;-----------------------------------------------------------------------;
sendnbr:
         movwf PORTB          ; light LEDs
         call onesecond       ; wait 1 second
         clrf PORTB           ; clear the LEDs
         movlw D'100'         ; pause for 0.1 sec
         call nmsec
         return 

;-----------------------------------------------------------------------;
;                   Send the current time out LEDs                      ;
;-----------------------------------------------------------------------;

disptime:              
             movf hr10, W
             call sendnbr
             movf hr, W
             call sendnbr
             movf min10, W
             call sendnbr
             movf mins, W
             call sendnbr
             return

;-----------------------------------------------------------------------;
;                 Wait until selected button is released                ;
;-----------------------------------------------------------------------;
waitup6:                             ; wait for show pushbutton up
            movlw B'01000000'        ; RB6 mask
            movwf button
            goto wait
waitup5:                             ; wait for select pushbutton up
            movlw B'00100000'        ; RB5 mask
            movwf button
            goto wait
waitup4:                             ; wait for set pushbutton up
            movlw B'00010000'        ; RB4 mask
            movwf button
wait:
            movf button, W           ; mask into W
            andwf PORTB, W
            btfsc STATUS, Z          ; skip if not zero (released)
            goto wait
            movlw D'10'
            call nmsec               ; wait 10 msec for debounce
            movf button, W           ; check for release again
            andwf PORTB, W
            btfsc STATUS, Z          ; skip if selected button released
            goto wait
            return                   ; yes, finished


;-----------------------------------------------------------------------;
;                       Initilization Subroutine                        ;
;-----------------------------------------------------------------------;
init:
            movlw B'0000000'           ; all outputs port A
            tris PORTA                 
            movlw B'01110000'          ; RB4 - RB6 inputs, others outputs
            tris PORTB                 ; on port B
            movlw H'0'                 ; all low (off)
            movlw PORTB
            movlw B'00000100'          ; pull-ups enabled                                    
                                       ; prescaler assigned to TMR0
                                       ; prescaler set to 1:32
                                       ; rolls over each second
            option                
            movlw 0
            movwf hr10
            movlw H'9'                 ; initialize hrs, mins and secs
            movwf hr                   ; Do this before interrupts are
            movlw H'5'                 ; turned on because isr also acts
            movwf min10                  ; on these registers
            movlw H'0'
            movwf mins
            movwf sec10
            movwf sec
            movlw B'10100000'          ; GIE & T0IE set,  cleared
            movwf INTCON
            return

;-----------------------------------------------------------------------;
;  Interrupt routine, increments time by one second  (BCD)              ;
;-----------------------------------------------------------------------;
isr:
            movwf w_temp             ; save W
            swapf STATUS,W           ; save status
            movwf status_temp        ; without changing flags
            swapf FSR,W              ; save FSR
            movwf fsr_temp           ; without changing flags


            movlw sec                ; point at sec register
            movwf FSR
newdigit:   incf INDF, f             ; current digit up one
            movlw sec                ; get difference between sec and FSR
            subwf FSR, W
            call sethi               ; use to get high limit + 1
            subwf INDF, W            ; reached that number yet?
            btfss STATUS, Z          ; skip over if yes
            goto restore             ; else exit isr
            clrf INDF                ; set current digit to 0
            incf FSR, f               ; point at next digit
            btfss hr10, 1            ; has hr10 reached 2?
            goto newdigit            ; no, increment the next digit
            btfss hr, 2              ; has hr reached 4?
            goto newdigit            ; no
            clrf hr                  ; yes, set hour to 00
            clrf hr10                ; and hour 10
restore: 
            swapf status_temp,W      ; get original status back
            movwf STATUS             ; into status register
            swapf fsr_temp,W         ; get original fsr back
            movwf FSR                ; into status register
            swapf w_temp,f           ; old no flags trick again
            swapf w_temp,W           ; to restore W
            bcf INTCON,TMR0IF          ; clear the TMR0 interrupt flag
            retfie                   ; finished reset GIE

;-----------------------------------------------------------------------;
;           Increment and display digit pointed to by FSR               ;
;-----------------------------------------------------------------------;
updigit:
            incf INDF, f          ; selected digit up one
            movlw mins            ; set up to subtract mins address
            subwf FSR, W          ; from address of current digit
            call sethi            ; get maximum of digit + 1 into W
            subwf INDF, W         ; is it = to current digit value?
            btfsc STATUS, Z       ; gives zero if yes, skip if no
            clrf INDF             ; reset value of digit to zero
            movf INDF, W          ; get current value and ..
            movwf PORTB           ; display it
            call onesecond        ; pause for 1 second
            return

;-----------------------------------------------------------------------;
;          increment selected digit until select pressed                ;
;-----------------------------------------------------------------------;
setdigit:
            movwf PORTB
            btfss SETPB           ; set pressed?
            call updigit          ; yes
            btfsc SELECTPB        ; check if select pressed
            goto $ -3             ; repeat till select pressed again
            call waitup5          ; make sure select released
            incf FSR, f
            return

;-----------------------------------------------------------------------;
;                     Select and increment digits                       ;
;-----------------------------------------------------------------------;
select:
            bcf INTCON, GIE       ; no interrupts while setting time
            movlw mins            ; point at minutes register
            movwf FSR
            call waitup5          ; wait on select pushbutton up
            movlw B'00000001'     ; light right LED (mins)
            call setdigit
            movlw B'00000010'     ; light min10 LED
            call setdigit
            movlw B'00000100'     ; light hr LED
            call setdigit
            movlw B'00001000'     ; hr10 LED on
            call setdigit
            clrf PORTB            ; clear LEDs
            bsf INTCON, GIE       ; enable interrupts again
            return

;-----------------------------------------------------------------------;
;                          The main routine                             ;
;-----------------------------------------------------------------------;

main:      
            call init             ; set up initial conditions
loop:       
            btfss SHOWPB          ; check for show pushbutton
            call disptime         ; display the time
            btfss SELECTPB        ; check for select
            call select
            goto loop             ; do forever

            end