; ***************************************************************************
; 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 COMONUART0_READ_TIMEOUT = 3
.equ COMONUART0_MSG_INTERVAL = 0



.dseg

comOnUart0_iface: .byte UART_HW_IFACE_SIZE


.cseg


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

ComOnUart0_Init:
  ldi yl, LOW(comOnUart0_iface)
  ldi yh, HIGH(comOnUart0_iface)
  rcall comOnUart0SetAttnInput   ; (none)

  rcall UART_HW_Interface_Init            ; (R16, R17, X)
  rcall comOnUart0Init                ; (R16, R17, X)
  ldi r16, COMONUART0_IFACENUM
  std Y+NET_IFACE_OFFS_IFACENUM, r16

  sbi COM_IRQ_ADDR_ATTN, COM_IRQ_BIT_ATTN ; enable pin change irq for ATTN line


  in r16, GIMSK                           ; enable pin change irq PCIE0 or PCIE1
  sbr r16, (1<<COM_IRQ_GIMSK_ATTN)
  out GIMSK, R16
  ldi r16, (1<<COM_IRQ_GIFR_ATTN)         ; clear pending irq by writing 1 to ATTN bit
  out GIFR, r16

  ret
; @end



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

ComOnUart0_Periodically:
  ldi yl, LOW(comOnUart0_iface)
  ldi yh, HIGH(comOnUart0_iface)
  rjmp NET_Interface_Periodically
; @end



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

ComOnUart0_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(comOnUart0_iface)
                      ldi yh, HIGH(comOnUart0_iface)
                      rcall comOnUart0RxCharIsr ; (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 ComOnUart0_TxUdreIsr @global @isr
;
; @clobbers none

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



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

ComOnUart0_TxCharIsr:
  push r15
    in r15, SREG
    push r16
      push r17
        push r18
          push xl
            push xh
              push yl
                push yh
                  ldi yl, LOW(comOnUart0_iface)
                  ldi yh, HIGH(comOnUart0_iface)
                  rcall comOnUart0TxCharIsr ; (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 ComOnUart0_AttnChangeIsr @global @isr
;
; @clobbers none

ComOnUart0_AttnChangeIsr:
  push r15
    in r15, SREG
    rcall ComOnUart0_HandleAttnChange
    out SREG, r15
  pop r15
  reti
; @end



; ---------------------------------------------------------------------------
; @routine ComOnUart0_HandleAttnChange @global
;
; @clobbers none

ComOnUart0_HandleAttnChange:
  push r16
    push r17
      push r18
        push r24
          push r25
            push xl
              push xh
                push yl
                  push yh
                    ldi yl, LOW(comOnUart0_iface)
                    ldi yh, HIGH(comOnUart0_iface)
                    rcall comOnUart0ActOnAttn      ; (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 comOnUart0ActOnAttn @global
;
; @param Y pointer to interface data in SRAM
; @clobbers R16 (R17, R24, R25, X)

comOnUart0ActOnAttn:
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEMODE  ; test for active write mode
  cpi r16, UART_HW_WRITEMODE_IDLE
  brne comOnUart0ActOnAttn_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 comOnUart0ActOnAttn_end             ; already in read mode

  sbic COM_ATTN_INPUT, COM_ATTN_PIN        ; ATTN high?
    rjmp comOnUart0ActOnAttn_end
  rcall comOnUart0StartReading           ; (R16, R17, R24, R25, X)

comOnUart0ActOnAttn_end:
  ret
; @end



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

comOnUart0StartReading:
  rcall UART_HW_Interface_EnsureReadBuffer          ; (R16, R17, R24, R25, X)
  brcs comOnUart0EnterReading_okay
  ; no buffer, missed message
  ldi r16, NET_IFACE_OFFS_ERR_MISSED_LOW
  rcall NET_Interface_IncCounter16            ; (R24, R25)
  ret
comOnUart0EnterReading_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 comOnUart0StartRx                   ; R16
  ret
; @end



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

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



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

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



; ---------------------------------------------------------------------------
; @routine ComOnUart0_Run @global
;
; @clobbers all

ComOnUart0_Run:
  push r15
    in r15, SREG
    cli
    ldi yl, LOW(comOnUart0_iface)
    ldi yh, HIGH(comOnUart0_iface)

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



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

comOnUart0RunWriteModes:
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEMODE   ; handle write functions
  cpi r16, UART_HW_WRITEMODE_IDLE
  breq comOnUart0RunWriteIdle
  cpi r16, UART_HW_WRITEMODE_WRITING
  breq comOnUart0RunWriting
  cpi r16, UART_HW_WRITEMODE_WAITBUFFEREMPTY
  breq comOnUart0RunWaitBufferEmpty
  cpi r16, UART_HW_WRITEMODE_WRITEBUFFEREMPTY
  breq comOnUart0RunWriteBufferEmpty
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0RunWriteIdle
;
; @clobbers

comOnUart0RunWriteIdle:
  ldd r16, Y+NET_IFACE_OFFS_WRITETIMER
  cpi r16, COMONUART0_MSG_INTERVAL           ; wait a bit between messages
  brcs comOnUart0RunWriteIdle_end

  rcall NET_Interface_PeekNextOutgoingMsgNum ; r16=msgNum
  brcc comOnUart0RunWriteIdle_end            ; no outmsg in queue
  
  sbis COM_ATTN_INPUT, COM_ATTN_PIN          ; ATTN low?
    rjmp comOnUart0RunWriteIdle_end          ; yes, line busy, jmp

  rcall comOnUart0SetAttnLow                 ; 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 comOnUart0SendBuffer                 ; (R16, R17)
  rcall NET_Interface_GetNextOutgoingMsgNum  ; take msg from queue (R17, R18, X)
comOnUart0RunWriteIdle_end:
  ret


; ---------------------------------------------------------------------------
; @routine comOnUart0RunWriting
;
; @clobbers

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



; ---------------------------------------------------------------------------
; @routine comOnUart0RunWaitBufferEmpty
;
; @clobbers none

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



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

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

  rcall comOnUart0SetAttnInput            ; 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 comOnUart0RunReadModes
;
; @clobbers all, !Y

comOnUart0RunReadModes:
  ldd r16, Y+UART_HW_IFACE_OFFS_READMODE   ; handle read functions
  cpi r16, UART_HW_READMODE_IDLE
  breq comOnUart0RunReadIdle               ; (R16, R17, R24, R25, X)
  cpi r16, UART_HW_READMODE_READING
  breq comOnUart0RunReading                ; (none)
  cpi r16, UART_HW_READMODE_SKIPPING
  breq comOnUart0RunSkipping               ; (R16)
  cpi r16, UART_HW_READMODE_MSGRECEIVED
  breq comOnUart0RunMsgReceived            ; (R16, R17, R18, R24, R25)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0RunReadIdle
;
; @clobbers none

comOnUart0RunReadIdle:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0RunReading
;
; @clobbers none

comOnUart0RunReading:
  ret

  ldd r16, Y+NET_IFACE_OFFS_READTIMER
  cpi r16, COMONUART0_READ_TIMEOUT
  brcs comOnUart0RunReading_end
  rcall comOnUart0StopRx                   ; (R16)
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  ldi r17, 0xff
  std Y+UART_HW_IFACE_OFFS_READBUFNUM, r17
  cp r16, r17
  breq comOnUart0RunReading_enterIdle
  rcall NET_Buffer_ReleaseByNum               ; (R16, X)
comOnUart0RunReading_enterIdle:
  ldi r16, UART_HW_READMODE_IDLE
  std Y+UART_HW_IFACE_OFFS_READMODE, r16
comOnUart0RunReading_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0RunSkipping
;
; @clobbers R16

comOnUart0RunSkipping:
  ldi r17, 0xff
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  std Y+UART_HW_IFACE_OFFS_READBUFNUM, r17
  cp r16, r17
  breq comOnUart0RunSkipping_checkAttn
  rcall NET_Buffer_ReleaseByNum                 ; (R16, X)
comOnUart0RunSkipping_checkAttn:
  sbis COM_ATTN_INPUT, COM_ATTN_PIN              ; ATTN low?
    rjmp comOnUart0RunSkipping_end               ; yes, jmp
  ; ATTN ishigh, skipped message finished
  ldi r16, UART_HW_READMODE_IDLE
  std Y+UART_HW_IFACE_OFFS_READMODE, r16
comOnUart0RunSkipping_end:
  ret
; @end



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

comOnUart0RunMsgReceived:
  rjmp UART_HW_Interface_HandleMsgReceived
; @end



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

comOnUart0Init:
.ifdef COM_ATTN_PUE
    lds r16, COM_ATTN_PUE
    cbr r16, (1<<COM_ATTN_PIN)           ; disable pullup on ATTN
    sts COM_ATTN_PUE, r16
.endif
  rcall comOnUart0SetAttnInput
  M_UART_HW_Uart_Init 0
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0StartRx @global
;
; @clobbers R16

comOnUart0StartRx:
  M_UART_HW_Uart_StartRx 0
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0StopRx @global
;
; @clobbers R16

comOnUart0StopRx:
  M_UART_HW_Uart_StopRx 0
  ret
; @end



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

comOnUart0StartTx:
  M_UART_HW_Uart_StartTx 0
  ret
; @end



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

comOnUart0StopTx:
  M_UART_HW_Uart_StopTx 0
  ret
; @end


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

comOnUart0RxCharIsr:
  M_UART_HW_Uart_RxCharHalfDuplexIsr 0
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0TxUdreIsr @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

comOnUart0TxUdreIsr:
  M_UART_HW_Uart_TxUdreIsr 0
  ret
; @end



; ---------------------------------------------------------------------------
; @routine comOnUart0TxCharIsr @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

comOnUart0TxCharIsr:
  M_UART_HW_Uart_TxCharIsr 0
  ret
; @end



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

comOnUart0SetAttnInput:
  cbi COM_ATTN_DDR, COM_ATTN_PIN           ; set ATTN as input
.ifdef COM_ATTN_PUE
;  cbi COM_ATTN_PUE, COM_ATTN_PIN           ; disable pullup on ATTN
.else
  cbi COM_ATTN_OUTPUT, COM_ATTN_PIN        ; disable pullup on ATTN
.endif
  ret
; @end



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

comOnUart0SetAttnLow:
  sbi COM_ATTN_DDR, COM_ATTN_PIN            ; set ATTN as output
  cbi COM_ATTN_OUTPUT, COM_ATTN_PIN         ; set ATTN low
  ret
; @end



