#include "msp430.h"                     ; #define controlled include file

pressTsch       EQU   03Fh + 1          ; threscholds for button press and
releaseTsch     EQU   0FCh              ;  release (for the shift register)

        ORG     0FFFEh
        DC16    main                    ; set reset vector to 'main' label
        
        ORG     0FFF0h
        DC16    TA_ISR                  ; set timer_A interrupt vector

        RSEG    CSTACK                  ; stack segment
        RSEG    DATA16_N                ; data segment
seconds DS      1                       ; counter for seconds (0 - 59)
colon   DS      1                       ; colon code (alternates bwn 10 & 11)
hrsU    DS      1                       ; storage for hours   (units)
hrsT    DS      1                       ;   - " -             (tens)
minT    DS      1                       ; storage for minutes (tens)
minU    DS      1                       ;   - " -             (units)
btState DS      1                       ; buttons state
shiftH  DS      1                       ; shift reg for debouncing
shiftM  DS      1                       ; same for the set minutes button       
       
        RSEG    CODE                    ; code segment
main    mov     #SFE(CSTACK), SP        ; set up stack

        mov.w   #WDTPW+WDTHOLD, &WDTCTL ; stop watchdog timer
        bis.b   #XCAP_3, &BCSCTL3       ; set up 12.5 pF crystal load caps
        bic.w   #OSCOFF, SR             ; enable crystal oscillator
        mov.b   #07Fh, &P1DIR           ; enable P1<6:0> for output
        mov.b   #01Fh, &P2DIR           ; enable P2<4:0> for output
        mov.b   #BIT7, &P1REN           ; enable pull up/down resistors
        mov.b   #BIT5, &P2REN           ;  for buttons
        bis.b   #BIT7, &P1OUT           ; select pull-up resistors 

        mov.w   #63, &TACCR0            ; set up Timer_A upper counting limit
        mov.w   #TASSEL_1+MC_1+TAIE, &TACTL  ; ACLK, up mode, interrupt

        clr.b   R15                     ; counter for 1 sec intervals
        mov.b   #1, R14                 ; active digit code (1,2,4,8,16)
        mov.b   R14, R13                ; active digit index (1 - 5)
        mov.b   R14, &P2OUT             ; display the :
        bis.b   #BIT5, &P2OUT           ; select pull-up resistor
      
        mov.b   #0FFh, &shiftH          ; initialize shift regs 
        mov.b   #0FFh, &shiftM          ;  for buttons debouncing
        mov.b   #3, &btState            ; initial buttons state "all up"
 
        mov.w   #0A00h, &seconds        ; initialize time to 12:00:00
        clr.w   &minT              
        mov.w   #0102h, &hrsU
        mov.b   &code7+10, &P1OUT       ; display ":"
 
        bis.w   #LPM3+GIE, SR           ; enter LPM3, interrupts enabled
        nop                             ; is needed for debugging only   

TA_ISR  add.w   &TAIV,PC                ; Add Timer_A offset vector
        reti                            ; CCR0 - no source
        reti                            ; CCR1
        reti                            ; CCR2
        reti                            ; CCR3
        reti                            ; CCR4 

process                                 ; Timer_A overflow event processing
        mov.b   #020h, &P2OUT           ; turn off all digits
        rla.b   R14                     ; update active digit code
        inc.b   R13                     ; update digit index
        bit.b   #BIT5, R14
        jz      $+6                     ; the upper limit is not reached yet
         mov.b  #1, R14                 ; reset the active digit # to 1
         mov.b  R14, R13
         
        mov.b   seconds(R13), R10       ; get digit
        mov.b   code7(R10), &P1OUT      ; move new data to output reg
        add.b   R14, &P2OUT             ; turn on new digit

        bit.b   #BIT0, R15              ; debounce the buttons every other
        jne     checkSecond             ; interrupt, i.e. once in 4 msec
        
debounceH     
        clrc
        rrc.b   &shiftH                 ; debouncing the hours setup button          
        bit.b   #BIT5, &P2IN            ; shift in the button value
        jz      $+8
         bis.b  #BIT7, &shiftH
        
        bit.b   #BIT1, &btState         ; old state == "up" ?
        jz      debounce1               ; NO - it is "down"
         cmp.b  #pressTsch, &shiftH
         jhs    debounceM
          bic.b #BIT1, &btState         ; set new state = "down"
          call  #updateHours
          jmp   debounceM     
debounce1        
        cmp.b   #releaseTsch, &shiftH 
        jlo     debounceM
         bis.b  #BIT1, &btState         ; set new state = "up"
        
debounceM 
        clrc
        rrc.b   &shiftM                 ; debouncing the minutes setup button
        bit.b   #BIT7, &P1IN
        jz      $+8
         bis.b  #BIT7, &shiftM
        
        bit.b   #BIT0, &btState         ; old state == "up" ?
        jz      debounce2               ; NO - it is "down"
         cmp.b  #pressTsch, &shiftM
         jhs    checkSecond
          bic.b #BIT0, &btState         ; set new state = "down"
          call  #updateMinutes
          jmp   checkSecond 
debounce2        
        cmp.b   #releaseTsch, &shiftM 
        jlo     checkSecond
         bis.b  #BIT0, &btState         ; set new state = "up"
        
checkSecond        
        inc.w   R15                     ; is it a 1-sec time stamp?
        bit.w   #BIT9, R15
        jnz     $+4                     ; YES - update seconds counter
         reti                           ; NO - wait

        clr.w   R15                     ; clear seconds counter
        xor.b   #BIT0, &colon           ; toggle the : on display
        
        inc.b   &seconds                ; update seconds
        cmp.b   #60, &seconds           ; time to update display?
        jne     $+16                    ; NO - wait        
         clr.b  &seconds                ; reset seconds
         call   #updateMinutes
         jnz    $+6                     ; Z=1 if hours update is needed
          call  #updateHours
        reti

updateMinutes
        inc.b   &minU                   ; update units
        cmp.b   #10, &minU
        jeq     $+4
         ret
        clr.b   &minU                   ; clear units
        inc.b   &minT                   ; update tens
        cmp.b   #6, &minT
        jne     $+6                
         clr.b  &minT                   ; clear tens
        ret

updateHours
        inc.b   &hrsU                   ; update units
  
        mov.b   #10, R10
        cmp.b   #2, &hrsT
        jne     $+4
         mov.b  #4, R10

        cmp.b   R10, &hrsU
        jeq     $+4
         ret
        
        clr.b   &hrsU                   ; update tens
        inc.b   &hrsT
        cmp.b   #3, &hrsT
        jlo     exit1
        jeq     exit2
         mov.b  #1, &hrsT          
         ret
exit2   mov.b   #11, &hrsT              ; empty digit (do not display
exit1   ret                             ; leading 0 for hours)

code7   ;       pfcagbde                ; 7-segment code
        DB      11110111b	        ; code for 0
        DB      10100100b	        ; code for 1
        DB      10011111b	        ; code for 2
        DB      10111110b	        ; code for 3
        DB      11101100b	        ; code for 4
        DB      11111010b	        ; code for 5
        DB      11111011b	        ; code for 6
        DB      10110100b	        ; code for 7
        DB      11111111b	        ; code for 8
        DB      11111110b	        ; code for 9
        DB      10010100b               ; code for :
        DB      10000000b               ; empty

        END

