Skip to content

Macro Language

kombos edited this page Oct 6, 2026 · 1 revision

Macro Language

Normative source: SPRU018D, chapter 6 (Macro Language).

A macro lets you name a block of source statements once and emit it wherever you need it, instead of retyping the block. Using a macro is three steps: define it, call it by its name in the opcode field, and let the assembler expand the call back into the stored statements. When the assembler reads a macro definition it puts the macro name into its opcode table; that name then overrides any earlier macro, library entry, directive, or instruction mnemonic of the same name, so a macro can extend or replace a built-in instruction as well as add a new one.

Step What you do What the assembler does
Define Write the definition before the first use (in the file, in an .include/.copy file, or in a macro library) Records the name in the opcode table
Call Use the macro name as an opcode Matches the name against the macro table
Expand (nothing) Binds arguments to parameters, substitutes the body, assembles it

By default the expanded body is printed in the listing; .mnolist turns that off (see "Formatting the listing" below).


1. Defining a macro

A definition runs from a .macro line to an .endm line:

macname  .macro  [parameter1] [,parameter2] ... [,parametern]
         model statements or macro directives
         [.mexit]
         .endm
Element Meaning
macname The macro name, placed in the label field. The first 32 characters are significant. It replaces any instruction or earlier macro of the same name.
.macro Marks the first line of the definition; goes in the opcode field.
parameters Optional substitution symbols, listed as operands of .macro.
model statements Instructions or directives assembled on each call.
macro directives Directives that control the expansion.
.mexit Acts like a jump to .endm; handy for bailing out when a test shows the expansion cannot succeed.
.endm Ends the definition.

All parts of a macro must live in the same file, though definitions may be nested and may call other macros.

Example - define, call, and expand a three-parameter macro that sums p1, p2 and p3 into p3:

add3    .macro  p1, p2, p3
        lac     p1
        add     p2
        add     p3
        sacl    p3
        .endm

        add3    a, b, c      ; call

; expands to:
        lac     a
        add     b
        add     c
        sacl    c

To attach a comment that should NOT appear in the expansion, start it with an exclamation point. A comment that should appear in the expansion starts with an asterisk or a semicolon.


2. Macro parameters and substitution symbols

Macro parameters are substitution symbols: named stand-ins for a character string. A valid substitution symbol is up to 32 characters, begins with a letter, and otherwise uses letters, digits, underscores, and dollar signs. Symbols used as parameters are local to their macro; you may define up to 32 local symbols per macro (parameters plus any .var symbols together).

On expansion, each argument is passed by variable: its character-string value is bound to the matching parameter. Rules for the binding:

  • A parameter with no matching argument is set to the null string.
  • If there are more arguments than parameters, the final parameter receives all the leftover arguments as one string.
  • To pass an argument list, a comma, or a semicolon to a single parameter, wrap the argument in quotation marks.

The assembler replaces the symbol with its string first, then assembles the resulting line.

Directives that set substitution symbols

.asg assigns a string; .eval assigns the string form of an evaluated expression:

.asg  ["]character string["], substitution symbol
.eval well-defined expression, substitution symbol

With .asg the quotation marks are optional; without them the assembler reads up to the first comma and trims leading and trailing blanks. .eval evaluates its expression and stores the result as a string; if the expression is not well defined, it errors and assigns the null string. Use .eval whenever a value must be computed from an expression (for example a running counter); .asg only copies a string.

.asg   1, counter
.loop  100
.word  counter
.eval  counter + 1, counter
.endloop

Built-in substitution symbol functions

These functions return a value usable in expressions, and are most useful in conditional-assembly tests. In the table, a and b are substitution symbols or character-string constants, and "string" means the string value of the parameter.

Function Return value
$symlen(a) Length of string a
$symcmp(a,b) < 0 if a < b, 0 if a = b, > 0 if a > b
$firstch(a,ch) Index of the first occurrence of character constant ch in a
$lastch(a,ch) Index of the last occurrence of character constant ch in a
$isdefed(a) 1 if a is defined in the symbol table, else 0
$ismember(a,b) Assigns the top member of list b to a; 0 if b is a null string
$iscons(a) 1 binary, 2 octal, 3 hexadecimal, 4 character, 5 decimal constant
$isname(a) 1 if a is a valid symbol name, else 0
$isreg(a) 1 if a is a valid predefined register name, else 0

Recursive, forced, and subscripted substitution

When the string bound to a symbol is itself a symbol, the assembler substitutes again, and keeps going until it reaches a non-symbol token or a symbol it has already seen in this evaluation (which it treats as infinite recursion and stops).

The forced substitution operator - a pair of colons around a symbol, with no spaces - makes the assembler substitute a symbol's string even where it would not normally be recognized. Forced substitutions are expanded before other substitutions, may be used only inside macros, and may not be nested:

:symbol:

Inside a macro you can also reach individual characters of a symbol with a subscript. :symbol(expr): yields the one character at that position; :symbol(expr1,expr2): yields the substring starting at position expr1 with length expr2. Substring indexing starts at 1.

Local variables: .var

.var creates up to 32 local substitution symbols per macro (counted together with the parameters). Each starts as the null string, is not passed in as a parameter, and is discarded when the expansion finishes:

.var  sym1 [,sym2] ... [,symn]

Example - redefine add with .var, a subscript, and a conditional so it picks the right opcode for a short immediate or an indirect operand:

addx    .macro  a
        .var    tmp
        .asg    :a(1):, tmp
        .if     $symcmp(tmp,"#") = 0
        addk    a
        .elseif $symcmp(tmp,"*") = 0
        add     a
        .else
        .emsg   "Bad Macro Parameter"
        .endif
        .endm

        addx    #100
        addx    *+

3. Macro libraries

A macro library is an archive (built with the archiver) whose members are unassembled source files, one macro definition per member. The member name and the macro name must match, and each source file must have the .asm extension (for example macro add3 in file add3.asm). Point the assembler at a library with:

.mlib macro library filename

On .mlib the assembler opens the archive, builds a table of its members, and enters each member name into the opcode table as a library entry, overriding any existing opcode or macro of the same name. A library macro is pulled in and loaded into the macro table only when it is actually called, and only once. Put only macro definitions in a macro library; object code or stray source files may cause undesirable results. Listing of library-entry expansions is controlled by .mlist.


4. Conditional assembly in macros

Two directive groups do conditional assembly; they nest up to 32 levels.

The .if group assembles exactly one block:

.if well-defined expression
[.elseif well-defined expression]
[.else]
.endif

.elseif and .else are optional, and .elseif may appear more than once. When both are absent and the .if expression is false (zero), the assembler skips to after .endif.

The .loop group assembles a block repeatedly:

.loop [well-defined expression]
[.break [well-defined expression]]
.endloop

The .loop expression is the loop count; if it is omitted, the count defaults to 1024 unless a .break with a true (nonzero) expression stops it sooner. The .break directive is optional: while its expression is false the loop continues; the loop ends when the .break expression is true or when .break has no expression, after which assembly resumes past .endloop.

.asg   1, x
.loop                 ; "infinite" loop
.break (x == 10)      ; stop when x reaches 10
.eval  x + 1, x
.endloop

5. Unique labels in macros

Every label must be unique, so a label written plainly inside a macro would collide when the macro expands more than once. Append a question mark to the label and the assembler substitutes a unique number for the question mark at each expansion. The listing shows the label with its question mark, not the number; such a label cannot be declared global. Syntax: label?.

MIN     .macro  a, b
        lac     a
        sub     b
        blz     m1?
        lac     b
        b       m2?
m1?     lac     a
m2?
        .endm

The maximum label length shrinks to leave room for the suffix: 126 characters when the macro expands fewer than 10 times, 125 characters for 10 to 99 expansions. The suffixed label appears in the cross-listing file.


6. Producing messages

Three directives emit your own assembly-time messages; the error and warning counts appear on the last line of the listing.

Directive Effect
.emsg Sends an error message; increments the error count and stops object-file generation.
.wmsg Sends a warning message; increments the warning count but still generates the object file.
.mmsg Sends an assembly-time message; does not touch the error count and does not stop object-file generation.
TEST    .macro  x, y
!
! This macro checks that both parameters are present.
!
        .if  ($symlen(x) == 0 | $symlen(y) == 0)
        .emsg "ERROR - missing parameter in call to TEST"
        .mexit
        .else
        .endif
        .endm

7. Formatting the listing

Macros, substitution symbols, and conditional directives can hide code from the listing. Four directive pairs control what the expanded listing shows; the default member of each pair is noted.

Directive Effect
.mlist List macro and .loop/.endloop expansions (default).
.mnolist Suppress macro and loop expansions.
.fclist List false conditional blocks (default).
.fcnolist Suppress false conditional blocks; drop the .if/.elseif/.else/.endif lines.
.sslist Expand substitution symbols in the listing, printed below the source line.
.ssnolist Turn off substitution-symbol expansion (default).
.drlist List all directive lines (default).
.drnolist Suppress listing of .asg, .eval, .var, .sslist, .mlist, .fclist, .ssnolist, .mnolist, .fcnolist, .emsg, .wmsg, .mmsg, .length, .width, and .break.

8. Recursive and nested macros

A macro definition may call other macros (nested, up to 32 levels) or call itself (recursive). Parameters use dynamic scoping: a called macro sees the environment of the macro that called it. In a nested call, a parameter in the inner macro with the same name as one in the outer macro hides the outer one, while the outer parameters it does not shadow remain visible.

Example - a recursive macro that emits code to compute a factorial. fact sets up the first step and hands off to fact1, which calls itself until the counter reaches 1:

fact    .macro  n, loc        ; loc memory address = n!
        .if     n < 2         ; 0! = 1! = 1
        lack    1
        sacl    loc
        .else
        lack    n
        sacl    loc
        .eval   n - 1, n
        fact1                 ; continue with current environment
        .endif
        .endm

fact1   .macro
        .if     n > 1
        lt      loc
        mpyk    n
        pac
        sacl    loc
        .eval   n - 1, n
        fact1                 ; recursive call
        .endif
        .endm

9. Macro directive summary

Creating macros

Directive Syntax Description
.macro macname .macro [parameter1] ... [,parametern] Define a macro
.mlib .mlib filename Identify a library containing macro definitions
.mexit .mexit Jump to .endm
.endm .endm End a macro definition

Manipulating substitution symbols

Directive Syntax Description
.asg .asg ["]character string["], substitution symbol Assign a character string to a substitution symbol
.eval .eval well-defined expression, substitution symbol Perform arithmetic on a numeric substitution symbol
.var .var substitution symbol1 [,...,substitution symboln] Define local macro symbols

Conditional assembly

Directive Syntax Description
.if .if well-defined expression Assemble the block if the condition is true
.elseif .elseif well-defined expression Assemble the block if the .if was false and this condition is true (optional)
.else .else Assemble the block if the .if was false (optional)
.endif .endif End the .if block
.loop .loop [well-defined expression] Begin repeatable assembly of a block
.break .break [well-defined expression] End the .loop if the condition is true (optional)
.endloop .endloop End the .loop block

Producing assembly-time messages

Directive Syntax Description
.emsg .emsg Send an error message to standard output
.wmsg .wmsg Send a warning message to standard output
.mmsg .mmsg Send an assembly-time message to standard output

Formatting the listing

Directive Syntax Description
.drlist .drlist Enable listing of all directive lines (default)
.drnolist .drnolist Inhibit listing of .asg, .eval, .var, .sslist, .mlist, .fclist, .ssnolist, .mnolist, .fcnolist, .emsg, .mmsg, .wmsg, .length, .width, and .break
.fclist .fclist Allow false conditional code block listing (default)
.fcnolist .fcnolist Inhibit false conditional code block listing
.mlist .mlist Allow macro listings (default)
.mnolist .mnolist Inhibit macro listings
.sslist .sslist Allow expanded substitution symbol listing
.ssnolist .ssnolist Inhibit expanded substitution symbol listing (default)

License

MIT - (c) 2026 Grzegorz Worona grzegorz@worona.pl. See LICENSE.

c5xtools wiki

Start here

The TMS320C5x processor

Writing assembly

The instruction set

A: ABS ADCB ADD ADDB ADDC ADDH ADDI ADDK ADDS ADDT ADLK ADRK AND ANDB ANDI ANDK APAC APL APLI APLK

B: B BACC BACCD BANZ BANZD BBNZ BBZ BC BCND BCNDD BD BDDD BDSD BGEZ BGZ BIOZ BIT BITT BKDK BLDD BLDP BLEZ BLKD BLKP BLPD BLZ BNC BNV BNZ BPSD BSAR BV BZ

C: CALA CALAD CALL CALLD CC CCD CLRC CMPL CMPR CNFD CNFP CONF CPL CPLI CPLK CRGT CRLT

D: DINT DMOV

E: EHWBS EHWBT EINT EMPSD EMRET ESTOP ESTPD ETRAP EXAR

F: FORT

I: IDLE IDLE1 IDLE2 IN INTR

L: LAC LACB LACC LACI LACK LACL LACT LALK LAMM LAR LARK LARP LDP LDPI LDPK LMMR LPH LRLK LST LST0 LST1 LT LTA LTD LTP LTS

M: MAC MACD MADD MADS MAR MPY MPYA MPYI MPYK MPYS MPYU MRKL

N: NEG NMI NOP NORM

O: OPL OPLI OPLK OR ORB ORI ORK OUT

P: PAC POP POPD PSHD PUSH

R: RC RET RETC RETCD RETD RETE RETI RFSM RHM ROL ROLB ROR RORB ROVM RPT RPTB RPTI RPTK RPTR RPTZ RSXM RTC RTXM RXF

S: SACB SACH SACL SAMM SAR SATH SATL SBB SBBB SBLK SBRK SC SETC SFL SFLB SFR SFRB SFSM SHM SMMR SOVM SPAC SPH SPL SPLK SPM SQRA SQRS SST SST0 SST1 SSXM STC STXM SUB SUBB SUBC SUBH SUBI SUBK SUBS SUBT SWI SXF

T: TBLR TBLW TRAP TRAPD

X: XC XOR XORB XORI XORK XPL XPLI XPLK

Z: ZAC ZALH ZALR ZALS ZAP ZPR

Building a program

The tools

Normative sources: TI SPRU056D and SPRU018D (see References).

Changelog


License: MIT - (c) 2026 Grzegorz Worona.

Clone this wiki locally