-
Notifications
You must be signed in to change notification settings - Fork 0
Example Macros
- MR Table Generation With $_eval() Function
- Normalize Memory Register Number
- Normalize Memory Register Name
- Name and Initialize a Memory Register
- Set MR From (2) 16 Bit Constants
- Branch on Switch State LOW
- Branch on Switch State HIGH
- Macro Synonym
- Invert a Core Register (1.0-CR)
- Multiply Two 16 Bit Numbers
- Convert Msec to Number of Samples (At 48k)
- Convert Msec to Number of Samples (At Current Sample Rate)
- Encode 4 Bytes Into 32-Bit Word
- 4-Way Switch Statement
- 3-Way Switch Statement
- MR Symbolic Name and Initial Value
- Copy SFR to MR
- Copy MR to SFR
- Copy MR to MR (uses ACC32)
- Copy MR to MR
TODO: Similar to MR Table example, add example invocations and show generated code for all examples
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 are set to the values 3/X, 6/X, 9/X, and 12/X where X is the value passed as the 2nd arg. The first arg is the memory register of the first entry (e.g. the table base address).
$macro DIVIDER_TABLE(baseMRNum, value) ++
.mreg mr$_eval(${baseMRNum}+0) 3/${value}
.mreg mr$_eval(${baseMRNum}+1) 6/${value}
.mreg mr$_eval(${baseMRNum}+2) 9/${value}
.mreg mr$_eval(${baseMRNum}+3) 12/${value}
$endmacro
For example, the following use of the macro:
.equ DIV32_TABLE_BASE 40 ; MR of first entry of 32/x table
$DIVIDER_TABLE(DIV32_TABLE_BASE, 32)
.equ DIV48_TABLE_BASE 50 ; MR of first entry of 48/x table
$DIVIDER_TABLE(DIV48_TABLE_BASE, 48)
Would produce the following assembler code:
.equ DIV32_TABLE_BASE 40 ; MR of first entry of table
;--- BEGIN MACRO: DIVIDER_TABLE
.mreg mr40 3/32
.mreg mr41 6/32
.mreg mr42 9/32
.mreg mr43 12/32
;--- END MACRO: DIVIDER_TABLE
.equ DIV48_TABLE_BASE 50 ; MR of first entry of table
;--- BEGIN MACRO: DIVIDER_TABLE
.mreg mr50 3/48
.mreg mr51 6/48
.mreg mr52 9/48
.mreg mr53 12/48
;--- END MACRO: DIVIDER_TABLE
Extracts the numeric portion of a memory register name, or returns the input unchanged if it does not start with "mr". This can be used when the input may be a full MR name ("mr46") or just the MR number ("46").
; $MR_NUMBER(mr46) will substitute "46"
; $MR_NUMBER(46) will substitute "46"
$macro MR_NUMBER(mr) $_eval(IF (STR_STARTS_WITH("${mr}", "mr"), STR_SUBSTRING("${mr}", 2), "${mr}"))
Returns a memory register name from an MR number or full MR name.
; $MR_NAME(46) returns "mr46"
; $MR_NAME(mr46) returns "mr46"
$macro MR_NAME(mr) mr$MR_NUMBER(${mr})
Defines a memory register symbolic name and initial (load) value. The MR 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)
Note a math expression for the MR cannot be used, e.g. "100+10" will cause an error. If an expression
is needed, evaluate it with $_eval() like $defMR(myreg, $_eval(100+10), 0);
$macro defMR(name, mr, initVal) ++
.rn ${name} $MR_NAME(${mr})
.mreg ${name} ${initVal}
$endmacro
; 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 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 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
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 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
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 (integral) number of delay samples, assuming 48kHz sample rate.
$macro MS_TO_SAMPLES_48K(msec) ((${msec}/1000)/(1/48000))
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 a 32-bit word
$macro WORD32_BYTES(msb, b2, b1, lsb) (${msb}<<24)|(${b2}<<16)|(${b1}<<8)|${lsb}
; 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
; 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
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)
; 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 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 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 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
Copyright © Cabintech Global LLC