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




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

.cseg

COM_BUFFER_BEGIN:


; ---------------------------------------------------------------------------
; COM_AllocBufferAndGetXY
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear otherwise
; - Y: pointer to buffer
; - X: pointer to data portion of the buffer
; MODIFIED REGS: R16, R17 (R21)

COM_AllocBufferAndGetXY:
  rcall COM_BufferAlloc              ; (r16, r17, r21)
  brcc COM_AllocBufferAndGetXY_error
  mov xl, yl
  mov xh, yh
  adiw xh:xl, COM_BUFFER_OFFS_DATA
  sec
  ret
COM_AllocBufferAndGetXY_error:
  ldi xl, LOW(comStatsSendNoBuffer)
  ldi xh, HIGH(comStatsSendNoBuffer)
  clc
  ret



; ---------------------------------------------------------------------------
; COM_BufferAlloc
;
; Allocate a transfer buffer.
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear otherwise
; - Y: pointer to allocated buffer in SRAM
; MODIFIED REGISTERS: r16, r17, r21


COM_BufferAlloc:
  in r21, SREG                           ; save global interrupt enable bit from SREG
  cli
  lds r17, comRecvBuffersUsed
  cpi r17, COM_BUFFER_NUM
  brcc COM_AllocBuffer_error             ; no buffer available
  inc r17                                ; increment number of buffers used
  sts comRecvBuffersUsed, r17            ; store new value
  lds r16, comRecvBuffersWritePos        ; get current write pos
  mov r17, r16                           ; increment for next call
  inc r17
  cpi r17, COM_BUFFER_NUM                ; CF set if COM_BUFFER_NUM > R17
  brcs COM_AllocBuffer_l1
  clr r17                                ; wraparound
COM_AllocBuffer_l1:
  sts comRecvBuffersWritePos, r17        ; store new writepos for next caller
  rcall COM_BufferPosToY                 ; (R16, R17)
  clr r17
  std y+COM_BUFFER_OFFS_FLAGS, r17       ; preset flags
  out SREG, r21                          ; restore global interrupt enable bit in SREG
  sec
  ret
COM_AllocBuffer_error:
  out SREG, r21
  clc
  ret



; ---------------------------------------------------------------------------
; COM_BufferDeallocBack
;
; Release a transfer buffer at the end of the list by decreasing the write pos.
; This releases the last allocated buffer!
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear otherwise
; MODIFIED REGISTERS: r16, r17, r21

COM_BufferDeallocBack:
  in r21, SREG                           ; save global interrupt enable bit from SREG
  cli
  lds r17, comRecvBuffersUsed
  tst r17
  breq COM_BufferDeallocBack_error       ; no buffer allocated, nothing to release
  dec r17
  sts comRecvBuffersUsed, r17            ; store new value
  lds r17, comRecvBuffersWritePos
  tst r17                                ; 0?
  brne COM_BufferDeallocBack_l1          ; nope go directly decrement r17
  ldi r17, COM_BUFFER_NUM                ; wrap-around
COM_BufferDeallocBack_l1:
  dec r17
  sts comRecvBuffersWritePos, r17
  out SREG, r21
  sec
  ret
COM_BufferDeallocBack_error:
  out SREG, r21
  clc
  ret



; ---------------------------------------------------------------------------
; COM_BufferDeallocFront
;
; Release a transfer buffer by increasing the read pos.
;
; IN:
; - nothing
; OUT:
; - CFLAG: set if okay, clear otherwise
; MODIFIED REGISTERS: r16, r17, r21

COM_BufferDeallocFront:
  in r21, SREG                           ; save global interrupt enable bit from SREG
  cli
  lds r17, comRecvBuffersUsed
  tst r17
  breq COM_BufferDeallocFront_error       ; no buffer allocated, nothing to release
  dec r17
  sts comRecvBuffersUsed, r17            ; store new value
  lds r17, comRecvBuffersReadPos
  inc r17
  cpi r17, COM_BUFFER_NUM
  brcs COM_BufferDeallocFront_l1
  clr r17                                ; wrap-around
COM_BufferDeallocFront_l1:
  sts comRecvBuffersReadPos, r17
  out SREG, r21
  sec
  ret
COM_BufferDeallocFront_error:
  out SREG, r21
  clc
  ret



; ---------------------------------------------------------------------------
; COM_BufferPosToY
;
; Get a pointer to the SRAM position of the given buffer.
; CAVE: Code must correspond to COM_BUFFER_SIZE!!
; IN:
; - R16: buffer number (starting with 0)
; OUT:
; - Y: pointer to buffer in SRAM
; MODIFIED REGISTERS: R16, R17

COM_BufferPosToY:
  ; calculate offset
  clr r17
  mov yl, r16
  clr yh

  lsl yl
  rol yh ; *2

  add yl, r16
  adc yh, r17 ; *3

  lsl yl
  rol yh ; *6

  lsl yl
  rol yh ; *12

  lsl yl
  rol yh ; *24

  ; add base position of buffers
  ldi r16, LOW(comRecvBuffers)
  ldi r17, HIGH(comRecvBuffers)
  add yl, r16
  adc yh, r17
  ret


COM_BUFFER_END:
.equ MODULE_SIZE_COM_BUFFER = COM_BUFFER_END-COM_BUFFER_BEGIN



