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.