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



; ***************************************************************************
; defines

.equ COM_MAXWAIT_US               = 100  ; maximum wait time in microseconds when waiting for rising/falling clock

.equ COM_MAINTENANCE_ADDR         = 0xf1

.equ COM_BUFFER_FLAGS_DONE        = 0x80
.equ COM_BUFFER_FLAGS_RECEIVED    = 0x40
.equ COM_BUFFER_FLAGS_TOSEND      = 0x20
.equ COM_BUFFER_FLAGS_RELEASED    = 0x10
.equ COM_BUFFER_FLAGS_PRIO1       = 0x08
.equ COM_BUFFER_FLAGS_PRIO0       = 0x04
.equ COM_BUFFER_FLAGS_IFACE1      = 0x02
.equ COM_BUFFER_FLAGS_IFACE0      = 0x01

.equ COM_BUFFER_PRIO_INFO         = 0
.equ COM_BUFFER_PRIO_NORMAL       = COM_BUFFER_FLAGS_PRIO0
.equ COM_BUFFER_PRIO_IMPORTANT    = COM_BUFFER_FLAGS_PRIO1
.equ COM_BUFFER_PRIO_VITAL        = (COM_BUFFER_FLAGS_PRIO0 | COM_BUFFER_FLAGS_PRIO1)

.equ COM_REPEAT_INFO              = 3
.equ COM_REPEAT_NORMAL            = 32
.equ COM_REPEAT_IMPORTANT         = 64
.equ COM_REPEAT_VITAL             = 255

.equ COM_BUFFER_NUM               = 6
.equ COM_BUFFER_SIZE              = 24 ; CAVE: Need to change routine COM_BufferPosToY when changing this value!!


.equ COM_BUFFER_OFFS_FLAGS        = 0 ; first byte in buffer is flags byte
.equ COM_BUFFER_OFFS_DATA         = 1

.equ COM_MSG_OFFS_DESTADDR        = 0
.equ COM_MSG_OFFS_MSGLEN          = 1
.equ COM_MSG_OFFS_MSGDATA         = 2
.equ COM_MSG_OFFS_CMD             = 2 ; first at COM_MSG_OFFS_MSGDATA
.equ COM_MSG_OFFS_SRCADDR         = 3
.equ COM_MSG_OFFS_PAYLOAD         = 4 ; payload for the cmd follows here

.equ COM_ERR_NOTFORME             = 1
.equ COM_ERR_CHECKSUM             = 2
.equ COM_ERR_IO                   = 3


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

.dseg


comDataBegin:
  comFlags:               .byte 1
  comAddress:             .byte 1
  comRepeatCount:         .byte 1
  comReserved1:           .byte 1

  comStatsPacketsIn:      .byte 2
  comStatsPacketsOut:     .byte 2

  comStatsRecvErrs:       .byte 2
  comStatsRecvCrcErrs:    .byte 2
  comStatsMissed:         .byte 2
  comStatsIgnored:        .byte 2
  comStatsHandled:        .byte 2

  comStatsCollisions:     .byte 2
  comStatsAborted:        .byte 2
  
  comStatsSendNoBuffer:   .byte 2
  comStatsRecvNoBuffer:   .byte 2

  comRecvBuffersReadPos:  .byte 1
  comRecvBuffersWritePos: .byte 1
  comRecvBuffersUsed:     .byte 1
  comRecvBuffers:         .byte (COM_BUFFER_SIZE*COM_BUFFER_NUM)
comDataEnd:



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

.cseg

COM_BEGIN:


comIsrPcint0:
  push r15
    in r15, SREG
    sbic COM_PIN_ATTN, COM_PINNUM_ATTN
      rjmp comIsrPcint0_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 yh
                         push yl
                           rcall comReceivePacketHandleBuffer ; (R1, R16, R17, R18, R19, R20, R21, R22, X, Y
                         pop yl
                       pop yh
                     pop xl
                    pop xh
                  pop r22
                pop r21
              pop r20
            pop r19
          pop r18
        pop r17
      pop r16
    pop r1
    
comIsrPcint0_end:
  pop r15
  reti



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

Com_Init:
  ; preset SRAM data area
  ldi xh, HIGH(comDataBegin)
  ldi xl, LOW(comDataBegin)
  clr r16
  ldi r17, (comDataEnd-comDataBegin)
  rcall Utils_FillSram

  ; set address to 0 (will be updated later)
  clr r16
  sts comAddress, 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)

Com_Run:
  rcall comHandleNextPacketInQueue ; nothing more than handling packages in ringbuffer
;  clc
  ret



; ---------------------------------------------------------------------------
; Mark a packet as enqueued.
;
; IN:
; - Y: pointer to buffer containing the enqueued packet
; - R20: priority
; OUT:
; - CFLAG: set if okay, clear otherwise
; MODIFIED REGS: R15, R16

COM_EnqueuePacket:
  in r15, SREG
  cli
    ldd r16, y+COM_BUFFER_OFFS_FLAGS
    andi r16, (~(COM_BUFFER_FLAGS_PRIO1 | COM_BUFFER_FLAGS_PRIO0 | COM_BUFFER_FLAGS_RECEIVED | COM_BUFFER_FLAGS_DONE)) & 0xff
    or r16, r20                       ; add priority
    ori r16, COM_BUFFER_FLAGS_TOSEND
    std y+COM_BUFFER_OFFS_FLAGS, r16
  out SREG, r15                       ; restore IRQ flag
  sec
  ret



; ---------------------------------------------------------------------------
; comHandleNextPacketInQueue
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if something done, can be called again immediately (otherwise: wait for timer interrupt and retry)
; MODIFIED REGS: R16, R17, Y (R15, R18, R21, R22, X)


comHandleNextPacketInQueue:
  lds r16, comRecvBuffersUsed
  tst r16
  breq comHandleNextPacketInQueue_retNc          ; no buffers in use
  
  lds r16, comRecvBuffersReadPos
  rcall COM_BufferPosToY                         ; get current read buffer to Y (R16, R17)
  ldd r16, y+COM_BUFFER_OFFS_FLAGS               ; get flags
  ; check for released buffer (dealloc)
  mov r17, r16
  andi r17, COM_BUFFER_FLAGS_RELEASED            ; check for buffer released
  brne comHandleNextPacketInQueue_releaseBuffer
  ; check for received packet
  mov r17, r16
  andi r17, COM_BUFFER_FLAGS_RECEIVED
  brne comHandleNextPacketInQueue_receivedPacket
  ; check for buffer to send
  mov r17, r16
  andi r17, COM_BUFFER_FLAGS_TOSEND              ; check for message to send
  brne comHandleNextPacketInQueue_sendPacket
  ; nothing to do (e.g. buffer not ready to be sent)
  rjmp comHandleNextPacketInQueue_retNc
comHandleNextPacketInQueue_sendPacket:
  rcall comSendPacketHandleRepeat                ; (R15, R16, R17, R18, R21, R22, X)
  ret                                            ; use CFLAG from subroutine
comHandleNextPacketInQueue_receivedPacket:
  clr r17
  sts comRepeatCount, r17                        ; set comRepeatCount to zero
  rcall comHandleReceivedPacket                  ; (r16, r17, r21, X)
  sec                                            ; always return with set CFLAG
  ret
comHandleNextPacketInQueue_releaseBuffer:
  rcall COM_BufferDeallocFront
  sec                                            ; always return with set CFLAG
  ret
comHandleNextPacketInQueue_retNc:
  clc
  ret



; ---------------------------------------------------------------------------
; comHandleReceivedPacket
;
; IN:
; - Y: pointer to current buffer (pointed to by comRecvBuffersReadPos)
; OUT:
; - nothing
; MODIFIED REGS: X (r16, r17, r21)


comHandleReceivedPacket:
  rcall onPacketReceived 
  brcs comHandleReceivedPacket_l1
  ldi xl, LOW(comStatsIgnored)
  ldi xh, HIGH(comStatsIgnored)
  rjmp comHandleReceivedPacket_l2
comHandleReceivedPacket_l1:
  ldi xl, LOW(comStatsHandled)
  ldi xh, HIGH(comStatsHandled)
comHandleReceivedPacket_l2:
  rcall comDeallocReadBufAndIncrCounter ; (r16, r17, r21)
  ret




.include "com_lowlevel.asm"
.include "com_buffer.asm"
.include "com_crc.asm"
.include "com_send.asm"
.include "com_recv.asm"



COM_END:
.equ MODULE_SIZE_COM = COM_END-COM_BEGIN

