; ***************************************************************************
; 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 AVR_MODULES_UARTFD_MACROS_ASM
#define AVR_MODULES_UARTFD_MACROS_ASM


.macro M_UARTFD_SET_CHARFORMAT
  ldi r16, (1<<UCSZ@01) | (1<<UCSZ@00)      ; 1 stop bit, 8 databits
  outr UCSR@0C, r16
.endmacro



.macro M_UARTFD_SET_BAUDRATE
.if clock == 8000000
  ldi r16, 25 ; (19.2Kb/s at 8MHz)
  ldi r17, 0
.elif clock == 1000000
  ldi r16, 2  ; (19.2Kb/s at 1MHz)
  ldi r17, 0  
.else
  .error "Unhandled clock frequency"
.endif
  outr UBRR@0H, r17
  outr UBRR@0L, r16
.endmacro



.macro M_UARTFD_START_RX
  inr r16, UCSR@0B
  sbr r16, (1<<RXCIE@0) | (1<<RXEN@0)  ; enable RX complete interrupt, enable receive
  outr UCSR@0B, r16
.endmacro



.macro M_UARTFD_STOP_RX
  inr r16, UCSR@0B
  cbr r16, (1<<RXCIE@0) | (1<<RXEN@0)  ; disable RX complete interrupt, disable receive
  outr UCSR@0B, r16
.endmacro



.macro M_UARTFD_START_TX
  inr r16, UCSR@0A
  cbr r16, (1<<TXC@0)                                 ; clear TXCn interrupt
  outr UCSR@0A, r16
  inr r16, UCSR@0B
;  sbr r16, (1<<UDRIE@0) | (1<<TXCIE@0) | (1<<TXEN@0)  ; enable TX UDRE and TXC interrupt, enable transceive
  sbr r16, (1<<UDRIE@0) | (1<<TXEN@0)  ; enable TX UDRE interrupt, enable transceive
  outr UCSR@0B, r16
.endmacro



.macro M_UARTFD_STOP_TX
  inr r16, UCSR@0B
  cbr r16, (1<<UDRIE@0) | (1<<TXCIE@0) | (1<<TXEN@0)  ; disable TX UDRE and TXC interrupt, disable transceive
  outr UCSR@0B, r16
.endmacro



.macro M_UARTFD_STOP_TXIRQS
  inr r16, UCSR@0B
  cbr r16, (1<<UDRIE@0) | (1<<TXCIE@0)  ; disable TX UDRE and TXC interrupt
  outr UCSR@0B, r16
.endmacro



.macro M_UARTFD_FLUSH_IN
l_loop_%:
    inr r16, UCSR@0A
    sbrs r16, RXC@0
      rjmp l_end_%                                  ; no data
    inr r16, UDR@0                                  ; r16=received char
  rjmp l_loop_%
l_end_%:
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UARTFD_RXCHAR_ISR
;
; @clobbers r16, r17, X

.macro M_UARTFD_RXCHAR_ISR
  ldd r16, Y+UARTFD_IFACE_OFFS_STATUS
  andi r16, (1<<UARTFD_IFACE_STATUS_BIT_SKIPPING)
  breq l_readNext_%
  ; skipping mode, just skip character
  inr r16, UDR@0                                  ; r16=received char
  rjmp l_resetReadTimer_%                         ; skipping

l_readNext_%:
  inr r16, UCSR@0A                                ; check for errors
  andi r16, (1<<FE@0) | (1<<DOR@0) | (1<<UPE@0)
  brne l_hwerr_%
  inr r16, UCSR@0A
  sbrs r16, RXC@0
    rjmp l_end_%                                  ; no data
  inr r16, UDR@0                                  ; r16=received char
  ldd r17, Y+UARTFD_IFACE_OFFS_RBUFLEFT
  tst r17
  breq l_overrun_%
  dec r17
  std Y+UARTFD_IFACE_OFFS_RBUFLEFT, r17
  ldd r17, Y+UARTFD_IFACE_OFFS_RBUFUSED
  inc r17
  std Y+UARTFD_IFACE_OFFS_RBUFUSED, r17
  ldd xl, Y+UARTFD_IFACE_OFFS_RPOS_LOW
  ldd xh, Y+UARTFD_IFACE_OFFS_RPOS_HIGH
  st X+, r16
  std Y+UARTFD_IFACE_OFFS_RPOS_LOW, xl
  std Y+UARTFD_IFACE_OFFS_RPOS_HIGH, xh
  cpi r17, 2                                      ; exactly 2 bytes in buffer?
  brne l_resetReadTimer_%
  ; determine message size
  inc r16                                         ; last byte was payload length, add byte for crc
  ldd r17, Y+UARTFD_IFACE_OFFS_RBUFLEFT
  cp r17, r16                                     ; compare remaining length against remaining space
  brcs l_badMsgSize_%
  std Y+UARTFD_IFACE_OFFS_RBUFLEFT, r16
l_resetReadTimer_%:
  clr r16
  std Y+NET_IFACE_OFFS_READTIMER, r16             ; reset read timer
  rjmp l_end_%
l_hwerr_%:
  ldd r16, Y+UARTFD_IFACE_OFFS_STATUS
  ori r16, (1<<UARTFD_IFACE_STATUS_BIT_HWERR) | (1<<UARTFD_IFACE_STATUS_BIT_SKIPPING)
  std Y+UARTFD_IFACE_OFFS_STATUS, r16
  rjmp l_end_%
l_badMsgSize_%:
  ldd r16, Y+UARTFD_IFACE_OFFS_STATUS
  ori r16, (1<<UARTFD_IFACE_STATUS_BIT_BADMSGSIZE) | (1<<UARTFD_IFACE_STATUS_BIT_SKIPPING)
  std Y+UARTFD_IFACE_OFFS_STATUS, r16
  rjmp l_end_%
l_overrun_%:
  ldd r16, Y+UARTFD_IFACE_OFFS_STATUS
  ori r16, (1<<UARTFD_IFACE_STATUS_BIT_OVERRUN) | (1<<UARTFD_IFACE_STATUS_BIT_SKIPPING)
  std Y+UARTFD_IFACE_OFFS_STATUS, r16
l_end_%:
.endmacro
; @end



; ---------------------------------------------------------------------------
; @macro M_UARTFD_TXUDRE_ISR
;
; @clobbers R16, R17, X

.macro M_UARTFD_TXUDRE_ISR
  lds r16, UCSR@0A                             ; check for UDRE flag
  sbrs r16,UDRE@0
    rjmp l_disable_irq_%                       ; not ready

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

  ; check whether there is data in the buffer to send
  ldd r17, Y+UARTFD_IFACE_OFFS_WBUFLEFT        ; 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+UARTFD_IFACE_OFFS_WPOS_LOW
  ldd xh, Y+UARTFD_IFACE_OFFS_WPOS_HIGH
  ld r16, X+                                   ; r16=byte to write
  std Y+UARTFD_IFACE_OFFS_WPOS_LOW, xl
  std Y+UARTFD_IFACE_OFFS_WPOS_HIGH, xh
  dec r17
  std Y+UARTFD_IFACE_OFFS_WBUFLEFT, 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_%
l_disable_irq_%:
  ; disable further UDRE 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_UARTFD_TXCHAR_ISR
;
; @clobbers R16, R17, X

.macro M_UARTFD_TXCHAR_ISR
  ; disable further TXC interrupts
  inr r16, UCSR@0B
  cbr r16, (1<<TXC@0)                          ; disable TXC interrupt
  outr UCSR@0B, r16
.endmacro
; @end










#endif

