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


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


.cseg


; ---------------------------------------------------------------------------
; @routine ioRawInit
; Send a message
;
; @clobbers r16, r17

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

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

  M_IO_WRITE UART_REG_UBRRH, r17
  M_IO_WRITE UART_REG_UBRRL, r16

  ; set character format (asynchronous USART, 8-bit, one stop bit, no parity)
.ifdef URSEL
  ldi r16, (1<<URSEL) | (1<<UART_BIT_USBS) | (3<<UART_BIT_UCSZ0)
.else
  ldi r16, (1<<UART_BIT_USBS)|(3<<UART_BIT_UCSZ0)
.endif
;  ldi r16, (1<<UART_BIT_UCSZ0) | (1<<UART_BIT_UCSZ1)
  M_IO_WRITE UART_REG_UCSRC, r16

  cbi COM_ATTN_DDR, COM_ATTN_PIN          ; set ATTN port as input

.ifdef COM_ATTN_PUE
    lds r16, COM_ATTN_PUE
    cbr r16, COM_ATTN_PIN             ; disable pullup on ATTN
    sts COM_ATTN_PUE, r16
.else
    cbi COM_ATTN_OUTPUT, COM_ATTN_PIN ; disable internal pullup for ATTN
.endif

  ret
;@end



; ---------------------------------------------------------------------------
; @routine ioRawSendMsg
; Send a message
;
; @clobbers r16, r17, X

ioRawSendMsg:
    ldi xl, LOW(flashSendBuffer)
    ldi xh, HIGH(flashSendBuffer)
    rcall ioRawSendMsgWithAttn
  brcc ioRawSendMsg
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawSendMsgWithAttn
;
; @param X buffer to send
; @return CFLAG: set if okay (packet sent), cleared on error
; @clobbers r16, r17 (X)

ioRawSendMsgWithAttn:
ioRawSendMsgWithAttn_loop:
    ldi r16, 0xff                        ; expect ATTN high
    ldi r17, 10
    rcall ioWaitForAttnState100ms        ; wait for up to 1s
    brcs ioRawSendMsgWithAttn_attnHigh
    ret
ioRawSendMsgWithAttn_attnHigh:
    rcall ioRawAcquireBus                ; (none)
  brcc ioRawSendMsgWithAttn_loop

  rcall ioRawWaitForOneBitLength         ; wait for one bit duration (R22)
  rcall ioRawWaitForOneBitLength         ; wait for one bit duration (R22)

  rcall ioRawSendMsgHandleTransceiver    ; (r16, r17, X)
  cbi COM_ATTN_DDR, COM_ATTN_PIN         ; release ATTN line (by setting direction to IN)
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawSendMsgHandleTransceiver
; Enable transceiver, send packet, disable transceiver.
;
; @param X buffer to send
; @return CFLAG: set if okay (packet sent), cleared on error
; @clobbers r16 (r17, X)

ioRawSendMsgHandleTransceiver:
  ; enable transceiver
  M_IO_READ r16, UART_REG_UCSRB
  sbr r16,(1<<UART_BIT_TXEN)           ; enable transmit
  M_IO_WRITE UART_REG_UCSRB, r16
  rcall ioRawSendMsgDirect
  ; disable transceiver
  M_IO_READ r16, UART_REG_UCSRB
  cbr r16,(1<<UART_BIT_TXEN)           ; disable transmit
  M_IO_WRITE UART_REG_UCSRB, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawSendMsgDirect
; Send packet.
;
; @param X buffer to send
; @return CFLAG: set if okay (packet sent), cleared on error
; @clobbers r16, r17, X

ioRawSendMsgDirect:
  adiw xh:xl, 1
  ld r17, X                      ; read msg size
  sbiw xh:xl, 1
  ldi r16, 3                     ; add DEST, LEN, CRC bytes
  add r17, r16
ioRawSendMsgDirect_loop:
    ; wait until transceiver ready
      M_IO_READ r16, UART_REG_UCSRA
      sbrs r16, UART_BIT_UDRE
    rjmp ioRawSendMsgDirect_loop
    ; clear TXC flag by sending a 1
    sbr r16, (1<<UART_BIT_TXC)
    M_IO_WRITE UART_REG_UCSRA, r16
    ; write byte to uart data register
    ld r16, X+
    M_IO_WRITE UART_REG_UDR, r16
    dec r17
  brne ioRawSendMsgDirect_loop
  ; wait until all data send (i.e. send buffer empty and all bits shifted out)
ioRawSendMsgDirect_loopComplete:
    M_IO_READ r16, UART_REG_UCSRA
    sbrs r16, UART_BIT_TXC
  rjmp ioRawSendMsgDirect_loopComplete
  sec
  ret
; @end






; ---------------------------------------------------------------------------
; @routine ioRawWaitForValidMsg
; Wait for valid incoming msg
;
; @return CFLAG set if okay (packet received), cleared on error
; @clobbers: r16, r17, r18 (r19, r22, X)

ioRawWaitForValidMsg:
  ldi r16, 0xff                         ; expect ATTN high
  ldi r17, 100
  rcall ioWaitForAttnState100ms         ; wait for up to 10s
  brcc ioRawWaitForValidMsg_end         ; ATTN not high, exit
  ldi r16, 0                            ; expect ATTN low
  ldi r17, 100
  rcall ioWaitForAttnState100ms         ; wait for up to 10s
  brcs ioRawWaitForValidMsg_attnLow
  ret
ioRawWaitForValidMsg_attnLow:
  ldi xl, LOW(flashRecvBuffer)
  ldi xh, HIGH(flashRecvBuffer)
  ldi r18, FLASH_RECVBUFFER_MAXLEN-3    ; maximum accepted msglen byte
  ldi r20, 10                           ; 10 secs
  rcall ioRecvMsgHandleReceiver         ; (r16, r17, r18, r19, r20, r22)
  brcs ioRawWaitForValidMsg_packetReceived
  ; wait until ATTN is high (up to 10s)
  ldi r16, 0xff                         ; expect ATTN high
  ldi r17, 100
  rcall ioWaitForAttnState100ms         ; wait for up to 10s
  clc
  ret
ioRawWaitForValidMsg_packetReceived:
  ldi r16, 0xff                         ; expect ATTN high
  ldi r17, 100
  rcall ioWaitForAttnState100ms         ; wait for up to 10s
  brcc ioRawWaitForValidMsg_end
  ldi xl, LOW(flashRecvBuffer)
  ldi xh, HIGH(flashRecvBuffer)
  rcall NETMSG_CheckMessageInBuffer
ioRawWaitForValidMsg_end:
  ret

; @end



; ---------------------------------------------------------------------------
; @routine ioRecvMsgHandleReceiver
;
; Turn receiver on, receive message, turn receiver off.
; @return CFLAG set if okay, cleared on error
; @param  r18   max accepted msglen size (buffersize-3)
; @param  R20   max number of secs to wait for incoming message
; @param  X     buffer to receive to
; @clobbers r16 (r17, r18, r19, r20, r22)

ioRecvMsgHandleReceiver:
  ; enable receiver
  M_IO_READ r16, UART_REG_UCSRB
  sbr r16,(1<<UART_BIT_RXEN)           ; enable receive
  M_IO_WRITE UART_REG_UCSRB, r16
  rcall ioRecvMsg
  M_IO_READ r16, UART_REG_UCSRB
  cbr r16,(1<<UART_BIT_RXEN)           ; disable receive
  M_IO_WRITE UART_REG_UCSRB, r16
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRecvMsg
;
; Wait for next message, if received check validity.
; On error skip the currently received message.
;
; @return CFLAG set if okay, cleared on error
; @param  r18   max accepted msglen size (buffersize-3)
; @param  R20   max number of secs to wait for incoming message
; @param  X     buffer to receive to
; @clobbers (r16, r17, r18, r19, r20, r22)

ioRecvMsg:
  rcall ioRawRecvMsg                      ; (r16, r17, r18, r19, r20, r22)
  brcc ioRecvMsg_error
  push xl
    push xh
      rcall NETMSG_CheckMessageInBuffer   ; (R16, R17, R18, R19, R20, X)
    pop xh
  pop xl
  brcs ioRecvMsg_end
ioRecvMsg_error:
;  rcall ioRecvSkipMessage                 ; skip remainder of the message
  clc
ioRecvMsg_end:
  ret
; @end



#if 0
; ---------------------------------------------------------------------------
; @routine ioRecvSkipMessage
;
; skip all receiption data until a data pause of about 10ms
;
; @clobbers r16

ioRecvSkipMessage:
ioRecvSkipMessage_loop:
    rcall ioRecvFlush               ; (r16)
    ; wait for a data pause of 10ms
    rcall ioRawRecvByteWithin10ms   ; (r20, r22)
  brcs ioRecvSkipMessage_loop
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRecvFlush
;
; flush receiption buffer.
;
; @clobbers r16

ioRecvFlush:
    M_IO_READ r16, UART_REG_UCSRA ; read status
    sbrs r16, UART_BIT_RXC
    ret
    M_IO_READ r16, UART_REG_UDR   ; read data byte
  rjmp ioRecvFlush
; @end
#endif



; ---------------------------------------------------------------------------
; @routine ioRawRecvMsg
;
; @return   CFLAG set if okay, cleared on error
; @param    r18   max accepted msglen size (buffersize-3)
; @param    R20   max number of secs to wait for incoming message
; @param    X     buffer to receive to
; @clobbers r16, r17, r19 (r18, r20, r22)

ioRawRecvMsg:
  M_IO_READ r19, UART_REG_UCSRA
  cbr r19, (1<<UART_BIT_FE) | (1<<UART_BIT_DOR) | (1<<UART_BIT_UPE)
  M_IO_WRITE UART_REG_UCSRA, r19               ; clear errors
  ; wait for begin of message
  rcall ioRawWaitForDataSeconds ; (r20, r22)
  brcc ioRawRecvMsg_end
  clr r19                       ; bytecounter
  ; read first two bytes
  ldi r17, 2                    ; 2 bytes: address byte, msg len
  add r19, r17
  rcall ioRawRecvBytes          ; (r16, r17, r20, r22)
  brcc ioRawRecvMsg_error
  cp r16, r18                   ; check size
  brcc ioRawRecvMsg_error
  inc r16                       ; account for checksum byte
  ; read remaining bytes including checksum byte
  mov r17, r16
  add r19, r17
  rcall ioRawRecvBytes          ; (r16, r17, r18, r22)
  brcc ioRawRecvMsg_error
  sub xl, r19                   ; let X point back to begin of message
  sbc xh, r19
  add xh, r19
  sec
  ret
ioRawRecvMsg_error:
  clc
ioRawRecvMsg_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawRecvBytes
;
; @return CFLAG set if okay (data available), cleared on error
; @return r16 last byte received
; @param r17 number of bytes to read
; @param x buffer to receive to
; @clobbers r16, r17 (r20, r22)

ioRawRecvBytes:
    rcall ioRawRecvByteWithin10ms ; (r20, r22)
    brcc ioRawRecvBytes_end
    st X+, r16
    dec r17
  brne ioRawRecvBytes
  sec
ioRawRecvBytes_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawRecvByteWithin10ms
;
; Wait up to 10ms for incoming byte and read it.
;
; @return CFLAG set if okay (data available), cleared on error
; @return r16 byte received (if CFLAG set)
; @clobbers: r20, r22

ioRawRecvByteWithin10ms:
  rcall ioRawWaitForData10ms                   ; (R20, R22)
  brcc ioRawRecvByteWithin10ms_end
  M_IO_READ r16, UART_REG_UCSRA                           ; check for errors
  andi r16, (1<<UART_BIT_FE) | (1<<UART_BIT_DOR) | (1<<UART_BIT_UPE)
  brne ioRawRecvByteWithin10ms_error
  M_IO_READ r16, UART_REG_UDR                             ; read data byte
  sec
  ret
ioRawRecvByteWithin10ms_error:
  clc
ioRawRecvByteWithin10ms_end:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawWaitForDataSeconds
;
; Wait for incoming data for max 1s
;
; @return CFLAG set if okay (data available), cleared on error
; @param r20 maximum number of seconds to wait
; @clobbers: r20, r22

ioRawWaitForDataSeconds:
ioRawWaitForDataSeconds_loop:
    push r20
      rcall ioRawWaitForData1s              ; (r20, r22)
    pop r20
    brcs ioRawWaitForDataSeconds_gotit
    sbi LED_PIN, LED_PINNUM                 ; toggle
    dec r20
  brne ioRawWaitForDataSeconds_loop
  clc
ioRawWaitForDataSeconds_gotit:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawWaitForData1s
;
; Wait for incoming data for max 1s
;
; @return CFLAG set if okay (data available), cleared on error
; @clobbers: r20, r22

ioRawWaitForData1s:
  ldi r20, 100
ioRawWaitForData1s_loop:
    push r20
      rcall ioRawWaitForData10ms   ; (R20, R22)
    pop r20
    brcs ioRawWaitForData1s_gotit
    dec r20
  brne ioRawWaitForData1s_loop
  clc
ioRawWaitForData1s_gotit:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawWaitForData10ms
;
; Wait for incoming data for max 10 milliseconds.
;
; @return CFLAG set if okay (data available), cleared on error
; @clobbers: r20, r22

ioRawWaitForData10ms:
.if clock == 8000000
  ldi r20, 80
.endif
.if clock == 1000000
  ldi r20, 10
.endif
ioRawWaitForData10ms_loop:
    push r20
      rcall ioRawWaitForData1000Cycles  ; (r20, r22)
    pop r20
    brcs ioRawWaitForData10ms_gotit
    dec r20
  brne ioRawWaitForData10ms_loop
  clc
ioRawWaitForData10ms_gotit:
  ret
; @end



; ---------------------------------------------------------------------------
; @routine ioRawWaitForData1000Cycles
;
; Wait for incoming data for max 1000 clock cycles
; (about 1ms at 1MHz, 0.125ms at 8MHz)
;
; @return CFLAG set if okay (packet received), cleared on error
; @clobbers: r20, r22

ioRawWaitForData1000Cycles:
  ldi r20, 140                         ; 1
ioRawWaitForData_loop:
    M_IO_READ r22, UART_REG_UCSRA      ; 2
    sbrc r22, UART_BIT_RXC             ; 2/3
      rjmp ioRawWaitForData_gotit      ; 2
    dec r20                            ; 1
  brne ioRawWaitForData_loop           ; 1/2  -> 7 per loop, max about 1000
  clc                                  ; 1
  ret                                  ; 4
ioRawWaitForData_gotit:
  sec                                  ; 1
  ret                                  ; 4
; @end




