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


.equ COMONUART1_READ_TIMEOUT = 3
.equ COMONUART1_MSG_INTERVAL = 0



.dseg

comOnUart1_iface: .byte UART_HW_IFACE_SIZE


.cseg


; ---------------------------------------------------------------------------
; @routine ComOnUart1_Init @global
;
; @clobbers Y (R16, R17, X)

ComOnUart1_Init:
  ldi yl, LOW(comOnUart1_iface)
  ldi yh, HIGH(comOnUart1_iface)
  rcall comOnUart1SetAttnInput   ; (none)

  rcall UART_HW_Interface_Init              ; (R16, R17, X)
  rcall comOnUart1Init                      ; (R16, R17, X)
  ldi r16, COMONUART1_IFACENUM
  std Y+NET_IFACE_OFFS_IFACENUM, r16

  inr r16, COM_IRQ_ADDR_ATTN1               ; enable pin change irq for ATTN line
  sbr r16, (1<<COM_IRQ_BIT_ATTN1)
  outr COM_IRQ_ADDR_ATTN1, r16
  in r16, GIMSK                             ; enable pin change irq PCIE0 or PCIE1
  sbr r16, (1<<COM_IRQ_GIMSK_ATTN1)
  out GIMSK, R16
  ldi r16, (1<<COM_IRQ_GIFR_ATTN1)          ; clear pending irq by writing 1 to ATTN bit
  out GIFR, r16

  ret
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_Periodically @global
;
; @clobbers R16, Y

ComOnUart1_Periodically:
  ldi yl, LOW(comOnUart1_iface)
  ldi yh, HIGH(comOnUart1_iface)
  rjmp NET_Interface_Periodically
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_RxCharIsr @global @isr
;
; @clobbers none

ComOnUart1_RxCharIsr:
  push r15
    in r15, SREG
    push r16
      push r17
        push r18
          push r24
            push r25
              push xl
                push xh
                  push yl
                    push yh
                      ldi yl, LOW(comOnUart1_iface)
                      ldi yh, HIGH(comOnUart1_iface)
                      rcall comOnUart1RxCharIsr ; (R16, R17, R18, R24, R25, X)
                    pop yh
                  pop yl
                pop xh
              pop xl
            pop r25
          pop r24
        pop r18
      pop r17
    pop r16
    out SREG, r15
  pop r15
  reti
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_TxUdreIsr @global @isr
;
; @clobbers none

ComOnUart1_TxUdreIsr:
  push r15
    in r15, SREG
    push r16
      push r17
        push xl
          push xh
            push yl
              push yh
                ldi yl, LOW(comOnUart1_iface)
                ldi yh, HIGH(comOnUart1_iface)
                rcall comOnUart1TxUdreIsr  ; (R16, R17, X)
              pop yh
            pop yl
          pop xh
        pop xl
      pop r17
    pop r16
    out SREG, r15
  pop r15
  reti
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_TxCharIsr @global @isr
;
; @clobbers none

ComOnUart1_TxCharIsr:
  push r15
    in r15, SREG
    push r16
      push r17
        push r18
          push xl
            push xh
              push yl
                push yh
                  ldi yl, LOW(comOnUart1_iface)
                  ldi yh, HIGH(comOnUart1_iface)
                  rcall comOnUart1TxCharIsr ; (R16, R17, R18, X)
                pop yh
              pop yl
            pop xh
          pop xl
        pop r18
      pop r17
    pop r16
    out SREG, r15
  pop r15
  reti
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_AttnChangeIsr @global @isr
;
; @clobbers none


ComOnUart1_AttnChangeIsr:
  push r15
    in r15, SREG
    rcall ComOnUart1_HandleAttnChange
    out SREG, r15
  pop r15
  reti
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_HandleAttnChange @global
;
; @clobbers none

ComOnUart1_HandleAttnChange:
  push r16
    push r17
      push r18
        push r24
          push r25
            push xl
              push xh
                push yl
                  push yh
                    ldi yl, LOW(comOnUart1_iface)
                    ldi yh, HIGH(comOnUart1_iface)
                    rcall comOnUart1ActOnAttn      ; (R16, R17, R24, R25, X)
                  pop yh
                pop yl
              pop xh
            pop xl
          pop r25
        pop r24
      pop r18
    pop r17
  pop r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1ActOnAttn @global
;
; @param Y pointer to interface data in SRAM
; @clobbers R16 (R17, R24, R25, X)

comOnUart1ActOnAttn:
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEMODE  ; test for active write mode
  cpi r16, UART_HW_WRITEMODE_IDLE
  brne comOnUart1ActOnAttn_end             ; in write mode, don't start read mode

  ldd r16, Y+UART_HW_IFACE_OFFS_READMODE   ; test for active read mode
  cpi r16, UART_HW_READMODE_IDLE
  brne comOnUart1ActOnAttn_end             ; already in read mode

  sbic COM_ATTN1_INPUT, COM_ATTN1_PIN        ; ATTN high?
    rjmp comOnUart1ActOnAttn_end
  rcall comOnUart1StartReading           ; (R16, R17, R24, R25, X)

comOnUart1ActOnAttn_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1StartReading
;
; @clobbers R16 (R17, R24, R25, X)

comOnUart1StartReading:
  rcall UART_HW_Interface_EnsureReadBuffer          ; (R16, R17, R24, R25, X)
  brcs comOnUart1EnterReading_okay
  ; no buffer, missed message
  ldi r16, NET_IFACE_OFFS_ERR_MISSED_LOW
  rcall NET_Interface_IncCounter16            ; (R24, R25)
  ret
comOnUart1EnterReading_okay:
  ldi r16, UART_HW_READMODE_READING
  std Y+UART_HW_IFACE_OFFS_READMODE, r16
  clr r16
  std Y+NET_IFACE_OFFS_READTIMER, r16
  rcall comOnUart1StartRx                   ; R16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1SendBuffer @global
;
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17

comOnUart1SendBuffer:
  push r15
    in r15, SREG
    cli
    ldd r17, Y+UART_HW_IFACE_OFFS_WRITEMODE
    cpi r17, UART_HW_WRITEMODE_IDLE
    breq comOnUart1SendBuffer_setBuffer
    out SREG, r15
  pop r15
  clc
  ret
comOnUart1SendBuffer_setBuffer:
    rcall UART_HW_Interface_SetWriteBuffer    ; (R17)
    ldi r17, UART_HW_WRITEMODE_WRITING
    std Y+UART_HW_IFACE_OFFS_WRITEMODE, r17
    rcall comOnUart1StartTx               ; (R16)
  pop r15
  out SREG, r15
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1ResetReadBuffer
;
; @clobbers R16 (R17, X)

comOnUart1ResetReadBuffer:
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  cpi r16, 0xff
  breq comOnUart1ResetReadBuffer_end
  rcall NET_Buffer_Locate                     ; (R17, X)
  rcall UART_HW_Interface_SetReadBuffer       ; (R17)
comOnUart1ResetReadBuffer_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart1_Run @global
;
; @clobbers all

ComOnUart1_Run:
  push r15
    in r15, SREG
    cli
    ldi yl, LOW(comOnUart1_iface)
    ldi yh, HIGH(comOnUart1_iface)

    rcall comOnUart1RunReadModes
    ldd r16, Y+UART_HW_IFACE_OFFS_READMODE    ; test for active read mode
    cpi r16, UART_HW_READMODE_IDLE
    brne ComOnUart1_Run_end
    rcall comOnUart1RunWriteModes             ; only call write routine if read idle
ComOnUart1_Run_end:
  pop r15
  out SREG, r15
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunWriteModes
;
; @clobbers all, !Y

comOnUart1RunWriteModes:
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEMODE   ; handle write functions
  cpi r16, UART_HW_WRITEMODE_IDLE
  breq comOnUart1RunWriteIdle
  cpi r16, UART_HW_WRITEMODE_WRITING
  breq comOnUart1RunWriting
  cpi r16, UART_HW_WRITEMODE_WAITBUFFEREMPTY
  breq comOnUart1RunWaitBufferEmpty
  cpi r16, UART_HW_WRITEMODE_WRITEBUFFEREMPTY
  breq comOnUart1RunWriteBufferEmpty
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunWriteIdle
;
; @clobbers

comOnUart1RunWriteIdle:
  ldd r16, Y+NET_IFACE_OFFS_WRITETIMER
  cpi r16, COMONUART1_MSG_INTERVAL           ; wait a bit between messages
  brcs comOnUart1RunWriteIdle_end

  rcall NET_Interface_PeekNextOutgoingMsgNum ; r16=msgNum
  brcc comOnUart1RunWriteIdle_end            ; no outmsg in queue
  
  sbis COM_ATTN1_INPUT, COM_ATTN1_PIN        ; ATTN low?
    rjmp comOnUart1RunWriteIdle_end          ; yes, line busy, jmp

  rcall comOnUart1SetAttnLow                 ; reserve bus as soon as possible
  Utils_WaitNanoSecs COM_BIT_LENGTH, 0, r22  ; wait for one bit duration
  Utils_WaitNanoSecs COM_BIT_LENGTH, 0, r22  ; wait for one bit duration

  rcall NET_Buffer_Locate                    ; (R17)
  rcall comOnUart1SendBuffer                 ; (R16, R17)
  rcall NET_Interface_GetNextOutgoingMsgNum  ; take msg from queue (R17, R18, X)
comOnUart1RunWriteIdle_end:
  ret


; ---------------------------------------------------------------------------
; @routine comOnUart1RunWriting
;
; @clobbers

comOnUart1RunWriting:
  ; TODO: check for timeout etc.
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunWaitBufferEmpty
;
; @clobbers none

comOnUart1RunWaitBufferEmpty:
  ; TODO: check for timeout etc.
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunWriteBufferEmpty
;
; @clobbers R16, R17, X

comOnUart1RunWriteBufferEmpty:
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEBUFNUM
  ldi r17, 0xff
  cp r16, r17
  breq comOnUart1RunWriteBufferEmpty_setIdle
  std Y+UART_HW_IFACE_OFFS_WRITEBUFNUM, r17
  rcall NET_Buffer_ReleaseByNum               ; (R16, X)
comOnUart1RunWriteBufferEmpty_setIdle:
  rcall comOnUart1StopTx                  ; disable transceiver and interrupts (R16)

  rcall comOnUart1SetAttnInput            ; set ATTN as input

  ldi r16, UART_HW_WRITEMODE_IDLE
  std Y+UART_HW_IFACE_OFFS_WRITEMODE, r16
  ldi r16, NET_IFACE_OFFS_PACKETSOUT_LOW      ; increment packets counter
  rcall NET_Interface_IncCounter16            ; (R24, R25)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunReadModes
;
; @clobbers all, !Y

comOnUart1RunReadModes:
  ldd r16, Y+UART_HW_IFACE_OFFS_READMODE   ; handle read functions
  cpi r16, UART_HW_READMODE_IDLE
  breq comOnUart1RunReadIdle               ; (R16, R17, R24, R25, X)
  cpi r16, UART_HW_READMODE_READING
  breq comOnUart1RunReading                ; (none)
  cpi r16, UART_HW_READMODE_SKIPPING
  breq comOnUart1RunSkipping               ; (R16)
  cpi r16, UART_HW_READMODE_MSGRECEIVED
  breq comOnUart1RunMsgReceived            ; (R16, R17, R18, R24, R25)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunReadIdle
;
; @clobbers none

comOnUart1RunReadIdle:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunReading
;
; @clobbers none

comOnUart1RunReading:
  ret

  ldd r16, Y+NET_IFACE_OFFS_READTIMER
  cpi r16, COMONUART1_READ_TIMEOUT
  brcs comOnUart1RunReading_end
  rcall comOnUart1StopRx                   ; (R16)
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  ldi r17, 0xff
  std Y+UART_HW_IFACE_OFFS_READBUFNUM, r17
  cp r16, r17
  breq comOnUart1RunReading_enterIdle
  rcall NET_Buffer_ReleaseByNum               ; (R16, X)
comOnUart1RunReading_enterIdle:
  ldi r16, UART_HW_READMODE_IDLE
  std Y+UART_HW_IFACE_OFFS_READMODE, r16
comOnUart1RunReading_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunSkipping
;
; @clobbers R16

comOnUart1RunSkipping:
  ldi r17, 0xff
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  std Y+UART_HW_IFACE_OFFS_READBUFNUM, r17
  cp r16, r17
  breq comOnUart1RunSkipping_checkAttn
  rcall NET_Buffer_ReleaseByNum                 ; (R16, X)
comOnUart1RunSkipping_checkAttn:
  sbis COM_ATTN1_INPUT, COM_ATTN1_PIN              ; ATTN low?
    rjmp comOnUart1RunSkipping_end               ; yes, jmp
  ; ATTN ishigh, skipped message finished
  ldi r16, UART_HW_READMODE_IDLE
  std Y+UART_HW_IFACE_OFFS_READMODE, r16
comOnUart1RunSkipping_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1RunMsgReceived
;
; @clobbers R16 (R17, R18, R24, R25)

comOnUart1RunMsgReceived:
  rjmp UART_HW_Interface_HandleMsgReceived
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1Init @global
;
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_STATE)
; @clobbers R16, R17, X

comOnUart1Init:
.ifdef COM_ATTN1_PUE
    lds r16, COM_ATTN1_PUE
    cbr r16, 1<<COM_ATTN1_PIN           ; disable pullup on ATTN
    sts COM_ATTN1_PUE, r16
.endif
  rcall comOnUart1SetAttnInput
  M_UART_HW_Uart_Init 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1StartRx @global
;
; @clobbers R16

comOnUart1StartRx:
  M_UART_HW_Uart_StartRx 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1StopRx @global
;
; @clobbers R16

comOnUart1StopRx:
  M_UART_HW_Uart_StopRx 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1StartTx @global
;
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_STATE)
; @clobbers R16

comOnUart1StartTx:
  M_UART_HW_Uart_StartTx 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1StopTx @global
;
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_STATE)
; @clobbers R16

comOnUart1StopTx:
  M_UART_HW_Uart_StopTx 1
  ret
; @end


; ---------------------------------------------------------------------------
; @routine comOnUart1RxCharIsr @global
;
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_MODE)
; @clobbers R16 (R17, R18, R24, R25, X)

comOnUart1RxCharIsr:
  M_UART_HW_Uart_RxCharHalfDuplexIsr 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1TxUdreIsr @global
;
; Handler for UDRE1 interrupt called when TX data register is empty.
;
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_MODE)
; @clobbers R16, R17, X

comOnUart1TxUdreIsr:
  M_UART_HW_Uart_TxUdreIsr 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1TxCharIsr @global
;
; Handler for TXC1 interrupt called when a last byte has been completely sent and
; the data register is empty.
;
; @param Y pointer to interface data in SRAM (see @ref UART_HW_IFACE_OFFS_MODE)
; @clobbers R16

comOnUart1TxCharIsr:
  M_UART_HW_Uart_TxCharIsr 1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1SetAttnInput @global
;
; Set ATTN line as input (making it effectively HIGH due to pull-up resistor on
; the line)
;
; @clobbers none

comOnUart1SetAttnInput:
  cbi COM_ATTN1_DDR, COM_ATTN1_PIN           ; set ATTN as input
.ifdef COM_ATTN1_PUE
;  cbi COM_ATTN1_PUE, COM_ATTN_PIN           ; disable pullup on ATTN
.else
  cbi COM_ATTN1_OUTPUT, COM_ATTN1_PIN        ; disable pullup on ATTN
.endif
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart1SetAttnLow @global
;
; Set ATTN line low.
;
; @clobbers none

comOnUart1SetAttnLow:
  sbi COM_ATTN1_DDR, COM_ATTN1_PIN            ; set ATTN as output
  cbi COM_ATTN1_OUTPUT, COM_ATTN1_PIN         ; set ATTN low
  ret
; @end



