; ***************************************************************************
; copyright   : (C) 2023 by Martin Preuss
; email       : martin@libchipcard.de
;
; ***************************************************************************
; * This file is part of the project "AqHome".                              *
; * Please see toplevel file COPYING of that project for license details.   *
; ***************************************************************************





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

.equ TIMER_FLAGS_IF_ADDR = 1




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

.dseg

timerModuleData:
timerModuleTickCounter:      .byte 1
timerModuleCounterSecs:      .byte 4
timerModuleData_end:




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

.cseg

TIMER_BEGIN:



; ---------------------------------------------------------------------------
; @routine Timer_Init
;
; @clobbers r16, r17, x

Timer_Init:
  ; reset data in SDRAM
  ldi xh, HIGH(timerModuleData)
  ldi xl, LOW(timerModuleData)
  ldi r16, 0
  ldi r17, (timerModuleData_end-timerModuleData)
  rcall Utils_FillSram

  ldi r16, 10                         ; every sec
  sts timerModuleTickCounter, r16
  
  rcall timerInitTimers
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Timer_Fini
;

Timer_Fini:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine onSystemTimerTick
;
; @return CFLAG set if something done, cleared otherwise
; @clobbers all

onSystemTimerTick:
  lds r16, timerModuleTickCounter
  dec r16
  breq onSystemTimerTick_SecondElapsed
  sts timerModuleTickCounter, r16
  rjmp onSystemTimerTick_call

onSystemTimerTick_SecondElapsed:
  ldi r16, 10                         ; reload counter (10=every sec)
  sts timerModuleTickCounter, r16

  ldi xl, LOW(timerModuleCounterSecs) 
  ldi xh, HIGH(timerModuleCounterSecs)
  rcall Utils_IncrementCounter32      ; inc uptime counter

;  sbi DEBUG_LED_DDR, DEBUG_LED_PINNUM     ; out
;  sbi DEBUG_LED_PORT_IN, DEBUG_LED_PINNUM ; toggle

onSystemTimerTick_call:
  rcall timerRunTimers
  rcall onEvery100ms
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Timer_SetValue @global
;
; Set timer value.
; Setting a timer to 0 effectively stops the timer.
;
; @param r16 new timer value (low)
; @param r17 new timer value (high)
; @param X   pointer to timer value in SRAM
; @clobbers X

Timer_SetValue:
  push r15
    in r15, SREG
    cli
    st X+, r16
    st X+, r17
    out SREG, r15
  pop r15
  ret
; @end



; ---------------------------------------------------------------------------
; @routine Timer_SetValueTo1s @global
;
; Set timer value to 1s.
;
; @param X pointer to timer value in SRAM
; @clobbers X

Timer_SetValueTo1s:
  push r16
    push r17
      ldi r16, 10
      clr r17
      rcall Timer_SetValue
    pop r17
  pop r16
  ret
; @end




; ---------------------------------------------------------------------------
; @routine timerInitTimers
;
; Init timers in table.
;
; @clobbers all

timerInitTimers:
  ldi zl, LOW(timerList*2)
  ldi zh, HIGH(timerList*2)
  clr r16                      ; run var for pos in time table
  clr r17                      ; 0 for adc
timerInitTimers_loop:
    rcall timerReadTableEntry
    mov r18, xl
    or r18, xh
    breq timerInitTimers_end
    mov r18, r20
    or r18, r21
    breq timerInitTimers_writeInitial
    add r20, r16               ; add counter pos in table so that not all timers elapse at the same time
    adc r21, r17
timerInitTimers_writeInitial:
    st X+, r20
    st X, r21
    inc r16
    inc r16
  rjmp timerInitTimers_loop

timerInitTimers_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine timerRunTimers
;
; Run timers in table.
;
; @clobbers all

timerRunTimers:
  ldi zl, LOW(timerList*2)
  ldi zh, HIGH(timerList*2)
timerRunTimers_loop:
    rcall timerReadTableEntry
    mov r16, xl
    or r16, xh
    breq timerRunTimers_end
    mov r16, r22
    andi r16, TIMER_FLAGS_IF_ADDR
    breq timerRunTimers_l1         ; no need to check address
    lds r16, com2Address           ; check address
    tst r16
    breq timerRunTimers_loop       ; no address, ignore counter
timerRunTimers_l1:
    ld r24, X+
    ld r25, X
    sbiw r25:r24, 1
    brcs timerRunTimers_loop        ; overflow, so already was zero, ignore entry
    breq timerRunTimers_reachedZero ; reached zero
    st X, r25                  
    st -X, r24
  rjmp timerRunTimers_loop
timerRunTimers_reachedZero:
    st X, r21                       ; reset initial value
    st -X, r20
    push zl
      push zh
        rcall timerCallR19R18
      pop zh
    pop zl
  rjmp timerRunTimers_loop
timerRunTimers_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine timerReadTableEntry
;
; read timer table entry.
;
; @param Z      pointer to time list entry (suitable for LPM)
; OUT:
; @return r19:r18 handler routine
; @return X       SRAM address for counter
; @return r21:r20 initial value
; @return r22     flags
; @return r23     reserved

timerReadTableEntry:
  lpm xl, Z+    ; SRAM address of counter
  lpm xh, Z+
  lpm r18, Z+   ; routine (low)
  lpm r19, Z+   ; routine (high)
  lpm r22, Z+   ; flags
  lpm r23, Z+   ; reserved
  lpm r20, Z+   ; initial value
  lpm r21, Z+
  ret
; @end



; ---------------------------------------------------------------------------
; @routine timerCallR19R18
; Call route at address r19:r18

timerCallR19R18:
  push r18
  push r19
  ret
; @end



TIMER_END:
.equ MODULE_SIZE_TIMER = TIMER_END-TIMER_BEGIN



