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





.equ RINGBUFFER_OFFS_MAXSIZE  = 0
.equ RINGBUFFER_OFFS_USED     = 1
.equ RINGBUFFER_OFFS_READPOS  = 2
.equ RINGBUFFER_OFFS_WRITEPOS = 3
.equ RINGBUFFER_OFFS_DATA     = 4



.cseg


RINGBUFFER_BEGIN:



; ***************************************************************************
; RingBuffer_Init
;
; IN:
; - Y: base address of the ringbuffer in SRAM
; - R16: maximum size in bytes (pure data excluding headers like pos pointers etc).
; OUT:
; - R16: pos of allocated buffer
; REGISTERS MODIFIED: r16, r17, r18, r19, r20, r21
RingBuffer_Init:
  clr r17
  std y+RINGBUFFER_OFFS_MAXSIZE, r16
  std y+RINGBUFFER_OFFS_USED, r17
  std y+RINGBUFFER_OFFS_READPOS, r17
  std y+RINGBUFFER_OFFS_WRITEPOS, r17
  ret



; ***************************************************************************
; RingBuffer_Alloc
;
; IN:
; - Y: base address of the ringbuffer in SRAM
; - R16: number of bytes to allocate
; OUT:
; - R16: pos of allocated buffer
; REGISTERS MODIFIED: r16, r17, r18, r19, r20, r21

RingBuffer_Alloc:
  in r21, SREG                           ; save global interrupt enable bit from SREG
  cli
  ldd r17, y+RINGBUFFER_OFFS_MAXSIZE
  ldd r18, y+RINGBUFFER_OFFS_USED
  ldd r19, y+RINGBUFFER_OFFS_WRITEPOS
  
  cpi r16, 2                             ; alloc at least two bytes
  brcs RingBuffer_Alloc_error            ; too few bytes
  
  mov r20, r16                           ; to_alloc
  add r20, r18                           ; to_alloc + used
  cp r20, r17                            ; new_used < max?
  brcc RingBuffer_Alloc_error
  
  std y+RINGBUFFER_OFFS_USED, r20
  mov r20, r19                           ; current write pos (for later)
  add r19, r16                           ; current_writepos+to_alloc
  cp r19, r17                            ; new_writepos < max?
  brcs RingBuffer_Alloc_l1
  sub r19, r17
RingBuffer_Alloc_l1:
  std y+RINGBUFFER_OFFS_WRITEPOS, r19
  mov r16, r20
  
  ; preset first byte (this is the length byte in COM module) with 0
  push r16
    mov r18, r16
    clr r16
    rcall RingBuffer_Write
    rcall RingBuffer_Write
  pop r16
  
  out SREG, r21                          ; restore global interrupt enable bit in SREG
  sec
  ret

RingBuffer_Alloc_error:
  out SREG, r21
  clc
  ret



; ***************************************************************************
; RingBuffer_DeallocRead
;
; IN:
; - Y: base address of the ringbuffer in SRAM
; - R16: number of bytes to deallocate (e.g. number of bytes to skip in read ptr)
; OUT:
; - nothing
; REGISTERS MODIFIED: r17, r18, r19, r20, r21

RingBuffer_DeallocRead:
  in r21, SREG
  cli
  ldd r17, y+RINGBUFFER_OFFS_MAXSIZE
  ldd r18, y+RINGBUFFER_OFFS_USED
  ldd r19, y+RINGBUFFER_OFFS_READPOS

  mov r20, r18
  sub r20, r16                           ; used - to_dealloc
  brcs RingBuffer_DeallocRead_error      ; overflow -> error
  std y+RINGBUFFER_OFFS_USED, r20
  mov r20, r19                           ; readpos
  add r20, r16                           ; readpos + to_dealloc
  cp r20, r17                            ; new readpos > max?
  brcs RingBuffer_DeallocRead_l1         ; no, rjmp
  sub r20, r17                           ; wrap around
RingBuffer_DeallocRead_l1:
  std y+RINGBUFFER_OFFS_READPOS, r20
  out SREG, r21
  sec
  ret

RingBuffer_DeallocRead_error:
  out SREG, r21
  clc
  ret



; ***************************************************************************
; RingBuffer_DeallocWrite
;
; IN:
; - Y: base address of the ringbuffer in SRAM
; - R16: number of bytes to deallocate (e.g. number of bytes to move back write ptr)
; OUT:
; - nothing
; REGISTERS MODIFIED: r17, r18, r19, r20, r21

RingBuffer_DeallocWrite:
  in r21, SREG
  cli
  ldd r17, y+RINGBUFFER_OFFS_MAXSIZE
  ldd r18, y+RINGBUFFER_OFFS_USED
  ldd r19, y+RINGBUFFER_OFFS_WRITEPOS

  mov r20, r18                           ; used
  sub r20, r16                           ; used - to_dealloc
  brcs RingBuffer_DeallocWrite_error     ; overflow -> error
  std y+RINGBUFFER_OFFS_USED, r20
  mov r20, r19                           ; writepos
  sub r20, r16                           ; writepos - to_dealloc
  brcc RingBuffer_DeallocWrite_l1        ; no, rjmp
  add r20, r17                           ; wrap around
RingBuffer_DeallocWrite_l1:
  std y+RINGBUFFER_OFFS_WRITEPOS, r20
  out SREG, r21
  sec
  ret

RingBuffer_DeallocWrite_error:
  out SREG, r21
  clc
  ret



; ***************************************************************************
; RingBuffer_Write
;
; Write a byte into the ringbuffer.
;
; IN:
; - Y: pointer to buffer
; - R16: byte to write
; - R18: pos pointer inside ring buffer to write to next
; - R19: maximum size of the ringbuffer (for wrap-around)
; OUT:
; - R18: incremented pos pointer inside ringbuffer
; MODIFIED REGS: r3, r22

RingBuffer_Write:
  push yh
    push yl
      clr r3
      ldi r22, RINGBUFFER_OFFS_DATA
      add yl, r22
      adc yh, r3
      add yl, r18
      adc yh, r3
      st y, r16
    pop yl
  pop yh
  inc r18
  cp r18, r19         ; r19 > r18; yes: CF set
  brcs RingBuffer_Write_end
  sub r18, r19
  
RingBuffer_Write_end:
  ret



; ***************************************************************************
; RingBuffer_Read
;
; Read a byte from the ringbuffer.
;
; IN:
; - Y: pointer to buffer
; - R18: pos pointer inside ring buffer to read from next
; - R19: maximum size of the ringbuffer (for wrap-around)
; OUT:
; - R16: byte read
; - R18: incremented pos pointer inside ringbuffer
; MODIFIED REGS: r3, r22

RingBuffer_Read:
  push yh
    push yl
      clr r3
      ldi r22, RINGBUFFER_OFFS_DATA
      add yl, r22
      adc yh, r3
      add yl, r18
      adc yh, r3
      ld r16, y
    pop yl
  pop yh
  inc r18
  cp r18, r19
  brcs RingBuffer_Read_end
  sub r18, r19
  
RingBuffer_Read_end:
  ret




RINGBUFFER_END:
.equ MODULE_SIZE_RINGBUFFER = RINGBUFFER_END-RINGBUFFER_BEGIN


