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

; work in progress!

#ifndef AVR_MODULES_COM2W_COM2WI_H
#define AVR_MODULES_COM2W_COM2WI_H


.equ COM2WI_BUFFER_SIZE = NET_BUFFERS_SIZE-1

.equ COM2WI_R_MAX_WAIT_HI_TIME = 50  ; max 1ms
.equ COM2WI_S_MAX_WAIT_HI_TIME = 50  ; max 1ms
.equ COM2WI_S_MAX_WAIT_LO_TIME = 50  ; max 1ms



.equ COM2WI_STATE_IDLE               = 0

.equ COM2WI_STATE_R_WAITFORCLOCKLO   = 1
.equ COM2WI_STATE_R_WAITFORCLOCKHI   = 2
.equ COM2WI_STATE_R_MSGRECVD         = 3

.equ COM2WI_STATE_S_WAITFORCLOCKLO   = 4
.equ COM2WI_STATE_S_WAITFORCLOCKHI   = 5

.equ COM2WI_STATE_W_WAITFREELINE     = 6
.equ COM2WI_STATE_W_SENDCLOCKLO      = 7
.equ COM2WI_STATE_W_SENDCLOCKHI      = 8

.equ COM2WI_STATE_W_WAITBETWEENBITS  = 9
.equ COM2WI_STATE_W_WAITBETWEENBYTES = 10
.equ COM2WI_STATE_W_MSGSENT          = 11

.equ COM2WI_STATE_COUNT              = 12



.equ COM2WI_IFACE_OFFS_BEGIN          = NET_IFACE_SIZE
.equ   COM2WI_IFACE_OFFS_STATE        = COM2WI_IFACE_OFFS_BEGIN+0
.equ   COM2WI_IFACE_OFFS_STATETIMER   = COM2WI_IFACE_OFFS_BEGIN+1
.equ   COM2WI_IFACE_OFFS_STATECOUNTER = COM2WI_IFACE_OFFS_BEGIN+2
.equ   COM2WI_IFACE_OFFS_PINMASK_CLK  = COM2WI_IFACE_OFFS_BEGIN+3
.equ   COM2WI_IFACE_OFFS_PINMASK_DATA = COM2WI_IFACE_OFFS_BEGIN+4
.equ   COM2WI_IFACE_OFFS_BITCOUNTER   = COM2WI_IFACE_OFFS_BEGIN+5
.equ   COM2WI_IFACE_OFFS_CURRBYTE     = COM2WI_IFACE_OFFS_BEGIN+6
.equ   COM2WI_IFACE_OFFS_BUFPOS_LOW   = COM2WI_IFACE_OFFS_BEGIN+7
.equ   COM2WI_IFACE_OFFS_BUFPOS_HIGH  = COM2WI_IFACE_OFFS_BEGIN+8
.equ   COM2WI_IFACE_OFFS_BUFUSED      = COM2WI_IFACE_OFFS_BEGIN+9
.equ   COM2WI_IFACE_OFFS_BUFLEFT      = COM2WI_IFACE_OFFS_BEGIN+10
.equ   COM2WI_IFACE_OFFS_BUFFER       = COM2WI_IFACE_OFFS_BEGIN+11
.equ COM2WI_IFACE_SIZE                = COM2WI_IFACE_OFFS_BUFFER+COM2WI_BUFFER_SIZE




; ---------------------------------------------------------------------------
; @macro mCOM2WI_INIT
;
; @param @0 interface number (beginning with 0)

.macro mCOM2WI_INIT
  ldi yl, LOW(com2wi@0_iface)
  ldi yh, HIGH(com2wi@0_iface)
  rcall com2wiInit
  ldi r16, COM_MASK_CLK@0
  std Y+COM2WI_IFACE_OFFS_PINMASK_CLK, r16
  ldi r16, COM_MASK_DATA@0
  std Y+COM2WI_IFACE_OFFS_PINMASK_DATA, r16
  ldi r16, COM_MASK_IRQ@0
  std Y+COM2WI_IFACE_OFFS_PINMASK_IRQ, r16
.endmacro
; @end





.dseg



com2wi0_iface: .byte COM2WI_IFACE_SIZE



.cseg



; ---------------------------------------------------------------------------
; @routine COM2WI_Init

COM2WI_Init:
  mCOM2WI_INIT 0

  rcall com2wiSetupTimer1
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wiInit

com2wiInit:
  rcall com2wiSetupLines

  ldi r16, COM2WI_STATE_IDLE
  std Y+COM2WI_IFACE_OFFS_STATE, r16

  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wiSetupLines

com2wiSetupLines:
  ; setup CLK line (as input, disable internal pull-up resistor)
.ifdef COM_CLK_PUE
  ldd r16, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
  com r16
  inr r17, COM_CLK_PUE
  and r17, r16
  outr COM_CLK_PUE, r17
.endif
  rcall com2wiClkSetHigh

  ; setup DATA line (as input, disable internal pull-up resistor)
.ifdef COM_DATA_PUE
  ldd r16, Y+COM2WI_IFACE_OFFS_PINMASK_DATA
  com r16
  inr r17, COM_DATA_PUE
  and r17, r16
  outr COM_DATA_PUE, r17
.endif
  rcall com2wiDataSetHigh

  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wiSetupTimer1
;
; setup timer for IRQ every 20us

com2wiSetupTimer1:
  ; CTC mode, no prescaler, OCR1A=100 (every 20us at 20MHz)
  ldi r16, 0
  outr TCCR1A, r16                  ; WGM11=0, WGM10=0
  ldi r16, (0<<CS12) | (0<<CS11) | (1<<CS10) | (0<<WGM13) | (1<<WGM12)
  clr r17  HIGH(400)
  ldi r16, LOW(400)
  outr OCR1AH, r17
  outr OCR1AL, r16

  ldi r16, (1<<OCF1A)         ; clear pending interrupts
  outr TIFR1, r16

  inr r16, TIMSK1
  sbr r16, (1<<OCIE1A)        ; Timer/Counter1 Output Compare Match A Interrupt Enable
  outr TIMSK1, r16

  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wiClkSetHigh
;
; @clobbers none

com2wiClkSetHigh:
  push r16
    push r17
      ldd r16, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
      ; clear bit in io reg (AND with complement)
      com r16                        
      inr r17, COM_CLK_DDR
      and r17, r16
      outr COM_CLK_DDR, r17          ; make pin input
.ifndef COM_CLK_PUE
      inr r17, COM_CLK_OUTPUT
      and r17, r16                   ; disable pullup
      outr COM_CLK_OUTPUT, r17
.endif
    pop r17
  pop r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wiDataSetHigh
;
; @param Y pointer to interface data in SRAM
; @clobbers none

com2wiDataSetHigh:
  push r16
    push r17
      ldd r16, Y+COM2WI_IFACE_OFFS_PINMASK_DATA
      ; clear bit in io reg (AND with complement)
      com r16                        
      inr r17, COM_DATA_DDR
      and r17, r16
      outr COM_DATA_DDR, r17         ; make pin input
.ifndef COM_DATA_PUE
      inr r17, COM_DATA_OUTPUT
      and r17, r16                   ; disable pullup
      outr COM_DATA_OUTPUT, r17
.endif
    pop r17
  pop r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine com2wiDataSetLow
;
; @param Y pointer to interface data in SRAM
; @clobbers none

com2wiDataSetLow:
  push r16
    push r17
      ldd r16, Y+COM2WI_IFACE_OFFS_PINMASK_DATA
      ; set bit in io reg
      inr r17, COM_DATA_DDR
      or r17, r16
      outr COM_DATA_DDR, r17          ; make pin output

      com r16
      inr r17, COM_DATA_OUTPUT
      and r17, r16
      outr COM_DATA_OUTPUT, r17       ; set pin low

    pop r17
  pop r16
  ret
; @end




com2wiHandleIrq:
  ldi zl, LOW(com2wiIrqJumpTable)
  ldi zh, HIGH(com2wiIrqJumpTable)
  ldd r16, Y+COM2WI_IFACE_OFFS_STATE
  cpi r16, COM2WI_STATE_COUNT
  brcc com2wiHandleIrq_ret
  add zl, r16
  adc zh, r16
  sub zh, r16
  ijmp
com2wiHandleIrq_ret:
  ret
com2wiIrqJumpTable:
  rjmp com2wiHandleStateIdle         ;  0: COM2WI_STATE_IDLE
  rjmp com2wiHandleRWaitForClockLo   ;  1: COM2WI_STATE_R_WAITFORCLOCKLO
  rjmp com2wiHandleRWaitForClockHi   ;  2: COM2WI_STATE_R_WAITFORCLOCKHI
  rjmp com2wiHandleRMsgRecvd         ;  3: COM2WI_STATE_R_MSGRECVD
  rjmp com2wiHandleSWaitForClockLo   ;  4: COM2WI_STATE_S_WAITFORCLOCKLO
  rjmp com2wiHandleSWaitForClockHi   ;  5: COM2WI_STATE_S_WAITFORCLOCKHI
  rjmp com2wiHandleWWaitFreeLine     ;  6: COM2WI_STATE_W_WAITFREELINE
  rjmp com2wiHandleWSendClockLo      ;  7: COM2WI_STATE_W_SENDCLOCKLO
  rjmp com2wiHandleWSendClockHi      ;  8: COM2WI_STATE_W_SENDCLOCKHI
  rjmp com2wiHandleWWaitBetweenBits  ;  9: COM2WI_STATE_W_WAITBETWEENBITS
  rjmp com2wiHandleWWaitBetweenBytes ; 10: COM2WI_STATE_W_WAITBETWEENBYTES
  rjmp com2wiHandleWMsgSent          ; 11: COM2WI_STATE_W_MSGSENT



com2wiHandleStateIdle:	
  inr r16, COM_CLK_INPUT
  ldd r17, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
  and r16, r17
  brne com2wiHandleStateIdle_ret
  ; CLK became low, start reading
  ldi r16, COM2WI_R_MAX_WAIT_HI_TIME
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  ldi r16, COM2WI_STATE_R_WAITFORCLOCKHI
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  clr r16
  std Y+COM2WI_IFACE_OFFS_BITCOUNTER, r16
com2wiHandleStateIdle_ret:
  ret


; @clobbers r16, r17

com2wiHandleRWaitForClockLo:
  inr r16, COM_CLK_INPUT
  ldd r17, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
  and r16, r17
  breq com2wiHandleRWaitForClockLo_isLow
  ; CLK still high, check timer
  ldd r16, Y+COM2WI_IFACE_OFFS_STATETIMER
  dec r16
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  brne com2wiHandleRWaitForClockLo_ret
  ; timer ran out, enter idle mode
  ldi r16, COM2WI_STATE_IDLE
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  ret
com2wiHandleRWaitForClockLo_isLow:
  ; CLK became low, wait for CLK HI
  ldi r16, COM2WI_R_MAX_WAIT_HI_TIME
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  ldi r16, COM2WI_STATE_R_WAITFORCLOCKHI
  std Y+COM2WI_IFACE_OFFS_STATE, r16
com2wiHandleRWaitForClockLo_ret:
  ret



; @clobbers r16, r17, r18

com2wiHandleRWaitForClockHi:
  inr r16, COM_CLK_INPUT
  ldd r17, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
  and r16, r17
  brne com2wiHandleRWaitForClockHi_isHi
  push xl
    push xh
      rcall com2wiHandleRWaitForClockHi_savedRegs
    pop xh
  pop xl
  ret
com2wiHandleRWaitForClockHi_savedRegs:       ; (r16, r17, r18, X)
  ; CLK still low, check timer
  ldd r16, Y+COM2WI_IFACE_OFFS_STATETIMER
  dec r16
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  brne com2wiHandleRWaitForClockHi_ret
  ; timer ran out, enter skipping mode
com2wiHandleRWaitForClockHi_enterSkipMode:
  ldi r16, COM2WI_STATE_S_WAITFORCLOCKHI
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  ret
com2wiHandleRWaitForClockHi_isHi:
  inr r16, COM_DATA_INPUT
  ldd r17, Y+COM2WI_IFACE_OFFS_PINMASK_DATA
  and r16, r17
  clc
  breq com2wiHandleRWaitForClockHi_dataInCarry
  sec
com2wiHandleRWaitForClockHi_dataInCarry:
  ldd r16, Y+COM2WI_IFACE_OFFS_CURRBYTE
  ror r16
  std Y+COM2WI_IFACE_OFFS_CURRBYTE, r16
  ldd r16, Y+COM2WI_IFACE_OFFS_BITCOUNTER
  inc r16
  std Y+COM2WI_IFACE_OFFS_BITCOUNTER, r16
  cpi r16, 8
  brne com2wiHandleRWaitForClockHi_ret
  ; 8 bits received, store byte
  ldd xl, Y+COM2WI_IFACE_OFFS_BUFPOS_LOW
  ldd xh, Y+COM2WI_IFACE_OFFS_BUFPOS_HIGH
  ldd r17, Y+COM2WI_IFACE_OFFS_BUFLEFT
  tst r17
  breq com2wiHandleRWaitForClockHi_overrun
  dec r17
  std Y+COM2WI_IFACE_OFFS_BUFLEFT
  st X+, r16
  std Y+COM2WI_IFACE_OFFS_BUFPOS_LOW, xl
  std Y+COM2WI_IFACE_OFFS_BUFPOS_HIGH, xh
  ldd r18, Y+COM2WI_IFACE_OFFS_BUFUSED
  inc r18
  std Y+COM2WI_IFACE_OFFS_BUFUSED, r18
  tst r17
  breq com2wiHandleRWaitForClockHi_msgRecvd
  cpi r18, 2
  brne com2wiHandleRWaitForClockHi_prepareNextByte
  ; determine message size
  inc r16                                         ; last byte was payload length, add byte for crc
  cp r17, r16                                     ; compare remaining length against remaining space
  brcs com2wiHandleRWaitForClockHi_badMsgSize
  std Y+UARTFD_IFACE_OFFS_RBUFLEFT, r16
  rjmp com2wiHandleRWaitForClockHi_prepareNextByte
com2wiHandleRWaitForClockHi_msgRecvd:
  ldi r16, COM2WI_STATE_R_MSGRECVD
  std Y+COM2WI_IFACE_OFFS_STATE, r16
#ifdef LED1_NETWORK_ACTIVITY
  bigcall LED1_SetActivityTiming  ; (r18-r20)
#endif
  ret
com2wiHandleRWaitForClockHi_overrun:
  ldi r16, NET_IFACE_OFFS_ERR_CONTENT_LOW
  rjmp com2wiHandleRWaitForClockHi_error
com2wiHandleRWaitForClockHi_badMsgSize:
  ldi r16, NET_IFACE_OFFS_ERR_MSGSIZE_LOW
com2wiHandleRWaitForClockHi_error:
  push r24
    push r25
      rcall NET_Interface_IncCounter16
    pop r25
  pop r24
  rjmp com2wiHandleRWaitForClockHi_enterSkipMode
com2wiHandleRWaitForClockHi_prepareNextByte:
  ; prepare for next byte
  clr r16
  std Y+COM2WI_IFACE_OFFS_BITCOUNTER, r16
  ldi r16, COM2WI_R_MAX_WAIT_LO_TIME
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  ldi r16, COM2WI_STATE_R_WAITFORCLOCKLO
  std Y+COM2WI_IFACE_OFFS_STATE, r16
com2wiHandleRWaitForClockHi_ret:
  ret



; @clobbers r16, r17, r18

com2wiHandleRMsgRecvd:
  push r19
    push r20
      push xl
        push xh
          push zl
            push zh
              rcall com2wiHandleRMsgRecvd_savedRegs
            pop zh
          pop zl
        pop xh
      pop xl
    pop r20
  pop r19
  ret
com2wiHandleRMsgRecvd_savedRegs:          ; (r16, r17, r18, r19, r20, X, Z)
  ; check message
  mov xl, yl
  mov xh, yh
  adiw xh:xl, UARTFD_IFACE_OFFS_RBUFFER
  rcall NETMSG_CheckMessageInBuffer       ; (R16, R17, R18, R19, R20, X)
  brcc com2wiHandleRMsgRecvd_errorCrc
  ; allocate buffer for storage
  rcall NET_Buffer_Alloc                  ; R16=number, X=ptr (R16, R17, X)
  brcc com2wiHandleRMsgRecvd_errorMem
  ldd r18, Y+NET_IFACE_OFFS_IFACENUM
  andi r18, 0x0f
  ld r17, X
  andi r17, 0xf0
  or r17, r18
  st X+, r17                              ; store interface number
  ; copy to newly allocated buffer
  mov zl, yl
  mov zh, yh
  adiw zh:zl, UARTFD_IFACE_OFFS_RBUFFER
  ldd r17, Y+UARTFD_IFACE_OFFS_RBUFUSED
com2wiHandleRMsgRecvd_copyLoop:
    ld r18, Z+
    st X+, r18
    dec r17
  brne com2wiHandleRMsgRecvd_copyLoop
  ; add new buffer to incoming list

; DEBUG
;  rcall NET_AddIncomingMsgNum
;  brcs com2wiHandleRMsgRecvd_msgSaved

  rcall NET_Buffer_ReleaseByNum           ; (R16, X)
  rjmp com2wiHandleRMsgRecvd_errorMem
com2wiHandleRMsgRecvd_msgSaved:
#ifdef LED1_NETWORK_ACTIVITY
  bigcall LED1_SetActivityTiming  ; (r18-r20)
#endif
  ldi r16, NET_IFACE_OFFS_PACKETSIN_LOW
  rcall NET_Interface_IncCounter16        ; (R24, R25)
  ldi r16, COM2WI_STATE_IDLE
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  ret
com2wiHandleRMsgRecvd_errorCrc:
  ldi r16, NET_IFACE_OFFS_ERR_CONTENT_LOW
  rjmp com2wiHandleRMsgRecvd_incCounter
com2wiHandleRMsgRecvd_errorMem:
  ldi r16, NET_IFACE_OFFS_ERR_NOBUF_LOW
com2wiHandleRMsgRecvd_incCounter:
  rcall NET_Interface_IncCounter16        ; (R24, R25)
  ldi r16, COM2WI_S_MAX_WAIT_HI_TIME
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  ldi r16, COM2WI_STATE_S_WAITFORCLOCKHI
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  ret
; @end



; @clobbers r16, r17

com2wiHandleSWaitForClockLo:
  inr r16, COM_CLK_INPUT
  ldd r17, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
  and r16, r17
  breq com2wiHandleSWaitForClockLo_isLow
  ; is still high, check timer
  ldd r16, Y+COM2WI_IFACE_OFFS_STATETIMER
  dec r16
  std Y+COM2WI_IFACE_OFFS_STATETIMER, r16
  brne com2wiHandleSWaitForClockLo_ret
  ; timer ran out, enter idle mode
  ldi r16, COM2WI_STATE_IDLE
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  ret
com2wiHandleSWaitForClockLo_isLow:
  ; CLK became low, wait for HIGH again
  ldi r16, COM2WI_STATE_S_WAITFORCLOCKHI
  std Y+COM2WI_IFACE_OFFS_STATE, r16
com2wiHandleSWaitForClockLo_ret:
  ret



com2wiHandleSWaitForClockHi:
  inr r16, COM_CLK_INPUT
  ldd r17, Y+COM2WI_IFACE_OFFS_PINMASK_CLK
  and r16, r17
  breq com2wiHandleSWaitForClockHi_ret
  ; enter COM2WI_STATE_S_WAITFORCLOCKLO
  ldi r16, COM2WI_STATE_S_WAITFORCLOCKLO
  std Y+COM2WI_IFACE_OFFS_STATE, r16
  ldi r16, COM2WI_S_MAX_WAIT_LO_TIME
  std Y+COM2WI_IFACE_OFFS_STATETIMER
com2wiHandleSWaitForClockHi_ret:
  ret




com2wiHandleWWaitFreeLine:
  ret



com2wiHandleWSendClockLo:
  ret



com2wiHandleWSendClockHi:
  ret



com2wiHandleWWaitBetweenBits:
  ret



com2wiHandleWWaitBetweenBytes:
  ret



com2wiHandleWMsgSent:
  ret







#endif

