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

; ***************************************************************************
; includes

;.include "modules/uart_bitbang2/defs.asm"
;.include "modules/uart_bitbang2/lowlevel.asm"

;.include "modules/network/iface.asm"



; ***************************************************************************
; macros


.macro mUartBitbangSetupInt0
  inr r16, MCUCR
  cbr r16, (1<<ISC01) | (1<<ISC00)
  sbr r16, (1<<ISC01) | (0<<ISC00)        ; falling edge of ATTN
  outr MCUCR, r16
;  sbr r16, (0<<ISC01) | (0<<ISC00)         ; low level triggers
  inr r16, COM_IRQ_ADDR_ATTN             ; enable irq for ATTN line
  sbr r16, (1<<COM_IRQ_BIT_ATTN)
  outr COM_IRQ_ADDR_ATTN, r16
.endmacro



.macro mUartBitbangSetupPci
  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
.endmacro




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

.dseg

uart_bitbang_iface:     .byte NET_IFACE_SIZE




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

.cseg



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

UART_BitBang_Init:
  ldi yl, LOW(uart_bitbang_iface)
  ldi yh, HIGH(uart_bitbang_iface)
  rcall NET_Interface_Init                ; (R16, R17, X)

  ; init hw
  cbi COM_DATA_DDR, COM_DATA_PIN          ; set TXD port as input
  cbi COM_DATA_OUTPUT, COM_DATA_PIN       ; disable internal pullup for DATA
  cbi COM_ATTN_DDR, COM_ATTN_PIN          ; set ATTN port as input
  cbi COM_ATTN_OUTPUT, COM_ATTN_PIN       ; disable internal pullup for ATTN

  ; enable IRQ
.ifdef INT0
  .if COM_IRQ_BIT_ATTN == INT0
    mUartBitbangSetupInt0
  .else
    mUartBitbangSetupPci
  .endif
.else
  mUartBitbangSetupPci
.endif

#if 0
  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
#endif

  sec
  ret
; @end



; ---------------------------------------------------------------------------
; @routine UART_BitBang_Fini
;
; @return CFLAG set if okay, clear on error
; @clobbers R16, 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

#if 0
  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
#endif

  ret
; @end



; ---------------------------------------------------------------------------
; @routine UART_BitBang_Every100ms @global
;
; Routine doing periodical stuff (like sending outbound mesages)
;
; @param Y pointer to start of interface data
; @clobbers R16 (R17, R18, R21, R22, R24, R25, X)

UART_BitBang_Every100ms:
  ldi yl, LOW(uart_bitbang_iface)
  ldi yh, HIGH(uart_bitbang_iface)
  push r15
    in r15, SREG
    cli
    rcall NET_Interface_Periodically         ; (R16)
    rcall uartBitBang_sendNextPkg            ; (R16, R17, R18, R21, R22, R24, R25, X)
    out SREG, r15
  pop r15
  ret
; @end



; ---------------------------------------------------------------------------
; @routine uartBitBang_sendNextPkg @global
;
; Check whether there is an outgoing message in interface data
; and send it if possible.
;
; @return CFLAG set if okay, clear on error
; @param Y pointer to start of interface data
; @clobbers R16 (R17, R18, R21, R22, R24, R25, X)

uartBitBang_sendNextPkg:
  rcall NET_Interface_PeekNextOutgoingMsgNum ; (R17, R18, X)
  brcc uartBitBang_sendNextPkg_end
  rcall NET_Buffer_Locate                    ; get pointer to buffer (R17)
  brcc uartBitBang_sendNextPkg_end
  adiw xh:xl, 1                              ; skip buffer header
  rcall uartBitbang_SendMsg                  ; (R16, R17, R21, R22, R24, R25, X)
  brcc uartBitBang_sendNextPkg_end
  rcall NET_Interface_GetNextOutgoingMsgNum  ; remove from stack (R17, R18, X)
  rcall NET_Buffer_ReleaseByNum              ; release buffer (R16, X)
  sec
  rjmp uartBitBang_sendNextPkg_end
uartBitBang_sendNextPkg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine uartBitbang_receiveNextPkg
;
; Receive packet.
;
; @param Y pointer to start of interface data
; @clobbers R16, R17, R18, R19, R20, R21, R22, R24, R25, X

uartBitbang_receiveNextPkg:
  rcall NET_Buffer_Alloc                          ; R16=buffer num (R16, R17, X)
  brcs uartBitbang_receiveNextPkg_gotBuffer
  ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
  rcall NET_Interface_IncCounter16                ; (R24, R25)
  rjmp uartBitbang_receiveNextPkg_end
uartBitbang_receiveNextPkg_gotBuffer:
  push r16                                        ; buffer number
    adiw xh:xl, 1
    ldd r18, Y+NET_IFACE_OFFS_ADDRESS
    ldi r19, NET_BUFFERS_SIZE-1
    rcall uartBitbang_ReceiveAndCheckMsg          ; (R16, R17, R19, R20, R21, R22, R24, R25)
  pop r16
  brcc uartBitbang_receiveNextPkg_relBuffer
  rcall NET_AddIncomingMsgNum                     ; (R17, R18, X)
  brcs uartBitbang_receiveNextPkg_end
  push r16
    ldi r16, NET_IFACE_OFFS_ERR_MISSED_LOW
    rcall NET_Interface_IncCounter16              ; (R24, R25)
  pop r16
  ; fall-through to release buffer
uartBitbang_receiveNextPkg_relBuffer:
  rcall NET_Buffer_ReleaseByNum                   ; (R16, X)
uartBitbang_receiveNextPkg_end:
  ret
; @end






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



; ---------------------------------------------------------------------------
; @routine UART_BitBang_PcintIsr @global @isr
;
; ISR for PCINT0/1
;
; @clobbers: none

UART_BitBang_PcintIsr:
  push r15
    in r15, SREG
    sbic COM_ATTN_INPUT, COM_ATTN_PIN
      rjmp uartBitbangPcintIsr_end
    ; low, read packet
    push r16
      push r17
        push r18
          push r19
            push r20
              push r21
                push r22
                  push r24
                    push r25
                      push xl
                        push xh
                          push yl
                            push yh
                              ldi yl, LOW(uart_bitbang_iface)
                              ldi yh, HIGH(uart_bitbang_iface)
                              rcall uartBitbang_receiveNextPkg ; (R16, R17, R18, R19, R20, R21, R22, R24, R25, X)
                            pop yh
                          pop yl
                        pop xh
                      pop xl
                    pop r25
                  pop r24
                pop r22
              pop r21
            pop r20
          pop r19
        pop r18
      pop r17
    pop r16
uartBitbangPcintIsr_end:
    out SREG, r15
  pop r15
  reti
; @end




#endif ; UART_BITBANG_IFACE_H

