Files
aqhomecontrol/avr/ringbuffer.asm
Martin Preuss e1131871ef ringbuffer: alloc no less than 2 bytes.
This protects against race conditions. We can now preset the first two
bytes which are used for size and flags by the COM module (currently the
only user of the ringbuffer module).
2023-01-19 23:18:28 +01:00

244 lines
5.7 KiB
NASM

.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_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 yl
push yh
clr r17
add yl, r16
adc yh, r17
std y+RINGBUFFER_OFFS_DATA, r17 ; (length byte in COM module)
std y+RINGBUFFER_OFFS_DATA+1, r17 ; (flags byte in COM module)
pop yh
pop yl
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?
brcc 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
brcc 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
brcc RingBuffer_Read_end
sub r18, r19
RingBuffer_Read_end:
ret