; ***************************************************************************
; copyright   : (C) 2025 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.   *
; ***************************************************************************

#ifndef AQH_AVR_COMMON_EEPROM_TLV_H
#define AQH_AVR_COMMON_EEPROM_TLV_H


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

.cseg


; ---------------------------------------------------------------------------
; @routine EepromTlv_AddTlv @global
;
; @param r16 type (must not be 0xff!)
; @param r17 length
; @return CFLAG set if free bytes found, cleared on error
; @return X EEPROM address of data for TLV (behind TLV header)
; @clobbers R16, R20, R21, X (R18)

EepromTlv_AddTlv:
  mov r20, r16
  mov r21, r17
  ldi r16, 0xff
  rcall EepromTlv_FindFirst       ; (r16, r17, r18)
  brcc EepromTlv_AddTlv_end
  cpi r17, 0xff
  brne EepromTlv_AddTlv_clcEnd
  add xl, r21                     ; wanted size
  adc xh, r21
  sub xh, r21
  rcall Eeprom_CheckAddr          ; check end address (R16)
  brcc EepromTlv_AddTlv_end
  sub xl, r21
  sbc xh, r21
  add xh, r21
  sbiw xh:xl, 2                   ; go back to begin of TLV
  mov r16, r20                    ; type
  rcall Eeprom_WriteByte          ; (R17)
  adiw xh:xl, 1
  mov r16, r21                    ; length
  rcall Eeprom_WriteByte          ; (R17)
  adiw xh:xl, 1
  sec
  rjmp EepromTlv_AddTlv_end
EepromTlv_AddTlv_clcEnd:
  clc
EepromTlv_AddTlv_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine EepromTlv_FindFirst @global
;
; Find first matching TLV
;
; @param r16 TLV type to find
; @return CFLAG set if TLV found, cleared otherwise
; @return r16 type
; @return r17 length
; @return X points to begin of TLV data
; @clobbers r16, r17, r18

EepromTlv_FindFirst:
  ldi xl, LOW(EEPROM_OFFS_TLV)
  ldi xh, HIGH(EEPROM_OFFS_TLV)
  rjmp eepromTlvFind            ; (R18)
; @end



; ---------------------------------------------------------------------------
; @routine EepromTlv_FindNext @global
;
; Find next matching TLV
;
; @param  r16   TLV type to find
; @param  X     points to current TLV data (will be skipped when searching)
; @return CFLAG set if TLV found, cleared otherwise
; @return r16   type
; @return r17   length
; @return X     points to begin of TLV data
; @clobbers r16, r17, r18

EepromTlv_FindNext:
  ; skip current TLV
  rcall EepromTlv_ReadHeader       ; r16=type, r17=len (none), X=data
  brcc EepromTlv_FindNext_ret
  add xl, r17                      ; skip TLV data
  adc xh, r17
  sub xh, r17
  ; begin search with next tlv
  rcall eepromTlvFind            ; (R18)
EepromTlv_FindNext_ret:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine eepromTlvFind @global
;
; @param r16 TLV type to find
; @param X EEPROM address to start search
; @return CFLAG set if TLV found, cleared otherwise
; @return r16 type
; @return r17 length
; @return X points to begin of TLV data
; @clobbers r16, r17, r18

eepromTlvFind:
  mov r18, r16
eepromTlvFind_loop:
    rcall EepromTlv_ReadHeader     ; r16=type, r17=len (none), X=data
    brcc eepromTlvFind_notFound
    cp r16, r18                    ; the one we wanted?
    breq eepromTlvFind_found
    add xl, r17                    ; skip TLV data
    adc xh, r17
    sub xh, r17
  rjmp eepromTlvFind_loop
eepromTlvFind_notFound:
  clc
  rjmp eepromTlvFind_end
eepromTlvFind_found:
  sec
eepromTlvFind_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine EepromTlv_ReadHeader
;
; @param X EEPROM address
; @return CFLAG set if address okay, cleared if out of range
; @return r16 type
; @return r17 length (only data length, can be 0)
; @return X EEPROM address behind TLV header
; @clobbers none

EepromTlv_ReadHeader:
  rcall Eeprom_ReadByte   ; read type
  brcc EepromTlv_ReadHeader_ret
  adiw xh:xl, 1
  push r16
    rcall Eeprom_ReadByte ; read length
    mov r17, r16
  pop r16
  brcc EepromTlv_ReadHeader_ret
  adiw xh:xl, 1
  sec
EepromTlv_ReadHeader_ret:
  ret
; @end


#endif

