I know assembly is not a big thing in this forum and maybe not even for this category, but for those that might be interested, I've cooked together an algorithm which will be an integral part of the TWI (I2C) driver that I'm designing in ASM
The scope of this routine is to provide a convenient means by which to read/write characters to a 255 byte buffer, only by passing a pointer to the array in "X" and the character in R16. Optionally it can be a means by which establishing a pointer in "X" to the beginning or end of buffer dependant upon reading or writing.
The buffer needs to be declared in this format although alignment on any boundary is not necessary.
; =============================================================================================
.byte 256 ; 256 byte wrap around/circular buffer
BIdx: .byte 1 ; End Index
.byte 1 ; Start Index
.byte 2 ; Pointer to nested routine for subroutine.
I've tested this thuorouly and it's benchmarked out at approx. 2.45 µs/iteration and obviously, if a nested procedure is declared, that would have to be taken into account.
; =============================================================================================
; Read/Write a Cylindrical/Wrap Around 256 byte buffer, with optional nested function or
; subroutine.
; ENTER: X = Pointer to buffers indices
; TEMP = Byte to be written to buffer
; R0 : Bit 0 = 1 to write to buffer, read otherwise
; 1 = 1 to execute optional function/subroutine
; LEAVE: TEMP = Value returned from next position in buffer
; R16 (TEMP)
; R09
; R10 Altered
; R11
; FLAGS: If error R10 = 0FFH and carry flag is set, otherwise clear
; ---------------------------------------------------------------------------------------------
RW_Buffer: push ZL
push ZH
push XL
push XL
bst R0, 0 ; Simplifies condition @ RW_Post
; Caller has passed pointer to indices, so by default we'll calculate how bytes are
; left to be read or if bit 0 of R0 is set, then number of bytes that can be written.
ld R10, X+ ; Offset to end of data
ld R11, X+ ; Offset to beginning of data
sub R10, R11 ; R10 = bytes to be read
mov R9, R10 ; This simplifies
sbrc R0, 0
com R10 ; R10 = byte that can be written.
brne RW_Proc
; At this point R10 = 0, so we know dependant upon bit 0 of R0, buffer is either
; full or empty
dec R10 ; Set FF as return code buffer full/empty
sec ; Set carry (error)
pop XL ; Waste value on stack
rjmp RW_Error
; In the event Bit 1 of R0 is on (optional procedure), Z needs to be initialized
; for ICALL instruction.
RW_Proc: ld ZL, X+
ld ZH, X
; X needs to point to proper position in buffer dependant upon reading or writing.
; Space needs to be allocated a whole page (256 bytes) before Buffer_Indices.
dec XH ; Bump back one page
subi XL, 2 ; Re-align to beginning
add XL, R11 ; Offset where next byte is to be read from
sbrc R0, 0 ; Are we writing. Bit 0 = 1
add XL, R9 ; Bump ahead to next position to write to.
sbrs R0, 1
rjmp RW_Post ; Bounce is not executing nested routine
; R10 = bytes that can be read, or bytes that can be written to and X is the pointer.
; It will be callee's responsibility to return error codes and registers according
; to algorithm's scope.
icall
pop XL ; Waste parameter on stack
rjmp RW_Error
RW_Post: brts RW_Write ; Was "T" bit set in preamble
; Read byte from buffer, point "X" to appropriate index
ld TEMP, X
pop XL
inc XL
rjmp RW_Finished
; Write byte to buffer, point "X" to appropriate index
RW_Write: st X, TEMP
pop XL
; Update appropriate index
RW_Finished: inc XH ; Bump ahead one page
ld R9, X ; Read index
inc R9 ; Bump it by 1
st X, R9 ; Write it back
clc ; Assure carry is cleared
; Postable or Error cleans up stack at returns to caller
RW_Error: pop XL
pop ZH
pop ZL
ret
The need for this snippet was prompted by thinking about what I needed to send data packets to 1602 LCD via TWI using interrupts.