Skip to content

Example Macros

Cabintech edited this page Jan 21, 2025 · 51 revisions

TODO: Similar to MR Table example, add example invocations and show generated code for all examples

MR Table Generation With $_eval() Function

This example shows how the $_eval() function can be used to create a table in a series of memory registers (MRx) starting at an arbitrary base address. This can be useful when a macro is used in different programs that need to locate the table at different MR locations, or to create a series of tables in a single program at different MR locations.

This macro produces a table with 4 entries that divide the given value by 3, 6, 9, and 12.

$macro DIVIDER_TABLE(baseMRNum, value) ++
.mreg	mr$_eval(${baseMRNum}+0)	${value}/3
.mreg	mr$_eval(${baseMRNum}+1)	${value}/6
.mreg	mr$_eval(${baseMRNum}+2)	${value}/9
.mreg	mr$_eval(${baseMRNum}+3)	${value}/12
$endmacro

For example, the following use of the macro:

.equ	DIV8_TABLE_BASE		40		; MR of first entry of 8/x table
$DIVIDER_TABLE(DIV8_TABLE_BASE, 8)

.equ	DIV32_TABLE_BASE	50		; MR of first entry of 32/x table
$DIVIDER_TABLE(DIV32_TABLE_BASE, 32)

Would produce the following assembler code:

.equ	DIV8_TABLE_BASE		40		; MR of first entry of /8 table
.mreg	mr40	8/3
.mreg	mr41	8/6
.mreg	mr42	8/9
.mreg	mr43	8/12


.equ	DIV32_TABLE_BASE	50		; MR of first entry of /32 table
.mreg	mr50	32/3
.mreg	mr51	32/6
.mreg	mr52	32/9
.mreg	mr53	32/12

Copy SFR to MR

; Copy a Special Function Register (SFR) to a Memory Register (MR)
; Uses ACC32
$macro COPY_SFR_TO_MR(mr, sfr) ++
cpy_cs	acc32, ${sfr} 
cpy_mc	${mr}, acc32 
$endmacro

Copy MR to SFR

; Copy a Memory Register (MR) to a Special Function Register (SFR)
; Uses ACC32
$macro COPY_MR_TO_SFR(sfr, mr) ++
cpy_cm	acc32, ${mr} 
cpy_sc	${sfr}, acc32 
$endmacro

Copy MR to MR (uses ACC32)

; Copy a MR to another MR (uses ACC32)
$macro COPY_MR_TO_MR(mrTo, mrFrom) ++
cpy_cm	acc32, ${mrFrom}
cpy_mc	${mrTo}, acc32 
$endmacro

Copy MR to MR

; Copy MR to MR with a temp register (does not use acc32)
$macro COPY_MR_TO_MR_TEMP(mrTarget, mrSource, crTemp) ++
cpy_cm		${crTemp}, ${mrSource} 	
cpy_mc		${mrTarget}, ${crTemp}
$endmacro

Set MR from Two 16-Bit Constants

; Set an MR to a 32 bit value from (2) 16 bit constants
; Uses ACC32
$macro COPY_CONST_TO_MR(mrTo, constHi, constLo) ++
wrdld	acc32, ${constHi} 
ori		acc32, ${constLo}
cpy_mc	${mrTo}, acc32
$endmacro

Branch on Switch State LOW

; Branch to a target label if the given (debounced) SWITCH is LOW
; Uses ACC32
$macro IF_SWITCH_LOW(switch, label) ++
cpy_cs		acc32, SWITCH 
andi		acc32, ${switch}
jz			acc32, ${label}
$endmacro

Branch on Switch State HIGH

; Branch to a target label if the given (debounced) SWITCH is HIGH.
; The first arg should be one of the SWxDB assembler constants.
; Uses ACC32
$macro IF_SWITCH_HIGH(switch, label) ++
cpy_cs		acc32, SWITCH 
andi		acc32, ${switch}
jnz			acc32, ${label}
$endmacro

Macro Synonym

This is an example of providing an alternate name (synonym) for a macro

; Short hand for IF_SWITCH_HIGH()
$macro IF_SWITCH(a,b) ++
$IF_SWITCH_HIGH(${a},${b})
$endmacro

Invert a Core Register (1.0-CR)

; Invert a positive value in "cr" and leave result in acc32.
; The invert is defined as 1 minus the original value for any
; value between 0 and max pos (0x7FFFFFFF).
; Uses ACC32
$macro INVERT(cr) ++
wrdld	acc32, 0x7FFF 	; Load max pos value 
ori		acc32, 0xFFFF	
subs	acc32, ${cr}	; Leaves result in acc32
$endmacro

Multiply Two 16 Bit Numbers

Multiplying two 16 bit integers in FXCore takes several instructions, this macro simplifies this common operation.

; ACC32 = Multiply the lower 16 bits of two core registers
$macro MULT_16(cr1, cr2, crTemp) ++
sl			${cr1}, 16			; Move arg1 to upper 32 bits 
cpy_cc		${crTemp}, acc32	; Save in temp 
sl			${cr2}, 15			; Move arg 2, not sure why 15 instead of 16 bits 
multrr		acc32, ${crTemp}	; acc32 = upper 32 bits of 64 bit result
$endmacro

Convert Msec to Number of Samples (At 48k)

Convert msec to number (integral) number of delay samples, assuming 48kHz sample rate.

$macro MS_TO_SAMPLES_48K(msec) ((${msec}/1000)/(1/48000))

Convert Msec to Number of Samples (At Current Sample Rate)

Convert (fixed constant) msec to samples based on current sampling rate. Unlike MS_TO_SAMPLES_48K this macro expands to executable code that accounts for the current sampling rate. The results are left in ACC32.

The msec arg must be a fixed constant (or constant expression) that evaluates to less than 2048.

$macro MS_TO_SAMPLES(msec) ++

r0.u	= $_eval(FLOOR((${msec})*12)) ; Samples assuming 12kHz

acc32	= BOOTSTAT		
acc32	= acc32 andi 3          ; Mask all but PLL (sampling rate) bits [1:0]
acc32	= acc32 add -2          ; If PLL=2 then rate is 32k 
if acc32 =0 goto _mts_k32       ; Special case, 32k is not a multiple of 12k

acc32	= acc32 add 3           ; Get original PLL value plus 1, now a multiplier for 12k rate
acc32	= acc32 sl 15           ; Do multiply in upper 16 bits
acc32	= acc32 mult r0
goto _mts_end

_mts_k32: 
acc32.u	= $_eval(FLOOR((${msec})*32)) ; Samples at 32k
acc32	= acc32 sr 16

_mts_end:
$endmacro

Encode 4 Bytes Into 32-Bit Word

; Encode 4 bytes into a 32-bit word
$macro WORD32_BYTES(msb, b2, b1, lsb) (${msb}<<24)|(${b2}<<16)|(${b1}<<8)|${lsb}

4-Way Switch Statement

; Multi-target branch based on value of a CR. A value of 0 will
; branch to label target0, a value of 1 will branch to target1,
; etc. Any value is >=3 will branch to the last (default) label.
; Uses ACC32.
$macro SWITCH4(crValue, target0, target1, target2, default) ++
jz			${crValue}, ${target0}

cpy_cc		acc32, ${crValue}
addi		acc32, -1			
jz			acc32, ${target1}

cpy_cc		acc32, ${crValue}		
addi		acc32, -2			
jz			acc32, ${target2}

jmp			${default}
$endmacro

3-Way Switch Statement

; Multi-target branch based on value of a CR. A value of 0 will
; branch to label target0, a value of 1 will branch to target1,
; etc. If the value is >=2 will branch to the last (default) label.
; Uses ACC32.
$macro SWITCH3(crValue, target0, target1, default) ++
jz			${crValue}, ${target0}
cpy_cc		acc32, ${crValue}		
addi		acc32, -1			
jz			acc32, ${target1}
jmp			${default}
$endmacro

MR Symbolic Name and Initial Value

This macro is shorthand for the common practice of defining a symbolic name for a MR location, and initializing that location to a specific value. This example demonstrates the use of string expressions in the _eval() predefined macro.

$macro defMR(name, mr, initVal) ++
.rn     ${name}     mr$_eval(IF (STR_STARTS_WITH("${mr}", "mr"), STR_SUBSTRING("${mr}", 2), "${mr}"))
.mreg   ${name}     ${initVal}
$endmacro

The "mr" argument can either be a simple integer number 0-127, or "mr" followed by 0-127. E.g. these generate the same code:

$defMR(myreg, 110, 0)
$defMR(myreg, mr110, 0)

The above macros would both produce the same 2 statements:

.rn     myreg     mr110
.mreg   myreg     0

Note a math expression cannot be used for the "mr" argument, e.g. "100+10" will cause an error. If an expression is needed, evaluate it with $_eval() e.g. $defMR(myreg, $_eval(100+10), 0)

Clone this wiki locally