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

; 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

<!-- TOC --><a name="branch-on-switch-state-low"></a>
## 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

<!-- TOC --><a name="branch-on-switch-state-high"></a>
## 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

<!-- TOC --><a name="macro-synonym"></a>
## 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

<!-- TOC --><a name="invert-a-core-register-10-cr"></a>
## 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

<!-- TOC --><a name="multiply-two-16-bit-numbers"></a>
## 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

<!-- TOC --><a name="convert-msec-to-number-of-samples-at-48k"></a>
## 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))

<!-- TOC --><a name="convert-msec-to-number-of-samples-at-current-sample-rate"></a>
## 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

<!-- TOC --><a name="encode-4-bytes-into-32-bit-word"></a>
## 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}

<!-- TOC --><a name="4-way-switch-statement"></a>
## 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

<!-- TOC --><a name="3-way-switch-statement"></a>
## 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


<!-- TOC --><a name="mr-symbolic-name-and-initial-value"></a>
## 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)`

<!-- TOC --><a name="copy-sfr-to-mr"></a>
## 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

<!-- TOC --><a name="copy-mr-to-sfr"></a>
## 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

<!-- TOC --><a name="copy-mr-to-mr-uses-acc32"></a>
## 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

<!-- TOC --><a name="copy-mr-to-mr"></a>
## 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

<!-- TOC --><a name="set-mr-from-two-16-bit-constants"></a>
## Set MR from Two 16-Bit Constants

Clone this wiki locally