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



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

.dseg


com2DataBegin:
  com2Address:             .byte 1

  com2Interrupts:          .byte 2

  com2StatsPacketsIn:      .byte 2
  
  com2StatsIoError:        .byte 2
  com2StatsContentError:   .byte 2
  com2StatsNoBufferError:  .byte 2
  com2StatsIgnored:        .byte 2
  com2StatsHandled:        .byte 2
  com2StatsMissed:         .byte 2   ; currently not used

  com2StatsPacketsOut:     .byte 2
  com2StatsBusyError:      .byte 2
  com2StatsCollisions:     .byte 2

  
  com2RecvBuffersUsed:     .byte 1
  com2MaxBuffersUsed:      .byte 1
  com2RecvBuffersWritePos: .byte 1
  com2RecvBuffersReadPos:  .byte 1

  com2RecvBuffers:         .byte COM2_BUFFER_SIZE*COM2_BUFFER_NUM
  com2SendBuffer:          .byte COM2_BUFFER_SIZE
com2DataEnd:



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

.cseg

COM2_BEGIN:




; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Module interface
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



; ---------------------------------------------------------------------------
; Com2_Init
;
; IN:
; OUT:
; - CFLAG: set if okay, clear on error
; USED: R16, R17, X, Y

Com2_Init:
  ; preset SRAM data area
  ldi xh, HIGH(com2DataBegin)
  ldi xl, LOW(com2DataBegin)
  clr r16
  ldi r17, (com2DataEnd-com2DataBegin)
  rcall Utils_FillSram

  ; set address to 0 (will be updated later)
  clr r16
  sts com2Address, r16

  ; setup pins and interrupts
  cbi COM_PORT_DATA, COM_PINNUM_DATA      ; disable internal pullup for DATA
  cbi COM_DDR_DATA, COM_PINNUM_DATA       ; set DATA port as input

  cbi COM_PORT_ATTN, COM_PINNUM_ATTN      ; disable internal pullup for ATTN
  cbi COM_DDR_ATTN, COM_PINNUM_ATTN       ; set ATTN port as input

  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



; ---------------------------------------------------------------------------
; Com_Run
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if something done, can be called again immediately (otherwise: wait for timer interrupt and retry)
; REGS: (R1, R3, R16, R17, R18, R19, R22, X)

COM2_Run:
  rjmp com2HandleNextPacketInQueue



com2HandleNextPacketInQueue:
  lds r16, com2RecvBuffersUsed
  tst r16
  breq com2HandleNextPacketInQueue_retNc              ; no buffers in use
  
  lds r16, com2RecvBuffersReadPos
  rcall COM2_BufferPosToX                             ; get current read buffer to X (R16, R17)
  rcall onPacketReceived
  brcs com2HandleNextPacketInQueue_handled
  ldi xl, LOW(com2StatsIgnored)
  ldi xh, HIGH(com2StatsIgnored)
  rjmp com2HandleNextPacketInQueue_incCounterDeallocBuffer
com2HandleNextPacketInQueue_handled:
  ldi xl, LOW(com2StatsHandled)
  ldi xh, HIGH(com2StatsHandled)
com2HandleNextPacketInQueue_incCounterDeallocBuffer:
  rcall Utils_IncrementCounter16                      ; (r18, r19, r22)
  rcall COM2_BufferDeallocFront                       ; (r16, r17, r21)
  sec                                                 ; always return with set CFLAG
  ret
com2HandleNextPacketInQueue_retNc:
  clc
  ret






; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Preparing messages
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++



; ---------------------------------------------------------------------------
; Write a simple packet into the given buffer with payload being only CMD and source address.
;
; IN:
; - R16: destination address
; - R18: command (e.g. CPRO_CMD_PING or CPRO_CMD_PONG)
; - X  : pointer to buffer to write to
; OUT:
; - CFLAG: set if okay, clear otherwise
; REGS: R16, R17, X, Y (R3, R4, R15, R16, R17, R18, R19, R20, R21)

COM2_WriteMsgWithCmdAndSrcAddr:
  ldi r17, COM2_PAYLOAD_FLAGS_SECONDS
  push xh
    push xl
      rcall COM2_BeginMsgWithVariablePayload     ; (R3, R4, R16, R17, R18, R19, R20, R21, X)
    pop xl
  pop xh
  rcall com2CalcAndAddChecksumByte              ; (R16, R17, R18, R19, X)
  sec
  ret
COM2_WriteMsgWithCmdAndSrcAddr_error:
  clc
  ret



; ---------------------------------------------------------------------------
; begin packet with variable payload.
;
; IN:
; - R16: destination address
; - R17: flags
; - R18: command (e.g. CPRO_CMD_PING)
; - X  : pointer to packet buffer
; OUT:
; - X  : points to end of packet as it was written so far
; MODIFIED REGS: R3, R16, R17, R18, R19, R20, R21, X (R4)

COM2_BeginMsgWithVariablePayload:
  ; write header (dest address, msg length)
  st X+, r16                               ; destination address
  mov r16, r17                             ; calculate payload size
  mov r3, r17
  rcall com2CalcPayloadSize                ; (R4, R16, R17)
  inc r16                                  ; add CMD byte
  inc r16                                  ; add source address byte
  st X+, r16
  ; write payload
  st X+, r18                               ; 0: CMD
  lds r16, com2Address                     ; 1: source address
  st X+, r16
  lsr r3                                   ; shift out COM2_PAYLOAD_FLAGS_SECONDS
  brcc COM2_BeginMsgWithVariablePayload_l1
  ; write seconds
  lds r16, timerModuleCounterSecs          ; adding of current seconds counter requested
  st X+, r16
  lds r16, timerModuleCounterSecs+1
  st X+, r16
  lds r16, timerModuleCounterSecs+2
  st X+, r16
  lds r16, timerModuleCounterSecs+3
  st X+, r16
COM2_BeginMsgWithVariablePayload_l1:
  lsr r3                                   ; shift out shift out COM2_PAYLOAD_FLAGS_UID
  brcc COM2_BeginMsgWithVariablePayload_l2
  ; write uid
  push xh
    push xl
      rcall Utils_ReadUid                  ; (R16, X)
    pop xl
  pop xh
  st X+, r18
  st X+, r19
  st X+, r20
  st X+, r21
COM2_BeginMsgWithVariablePayload_l2:
  ret



; ---------------------------------------------------------------------------
; com2CalcPayloadSize
;
; Calculate payload size from given flags
;
; IN:
; - R16: flags
; OUT:
; - R16: payload size
; MODIFIED REGS: R4, R16, R17

com2CalcPayloadSize:
  clr r4
  ldi r17, 4
  lsr r16                       ; shift out COM2_PAYLOAD_FLAGS_SECONDS
  brcc com2CalcPayloadSize_l1
  add r4, r17                   ; add 4 bytes
com2CalcPayloadSize_l1:
  lsr r16                       ; shift out COM2_PAYLOAD_FLAGS_UID
  brcc com2CalcPayloadSize_l2
  add r4, r17                   ; add 4 bytes
com2CalcPayloadSize_l2:
  lsr r16                       ; shift out reserved1, after this R16 contains COM2_PAYLOAD_FLAGS_NUM0-4
  add r16, r4                   ; add previous bytes to R16
  ret







; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; Sending and receiving messages
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


; ---------------------------------------------------------------------------
; receive packet.
;
; OUT:
; - CFLAG: set if okay (packet received), cleared on error
; REGS: r16, r17, x (r18, r19, r20, r21, r22)

com2ReceivePacket:
  rcall COM2_BufferAlloc                 ; (r16, r17, r21)
  brcs com2ReceivePacket_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
com2ReceivePacket_bufferAvailable:
  push xl
    push xh
      lds r16, com2Address
      ldi r17, (COM2_BUFFER_SIZE-3)      
      rcall com2ReceivePacketRaw
    pop xh
  pop xl
  brcc com2ReceivePacket_error

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

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

com2ReceivePacket_error:
  cpi r16, COM2_ERROR_IOERROR
  breq com2ReceivePacket_ioError
  cpi r16, COM2_ERROR_DATAERROR
  breq com2ReceivePacket_dataError
  rjmp com2ReceivePacket_retnc
com2ReceivePacket_ioError:
  ldi xl, LOW(com2StatsIoError)
  ldi xh, HIGH(com2StatsIoError)
  rjmp com2ReceivePacket_incCounterDeallocNc
com2ReceivePacket_dataError:
  ldi xl, LOW(com2StatsContentError)
  ldi xh, HIGH(com2StatsContentError)
  rjmp com2ReceivePacket_incCounterDeallocNc
com2ReceivePacket_incCounterDeallocNc:
  rcall Utils_IncrementCounter16         ; (r18, r19, 22)
com2ReceivePacket_deallocRetnc:
  rcall COM2_BufferDeallocBack
com2ReceivePacket_retnc:
  clc
  ret



; ---------------------------------------------------------------------------
; send packet from sendBuffer, if line free.
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay (packet sent), cleared on error
; REGS: r22, x (r18, r19, r22)

COM2_SendPacket:
  ldi xl, LOW(com2SendBuffer)
  ldi xh, HIGH(com2SendBuffer)

COM2_SendPacketAtX:
  rcall COM2_SendPacketWithAttn
  brcs COM2_SendPacket_okay
  
  cpi r16, COM2_ERROR_BUSY
  breq COM2_SendPacket_busyError
  cpi r16, COM2_ERROR_COLLISION
  breq COM2_SendPacket_collError

COM2_SendPacket_busyError:
  ldi xl, LOW(com2StatsBusyError)
  ldi xh, HIGH(com2StatsBusyError)
  rjmp COM2_SendPacket_incCounterRetNc
COM2_SendPacket_collError:
  ldi xl, LOW(com2StatsCollisions)
  ldi xh, HIGH(com2StatsCollisions)
COM2_SendPacket_incCounterRetNc:
  rcall Utils_IncrementCounter16                 ; (r18, r19, r22)
  clc
  ret
COM2_SendPacket_okay:
  ldi xl, LOW(com2StatsPacketsOut)
  ldi xh, HIGH(com2StatsPacketsOut)
  rcall Utils_IncrementCounter16                 ; (r18, r19, r22)
  sec
  ret



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



com2IsrPcint0:
  push r15
    in r15, SREG
    sbic COM_PIN_ATTN, COM_PINNUM_ATTN
      rjmp com2IsrPcint0_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 com2ReceivePacket ; (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
com2IsrPcint0_end:
    out SREG, r15
  pop r15
  reti






.include "modules/com2/packets.asm"
.include "modules/com2/lowlevel.asm"
.include "modules/com2/buffer.asm"
.include "modules/com2/crc.asm"



COM2_END:
.equ MODULE_SIZE_COM2 = COM2_END-COM2_BEGIN

