;
;***********************************************************************
;
;   FIFO (first in, first out) queue handling.
;
;   The format of a FIFO in memory is:
;
;     name+0  -  number of data bytes currently in the queue
;
;     name+1  -  offset into the queue of where to put the next byte + 1
;
;     name+2  -  offset into the queue of where to get the next byte from + 1
;
;     name+3 thru name+2+size  -  Data buffer.  Sequential bytes are written
;       and read at decreasing addresses until the start of the buffer is
;       reached, in which case the next address wraps back to the end of the
;       buffer.
;
;   Define symbolic constants for the offset of various special bytes from
;   the start of a FIFO.  All subsequent code uses these symbols instead
;   of assuming the bytes are at fixed offsets.
;
fifo_ofs_n equ   0           ;offset from FIFO label for number of bytes in buffer
fifo_ofs_put equ 1           ;offset from FIFO label for PUT index
fifo_ofs_get equ 2           ;offset from FIFO label for GET index
fifo_ofs_buf equ 3           ;offset from FIFO label for start of buffer
;
;********************
;
;   Macro FIFO_DEFINE name size
;
;   Define a first in, first out queue.  The symbol NAME will be defined as
;   the first byte of the queue structure.  Size is the maximum number of
;   data bytes the queue will be able to hold.
;
fifo_define macro name, size
name     res     1           ;number of data bytes currently in the queue
         res     1           ;put offset
         res     1           ;get offset
         res     size        ;the data buffer
         endm
;
;********************
;
;   Macro FIFO_INIT name
;
;   Initialize the FIFO at NAME.  The register bank must be set for access
;   to the FIFO state.
;
fifo_init macro  name
         clrf    name + fifo_ofs_n ;indicate the FIFO is empty
         movlw   1
         movwf   name + fifo_ofs_put ;init PUT index
         movwf   name + fifo_ofs_get ;init GET index
         endm
;
;********************
;
;   Macro FIFO_SKIP_EMPTY name
;
;   Skips the next instruction after the macro if the FIFO at NAME is
;   empty.  The register bank must be set for access to the FIFO state.
;
fifo_skip_empty macro name

         movf    name + fifo_ofs_n ;set Z if FIFO empty
         skpz
         exitm

         endm
;
;********************
;
;   Macro FIFO_SKIP_NEMPTY name
;
;   Skips the next instruction after the macro if the FIFO at NAME
;   contains at least one data byte.  The register bank must be set for
;   access to the FIFO state.
;
;   W may be trashed.
;
fifo_skip_nempty macro name

         movf    name + fifo_ofs_n, F ;set Z if FIFO empty
         skpnz         ;FIFO not empty ?
         exitm

         endm
;
;********************
;
;   Macro FIFO_SKIP_FULL name size
;
;   Skips the next instruction after the macro if the FIFO at NAME is
;   completely full.  The register bank must be set for access to the
;   FIFO state.
;
;   W is trashed.
;
fifo_skip_full macro name, size
         movlw   size        ;get max bytes the FIFO can hold
         subwf   name + fifo_ofs_n, w ;compare to number of bytes currently in FIFO
         skpz            ;FIFO is completely full ?
         endm
;
;********************
;
;   Macro FIFO_SKIP_NFULL name size
;
;   Skips the next instruction after the macro if the FIFO at NAME is
;   not completely full.  The register bank must be set for access to
;   the FIFO state.
;
;   W is trashed.
;
fifo_skip_nfull macro name, size
         movlw   size        ;get max bytes the FIFO can hold
         subwf   name + fifo_ofs_n, w ;compare to number of bytes currently in FIFO
         skpnz            ;FIFO is not completely full ?
         endm
;
;********************
;
;   Macro FIFO_PUT name size data
;
;   Add the byte in DATA as the last byte in the FIFO at NAME.  SIZE must
;   be the maximum number of bytes the FIFO was defined to hold.  The
;   register bank must be set for access to the FIFO and DATA.  Since the
;   FIFO is usually not in global RAM, this means DATA must be either in
;   global RAM or in the same register bank as the FIFO.
;
;   The FIFO may be trashed if it is already full.  This should be checked
;   before this macro is called.
;
;   Note that if the FIFO could be accessed from the interrupt service
;   routine, then interrupts should be temporarily disabled around this
;   macro.
;
;   The indirect register bank must be set for access to the FIFO.
;
;   W is trashed.
;
fifo_put macro name, size, data
;
;   12 and 16 family processors.
;
         movlw   name + (fifo_ofs_buf - 1) ;get address for buffer index 0
         addwf   name + fifo_ofs_put, w ;make address of where to write this new byte
         movwf   fsr         ;point to where to write the byte
         movf    data, w     ;get the data byte into W
         movwf   indf        ;write the data byte into the buffer

         incf    name + fifo_ofs_n ;indicate one more byte now in the FIFO
         movlw   size        ;get offset for last byte in buffer
         decf    name + fifo_ofs_put ;update buffer index to next byte, set Z on wrap
         skpnz             ;not just decrement past beginning of buffer ?
         movwf   name + fifo_ofs_put ;wrap back to end of buffer

         endm
;
;********************
;
;   Macro FIFO_GET name size data
;
;   Get the next byte from the FIFO at NAME into DATA.  SIZE must
;   be the maximum number of bytes the FIFO was defined to hold.  The
;   register bank must be set for access to the FIFO and DATA.  Since the
;   FIFO is usually not in global RAM, this means DATA must be either in
;   global RAM or in the same register bank as the FIFO.
;
;   The FIFO may be trashed if it is already empty.  This should be
;   checked before this macro is called.
;
;   Note that if the FIFO could be accessed from the interrupt service
;   routine, then interrupts should be temporarily disabled around this
;   macro.
;
;   The indirect register bank must be set for access to the FIFO.
;
;   W is trashed.
;
fifo_get macro name, size, data
;
;   12 and 16 family processors.
;
         movlw   name + (fifo_ofs_buf - 1) ;get address for buffer index 0
         addwf   name + fifo_ofs_get, w ;make address of where to read new byte from
         movwf   fsr         ;point to where to read the byte from
         movf    indf, w     ;get the data byte
         movwf   data        ;pass it back

         decf    name + fifo_ofs_n ;indicate one less byte now in the FIFO
         movlw   size        ;get offset for last byte in buffer
         decf    name + fifo_ofs_get ;update buffer index to next byte, set Z on wrap
         skpnz            ;not just decrement past beginning of buffer ?
         movwf   name + fifo_ofs_get ;wrap back to end of buffer

         endm
