

; ***************************************************************************
; defs

.equ TIMER_FLAGS_100MS = 1
.equ TIMER_FLAGS_1S    = 2




; ***************************************************************************
; data

.dseg

timerModuleData:
timerModuleTickCounter:      .byte 1 ; only low byte used
timerModuleFlags:            .byte 1
timerModuleCounter10s:       .byte 1
timerModuleCounter1m:        .byte 1
timerModuleCounterSecs:      .byte 4
timerModuleData_end:




; ***************************************************************************
; code

.cseg



; ***************************************************************************
; Timer_Init
;
; IN:
; - nothing
; OUT:
; - nothing
; USED: r16, r17, x

Timer_Init:            ; setup timer for 15.2588 interrupts per second (e.g. every 65.5 ms)
  ; reset data in SDRAM
  ldi xh, HIGH(timerModuleData)
  ldi xl, LOW(timerModuleData)
  ldi r16, 0
  ldi r17, (timerModuleData_end-timerModuleData)
  rcall Utils_FillSram

; CTC mode (WGM2:0=2, OCR0A=value, OCF0A Flag =1,  -> IRQ_OC0A

  ; CMP-A interrupt about every 100ms
  ldi r16, (1<<CS02) | (1<<CS00)  ; Prescaler 1024
  out TCCR0B, r16
  
  ldi r16, (1<<WGM01) ; CTC mode
  out TCCR0A, r16
  
  ldi r16, 98-1      ; (1,000,000/1024)/10 = 97.65625
  out OCR0A, r16

  ldi r16, (1<<OCF0A) ; clear pending interrupts
  out TIFR0, r16
  
  ldi r16, (1<<OCIE0A)
  out TIMSK0, r16
  
  ret



; ***************************************************************************
; Timer_Fini
;
; IN:
; - nothing
; OUT:
; - nothing
; USED:

Timer_Fini:
  ret



; ***************************************************************************
; Timer_Run
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if something done, reset otherwise
; USED: r15, r16, r17 (more depending on called routines)

Timer_Run:
  in r15, SREG
  cli
  clr r16
  lds r17, timerModuleFlags
  sts timerModuleFlags, r16
  out SREG, r15  ; restore global IRQ flag
  mov r16, r17
  andi r16, TIMER_FLAGS_100MS
  breq Timer_Run_l1
  push r17
    rcall onEvery100ms
  pop r17
Timer_Run_l1:
  mov r16, r17
  andi r16, TIMER_FLAGS_1S
  breq Timer_Run_l2
  ; another 1s passed, invoke callback, check for 10s and 1m
  push r17
    rcall onEverySecond
  pop r17
  ; 1s
  lds r16, timerModuleCounter10s
  inc r16
  sts timerModuleCounter10s, r16
  cpi r16, 10
  brcs Timer_Run_check1m
  clr r16
  sts timerModuleCounter10s, r16
  push r17
    rcall onEvery10s
  pop r17
  
Timer_Run_check1m:
  ; 1m
  lds r16, timerModuleCounter1m
  inc r16
  sts timerModuleCounter1m, r16
  cpi r16, 60
  brcs Timer_Run_l2
  clr r16
  sts timerModuleCounter1m, r16
  push r17
    rcall onEveryMinute
  pop r17

Timer_Run_l2:
  sec
  tst r17
  brne Timer_Run_end
  clc
Timer_Run_end:
  ret



; ***************************************************************************
; OC0A interrupt handler
;
; Called every 100 milliseconds, sets flag TIMER_FLAGS_100MS.
; Increments an internal counter. If that counter reaches 10 the seconds counter is incremented and a the flag
; TIMER_FLAGS_1S is set.

timerIrqOC0A:
  push r15
    in r15, SREG

    push r16
      push r17
        lds r17, timerModuleFlags
        ori r17, TIMER_FLAGS_100MS
        lds r16, timerModuleTickCounter
        inc r16
        cpi r16, 10
        brcc timerIrq2_l1
        sts timerModuleTickCounter, r16
        rjmp timerIrq2_l2
timerIrq2_l1: ; increment seconds
        ; reset 100ms counter
        clr r16
        sts timerModuleTickCounter, r16
        ; increment seconds
        push xh
          push xl
            push r18
              push r19
                push r20
                  push r21
                    push r22
                      ldi xh, HIGH(timerModuleCounterSecs)
                      ldi xl, LOW(timerModuleCounterSecs)
                      ld r18, x+
                      ld r19, x+
                      ld r20, x+
                      ld r21, x
                      clr r22
                      inc r18
                      adc r19, r22
                      adc r20, r22
                      adc r21, r22
                      st x, r21
                      st -x, r20
                      st -x, r19
                      st -x, r18
                    pop r22
                  pop r21
                pop r20
              pop r19
            pop r18
          pop xl
        pop xh
        ori r17, TIMER_FLAGS_1S
timerIrq2_l2:
        sts timerModuleFlags, r17 ; store new timer flags
      pop r17
    pop r16
    out SREG, r15
  pop r15
  reti






