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



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_Init
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17, X

.macro M_UART_HW_Uart_Init
  rcall NET_Interface_Init

  ; set baudrate
.if clock == 8000000
  ldi r16, 25 ; (19.2Kb/s at 8MHz)
  ldi r17, 0
.endif

.if clock == 1000000
  ldi r16, 2  ; (19.2Kb/s at 1MHz)
  ldi r17, 0  
.endif

  sts UBRR@0H, r17
  sts UBRR@0L, r16

  ; set character format 
  ldi r16, (1<<USBS@0)|(3<<UCSZ@00)
  sts UCSR@0C, r16
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_StartRx
;
; @param %0 UART number ("0" for UART0)
; @clobbers none

.macro M_UART_HW_Uart_StartRx
  lds r16, UCSR@0B
  sbr r16, (1<<RXCIE@0) | (1<<RXEN@0)  ; enable RX complete interrupt, enable receive
  sts UCSR@0B, r16
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_StopRx
;
; @param %0 UART number ("0" for UART0)
; @clobbers R16

.macro M_UART_HW_Uart_StopRx
  lds r16, UCSR@0B
  cbr r16, (1<<RXCIE@0 | (1<<RXEN@0))   ; disable RX complete interrupt, disable receive
  sts UCSR@0B, r16
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_StartTx
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16

.macro M_UART_HW_Uart_StartTx
  lds r16, UCSR@0A
  cbr r16, (1<<TXC@0)                               ; clear TXCn interrupt
  sts UCSR@0A, r16
  lds r16, UCSR@0B
  sbr r16, (1<<UDRIE@0) | (1<<TXC@0) | (1<<TXEN@0)  ; enable TX UDRE and TXC1 interrupt, enable transceive
  sts UCSR@0B, r16
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_StopTx
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16

.macro M_UART_HW_Uart_StopTx
  lds r16, UCSR@0B
  cbr r16, (1<<UDRIE@0) | (1<<TXC@0) | (1<<TXEN@0)  ; disable TX UDRE and TXC1 interrupt, enable transceive
  sts UCSR@0B, r16
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_Flush
;
; Flush receiption buffer.
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16

.macro M_UART_HW_Uart_Flush
l_loop_%:
    lds r16, UCSR@0A
    sbrs r16, RXC@0
      rjmp l_end_%
    lds r16, UDR@0
    clr r16
    std Y+NET_IFACE_OFFS_READTIMER, r16
  rjmp l_loop_%
l_end_%:
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_RxCharHalfDuplexIsr
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16 (R17, R18, R24, R25, X)

.macro M_UART_HW_Uart_RxCharHalfDuplexIsr
  ; check for errors
  lds r16, UCSR@0A                              ; check for errors
  andi r16, (1<<FE@0) | (1<<DOR@0) | (1<<UPE@0)
  brne l_hwerr_%
  ; read char
  lds r16, UCSR@0A
  sbrs r16, RXC@0
    rjmp l_end_%                                ; no data
  lds r16, UDR@0                                ; r16=received char
  ; check read mode
  ldd r17, Y+UART_HW_IFACE_OFFS_READMODE
  cpi r17, UART_HW_READMODE_READING
  breq l_storeChar_%
  cpi r17, UART_HW_READMODE_SKIPPING
  breq l_skipChar_%
  rjmp l_overrun_%                              ; neither read nor skip mode
l_skipChar_%:
  clr r16
  std Y+NET_IFACE_OFFS_READTIMER, r16           ; reset read timer
  rjmp l_end_%
l_storeChar_%:
  mov r18, r16                                  ; r18=received char
  ; check buffer
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  cpi r16, 0xff
  breq l_overrun_%

  ; check for buffer overrun
  ldd r17, Y+UART_HW_IFACE_OFFS_READBUFLEFT     ; r17=bytes left
  tst r17
  breq l_econtent_%         ; msg too long

  ; actually store byte, increment/decrement counters and pos
  ldd xl, Y+UART_HW_IFACE_OFFS_READBUFPOS_LOW
  ldd xh, Y+UART_HW_IFACE_OFFS_READBUFPOS_HIGH
  st X+, r18                                    ; r18=byte to store
  clr r16
  std Y+NET_IFACE_OFFS_READTIMER, r16           ; reset read timer
  std Y+UART_HW_IFACE_OFFS_READBUFPOS_LOW, xl
  std Y+UART_HW_IFACE_OFFS_READBUFPOS_HIGH, xh
  ldd r18, Y+UART_HW_IFACE_OFFS_READBUFUSED     ; r18=bytes in buffer
  inc r18
  std Y+UART_HW_IFACE_OFFS_READBUFUSED, r18
  dec r17
  std Y+UART_HW_IFACE_OFFS_READBUFLEFT, r17
  breq l_msgFinished_%

  ; check msg size
  cpi r18, 2                                    ; bytes in buffer, exactly 2?
  brne l_end_%                                    ; nope, done
  sbiw xh:xl, 1                                 ; yes, determine message length (msgLen at previous pos)
  ld r16, X+                                    ; read payload length byte
  subi r16, -3                                  ; add 3 (dest addr, length, crc byte)
  cpi r16, (NET_BUFFERS_SIZE-2)                 ; total msg length ok? (subtract 1 for buffer header byte)
  brcc l_econtent_%                               ; content error (msg too long)
  subi r16, 2                                   ; subtract bytes already received
  std Y+UART_HW_IFACE_OFFS_READBUFLEFT, r16     ; set new number of bytes left
  brne l_end_%              ; jmp if still bytes left to receive

l_msgFinished_%:
  M_UART_HW_Uart_StopRx @0                ; (R16)
  ldi r16, NET_IFACE_OFFS_PACKETSIN_LOW
  ldi r17, UART_HW_READMODE_MSGRECEIVED
  rjmp l_incCounterAndEnterMode_%
l_hwerr_%:
  ldi r16, NET_IFACE_OFFS_ERR_IO_LOW
  rjmp l_incCounterAndEnterSkipping_%
l_econtent_%:
  ldi r16, NET_IFACE_OFFS_ERR_CONTENT_LOW
  rjmp l_incCounterAndEnterSkipping_%
l_overrun_%:
  ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
l_incCounterAndEnterSkipping_%:
  M_UART_HW_Uart_StopRx @0                ; (R16)
  M_UART_HW_Uart_Flush @0
  ldi r17, UART_HW_READMODE_SKIPPING
l_incCounterAndEnterMode_%:
  rcall NET_Interface_IncCounter16              ; (R24, R25)
  std Y+UART_HW_IFACE_OFFS_READMODE, r17        ; set read mode
l_end_%:
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_RxCharFullDuplexIsr
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16 (R17, R18, R24, R25, X)

.macro M_UART_HW_Uart_RxCharFullDuplexIsr
  ; check for errors
  lds r16, UCSR@0A                              ; check for errors
  andi r16, (1<<FE@0) | (1<<DOR@0) | (1<<UPE@0)
  brne l_hwerr_%
  ; read char
  lds r16, UCSR@0A
  sbrs r16, RXC@0
    rjmp l_end_%                                ; no data
  lds r16, UDR@0                                ; r16=received char
  ; check read mode
  ldd r17, Y+UART_HW_IFACE_OFFS_READMODE
  cpi r17, UART_HW_READMODE_READING
  breq l_storeChar_%
  cpi r17, UART_HW_READMODE_SKIPPING
  breq l_skipChar_%
  rjmp l_overrun_%                              ; neither read nor skip mode
l_skipChar_%:
  clr r16
  std Y+NET_IFACE_OFFS_READTIMER, r16           ; reset read timer
  rjmp l_end_%
l_storeChar_%:
  mov r18, r16                                  ; r18=received char
  ; check buffer
  ldd r16, Y+UART_HW_IFACE_OFFS_READBUFNUM
  cpi r16, 0xff
  breq l_overrun_%

  ; check for buffer overrun
  ldd r17, Y+UART_HW_IFACE_OFFS_READBUFLEFT     ; r17=bytes left
  tst r17
  breq l_econtent_%         ; msg too long

  ; actually store byte, increment/decrement counters and pos
  ldd xl, Y+UART_HW_IFACE_OFFS_READBUFPOS_LOW
  ldd xh, Y+UART_HW_IFACE_OFFS_READBUFPOS_HIGH
  st X+, r18                                    ; r18=byte to store
  clr r16
  std Y+NET_IFACE_OFFS_READTIMER, r16           ; reset read timer
  std Y+UART_HW_IFACE_OFFS_READBUFPOS_LOW, xl
  std Y+UART_HW_IFACE_OFFS_READBUFPOS_HIGH, xh
  ldd r18, Y+UART_HW_IFACE_OFFS_READBUFUSED     ; r18=bytes in buffer
  inc r18
  std Y+UART_HW_IFACE_OFFS_READBUFUSED, r18
  dec r17
  std Y+UART_HW_IFACE_OFFS_READBUFLEFT, r17
  breq l_msgFinished_%

  ; check msg size
  cpi r18, 2                                    ; bytes in buffer, exactly 2?
  brne l_end_%                                    ; nope, done
  sbiw xh:xl, 1                                 ; yes, determine message length (msgLen at previous pos)
  ld r16, X+                                    ; read payload length byte
  subi r16, -3                                  ; add 3 (dest addr, length, crc byte)
  cpi r16, (NET_BUFFERS_SIZE-2)                 ; total msg length ok?
  brcc l_econtent_%                               ; content error (msg too long)
  subi r16, 2                                   ; subtract bytes already received
  std Y+UART_HW_IFACE_OFFS_READBUFLEFT, r16     ; set new number of bytes left
  brne l_end_%              ; jmp if still bytes left to receive

l_msgFinished_%:
  ldi r16, NET_IFACE_OFFS_PACKETSIN_LOW
  ldi r17, UART_HW_READMODE_MSGRECEIVED
  rjmp l_incCounterAndEnterMode_%
l_hwerr_%:
  ldi r16, NET_IFACE_OFFS_ERR_IO_LOW
  rjmp l_incCounterAndEnterSkipping_%
l_econtent_%:
  ldi r16, NET_IFACE_OFFS_ERR_CONTENT_LOW
  rjmp l_incCounterAndEnterSkipping_%
l_overrun_%:
;  ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
  ldi r16, NET_IFACE_OFFS_ERR_MISSED_LOW
l_incCounterAndEnterSkipping_%:
  ldi r17, UART_HW_READMODE_SKIPPING
  std Y+UART_HW_IFACE_OFFS_READMODE, r17        ; set read mode
  rjmp l_end_%
l_incCounterAndEnterMode_%:
  rcall NET_Interface_IncCounter16              ; (R24, R25)
  std Y+UART_HW_IFACE_OFFS_READMODE, r17        ; set read mode
l_end_%:
.endmacro
; @end




; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_TxUdreIsr
;
; Handler for UDREn interrupt called when TX data register is empty.
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16, R17, X

.macro M_UART_HW_Uart_TxUdreIsr
  lds r16, UCSR@0A
  sbrs r16,UDRE@0
    rjmp l_disable_irq_%                       ; not ready

  ; check write mode
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEMODE
  cpi r16, UART_HW_WRITEMODE_WRITING
  brne l_disable_irq_%                         ; not in writing mode

  ; check whether we have an active write buffer
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEBUFNUM
  cpi r16, 0xff
  breq l_disable_irq_%                         ; no buffer

  ; check whether there is data in the buffer to send
  ldd r17, Y+UART_HW_IFACE_OFFS_WRITEBUFLEFT   ; r17=bytes left
  tst r17
  breq l_disable_irq_%                         ; nothing left to write
  
  ; get read ptr, read byte, inc read ptr, store ptr and bytesLeft
  ldd xl, Y+UART_HW_IFACE_OFFS_WRITEBUFPOS_LOW
  ldd xh, Y+UART_HW_IFACE_OFFS_WRITEBUFPOS_HIGH
  ld r16, X+                                   ; r16=byte to write
  std Y+UART_HW_IFACE_OFFS_WRITEBUFPOS_LOW, xl
  std Y+UART_HW_IFACE_OFFS_WRITEBUFPOS_HIGH, xh
  dec r17
  std Y+UART_HW_IFACE_OFFS_WRITEBUFLEFT, r17
  
  ; send byte, reset write timer
  sts UDR@0, r16
  clr r16
  std Y+NET_IFACE_OFFS_WRITETIMER, r16     ; reset write timer
  
  ; still bytes left to write?
  tst r17
  brne l_end_%
  ldi r16, UART_HW_WRITEMODE_WAITBUFFEREMPTY
  std Y+UART_HW_IFACE_OFFS_WRITEMODE, r16      ; change mode to WAITBUFFEREMPTY

l_disable_irq_%:
  ; disable further DRE1 interrupts
  lds r16, UCSR@0B
  cbr r16, (1<<UDRIE@0)                        ; disable TX data register empty interrupt
  sts UCSR@0B, r16
l_end_%:
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_TxCharIsr
;
; Handler for TXCn interrupt called when a last byte has been completely sent and
; the data register is empty.
;
; @param %0 UART number ("0" for UART0)
; @param Y pointer to interface data in SRAM
; @clobbers R16

.macro M_UART_HW_Uart_TxCharIsr
  ; check write mode
  ldd r16, Y+UART_HW_IFACE_OFFS_WRITEMODE
  cpi r16, UART_HW_WRITEMODE_WAITBUFFEREMPTY
  brne l_end_%

  ; disable further TXC1 interrupts
  lds r16, UCSR@0B
  cbr r16, (1<<TXC@0)                          ; disable TXC1 interrupt
  sts UCSR@0B, r16

  ; change write mode to WRITEBUFFEREMPTY
  ldi r16, UART_HW_WRITEMODE_WRITEBUFFEREMPTY
  std Y+UART_HW_IFACE_OFFS_WRITEMODE, r16
l_end_%:
.endmacro
; @end



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

.macro M_UART_HW_Uart_SetAttnInput
  cbi COM_ATTN_DDR, COM_ATTN_PIN           ; set ATTN as input
.ifdef COM_ATTN_PUE
.else
  cbi COM_ATTN_PUE, COM_ATTN_PIN           ; disable pullup on ATTN
.endif
  cbi COM_ATTN_OUTPUT, COM_ATTN_PIN        ; disable pullup on ATTN
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UART_HW_Uart_SetAttnLow
;
; Set ATTN line low.
;
; @clobbers none

.macro M_UART_HW_Uart_SetAttnLow
  sbi COM_ATTN_DDR, COM_ATTN_PIN            ; set ATTN as output
  cbi COM_ATTN_OUTPUT, COM_ATTN_PIN         ; set ATTN low
.endmacro




