; ***************************************************************************
; copyright   : (C) 2024 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.   *
; ***************************************************************************



; UART_BitBang_Init
; UART_BitBang_Fini
; UART_BitBang_Run
; UART_BitBang_SendPacket
; UART_BitBang_GetNextReceivedPacket
; UART_BitBang_ReleaseReceivedPacket





; ***************************************************************************
; data

.dseg


uartBitbangDataBegin:
  uartBitbangRecvBuffersUsed:     .byte 1
  uartBitbangRecvBuffersWritePos: .byte 1
  uartBitbangRecvBuffersReadPos:  .byte 1
  uartBitbangRecvBuffers:         .byte COM2_BUFFER_SIZE*UART_BITBANG_BUFFER_NUM
uartBitbangDataEnd:




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

.cseg




; ---------------------------------------------------------------------------
; @routine UART_BitBang_Init
;
; @return CFLAG set if okay, clear on error
; @clobbers R16, R17, X, Y

UART_BitBang_Init:
  ; preset SRAM data area
  ldi xh, HIGH(uartBitbangDataBegin)
  ldi xl, LOW(uartBitbangDataBegin)
  clr r16
  ldi r17, (uartBitbangDataEnd-uartBitbangDataBegin)
  rcall Utils_FillSram

  ; setup pins and interrupts
  cbi COM_DATA_DDR, COM_DATA_PIN          ; set DATA port as input
  cbi COM_DATA_OUTPUT, COM_DATA_PIN       ; disable internal pullup for TXD

  cbi COM_ATTN_DDR, COM_ATTN_PIN          ; set ATTN port as input
  cbi COM_ATTN_OUTPUT, COM_ATTN_PIN       ; disable internal pullup for ATTN

  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
  ori 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
  
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine UART_BitBang_Fini
;
; @return CFLAG set if okay, clear on error
; @clobbers R16, R17, X, Y

UART_BitBang_Fini:
  cbi COM_DATA_DDR, COM_DATA_PIN          ; set TXD port as input
  cbi COM_ATTN_DDR, COM_ATTN_PIN          ; set ATTN port as input

  cbi COM_IRQ_ADDR_ATTN, COM_IRQ_BIT_ATTN ; disable pin change irq for ATTN line
  in r16, GIMSK                           ; enable pin change irq PCIE0 or PCIE1
  andi 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 UART_BitBang_Run
;
; @clobbers any

UART_BitBang_Run:
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine UART_BitBang_SendPacket
; Send packet, if line free.
;
; @param X buffer to send
; @return CFLAG: set if okay (packet sent), cleared on error
; @clobbers r22, x (r18, r19, r22)

UART_BitBang_SendPacket:
  push r15
    in r15, SREG
    cli
    rcall UART_BitBang_SendPacket_noIrqs
    brcc UART_BitBang_SendPacket_retErr
    out SREG, r15
  pop r15
  sec
  ret
UART_BitBang_SendPacket_retErr:
    out SREG, r15
  pop r15
  clc
  ret

UART_BitBang_SendPacket_noIrqs:
  rcall uartBitbang_SendPacket
  brcs UART_BitBang_SendPacket_okay
  cpi r16, COM2_ERROR_BUSY
  breq UART_BitBang_SendPacket_busyError
  cpi r16, COM2_ERROR_COLLISION
  breq UART_BitBang_SendPacket_collError
  ; fall-through
UART_BitBang_SendPacket_busyError:
  ldi xl, LOW(com2StatsBusyError)
  ldi xh, HIGH(com2StatsBusyError)
  rjmp UART_BitBang_SendPacket_incCounterRetNc
UART_BitBang_SendPacket_collError:
  ldi xl, LOW(com2StatsCollisions)
  ldi xh, HIGH(com2StatsCollisions)
UART_BitBang_SendPacket_incCounterRetNc:
  rcall Utils_IncrementCounter16                 ; (r18, r19, r22)
  clc
  ret
UART_BitBang_SendPacket_okay:
  ldi xl, LOW(com2StatsPacketsOut)
  ldi xh, HIGH(com2StatsPacketsOut)
  rcall Utils_IncrementCounter16                 ; (r18, r19, r22)
  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine UART_BitBang_GetNextReceivedPacket
;
; Get next received packet from receiption buffers. 
; Call @ref UART_BitBang_ReleaseReceivedPacket to release the packet buffer.
;
; @return CFLAG set if okay (packet received), cleared on error
; @return X pointer to packet received
; @clobbers r16, r17

UART_BitBang_GetNextReceivedPacket:
  lds r16, uartBitbangRecvBuffersUsed
  tst r16
  breq UART_BitBang_GetNextReceivedPacket_retNc  ; no buffers in use
  lds r16, uartBitbangRecvBuffersReadPos
  rcall uartBitbang_BufferPosToX                 ; get current read buffer to X (R16, R17)
  sec
  ret
UART_BitBang_GetNextReceivedPacket_retNc:
  clc
  ret
; @end



; ---------------------------------------------------------------------------
; @routine UART_BitBang_ReleaseReceivedPacket
;
; Release first packet in queue from from receiption buffers (i.e. the one returned
; by @ref UART_BitBang_GetNextReceivedPacket)
;
; @clobbers r16, r17, r21

UART_BitBang_ReleaseReceivedPacket:
  rjmp uartBitbang_BufferDeallocFront  ; (r16, r17, r21)
; @end



; ---------------------------------------------------------------------------
; @routine uartBitbang_ReceivePacket
;
; Receive packet.
;
; @return CFLAG set if okay (packet received), cleared on error
; @clobbers r16, r17, x (r18, r19, r20, r21, r22)

uartBitbang_ReceivePacket:
  rcall uartBitbang_BufferAlloc                 ; (r16, r17, r21)
  brcs uartBitbang_ReceivePacket_bufferAvailable
  
  ldi xl, LOW(com2StatsNoBufferError)           ; buffer in use, don't release
  ldi xh, HIGH(com2StatsNoBufferError)          ; just increment error counter
  rcall Utils_IncrementCounter16                ; (r18, r19, 22)
  clc
  ret
uartBitbang_ReceivePacket_bufferAvailable:
  push xl
    push xh
      lds r16, com2Address
      ldi r17, (COM2_BUFFER_SIZE-3)             ; buffer size minus dst addr, payload len, crc byte
      rcall uartBitbang_ReceivePacketIntoBuffer ; r16, r17, r18, X (r19, r20, r21, r22)
    pop xh
  pop xl
  brcc uartBitbang_ReceivePacket_error

  rcall com2CheckMessageInBuffer                ; (R16, R17, R18, R19, R20, X)
  brcc uartBitbang_ReceivePacket_dataError

  ; everything okay
  ldi xl, LOW(com2StatsPacketsIn)
  ldi xh, HIGH(com2StatsPacketsIn)
  rcall Utils_IncrementCounter16         ; (r18, r19, 22)
  sec
  ret

uartBitbang_ReceivePacket_error:
  cpi r16, COM2_ERROR_NOTFORME
  breq uartBitbang_ReceivePacket_notForMe
  cpi r16, COM2_ERROR_DATAERROR
  breq uartBitbang_ReceivePacket_dataError
  ;cpi r16, COM2_ERROR_IOERROR               
  ;breq uartBitbang_ReceivePacket_ioError
  ;rjmp uartBitbang_ReceivePacket_retnc
  rjmp uartBitbang_ReceivePacket_ioError     ; default to IO ERROR
uartBitbang_ReceivePacket_ioError:
  ldi xl, LOW(com2StatsIoError)
  ldi xh, HIGH(com2StatsIoError)
  rjmp uartBitbang_ReceivePacket_incCounterDeallocNc
uartBitbang_ReceivePacket_dataError:
  ldi xl, LOW(com2StatsContentError)
  ldi xh, HIGH(com2StatsContentError)
  rjmp uartBitbang_ReceivePacket_incCounterDeallocNc
uartBitbang_ReceivePacket_notForMe:
  ldi xl, LOW(com2StatsNotForMe)
  ldi xh, HIGH(com2StatsNotForMe)
  rjmp uartBitbang_ReceivePacket_incCounterDeallocNc
uartBitbang_ReceivePacket_incCounterDeallocNc:
  rcall Utils_IncrementCounter16         ; (r18, r19, 22)
uartBitbang_ReceivePacket_deallocRetnc:
  rcall uartBitbang_BufferDeallocBack
uartBitbang_ReceivePacket_retnc:
  clc
  ret
; @end









; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Buffer Management
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



; ---------------------------------------------------------------------------
; @routine uartBitbang_BufferAlloc
;
; Allocate a transfer buffer.
;
; @return CFLAG set if okay, clear otherwise
; @return X     pointer to allocated buffer in SRAM
; @clobbers r16, r17, r21

uartBitbang_BufferAlloc:
  COM2_M_BufferAlloc UART_BITBANG_BUFFER_NUM, \
    com2MaxRecvBuffersUsed, \
    uartBitbangRecvBuffersUsed, \
    uartBitbangRecvBuffersWritePos, \
    uartBitbang_BufferPosToX
  ret
; @end



; ---------------------------------------------------------------------------
; @routine uartBitbang_BufferDeallocBack
;
; Release a transfer buffer at the end of the list by decreasing the write pos.
; This releases the last allocated buffer!
;
; @return CFLAG set if okay, clear otherwise
; @clobbers r16, r17, r21

uartBitbang_BufferDeallocBack:
  COM2_M_BufferDeallocBack UART_BITBANG_BUFFER_NUM, uartBitbangRecvBuffersUsed, uartBitbangRecvBuffersWritePos
  ret
; @end



; ---------------------------------------------------------------------------
; @routine uartBitbang_BufferDeallocFront
;
; Release a transfer buffer by increasing the read pos.
;
; @return CFLAG set if okay, clear otherwise
; @clobbers r16, r17, r21

uartBitbang_BufferDeallocFront:
  COM2_M_BufferDeallocFront UART_BITBANG_BUFFER_NUM, uartBitbangRecvBuffersUsed, uartBitbangRecvBuffersReadPos
  ret
; @end



; ---------------------------------------------------------------------------
; @routine uartBitbang_BufferPosToX
;
; Get a pointer to the SRAM position of the given buffer.
; CAVE: Code must correspond to COM2_BUFFER_SIZE!!
;
; @param  R16 buffer number (starting with 0)
; @return X   pointer to buffer in SRAM
; @clobbers R16, R17

uartBitbang_BufferPosToX:
  COM2_M_BufferPosToX uartBitbangRecvBuffers
  ret
; @end










; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ISR
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



uartBitbangIsrPcint0:
  push r15
    in r15, SREG
    sbic COM_ATTN_INPUT, COM_ATTN_PIN
      rjmp uartBitbangIsrPcint0_end
    ; low, read packet
    push r1
      push r16
        push r17
          push r18
            push r19
              push r20
                push r21
                  push r22
                    push xh
                     push xl
                       push r15
                         rcall uartBitbang_ReceivePacket ; (r16, r17, r18, r19, r20, r21, r22, x)
                       pop r15
                       lds xl, com2Interrupts
                       lds xh, com2Interrupts+1
                       adiw xh:xl, 1
                       sts com2Interrupts, xl
                       sts com2Interrupts+1, xh
                     pop xl
                    pop xh
                  pop r22
                pop r21
              pop r20
            pop r19
          pop r18
        pop r17
      pop r16
    pop r1
uartBitbangIsrPcint0_end:
    out SREG, r15
  pop r15
  reti



; set routine functions
.equ COMIO_Init                  = UART_BitBang_Init
.equ COMIO_Fini                  = UART_BitBang_Fini
.equ COMIO_Run                   = UART_BitBang_Run
.equ COMIO_SendPacket            = UART_BitBang_SendPacket
.equ COMIO_GetNextReceivedPacket = UART_BitBang_GetNextReceivedPacket
.equ COMIO_ReleaseReceivedPacket = UART_BitBang_ReleaseReceivedPacket


