Here's some code for a software FIFO / circular buffer that'll work on a PIC12 and up.
I was messing around with the EUSART peripheral on a 12F1572, and hit a couple of corner cases where the hardware buffers weren't quite enough. So I came up with this.
Let me know what you think.
; Two subroutines to move data on and off a FIFO.
; These can be configured for a FIFO size from
; 2^1 bytes to 2^7 bytes.
; In this example, the FIFO size is 8 bytes (10 bytes).
;
; These subroutines can be used to move data between
; the main program and the interrupt service routine,
; as long as it is one way per FIFO. i.e.:
; main => FIFO => ISR
; or
; ISR => FIFO => main
;
; The FIFO data structure occupies a contiguous block
; totalling 2^X + 2 bytes:
; Byte 0 is a pointer to the end of the queue.
; Byte 1 is a pointer to the start of the queue.
; The remaining bytes are the FIFO data registers.
;
; To initialise or reset a queue, point the FSR to the address
; of the FIFO data structure and clear bytes 0 and 1.
;
; Take care when setting FSR(n)(L) to ensure the data structure
; does not cross the 2^8 boundary.
; If boundary crossing is required, uncomment the carry checking lines.
; Enqueues data to FIFO / circular buffer.
; 16 instructions, 2 registers, 1 FSR, no loops.
; Works on PIC12 and up.
; Interrupt safe, if used one way only per FIFO.
;
; Initial state:
; FSR0 = address of FIFO data structure
; DATA_IN = data to be enqueued
; WREG = ignored
; TEMP = ignored
;
; Final state (FIFO not full):
; WREG = 0, Z clear
; FSR0 = address of FIFO data structure
; DATA_IN = unchanged
; TEMP = offset value
;
; Final state (FIFO full):
; WREG = 0, Z set
; FSR0 = address of FIFO data structure
; DATA_IN = unchanged
; TEMP = unchanged
DATA_IN = h'70'
TEMP = h'7F'
ENQF MOVIW 1 [FSR0] ; Do nothing if FIFO is full. Z will be set.
SUBWF INDF0, W
XORLW d'8' ; 4: 4-byte, 8: 8-byte, 16: 16-byte, 32: 32-byte etc.
BTFSC STATUS, Z
RETURN
MOVF INDF0, W ; Store pointer
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
MOVWF TEMP
ADDWF FSR0L, F ; Move to next free spot
;BTFSC STATUS, C ; Uncomment for 2^8 boundary crossing
;INCF FSR0H, F
MOVF DATA_IN, W ; Enqueue data
MOVWI 2 [FSR0]
MOVF TEMP, W
SUBWF FSR0L, F
;BTFSS STATUS, C ; Uncomment for 2^8 boundary crossing
;DECF FSR0H, F
INCF INDF0, F ; Increment pointer
BCF STATUS, Z
RETLW d'0' ; Done. Z will be clear.
; Dequeues data from FIFO / circular buffer.
; 16 instructions, 2 registers, 1 FSR, no loops.
; Works on PIC12 and up.
; Interrupt safe, if used one way only per FIFO.
;
; Initial state:
; FSR0 = address of FIFO data structure
; DATA_OUT = ignored
; WREG = ignored
; TEMP = ignored
;
; Final state (FIFO not full):
; WREG = 0, Z clear
; FSR0 = address of FIFO data structure
; DATA_OUT = dequeued data
; TEMP = offset value
;
; Final state (FIFO full):
; WREG = 0, Z set
; FSR0 = address of FIFO data structure
; DATA_OUT = unchanged
; TEMP = unchanged
DATA_OUT = h'71'
TEMP = h'7F'
DEQF MOVIW 1 [FSR0] ; Do nothing if FIFO is empty. Z will be set.
XORWF INDF0, W
BTFSC STATUS, Z
RETURN
MOVIW ++ FSR0 ; Increment FSR position and store pointer
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
MOVWF TEMP
ADDWF FSR0L, F ; Move to next item
;BTFSC STATUS, C ; Uncomment for 2^8 boundary crossing
;INCF FSR0H, F
MOVIW 1 [FSR0] ; Dequeue data
MOVWF DATA_OUT
MOVF TEMP, W
SUBWF FSR0L, F
;BTFSS STATUS, C ; Uncomment for 2^8 boundary crossing
;DECF FSR0H, F
INCF INDF0, F ; Increment pointer
ADDFSR FSR0, -1 ; Decrement FSR position
BCF STATUS, Z
RETLW d'0' ; Done. Z will be clear.
; ***** PIC10 compatible code below *****
; Two subroutines to move data on and off a FIFO.
; These can be configured for a FIFO size from
; 2^1 bytes to 2^7 bytes.
; In this example, the FIFO size is 8 bytes (10 bytes).
;
; These subroutines are NOT interrupt safe.
;
; The FIFO data structure occupies a contiguous block
; totaling 2^X + 2 bytes:
; Byte 0 is a count of the number of items queued.
; Byte 1 is a pointer to the start of the queue.
; Unused bits of byte 1 are masked out and should be ignored.
; The remaining bytes are the FIFO data registers.
;
; To initialise or reset a queue, point the FSR to the address
; of the FIFO data structure and "CLRF INDF". This simply
; clears the item count.
;
; Take care when setting FSR(n)(L) to ensure the data structure
; does not cross the 2^8 boundary.
; If boundary crossing is required, modify each subroutine with
; the following directly after "ADDWF FSR(n)L, F":
; BTFSC STATUS, C
; INCF FSR(n)H, F
; Enqueues data to FIFO / circular buffer.
; 13 instructions, 1 register, 1 FSR, no loops.
; Works on PIC10 and up.
;
; Initial state:
; TEMP = data to be enqueued
; FSR(n)(L) = address of FIFO data structure
;
; Final state (FIFO not full):
; WREG = 0, Z clear
; FSR(n)(L) = address of enqueued item on FIFO
;
; Final state (FIFO full):
; WREG = 0, Z set
; FSR(n)(L) = address of FIFO data structure
TEMP = h'40'
ENQF CLRW ; Do nothing if FIFO is full. Z will be set.
BTFSC INDF, 3 ; 2: 4-byte, 3: 8-byte, 4: 16-byte, 5: 32-byte etc.
RETURN
MOVF INDF, W ; Store current item count in W
INCF INDF, F ; Increment item count register
INCF FSR, F ; Advance to pointer register
; Replace this line with "ADDFSR FSRn, d'1'" on supported chips
ADDWF INDF, W ; Calculate the offset to the next available spot in the queue
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
ADDLW d'1'
ADDWF FSR, F ; Move to the end of the queue. Clears Z.
; Add CARRY to FSR high byte on supported chips, if crossing 2^8 boundary.
SWAPF TEMP, W ; Enqueue data. Use SWAPF to leave Z clear.
MOVWF INDF ; Optionally, this can be done elsewhere, leaving the subroutine
; to simply point the FSR at the appropriate location.
RETLW d'0' ; Done. Z will be clear.
; Dequeues data from FIFO / circular buffer.
; 12 instructions, 1 FSR, no loops.
; Works on PIC10 and up.
;
; Initial state:
; FSR(n)(L) = address of FIFO data structure
;
; Final state (FIFO not empty):
; WREG = dequeued data, Z clear
; FSR(n)(L) = address of dequeued item on FIFO
;
; Final state (FIFO empty):
; WREG = 0, Z set
; FSR(n)(L) = address of FIFO data structure
DEQF MOVF INDF, W ; Do nothing if FIFO is empty. Z will be set.
BTFSC STATUS, Z
RETURN
DECF INDF, F ; Decrement item count
INCF FSR, F ; Advance to pointer register
; Replace this line and the following MOVF
; with a pre-increment MOVIW on supported chips.
MOVF INDF, W ; Calculate the offset to the next item off the queue
ANDLW d'7' ; 3: 4-byte, 7: 8-byte, 15: 16-byte, 31: 32-byte etc.
ADDLW d'1'
INCF INDF, F ; Increment pointer
ADDWF FSR, F ; Move to the next item off the queue. Clears Z.
; Add CARRY to FSR high byte on supported chips, if crossing 2^8 boundary
SWAPF INDF, W ; Dequeue data. Use SWAPF to leave Z clear.
; Optionally, this can be done elsewhere, leaving the subroutine
; to simply point the FSR at the appropriate location.
RETURN ; Done. Z will be clear.
Edit 1: Added code for PIC12F and up including NorthGuy's suggestions for interrupt access and FSR instructions.
Dabbot.