; ***************************************************************
; * Copyright (C) 2007, Embed Inc (http://www.embedinc.com) *
; * *
; * Permission to copy this file is granted as long as this *
; * copyright notice is included in its entirety at the *
; * beginning of the file, whether the file is copied in whole *
; * or in part and regardless of whether other information is *
; * added to the copy. *
; * *
; * The contents of this file may be used in any way, *
; * commercial or otherwise. This file is provided "as is", *
; * and Embed Inc makes no claims of suitability for a *
; * particular purpose nor assumes any liability resulting from *
; * its use. *
; ***************************************************************
;
; Standard include file assumed by most dsPIC source modules. The specifics
; must be configured to the particular processor and application. This
; is done by setting assembly values before this file is included. The
; include file STD_DEF.INS.DSPIC is provided to set defaults for all
; the required assembly values. An application should include STD_DEF.INS.DSPIC,
; then set any values it knows and cares about, then include STD.INS.DSPIC.
; In this way, applications are protected from changes to this include
; file that may require additional values to be set.
;
; The following assembly values are assumed by this file, some of these
; are set to default values in STD_DEF.INS.DSPIC:
;
; FREQ_OSC - Processor oscillator frequency in Hz. This is the
; effective oscillator frequency after the PLL, if enabled, is
; applied. The instruction clock is FREQ_OSC / 4.
;
; Check for required constants and abort on error if any is missing.
;
.ifndef freq_osc
.error "FREQ_OSC not defined before STD.INS.DSPIC file included"
.endif
.set nflagb, 0 ;init to no GFLn flag registers allocated
;
; Derived constants.
;
.equiv freq_inst, freq_osc / 4 ;instruction clock frequency in Hz
.equiv nsec_inst, 1000000000 / freq_inst ;instruction cycle time in nS
.set ii, freq_inst / 4
.equiv inst8nsmin, (2000000000 - 1 + ii) / ii ;min guaranteed nS in 8 instructions
;
;***********************************************************************
;
; General utility macros.
;
;********************
;
; Macro SKIP_Z
;
; Skip the next instruction if the zero flag is set.
;
.macro skip_z
bra z, .+4
.endm
;
;********************
;
; Macro SKIP_NZ
;
; Skip the next instruction if the zero flag is not set.
;
.macro skip_nz
bra nz, .+4
.endm
;
;********************
;
; Macro SKIP_CARR
;
; Skip the next instruction if a carry occurred.
;
.macro skip_carr
bra c, .+4
.endm
;
;********************
;
; Macro SKIP_NCARR
;
; Skip the next instruction if no carry occurred.
;
.macro skip_ncarr
bra nc, .+4
.endm
;
;********************
;
; Macro SKIP_BORR
;
; Skip the next instruction if a borrow occurred.
;
.macro skip_borr
bra nc, .+4
.endm
;
;********************
;
; Macro SKIP_NBORR
;
; Skip the next instruction if no borrow occurred.
;
.macro skip_nborr
bra c, .+4
.endm
;
;********************
;
; Macro BRA_BORR target
;
; Branch to TARGET if the last arthmetic operation resulted in a
; borrow (C flag not set).
;
.macro bra_borr target
bra nc, \target
.endm
;
;********************
;
; Macro BRA_NBORR target
;
; Branch to TARGET if the last arthmetic operation did not result in a
; borrow (C flag set).
;
.macro bra_nborr target
bra c, \target
.endm
;
;********************
;
; Macro BRA_CARR target
;
; Branch to TARGET if the last arthmetic operation resulted in a
; carry (C flag set).
;
.macro bra_carr target
bra c, \target
.endm
;
;********************
;
; Macro BRA_NCARR target
;
; Branch to TARGET if the last arthmetic operation did not result in a
; carry (C flag not set).
;
.macro bra_ncarr target
bra nc, \target
.endm
;
;********************
;
; Macro SETFLAG flag
;
; Set the flag defined by a /FLAG preprocessor directive.
;
.macro setflag flag
bset flag_&flag&_reg, #flag_&flag&_bit
.endm
;
;********************
;
; Macro CLRFLAG flag
;
; Clear the flag defined by a /FLAG preprocessor directive.
;
.macro clrflag flag
bclr flag_&flag&_reg, #flag_&flag&_bit
.endm
;
;********************
;
; Macro SKIP_FLAG flag
;
; Skip the next instruction if the flag defined by a /FLAG preprocessor
; directive is set.
;
.macro skip_flag flag
btss flag_&flag&_reg, #flag_&flag&_bit
.endm
;
;********************
;
; Macro SKIP_NFLAG flag
;
; Skip the next instruction if the flag defined by a /FLAG preprocessor
; directive is clear.
;
.macro skip_nflag flag
btsc flag_&flag&_reg, #flag_&flag&_bit
.endm
;
;********************
;
; Macro WAITNOP n
;
; Write instructions that do nothing for the next N instruction cycles.
; Nothing is done if N <= 0.
;
.macro waitnop n
.if \n >> 23
.exitm ;abort if N is negative
.endif
.rept \n
nop
.endr
.endm
;
;********************
;
; Macro WAITNS ns, minst
;
; Guarantee a wait of at least NS nanoseconds minus MINST instruction
; cycles. This macro writes NOPs and related instructions, and should
; only be used for very short delays. The minimum delay is always
; guaranteed. If MINST instruction cycles is longer than NS nanoseconds,
; then no instructions are written.
;
.macro waitns ns, minst
.set ii\@, (\ns * 8 - 1 + inst8nsmin) / inst8nsmin ;total wait cycles
.set ii\@, ii\@ - \minst ;cycles wait to insert
waitnop ii\@
.endm
;
;********************
;
; Macro ALLOC name [, size [, align]]
;
; Allocate space in the current section and define the label NAME as the first address
; of the allocated space. SIZE is the number of address increments to allocate. This
; is in bytes if allocating in a data section. The default SIZE is 2. ALIGN is the
; minimum required starting alignment multiple of the allocated space. The default
; ALIGN is 1 for SIZE of 1 or less and 2 for SIZE of 2 or more.
;
.macro alloc name, size=2, align=-1
.set align\@, \align
.if align\@ == -1 ;using default alignment ?
.if \size <= 1
.set align\@, 1 ;use byte alignment for bytes
.else
.set align\@, 2 ;use word alignment for words or larger
.endif
.endif
.align align\@
\name: .skip \size
.endm
;
;********************
;
; Macro STRUCT_START
;
; Start the definition of a memory structure with named fields. After
; this macro is called, the FIELD macro can be called any number of
; times to define successive fields in the structure.
;
.macro struct_start
.set struct_offset, 0 ;initialize the offset for the next field
.endm
;
;********************
;
; Macro FIELD name [, size [, align]]
;
; Define one more field in a structure. NAME will be defined as the
; offset from the start of the structure to the start of the new field.
; Macro STRUCT_START must be called to initialize assembler state
; for that structure before the FIELD macro is used.
;
; SIZE is the size of the field in bytes. It defaults to 2, meaning
; the default field is a 16 bit word.
;
; ALIGN is the minimum required address alignment of the field. Addresses
; will be skipped as necessary so that the offset of the field is a
; multiple of ALIGN. The default alignment is 2 (field starts on a
; 16 bit word boundary) for SIZE values of 2 or more. The default
; alignment is 1 for SIZE values of 1 or less.
;
.macro field name, size=2, align=-1
.set struct_align, \align
.if struct_align == -1 ;using default alignment ?
.if \size <= 1
.set struct_align, 1 ;use byte alignment for bytes
.else
.set struct_align, 2 ;use word alignment for words or larger
.endif
.endif
.set struct_offset, (struct_offset + struct_align - 1) / struct_align
.set struct_offset, struct_offset * struct_align ;skip forward to alignment
.equiv \name, struct_offset ;define the new field here
.set struct_offset, struct_offset + \size ;update next available offset
.endm
;
;********************
;
; Macro TIMER_PERIOD timer, cycles
;
; Compute the period setup parameters for timer TIMER so that its period comes as
; close as possible to CYCLES instruction cycles. This macro only computes values
; and does not emit any executable code. The parameter TIMER must be an integer
; indicating the number of the timer to compute the setup values for. An error
; is generated if the period is out of range for the hardware to achieve.
;
; The following assembly variables are set:
;
; VAL_PR - PRn register value to achieve the desired period.
;
; VAL_TCKPS - TCKPS field value in the TnCON control register for the timer.
; This field controls the prescaler divide value.
;
; VAL_PRESCALE - Actual presscaler divide value selected by VAL_TCKPS.
;
; VAL_PERIOD - Actual period resulting from the computed setup in units of
; instruction cycles. This may not be exactly the same as CYCLES if the
; prescaler divide value is greater than 1.
;
; VAL_TnCON - Timer control register value for periodic ticks as specified.
;
; The lowest possible prescaler divide value is used such that the VAL_PR is within
; the range of the period register.
;
.macro timer_period timer, cycles
.set val_prescale, 1 ;init prescaler divide value to smallest possible
.set val_tckps, 0 ;init prescaler select field to match divide value
.set val_pr, ((((\cycles * 2) / val_prescale) + 1) >> 1) - 1
.if val_pr & ~0xFFFF ;need larger prescaler ?
.set val_prescale, 8
.set val_tckps, 1
.set val_pr, ((((\cycles * 2) / val_prescale) + 1) >> 1) - 1
.endif
.if val_pr & ~0xFFFF ;need larger prescaler ?
.set val_prescale, 64
.set val_tckps, 2
.set val_pr, ((((\cycles * 2) / val_prescale) + 1) >> 1) - 1
.endif
.if val_pr & ~0xFFFF ;need larger prescaler ?
.set val_prescale, 256
.set val_tckps, 3
.set val_pr, ((((\cycles * 2) / val_prescale) + 1) >> 1) - 1
.endif
.if val_pr & ~0xFFFF ;need larger prescaler ?
.print "Timer period can not be achieved"
.fail 0
.endif
.set val_period, (val_pr + 1) * val_prescale ;compute final instructions per period
.set val_t&timer&con, 0b1010000000000000 | (val_tckps << 4)
; -X-XXXXXX------X unused bits
; 1--------------- enable the timer
; --1------------- turn off timer in idle mode
; ---------0------ disable timer gate
; ----------00---- prescaler select, VAL_TCKPS will be merged in
; ------------0--- do not make part of 32 bit timer (type B only)
; -------------0-- do not sync external clock (type A only)
; --------------0- clock source is intruction clock
.endm
;
;***********************************************************************
;
; Subroutine and other entry point linkage.
;
; By convention, subroutines preserve the general registers W0 - W14
; and trash status bit and other related state unless explicitly documented
; to the contrary.
;
; Create the REGF0 - REGF14 flag bits. These can be ORed together to
; indicate an arbitrary set of W0 - W14 registers.
;
.irp ii, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
.equiv regf\ii, 1 << \ii
.endr
; Mask of all registers trashed by C subroutines:
;
.equiv regf_ctrash, regf0 | regf1 | regf2 | regf3 | regf4 | regf5 | regf6 | regf7
;
;********************
;
; Macro PUSHREGS regflags
;
; Push the indicated registers W0-W14 onto the stack. REGFLAGS is the
; mask of the registers to push. Bit 0 set indicates to push W0, bit 1
; set to push W1, etc. Registers are pushed in W0 to W14 order.
;
.macro pushregs regflags
.irp ii, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
.if (\regflags) & (1 << \ii)
push w\ii
.endif
.endr
.endm
;
;********************
;
; Macro POPREGS regflags
;
; Pop the indicated registers off the stack. REGFLAGS is the mask of the
; selected registers in the same format as for macro PUSHREGS, above. This
; macro undoes what PUSHREGS did if the same REGFLAGS value is passed to
; both. The registers are popped in W14 to W0 order.
;
.macro popregs regflags
.irp ii, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
.if (\regflags) & (1 << \ii)
pop w\ii
.endif
.endr
.endm
;
;********************
;
; Macro ENTER [regflags]
;
; Perform standard subroutine entry.
;
; REGFLAGS is the logical OR of any combination of REGF0 - REGF14 to
; indicate which of the general registers W0 - W14 are to be
; automatically saved on the stack. The appropriate PUSH instructions
; will be emitted, and the mask of pushed registers is saved in the
; assembler variable SAVEDREGS. The registers will be pushed in low to
; high register number order.
;
; The default value for REGFLAGS is zero, meaning no registers are pushed
; onto the stack.
;
.macro enter regflags = 0
pushregs \regflags
.set savedregs, \regflags
.endm
;
;********************
;
; Macro POPSAVED
;
; Pop saved registers from the stack.
;
; The registers indicated by SAVEDREGS are popped. SAVEDREGS is usually
; set by the GLBSUB macro at the beginning of a subroutine. The POPSAVED
; macro allows those registers to be popped without having to know the
; explicit list of registers.
;
.macro popsaved
popregs savedregs
.endm
;
;********************
;
; Macro LEAVE [regflags]
;
; Perform standard subroutine exit. This macro is intended to be used
; together with ENTER at the beginning of the subroutine.
;
; REGFLAGS is the logical OR of any combination of REGF0 - REGF14 to
; indicate which of the general registers W0 - W14 are to be automatically
; restored from the stack. The appropriate POP instructions will be
; emitted in high to low register number order.
;
; The default value for REGFLAGS is zero, meaning no registers are popped
; from the stack.
;
.macro leave regflags = 0
popregs \regflags
return
.endm
;
;********************
;
; Macro LEAVEREST
;
; Just like LEAVE except that the registers indicated by SAVEDREGS are
; automatically restored. SAVEDREGS should have been set by the ENTER
; macro to the routine being left.
;
.macro leaverest
leave savedregs
.endm
;
;********************
;
; Macro GLBSUB name, [regflags]
;
; Define the entry point of a globally callable subroutine. NAME is the
; name the subroutine will be called by. REGFLAGS indicates which
; registers to automatically save on the stack. See the ENTER macro
; description for details.
;
.macro glbsub name, regflags = 0
\name: ;define the entry point label
.global \name ;declare the label global
enter \regflags ;perform standard subroutine entry
.if debug
nop
nop
.endif
.endm
;
;********************
;
; Macro LOCSUB name, regflags
;
; Define the entry point of a local subroutine. NAME is the
; name the subroutine will be called by. REGFLAGS indicates which
; registers to automatically save on the stack. See the ENTER macro
; description for details
;
.macro locsub name, regflags = 0
\name: ;define the entry point label
enter \regflags
.if debug
nop
nop
.endif
.endm
;
;********************
;
; Macro GLBENT name
;
; Define a generic global entry point. NAME is the name of the entry
; point.
;
.macro glbent name
\name: ;define the entry point label
.global \name ;declare the label global
.if debug
nop
nop
.endif
.endm
;
;********************
;
; Macro GLBLAB name
;
; Define a global label. This label is not intended to be a code entry
; point, so NOPs for debugging are not automatically added after the label
; as they are by the GLBENT macro.
;
.macro glblab name
\name:
.global \name
.endm
;
;********************
;
; Macro GCALL target
;
; Call subroutine TARGET, which is assumed to be outside the current
; module and can therefore be anywhere in program memory.
;
.macro gcall target
call \target
.if debug
nop
nop
.endif
.endm
;
;********************
;
; Macro GJUMP target
;
; Jump to global label TARGET, which is assumed to be outside the current
; module and can therefore be anywhere in program memory.
;
.macro gjump target
goto \target
.endm
;
;********************
;
; Macro MCALL target
;
; Call subroutine TARGET, which is assumed to be in the same module and
; the same program memory linker section as the call.
;
.macro mcall target
rcall \target
.if debug
nop
nop
.endif
.endm
;
;********************
;
; Macro JUMP target
;
; Jump to label TARGET, which is assumed to be in the same module and
; the same program memory linker section as this macro.
;
.macro jump target
bra \target
.endm
;
;***********************************************************************
;
; I/O port initialization.
;
; Create and initialize the VAL_xxxx variables so that all I/O pins
; default to outputs driven low.
;
.ifdef Porta
.set val_porta, 0
.set val_trisa, 0
.set val_pullupa, 0
.endif
.ifdef Portb
.set val_portb, 0
.set val_trisb, 0
.set val_pullupb, 0
.endif
.ifdef Portc
.set val_portc, 0
.set val_trisc, 0
.set val_pullupc, 0
.endif
.ifdef Portd
.set val_portd, 0
.set val_trisd, 0
.set val_pullupd, 0
.endif
.ifdef Porte
.set val_porte, 0
.set val_trise, 0
.set val_pullupe, 0
.endif
.ifdef Portf
.set val_portf, 0
.set val_trisf, 0
.set val_pullupf, 0
.endif
.ifdef Portg
.set val_portg, 0
.set val_trisg, 0
.set val_pullupg, 0
.endif
.ifdef Porth
.set val_porth, 0
.set val_trish, 0
.set val_pulluph, 0
.endif
.ifdef Porti
.set val_porti, 0
.set val_trisi, 0
.set val_pullupi, 0
.endif
.ifdef Portj
.set val_portj, 0
.set val_trisj, 0
.set val_pullupj, 0
.endif
.ifdef Portk
.set val_portk, 0
.set val_trisk, 0
.set val_pullupk, 0
.endif
.ifdef Portl
.set val_portl, 0
.set val_trisl, 0
.set val_pullupl, 0
.endif
.ifdef Portm
.set val_portm, 0
.set val_trism, 0
.set val_pullupm, 0
.endif
.ifdef Portn
.set val_portn, 0
.set val_trisn, 0
.set val_pullupn, 0
.endif
.ifdef Porto
.set val_porto, 0
.set val_triso, 0
.set val_pullupo, 0
.endif
.ifdef Portp
.set val_portp, 0
.set val_trisp, 0
.set val_pullupp, 0
.endif
.ifdef Portq
.set val_portq, 0
.set val_trisq, 0
.set val_pullupq, 0
.endif
.ifdef Portr
.set val_portr, 0
.set val_trisr, 0
.set val_pullupr, 0
.endif
.ifdef Ports
.set val_ports, 0
.set val_triss, 0
.set val_pullups, 0
.endif
.ifdef Portt
.set val_portt, 0
.set val_trist, 0
.set val_pullupt, 0
.endif
.ifdef Portu
.set val_portu, 0
.set val_trisu, 0
.set val_pullupu, 0
.endif
.ifdef Portv
.set val_portv, 0
.set val_trisv, 0
.set val_pullupv, 0
.endif
.ifdef Portw
.set val_portw, 0
.set val_trisw, 0
.set val_pullupw, 0
.endif
.ifdef Portx
.set val_portx, 0
.set val_trisx, 0
.set val_pullupx, 0
.endif
.ifdef Porty
.set val_porty, 0
.set val_trisy, 0
.set val_pullupy, 0
.endif
.ifdef Portz
.set val_portz, 0
.set val_trisz, 0
.set val_pullupz, 0
.endif
;
;***********************************************************************
;
; FIFOs
;
; FIFOs that contain 8 bit bytes have the following format:
;
; name + FIFOB_OFS_N - Number of data bytes currently in the FIFO (byte).
;
; name + FIFOB_OFS_PUT - Offset into the buffer where to write next
; byte (byte).
;
; name + FIFOB_OFS_GET - Offset into the buffer where to read the
; next byte from (byte).
;
; name + FIFOB_OFS_BUF - Start of the data buffer.
;
; Each of these fields can have arbitrary byte alignment.
;
; Define a byte FIFO data structure:
;
struct_start
field fifob_ofs_n, 1 ;number of bytes currently in the FIFO
field fifob_ofs_put, 1 ;buffer offset where to write next data byte
field fifob_ofs_get, 1 ;buffer offset where to read next data byte from
.equiv fifob_ofs_buf, struct_offset ;start of data buffer
.equiv fifob_size, struct_offset ;size of FIFO without the data buffer
;
;********************
;
; Macro FIFOB_DEFINE name, size
;
; Define a byte FIFO. NAME will be defined as the starting address of the
; FIFO data structure. SIZE is the maximum number of bytes the FIFO must
; be able to hold, and must not exceed 255. The constant <name>_SZ will
; be defined to be the size of the FIFO unless it already exists. It is
; an error if <name>_SZ is previously defined but is not equal to SIZE.
;
.macro fifob_define name, size
.ifdef &name&_sz
.if (&name&_sz - (\size))
.error "Pre-existing FIFO size constant not equal to new FIFO size."
.endif
.endif
.ifndef &name&_sz
.equiv &name&_sz, \size
.endif
alloc \name, (\size) + fifob_size, 1
.endm
;
;********************
;
; Macro FIFOB_INIT name
;
; Initialize the indicated byte FIFO.
;
; W0 is trashed.
;
.macro fifob_init name
mov #\name, w0 ;point to start of FIFO data structure
clr.b [w0++] ;init buffer to empty
clr.b [w0++] ;init write index to start of buffer
clr.b [w0++] ;init read index to start of buffer
.endm
;
;********************
;
; Macro FIFOB_Z_EMPTY name
;
; Set the Z flag if the indicated FIFO is completely empty.
;
; W0 is trashed.
;
.macro fifob_z_empty name
mov #\name, w0 ;point to start of FIFO data structure
mov.b [w0 + fifob_ofs_n], w0 ;get number of bytes in the FIFO
and #0xFF, w0 ;mask off upper byte, set Z if FIFO is empty
.endm
;
;********************
;
; Macro FIFOB_EMPTY_N name
;
; Set W0 to the number of empty slots in the FIFO.
;
.macro fifob_empty_n name
mov #\name, w0 ;point to start of FIFO data structure
mov.b [w0 + fifob_ofs_n], w0 ;get number of bytes in the FIFO
and #0xFF, w0 ;mask off upper byte which contains garbage
neg w0, w0
add #&name&_sz, w0 ;make number of empty FIFO slots
.endm
;
;********************
;
; Macro FIFOB_Z_FULL name
;
; Set the Z flag if the indicated FIFO is completely full.
;
; W0 and W1 are trashed.
;
.macro fifob_z_full name
mov #\name, w0 ;point to start of FIFO data structure
mov.b [w0 + fifob_ofs_n], w0 ;get number of bytes in the FIFO
and #0xFF, w0 ;mask off upper byte which contains garbage
mov #&name&_sz, w1
cp w0, w1
.endm
;
;********************
;
; Macro FIFOB_FULL_N
;
; Set W0 to the number of full slots in the FIFO.
;
.macro fifob_full_n name
mov #\name, w0 ;point to start of FIFO data structure
mov.b [w0 + fifob_ofs_n], w0 ;get number of bytes in the FIFO
and #0xFF, w0 ;mask off upper byte which contains garbage
.endm
;
;********************
;
; Macro FIFOB_PUT name
;
; Write the byte in the low 8 bits of W0 to the indicated FIFO. The
; FIFO must not be full, although this is not checked.
;
; Trashes W1, W2, W3.
;
.macro fifob_put name
mov #\name, w1 ;point W1 to FIFO
mov.b [w1 + fifob_ofs_put], w2 ;get PUT index in W2
and #0xFF, w2
add w2, w1, w3
mov.b w0, [w3 + fifob_ofs_buf] ;write the data byte into the FIFO buffer
add w2, #1, w2 ;increment local copy of PUT index
mov #&name&_sz, w3 ;get buffer size
cp w2, w3 ;compare new PUT index to buffer size
bra ltu, m\@_1 ;still within buffer ?
mov #0, w2 ;no, wrap back to buffer start
m\@_1: ;W2 contains new PUT index
mov.b w2, [w1 + fifob_ofs_put] ;update PUT index in FIFO structure
mov.b [w1 + fifob_ofs_n], w2 ;update number of bytes in the FIFO
inc w2, w2
mov.b w2, [w1 + fifob_ofs_n]
.endm
;
;********************
;
; Macro FIFOB_GET name
;
; Get the next byte from the indicated FIFO into W0. The high byte of W0
; will be zero. The FIFO must not be empty, although this is not checked.
;
; Trashes W1, W2.
;
.macro fifob_get name
mov #\name, w1 ;point W1 to the FIFO structure
mov.b [w1 + fifob_ofs_n], w0 ;count one less byte in the FIFO
dec w0, w0
mov.b w0, [w1 + fifob_ofs_n]
mov.b [w1 + fifob_ofs_get], w2 ;get the GET index into W2
and #0xFF, w2
inc w2, w2 ;advance the index
mov #&name&_sz, w0 ;get the buffer size
cp w2, w0
bra ltu, m\@_1 ;still within the buffer ?
mov #0, w2 ;no, wrap back to buffer start
m\@_1: ;W2 contains new GET index
mov.b [w1 + fifob_ofs_get], w0 ;get the old GET index into W0
and #0xFF, w0
add w0, w1, w0
mov.b [w0 + fifob_ofs_buf], w0 ;get the data byte into W0
and #0xFF, w0 ;mask in only the data byte in all of W0
mov.b w2, [w1 + fifob_ofs_get] ;update GET index in FIFO structure
.endm