; ***************************************************************************
; 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 LED_DATA_OFFS_ADDRDDR  = 0
.equ LED_DATA_OFFS_ADDRPORT = 1
.equ LED_DATA_OFFS_ADDRPIN  = 2
.equ LED_DATA_OFFS_PINMASK  = 3


.equ LED_SRAM_OFFS_PATTERNADDR = 0
.equ LED_SRAM_OFFS_COUNTER     = 2
.equ LED_SRAM_OFFS_POS         = 3
.equ LED_SRAM_SIZE             = 4





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

.cseg


LED_BEGIN:


; ---------------------------------------------------------------------------
; Led_Init
;
; IN:
; - Y: pointer to SRAM data
; - Z: pointer to FLASH data
; OUT:
; - CFLAG: set if okay, clear on error
; USED: R1, R2, R3, R4, R16, R17, X

Led_Init:
  mov xh, yh
  mov xl, yl
  clr r16
  ldi r17, LED_SRAM_SIZE
  rcall Utils_FillSram

  rcall ledGetFlashDataIntoRegs
  brcc Led_Init_end
  
  ; set bit in DDR register (-> output)
  mov xl, r1   ; DDR register address
  clr xh
  ld r16, x
  or r16, r4   ; output
  st x, r16

  ; turn off led
  rcall ledOff
  sec
Led_Init_end:
  ret



; ---------------------------------------------------------------------------
; Led_SetPattern
;
; IN:
; - X: pointer to led pattern in flash
; - Y: pointer to SRAM data
; - Z: pointer to FLASH data
; OUT:
; - CFLAG: set if okay, clear on error
; USED: R1, R2, R3, R4, R16, R17, Z

Led_SetPattern:
  rcall ledGetFlashDataIntoRegs
  brcc Led_SetPattern_l2

  std y+LED_SRAM_OFFS_PATTERNADDR, xl   ; param 1
  std y+LED_SRAM_OFFS_PATTERNADDR+1, xh ; param 2

  ; reset pos in pattern
  clr r19
  std y+LED_SRAM_OFFS_POS, r19
  
  ; store counter for current pattern element
  mov zl, xl
  mov zh, xh
  lsl zl     ; multiplay Z by 2
  rol zh
  lpm r19, z ; read current pattern counter
  std y+LED_SRAM_OFFS_COUNTER, r19

  ; each pattern starts with LED on
  rcall ledOn
  
Led_SetPattern_l2:
  ret



; ---------------------------------------------------------------------------
; Led_Tick
;
; IN:
; - Y: pointer to SRAM data
; - Z: pointer to FLASH data
; OUT:
; - CFLAG: set if something done, reset otherwise
; USED: 

Led_Tick:
  rcall ledGetFlashDataIntoRegs
  brcs Led_Tick_l1
  ret

Led_Tick_l1:
  ldd zl, y+LED_SRAM_OFFS_PATTERNADDR
  ldd zh, y+LED_SRAM_OFFS_PATTERNADDR+1
  ldd r18, y+LED_SRAM_OFFS_POS
  ldd r19, y+LED_SRAM_OFFS_COUNTER

  ; test NULL ptr
  mov r16, zl;
  or r16, zh
  breq Led_Tick_end ; no current pattern, end

  cpi r19, 2 ; current counter less than 2?
  brcs Led_Tick_nextPhase
  
  dec r19
  std y+LED_SRAM_OFFS_COUNTER, r19
  ret

Led_Tick_nextPhase:
  lsl zl  ; multiplay Z by 2
  rol zh
  inc r18   ; next pos
  rcall Led_Tick_getPattern
  cpi r16, 0xff
  breq Led_Tick_restart
  cpi r16, 0
  breq Led_Tick_stop

  std y+LED_SRAM_OFFS_POS, r18
  std y+LED_SRAM_OFFS_COUNTER, r16

  mov r17, r18
  andi r17, 1 ; even?
  breq Led_Tick_switchOn
  rjmp ledOff
  
Led_Tick_switchOn:
  ; turn on led
  rjmp ledOn


Led_Tick_stop:
  clr r16
  std y+LED_SRAM_OFFS_PATTERNADDR, r16
  std y+LED_SRAM_OFFS_PATTERNADDR+1, r16
  std y+LED_SRAM_OFFS_COUNTER, r16
  std y+LED_SRAM_OFFS_POS, r16
  
  ; LED off
  rcall ledOff


Led_Tick_restart:
  ldi r18, 0
  rcall Led_Tick_getPattern
  cpi r16, 0xff
  breq Led_Tick_stop       ; stop, because restart as first pattern is invalid
  cpi r16, 0
  breq Led_Tick_stop
  std y+LED_SRAM_OFFS_POS, r18     ; incremented pos in pattern
  std y+LED_SRAM_OFFS_COUNTER, r16 ; new counter value
  
  rcall ledOn
  ret


Led_Tick_getPattern: ; r18=pos
  ldd zl, y+LED_SRAM_OFFS_PATTERNADDR
  ldd zh, y+LED_SRAM_OFFS_PATTERNADDR+1
  lsl zl  ; multiplay Z by 2
  rol zh

  ldi r16, 0
  add zl, r18
  adc zh, r16
  lpm r16, z
  ret
  
Led_Tick_end:
  ret






; ---------------------------------------------------------------------------
; ledGetFlashDataIntoRegs
;
; IN:
; - R28/ZL: pointer to data in flash (low)
; - R29/ZH: pointer to data in flash (hi)
; OUT:
; - CARRY flag set if okay, clear on error
; - R1: memory address of DDR register
; - R2: memory address of PORT register
; - R3: memory address of PIN register
; - R4: mask for used pin
; USED: R16

ledGetFlashDataIntoRegs:
  push zh
    push zl
      mov r16, zl
      or r16, zh
      breq ledGetFlashDataIntoRegs_error
      lsl zl
      rol zh
      lpm r1, z+ ; DDR
      lpm r2, z+ ; PORTR
      lpm r3, z+ ; PINR
      lpm r4, z  ; pin mask
    pop zl
  pop zh
  sec
  ret

ledGetFlashDataIntoRegs_error:
    pop zl
  pop zh
  clc
  ret



; ---------------------------------------------------------------------------
; ledOff
;
; IN:
; - R2: port register address (low part only)
; - R4: bit mask for pin to use
; OUT:
; - nothing
; MODIFIED REGS: none
; CYCLES: 26 (if R2 and R4 valid)

ledOff:
  tst r2                      ; 1
  breq ledOff_end             ; 1 if not taken
  tst r4                      ; 1
  breq ledOff_end             ; 1 if not taken
  push xh                     ; 2
    push xl                   ; 2
      push r16                ; 2
        mov xl, r2            ; 1  PORT register address
        clr xh                ; 1
        ld r16, x             ; 1
        or r16, r4            ; 1
        st x, r16             ; 2
      pop r16                 ; 2
    pop xl                    ; 2
  pop xh                      ; 2
ledOff_end:
  ret                         ; 4



; ---------------------------------------------------------------------------
; ledOn
;
; IN:
; - R2: port register address (low part only)
; - R4: bit mask for pin to use
; OUT:
; - nothing
; MODIFIED REGS: none
; CYCLES: 28

ledOn:                         ; clock cycles
  tst r2                        ; 1
  breq ledOn_end                ; 1 if not taken
  tst r4                        ; 1
  breq ledOn_end                ; 1 if not taken

  push xh                       ; 2
    push xl                     ; 2
      push r16                  ; 2
        mov xl, r2              ; 1 PORT register address
        clr xh                  ; 1
        ld r16, x               ; 1
        com r4                  ; 1 invert bit mask for following AND
        and r16, r4             ; 1
        com r4                  ; 1 undo inversion
        st x, r16               ; 2
      pop r16                   ; 2
    pop xl                      ; 2
  pop xh                        ; 2
ledOn_end:
  ret                           ; 4



LED_END:
.equ MODULE_SIZE_LED = LED_END-LED_BEGIN



