; ***************************************************************************
; 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.   *
; ***************************************************************************





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


.cseg


FLASH_BEGIN:


; ---------------------------------------------------------------------------
; start flashing a page
;
; Interrupts must be disabled!
;
; IN:
; - Z: Address to work on (byte address as for LPM!)
; OUT:
; - nothing
; REGS: R16 (R0, R1, R16, R20, R24, R25)

Flash_StartPage:
  push zh
    push zl
      ldi r16, ~((PAGESIZE*2)-1)
      and zl, r16
      rcall Flash_ReadPageIntoPageBuffer ; (R0, R1, R16, R20, R24, R25, Z)
    pop zl
  pop zh
  ret



; ---------------------------------------------------------------------------
; finish flashing a page
;
; Interrupts must be disabled!
;
; IN:
; - Z: Address to work on (byte address as for LPM!)
; OUT:
; - nothing
; REGS: R16 (R15, R20)

Flash_FinishPage:
  ldi r16, ~((PAGESIZE*2)-1)
  and zl, r16
  rcall Flash_ErasePage  ; (R15, R16, R20)
  rcall Flash_WritePage  ; (R15, R16, R20)
  ret



; ---------------------------------------------------------------------------
; Flash_ReadPageIntoPageBuffer
;
; Interrupts must be disabled!
;
; IN:
; - Z: Address to read from (byte address as for LPM!)
; OUT:
; - nothing
; REGS: R0, R1, R16, R20, R24, R25, Z

Flash_ReadPageIntoPageBuffer:
  ldi r24, LOW(PAGESIZE*2)
  ldi r25, HIGH(PAGESIZE*2)
Flash_ReadPageIntoPageBuffer_loop:
    lpm r0, Z+                                ; read source data from FLASH (low)
    lpm r1, Z                                 ; read source data from FLASH (high)
    sbiw ZH:ZL, 1                             ; rewind Z address for following SPM
    ldi r20, (1<<SPMEN)                       ; enable next SPM, write into temp page buffer
    rcall flashDoSpm                          ; (R16)
    adiw ZH:ZL, 2
    sbiw r25:r24, 2                           ; use subi for PAGESIZEB<=256
  brne Flash_ReadPageIntoPageBuffer_loop
  ret



; ---------------------------------------------------------------------------
; Flash_WriteIntoPage
;
; Interrupts must be disabled!
;
; IN:
; - Z: Address to write to (byte address as for LPM!)
; - R1:R0: Word to write into page
; OUT:
; - Z: address for next write
; REGS: R16

Flash_WriteIntoPage:
  ldi r20, (1<<SPMEN)                       ; enable next SPM, write into temp page buffer
  rcall flashDoSpm                          ; (R16)
  adiw zh:zl, 2
  ret



; ---------------------------------------------------------------------------
; Flash_ErasePage
;
; Interrupts must be disabled!
;
; IN:
; - Z: Address of the page to erase.
; OUT:
; - nothing
; REGS: R20 (R16)

Flash_ErasePage:
  ldi r20, (1<<PGERS) + (1<<SPMEN)          ; enable next SPM, erase page (R1/R0 ignored)
  rcall flashDoSpm                          ; (R16)
  ret



; ---------------------------------------------------------------------------
; Flash_WritePage
;
; Interrupts must be disabled!
;
; IN:
; - Z: Address of the page to erase.
; OUT:
; - nothing
; REGS: R15, R20 (R16)

Flash_WritePage:
  ldi r20, (1<<PGWRT) + (1<<SPMEN)          ; enable next SPM, write page (R1/R0 ignored)
  rcall flashDoSpm                          ; (R16)
  ret



; ---------------------------------------------------------------------------
; flashDoSpm
;
; wait until possible previous SPM finished and then issue another SPM.
;
; IN:
; - R20: value for register SPMCR
; - R0:  low value for SPM
; - R1:  high value for SPM
; - Z  : address for SPM (byte address!)
; OUT:
; - nothing
; REGS: R16

flashDoSpm:
wait:                ; wait for possibly previous SPM to complete
    in r16, SPMCSR
    sbrc r16, SPMEN
  rjmp wait
  ; SPM timed sequence
  out SPMCSR, r20
  spm
  ret



; ---------------------------------------------------------------------------
; flashReadEepromIncr
;
; Read a byte from EEPROM (see example in ATtiny24/44/84 manual p.19).
;
; IN:
; - X: EEPROM Address to read from
; OUT:
; - R16: byte read
; - X: EEPROM Address incremented
; REGS: R16

flashReadEepromIncr:
  sbic EECR, EEPE          ; wait for previous write to complete (if any)
    rjmp flashReadEepromIncr
  out EEARH, xh            ; set EEPROM address
  out EEARL, xl
  sbi EECR, EERE           ; start EEPROM read by writing EERE
  in r16, EEDR             ; read data from data register
  adiw xh:xl, 1
  ret



; ---------------------------------------------------------------------------
; flashReadUid
;
; Read UID from EEPROM.
;
; IN:
; OUT:
; - R18:R19:R20:R21: UID
; REGS: R16, X

flashReadUid:
  push r15
    in r15, SREG
    cli
    ldi xl, LOW(EEPROM_OFFS_UUID)
    ldi xh, HIGH(EEPROM_OFFS_UUID)
    rcall flashReadEepromIncr ; (R16)
    mov r18, r16
    rcall flashReadEepromIncr ; (R16)
    mov r19, r16
    rcall flashReadEepromIncr ; (R16)
    mov r20, r16
    rcall flashReadEepromIncr ; (R16)
    mov r21, r16
    out SREG, r15
  pop r15
  ret





FLASH_END:
.equ MODULE_SIZE_FLASH = FLASH_END-FLASH_BEGIN



