#include <p16F690.inc>

     __config (_XT_OSC & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)

; Rate Adjustments
#define TMR_LO                  0xBE
#define TMR_HI                  0xEE
#define INTERRUPTS_PER_SECOND   .200

#define TIMER_DIVIDER           .1    ; should be .60
#define ROUGH_ADJUSTMENT        .7    ; how much to increase/decrease timer when the rough
                                      ; adjustments buttons are used

#define F_RUNNING                0    ; timer is running
#define F_TICK                   1    ; timer tick (after division)
#define F_DIGIT                  2    ; which digit LED display is ON
#define F_BLINKING               3    ; display blinking

    cblock 0x20
    FLAGS
    OLD_COUNTER
    TIMER
    DIVIDER_L
    DIVIDER_H
    LOOKUP
    TMP
    endc

    ; interrupt registers in shared space
    cblock 0x78
    INT_W
    INT_STATUS
    INT_PCLATH
    INT_FSR
    INT_PULSECTR
    INT_COUNTER
    endc

    org 0x00
    goto    Start
    org 0x04
    nop        ; interrupt code follows - avoid potential paging issues

Interrupt
    movwf   INT_W
    swapf   STATUS,w
    clrf    STATUS
    movwf   INT_STATUS
    movf    PCLATH,w
    movwf   INT_PCLATH
    clrf    PCLATH
    movf    FSR,w
    movwf   INT_FSR
    banksel T1CON    ; stop timer
    bcf     T1CON,TMR1ON
    banksel TMR1H    ; set TMR1 counter
    movlw   TMR_LO
    movwf   TMR1L
    movlw   TMR_HI
    movwf   TMR1H
    banksel INT_PULSECTR
    decf    INT_PULSECTR,f
    btfss   STATUS,Z
    goto    Interrupt_display
    incf    INT_COUNTER,f
    movlw   INTERRUPTS_PER_SECOND
    movwf   INT_PULSECTR
Interrupt_display
    movlw   0x01<<F_DIGIT
    xorwf   FLAGS,f
    btfss   FLAGS,F_DIGIT
    goto    Interrupt_displayHigh
    movf    TIMER,w
    andlw   0x0F
    lcall   Lookup_LEDs
    iorlw   0x80
    movwf   PORTC
    goto    Interrupt_continue
Interrupt_displayHigh
    swapf   TIMER,w
    andlw   0x0F
    lcall   Lookup_LEDs
    movwf   PORTC
Interrupt_continue
    banksel PIR1    ; clear TMR1 overflow bit
    bcf     PIR1,TMR1IF
    banksel PIE1    ; enable TMR1 interrupt
    bsf     PIE1,TMR1IE
    banksel T1CON    ; start timer
    bsf     T1CON,TMR1ON
    banksel PORTC
    movf    INT_FSR,w
    movwf   FSR
    movf    INT_PCLATH,w
    movwf   PCLATH
    swapf   INT_STATUS,w
    movwf   STATUS
    swapf   INT_W,f
    swapf   INT_W,w
    retfie

InitTimer
    banksel INTCON
    bsf     INTCON,PEIE
    bsf     INTCON,GIE
    banksel T1CON
    movlw   b'00000000'
    banksel PORTC
    movlw   INTERRUPTS_PER_SECOND
    movwf   INT_PULSECTR
    banksel PIR1        ; clear TMR1 overflow bit
    bcf     PIR1,TMR1IF
    banksel PIE1        ; enable TMR1 interrupt
    bsf     PIE1,TMR1IE
    banksel T1CON       ; start timer
    bsf     T1CON,TMR1ON
    banksel PORTC
    clrf    INT_COUNTER
    clrf    OLD_COUNTER
    clrf    TIMER
    movlw   high TIMER_DIVIDER
    movwf   DIVIDER_H
    movlw   low TIMER_DIVIDER
    movwf   DIVIDER_L
    return

InitPorts
    banksel PORTA
    clrf    PORTA
    clrf    PORTB
    movlw   0xFF
    movwf   PORTC
    banksel ANSEL
    clrf    ANSEL
    banksel ANSELH
    clrf    ANSELH
    banksel TRISA
    movlw   b'00001011'  ; 1=input, 0=output
    movwf   TRISA
    movlw   b'11111111'  ; all inputs
    movwf   TRISB
    clrf    TRISC        ; all outputs
    movlw   b'11000000'  ; enable interrupts
    movwf   INTCON
    banksel PORTA
    return

Start
    clrf    FLAGS
    call    InitPorts
    call    InitTimer
    clrf    TIMER

; test only - start timer from 15 seconds
    bsf     FLAGS,F_RUNNING
    movlw   0x15
    movwf   TIMER

MainLoop
    call    Divider
    btfsc   FLAGS,F_TICK
    call    HandleTick
    goto    MainLoop

; Sets the F_TICK flag once every TIMER_DIVIDER seconds, if timer is running
Divider
    btfss   FLAGS,F_RUNNING
    return
    movf    INT_COUNTER,w
    subwf   OLD_COUNTER,w
    btfsc   STATUS,Z
    return
    incf    OLD_COUNTER,f
    decf    DIVIDER_L,f
    btfss   STATUS,Z
    return
    movf    DIVIDER_H,f
    btfsc   STATUS,Z
    goto    Divider_continue
    decf    DIVIDER_H,f
    return
Divider_continue
    bsf     FLAGS,F_TICK
    movlw   high TIMER_DIVIDER
    movwf   DIVIDER_H
    movlw   low TIMER_DIVIDER
    movwf   DIVIDER_L
    return

; Handles a tick - decreases timer by one
; (Note that if timer is not running, there are no ticks coming, so no need to check
; for timer status here.)
HandleTick
    bcf     FLAGS,F_TICK
    call    DecreaseTimer
    return


DecreaseTimer
    movf    TIMER,w
    btfsc   STATUS,Z
    return
    andlw   0x0F
    btfsc   STATUS,Z
    goto    DecreaseTimer_decTens
    decf    TIMER,f
    return
DecreaseTimer_decTens
    swapf   TIMER,f
    decf    TIMER,w
    andlw   0x0F
    iorlw   0x90
    movwf   TIMER
    swapf   TIMER,f
    return


IncreaseTimer
    movf    TIMER,w
    sublw   0x99
    btfsc   STATUS,Z
    return
    movf    TIMER,w
    andlw   0x0F
    sublw   0x09
    btfsc   STATUS,Z
    goto    IncreaseTimer_incTens
    incf    TIMER,f
    return
IncreaseTimer_incTens
    swapf   TIMER,f
    incf    TIMER,w
    andlw   0x0F
    movwf   TIMER
    swapf   TIMER,f
    return


DecreaseTimer_Rough
    movlw   ROUGH_ADJUSTMENT
    movwf   TMP
    call    DecreaseTimer
    decf    TMP,f
    btfss   STATUS,Z
    goto    $-3
    return


IncreaseTimer_Rough
    movlw   ROUGH_ADJUSTMENT
    movwf   TMP
    call    IncreaseTimer
    decf    TMP,f
    btfss   STATUS,Z
    goto    $-3
    return


;MainLoop_1
;    clrf    BTN
;    btfss   PORTB,6
;    bsf     BTN,0
;    btfss   PORTA,3
;    bsf     BTN,1
;    btfss   PORTB,5
;    bsf     BTN,2
;    btfss   PORTB,4
;    bsf     BTN,3
;    btfss   PORTA,1
;    bsf     BTN,4
;    btfss   PORTA,0
;    bsf     BTN,5
;    movf    BTN,w
;    movwf   PORTC
;    call    Delay
;    clrf    BTN
;    btfss   PORTB,6
;    bsf     BTN,0
;    btfss   PORTA,3
;    bsf     BTN,1
;    btfss   PORTB,5
;    bsf     BTN,2
;    btfss   PORTB,4
;    bsf     BTN,3
;    btfss   PORTA,1
;    bsf     BTN,4
;    btfss   PORTA,0
;    bsf     BTN,5
;    bsf     BTN,7
;    movf    BTN,w
;    movwf   PORTC
;    call    Delay
;    goto    MainLoop_1

Delay
    clrf    TMP
Delay_1
    decf    TMP,f
    btfss   STATUS,Z
    goto Delay_1
    return


    org 0x0700
Lookup_LEDs
    movwf   LOOKUP
    movlw   high $
    movwf   PCLATH
    movf    LOOKUP,w
    addwf   PCL,f
    retlw   b'01000000'     ; 0
    retlw   b'01111001'     ; 1
    retlw   b'00100100'     ; 2
    retlw   b'00110000'     ; 3
    retlw   b'00011001'     ; 4
    retlw   b'00010010'     ; 5
    retlw   b'00000010'     ; 6
    retlw   b'01111000'     ; 7
    retlw   b'00000000'     ; 8
    retlw   b'00010000'     ; 9
    retlw   b'00001000'     ; A
    retlw   b'00000011'     ; b
    retlw   b'01000110'     ; C
    retlw   b'00100001'     ; d
    retlw   b'00000110'     ; E
    retlw   b'01111111'     ; (empty)
    END
