; ------------------------------------------------------------------------------
;
;                               Single Nixie Clock                        v0.0.2
;
; ------------------------------------------------------------------------------
;
; Written by :
;
;               Jan Wagemakers   : Donated to the Public Domain
;
; ------------------------------------------------------------------------------
        LIST     p=PIC16F648A
        INCLUDE "p16f648a.inc"
        errorlevel -302
        ;
        __CONFIG _CP_OFF&_DATA_CP_OFF&_LVP_OFF&_BODEN_OFF&_MCLRE_OFF&_PWRTE_ON&_WDT_OFF&_INTOSC_OSC_NOCLKOUT 
; ------------------------------------------------------------------------------
; Define variables
; ------------------------------------------------------------------------------
        cblock h'20'                                            
                menu                    ; 0   = display clock
                                        ; 1   = menu H(high)
                                        ; 2   = menu H(low)
                                        ; 3   = menu M(high)
                                        ; 4   = menu M(low)
                submenu                 ; 0   = menu
                                        ; 1   = submenu
                HZ50counter             ; Count the 50HZ pulse
                buttonr                 ; count how long button is released
                buttonp                 ; count how log button is pressed
                number                  ; number to display on Nixie
                DL                      ; used in number to digit conversion    
                DH                      ; used in number to digit conversion
                S                       ; Seconds in Binary (only used internal)
                H                       ; Hours in BCD
                M                       ; Minutes in BCD
                Hstep1                  ; Hours in BCD at the moment DisplayCounter = 1
                Mstep1                  ; Minutes in BCD at the moment DisplayCounter = 1
                Delay                   ; used in sleep routine
                DisplayCounter          ; Display what part of the clock?
        endc
; ------------------------------------------------------------------------------
; Let's start
; ------------------------------------------------------------------------------
        org h'0000'                                            
; ------------------------------------------------------------------------------
; Init PIC input output
; ------------------------------------------------------------------------------
        bcf STATUS, RP1
        bsf STATUS, RP0                 ; select bank 1
        bcf PCON, OSCF                  ; CLK=48Khz
        movlw b'11111000'               ; 1 = input 0 = output
        movwf TRISA                     ; set input/output PORTA      
        clrf TRISB                      ; PORTB = output
        bcf STATUS, RP0                 ; select bank 0
        movlw h'07'                     ; turn comparators off
        movwf CMCON
; ------------------------------------------------------------------------------
; Reset some variables
; ------------------------------------------------------------------------------
        clrf H
        clrf M
        clrf S
        clrf menu
        clrf submenu
        clrf buttonp
        clrf buttonr
        clrf DisplayCounter
; ------------------------------------------------------------------------------
; Loop : everything is done is this loop  
; ------------------------------------------------------------------------------
Loop:
; ------------------------------------------------------------------------------
; Start clock menu routine
; ------------------------------------------------------------------------------
        btfsc PORTA, D'3'               ; IF RA3 <> 0 THEN ...
        goto  released                  ; ... button is not pressed ELSE ...
pressed:                                ; ... button is pressed
        incf buttonp, F
        movlw D'2'                      ; check how long key is pressed
        subwf buttonp, w
        btfss STATUS, Z
        goto chkkey_done                ; not long enough, go away
        ; ----------------------------------------------------------------------
        ; button is pressed long enough
        ; ----------------------------------------------------------------------
        clrf buttonp                    ; reset counter
        ;
        movf submenu, w                 ; menu or submenu?
        btfss STATUS, Z
        goto psubmenu              
        ; ----------------------------------------------------------------------
        ; button pressed : menu routine (:menu)
        ; ----------------------------------------------------------------------
        incf menu, F
        movlw D'5'
        subwf menu, w
        btfsc STATUS, Z
        clrf menu                       ; if menu = 5 then menu = 0
        ;
        movf menu, W
        movwf number
        call number_to_digit            ; display menu number
        bsf PORTA, D'2'                 ; display menu number with :
        ;
        goto chkkey_done                ; go away
        ; ----------------------------------------------------------------------
        ; button pressed : submenu routine
        ; ----------------------------------------------------------------------
psubmenu:
        ; ----------------------------------------------------------------------
        ; IF menu=1 THEN H = H + 0x10
        ; ----------------------------------------------------------------------
        movlw D'1'
        subwf menu, w
        btfss STATUS, Z
        goto XH
        movlw H'10'
        addwf H, F
        ; ----------------------------------------------------------------------
        ; IF menu=2 THEN H++
XH:     ; ----------------------------------------------------------------------
        movlw D'2'
        subwf menu, w
        btfsc STATUS, Z
        incf H, F
        ; ----------------------------------------------------------------------
        ; IF menu=3 THEN M = M + 0x10
        ; ----------------------------------------------------------------------
        movlw D'3'
        subwf menu, w
        btfss STATUS, Z
        goto XM
        movlw H'10'
        addwf M, F
        ; ----------------------------------------------------------------------
        ; IF menu=4 THEN M++
XM:     ; ----------------------------------------------------------------------
        movlw D'4'
        subwf menu, w
        btfsc STATUS, Z
        incf M, F
        ; ----------------------------------------------------------------------
        ; Check for overflow of digits
        ; ----------------------------------------------------------------------
        movlw B'11110000'
        btfsc M, D'3'
        btfss M, D'1'
        goto check1 
        andwf M, f                      ; IF M(low) = 10 then M(low) = 0
check1:
        movlw B'00001111'
        btfsc M, D'6'
        btfss M, D'5'
        goto check2
        andwf M, f                      ; IF M(high) = 10 then M(high) = 0
check2:
        movlw B'11110000'
        btfsc H, D'3'
        btfss H, D'1'
        goto check3
        andwf H, f                      ; IF H(low) = 10 then H(low) = 0
check3:
        movlw B'00001111'
        btfsc H, D'5'
        btfss H, D'4'
        goto check4
        andwf H, f                      ; IF H(high) = 3 then H(high) = 0                    
check4:
        btfss H, D'5'                   ; IF H(high) = 2 AND ...
        goto check5
        movlw B'00001111'
        andwf H, w                      ; w = H(low)
        sublw D'3'                      ; ... H(low) > 3 then ...
        btfss STATUS, C                
        clrf H                          ; .... reset H because for ex. ...
                                        ; .... 25:00 is false
check5:
        ; ----------------------------------------------------------------------
        ; Display the right digit on the Nixie
        ; ----------------------------------------------------------------------
disp_digit:
        ; -----------------------------------------------------------------------
        ; IF menu = 1 THEN display H(high)
        ; -----------------------------------------------------------------------
        movlw B'11110000'
        andwf H, w
        movwf number  
        swapf number, F                                 ; number = H(high)
        movlw D'1'
        subwf menu, w
        btfsc STATUS, Z
        call number_to_digit
        ; -----------------------------------------------------------------------
        ; IF menu = 2 THEN display H(low)
        ; -----------------------------------------------------------------------
        movlw B'00001111'
        andwf H, w
        movwf number                                    ; number = H(low)
        movlw D'2'
        subwf menu, w
        btfsc STATUS, Z
        call number_to_digit
        ; -----------------------------------------------------------------------
        ; IF menu = 3 THEN display M(high)
        ; -----------------------------------------------------------------------
        movlw B'11110000'
        andwf M, w
        movwf number
        swapf number, F                                 ; number = M(high)
        movlw D'3'
        subwf menu, w
        btfsc STATUS, Z
        call number_to_digit
        ; -----------------------------------------------------------------------
        ; IF menu = 41 THEN display M(low)
        ; -----------------------------------------------------------------------
        movlw B'00001111'
        andwf M, w
        movwf number                                    ; number = M(low)
        movlw D'4'
        subwf menu, w
        btfsc STATUS, Z
        call number_to_digit
        ;      
        goto chkkey_done                ; and done.
        ; ----------------------------------------------------------------------
        ; END button pressed
        ; ----------------------------------------------------------------------
released:
        incf buttonr, F
        movlw D'2'
        subwf buttonr, w
        btfss STATUS, Z
        goto chkkey_done
        ; ----------------------------------------------------------------------
        ; button is released long enough
        ; ----------------------------------------------------------------------
        clrf buttonr                    ; reset counter
        ;
        movf menu, W
        btfsc STATUS, Z
        goto clock                      ; if menu = 0 then display normal clock
        ;
        movf submenu, w                 ; menu or submenu
        btfss STATUS, Z
        goto rsubmenu
        ; ----------------------------------------------------------------------
        ; button released : menu routine (:menu --> digit)
        ; ----------------------------------------------------------------------
rmenu:
        bsf submenu, D'1'               ; enter submenu
        goto disp_digit                 ; Mmmm, spaghetti code ;-)
        ; ----------------------------------------------------------------------
        ; button released : submenu routine
        ; ----------------------------------------------------------------------
rsubmenu:
        clrf menu
        clrf submenu
        clrf DisplayCounter             ; exit menu/submenu
        ; ----------------------------------------------------------------------
        ; Let's end the button/menu/submenu routine
        ; ----------------------------------------------------------------------
chkkey_done:
        movf menu, W
        btfsc STATUS, Z
        goto clock                      ; if menu = 0 then display normal clock
        goto HZ50
; ------------------------------------------------------------------------------
; Start normal clock routine
; ------------------------------------------------------------------------------
clock:
        incf DisplayCounter, F
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 1 THEN
        ;       Display Nothing AND
        ;       Hstep1 = H Mstep1 = M
        ; ----------------------------------------------------------------------
        movlw d'1'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call cls_and_saveHM
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 2 THEN
        ;       Display Hstep1(high)
        ; ----------------------------------------------------------------------
        movlw B'11110000'
        andwf Hstep1, w
        movwf number    
        swapf number, F                                 ; number = Hstep1(high)
        movlw d'2'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call number_to_digit
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 3 THEN
        ;       Display Nothing
        ; ----------------------------------------------------------------------
        movlw d'3'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call cls        
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 4 THEN
        ;       Display Hstep1(low)
        ; ----------------------------------------------------------------------
        movlw B'00001111'
        andwf Hstep1, w
        movwf number                                    ; number = Hstep1(low)
        movlw d'4'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call number_to_digit
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 5 THEN
        ;       Display Nothing
        ; ----------------------------------------------------------------------
        movlw d'5'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call cls
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 6 THEN
        ;       Display :
        ; ----------------------------------------------------------------------
        movlw d'6'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        bsf PORTA, D'2'                                 ; display :
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 7 THEN
        ;       Display Nothing
        ; ----------------------------------------------------------------------
        movlw d'7'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call cls
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 8 THEN
        ;       Display Mstep1(high)
        ; ----------------------------------------------------------------------
        movlw B'11110000'
        andwf Mstep1, w
        movwf number                                    
        swapf number, F                                 ; number = Mstep1(high)
        movlw d'8'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call number_to_digit
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 9 THEN
        ;       Display Nothing
        ; ----------------------------------------------------------------------
        movlw d'9'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call cls
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 10 THEN
        ;       Display Mstep1(low)
        ; ----------------------------------------------------------------------
        movlw B'00001111'
        andwf Mstep1, w
        movwf number                                    ; number = Mstep1(low)
        movlw d'10'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call number_to_digit
        ; ----------------------------------------------------------------------
        ; IF DisplayCounter = 11 THEN
        ;       Display Nothing  AND
        ;       DisplayCounter = 0
        ; ----------------------------------------------------------------------
        movlw d'11'
        subwf DisplayCounter, w
        btfsc STATUS, Z
        call cls_and_crlf_DisplayCounter        
; ------------------------------------------------------------------------------
; Synchronize with 50HZ __+--+__+--+__
; ------------------------------------------------------------------------------
HZ50:
        clrf HZ50counter
HZ50islow:
        call zzz                        ; sleep a while
        btfss PORTA, d'4'      
        goto HZ50islow
HZ50ishigh:
        call zzz                        ; sleep a while
        btfsc PORTA, d'4'
        goto HZ50ishigh
        ;
        incf HZ50counter, F             ; Do every 1/50 sec. : 50HZcounter++
        movlw d'50'
        subwf HZ50counter, w            ; 50 * 1/50 seconds -> 1 second
        btfss STATUS, Z
        goto HZ50islow
; ------------------------------------------------------------------------------
; INC clock sec, min, hour
; ------------------------------------------------------------------------------
        incf S, F                       ; Do every second : S = S + 1
        movlw d'60'
        subwf S, w
        btfss STATUS, Z 
        goto Loop
        clrf S                          ; IF S = 60 THEN S = 0 and ...
        ; ----------------------------------------------------------------------
        ; OK, it's time to update the clock by 1 minute
        ; ----------------------------------------------------------------------
        incf M, f                       ; M = M + 1
        movlw D'6'
        btfsc M, D'3'
        btfss M, D'1'
        goto Mok                        ; IF M(low) = 10 THEN
        addwf M, f                      ;    M(low) = 0  AND M(high)++
Mok:
        movlw H'60'
        subwf M, w
        btfss STATUS, Z
        goto Loop
        clrf M                          ; IF M(BCD)  = 60 THEN M = 0 and ...
        ; ----------------------------------------------------------------------
        ; OK, it's time to update the clock by 1 hour
        ; ----------------------------------------------------------------------
        incf H, F                       ; ... H = H + 1
        movlw D'6'
        btfsc H, D'3'
        btfss H, D'1'
        goto Hok                        ; IF H(low) = 10 THEN
        addwf H, f                      ;    H(low) = 0  AND H(high)++
Hok:
        movlw H'24'
        subwf H, w
        btfsc STATUS, Z        
        clrf H                          ; IF H(BCD) = 24 THEN H = 0
        goto Loop
; ------------------------------------------------------------------------------

; ------------------------------------------------------------------------------
; Subroutines
; ------------------------------------------------------------------------------
cls_and_crlf_DisplayCounter:
        clrf DisplayCounter
        goto cls
cls_and_saveHM:
        movf H, w
        movwf Hstep1
        movf M, w
        movwf Mstep1
cls:
        clrf PORTB                      ; Display nothing
        clrf PORTA
        return
        ;
number_to_digit:
        movf number, w
        BTFSC STATUS, Z                 ; if number = 0 then number = 10
        movlw D'10'
        movwf number
        movlw b'00000001'
        movwf DL
        clrf  DH                        ; DH:DL = 0000000000000001
dountilzero:
        decfsz number, F
        goto isnotzero
        movf DL, w
        movwf PORTB
        movf DH, w
        movwf PORTA
        return
isnotzero:
        BCF STATUS, C
        rlf DL, F
        rlf DH, F
        goto dountilzero
        ;
zzz:                                    ; zzz zzz zzz zzz (sleep)
        movlw D'4'
        movwf Delay
zloop:
        decfsz Delay, F
        goto zloop
        return
        ;
; ----------------------------------------------------------------------------
        end
; ----------------------------------------------------------------------------