Macros

Assembly language is a powerful tool—however, there are many tasks that need to be done repeatedly, and with mind-numbing minor modifications. Ophis includes a facility for macros to allow this. Ophis macros are very similar in form to function calls in higher level languages.

Defining Macros

Macros are defined with the .macro and .macend commands. Here's a simple one that will clear the screen on a Commodore 64:

.macro clr'screen
    lda #147
    jsr $FFD2
.macend

Invoking Macros

To invoke a macro, either use the .invoke command or backquote the name of the routine. The previous macro may be expanded out in either of two ways, at any point in the source:

.invoke clr'screen

or

`clr'screen

will work equally well.

Passing Arguments to Macros

Macros may take arguments. The arguments to a macro are all of the "word" type, though byte values may be passed and used as bytes as well. The first argument in an invocation is bound to the label _1, the second to _2, and so on. Here's a macro for storing a 16-bit value into a word pointer:

.macro store16   ; `store16 dest, src
        lda #<_2
        sta _1
        lda #>_2
        sta _1+1
.macend

Macro arguments behave, for the most part, as if they were defined by .alias commands in the calling context. (They differ in that they will not produce duplicate-label errors if those names already exist in the calling scope, and in that they disappear after the call is completed.)

Features and Restrictions of the Ophis Macro Model

Unlike most macro systems (which do textual replacement), Ophis macros evaluate their arguments and bind them into the symbol table as temporary labels. This produces some benefits, but it also puts some restrictions on what kinds of macros may be defined.

The primary benefit of this "expand-via-binding" discipline is that there are no surprises in the semantics. The expression _1+1 in the macro above will always evaluate to one more than the value that was passed as the first argument, even if that first argument is some immensely complex expression that an expand-via-substitution method may accidentally mangle.

The primary disadvantage of the expand-via-binding discipline is that only fixed numbers of words and bytes may be passed. A substitution-based system could define a macro including the line LDA _1 and accept as arguments both $C000 (which would put the value of memory location $C000 into the accumulator) and #$40 (which would put the immediate value $40 into the accumulator). If you really need this kind of behavior, a run a C preprocessor over your Ophis source, and use #define to your heart's content.