6. Macro Assembly
Part of
the SMAL Manual
|
The SMAL macro facilities allow assembly-time control structures comparable to the procedure structures of many high level languages. A macro is a named block of text which is assembled into the program wherever its name occurs in the context of a macro call. If a macro is defined as having formal parameters, the corresponding actual parameters are substituted for the formal parameters within the body of the macro before it is assembled.
Each macro definition begins with a macro directive and ends with an endmac directive. The macro directive is followed by the name of the macro and its formal parameter list.
<symbolic directive> ::= MACRO <identifier> [ <formal list> ] | ENDMAC
The semantics of a macro declaration are most easily described in terms of macro blocks which begin with a macro directive and end with an endmac directive. The body of the block between the two is saved by the assembler and assembled as a result of each call to the macro.
<macro block> ::= MACRO <identifier> [ <formal list> ] <line end> { <line> } ENDMAC
Note that any labels on the line containing the macro directive are not interpreted as having anything to do with the macro but are processed as normal labels. Labels on the line containing the endmac directive are completely ignored! If a macro block is not properly terminated with an endmac directive, a "missing endmac" error will result. The following fragment of valid SMAL32 code illustrates the definition of a simple parameterless macro:
MACRO ZO W 0 W -1 ENDMAC
A call to this macro can be used to put the sequence 0, -1 in memory.
Macro definitions may contain other macro definitions, but these do not create local macros, as might be expected by anology with high level languages. Instead, the internal macro definition will be processed by the assembler each time the containing macro is called. Thus, a macro may be used to create a number of other macros constructed from the values of its parameters.
The formal parameter list on a macro definition gives the names
of each parameter and the parameter passing mode. Forml parameter
names are identifiers.
There may be a limit on the number of formal parameters
allowed; if this is exceded, a "too many macro parameters" error will be
raised. All versions of SMAL support at least 8 parameters.
<formal list> ::= <formal parameter> { , <formal parameter> } <formal parameter> ::= <identifier> | ( <identifier> ) | = <identifier>
The three macro parameter modes are: Name mode, in which the formal parameter is declared by just its name; list mode, in which the formal parameter name is surrounded by parentheses; and value mode, in which the formal parameter name is preceded by an equals sign. The treatment of actual parameters in each of these three modes will be discussed in more detail later.
In summary, in name mode, the text of the actual parameter is substituted, without change, for the formal parameter; in this case, the actual parameter may be any text, excluding unparenthesized commas; this parameter passing mode is present in essentially all macro processors. The other two modes are eccentric but useful additions to SMAL: In list mode, the actual parameter must be parenthesized, and all or part of the text between the parentheses will be passed. In value mode, the actual parameter must be an expression and a textual representation of the value of that expression will be substituted for the formal parameter.
The following fragment of a SMAL assembly listing illustrates
the definition of a simple macro
with one parameter passed by name:
MACRO WZO TEXT W TEXT ZO ENDMAC
Note that the above macro contains a call to the macro "ZO" defined in the
previous example.
Within the macro body, each occurance of any macro formal parameter name is identified as the macro is defined. Formal parameters will be identified within comments and within character strings. The only time that a formal parameter will not be identified is when it is embedded in a larger identifier. For example, if "A" and "B" are formal parameters, neither of them will be identified in the string "AB". If "AB" is to be seen as the concatenation of two parameters, an apostrophe should be inserted between the two, as in "A'B". Within the body of any macro, single apostrophes will be deleted, and strings of two or more apostrophes will be shortened by one when that macro is defined. Thus, special care must be taken when using apostrophes as single quotes around quoted strings.
A macro call takes the form of a symbolic directive which begins with the name of a previously defined macro, followed by a list of actual parameters.
<symbolic directive> ::= <identifier> [ <actual list> ] <actual list> ::= <actual parameter> { , <actual parameter> } <actual parameter> ::= [ <by name> | <by list> | <by value> ]
The first step in processing a macro call is to store the text of each actual parameter, according to the parameter passing rules associated with that parameter type. Individual parameters may be omitted or blank, in which case, no text is passed. In all cases, the number of actual parameters must be less than or equal to the number of formal parameters. After the parameters are stored, assembly of the body of the macro begins. As the body is being assembled, all formal parameters which had been identified at the time the body was originally defined are replaced with the text of the corresponding actual parameters. If the resulting text for any line is longer than the implementation defined line length limit, the error "text too long for line" will result.
When a formal parameter is declared as a name parameter, the actual parameter may be any text which does not include commas unless the commas are parenthesized and as long as any parentheses in the text are balanced. The entire text of a name parameter will be passed with no changes other than to remove leading and trailing blanks.
When a formal parameter is declared as a list parameter, the actual parameter must be enclosed in parentheses and may contain any lexically valid text as long as the parentheses it contains are balanced. A substring specification is allowed after the end parenthesis; this consists of two expressions set off from the parameter and each other by colons.
<by list> ::= ( <balanced string> ) [ <substring> ] <substring> ::= : [ <expression> ] [ : <expression> ]
If the substring specification is absent, the entire balanced string will be passed as a parameter. Note that the enclosing parentheses are never passed. If the first expression in the substring specification is present, it specifies the index of the first character in the balanced string to be passed (the default value of the first expression is one). If the second expression in the substring specification is present, it specifies how many characters from the balanced string are to be passed (the default value of the second expression is "all"). Substring specifications should be used with care because they may introduce strings with unbalanced parentheses.
For formal parameters declared using value mode, the actual parameter must be an expression.
<by value> ::= <expression>
The expression will be evaluated at the time of the macro call; if the result is an absolute value, the text of the decimal representation of that value will be passed as a parameter. Note that the decimal representation used will be unsigned, that is, -1 will be passed as a large positive integer; this allows identifiers to be constructed by appending the value of the parameter to a letter. If the value is not absolute, an expression will be passed which is guarnateed to evaluate to the actual parameter's value at the time of the call.
Macro calls will not take place when errors are found in the actual parameter list. During the assembly of the body of a macro, other macro calls may be processed, even recursive calls.
The following fragments of SMAL32 assembly listings demonstrate the correct use of macros. The first example is contrived to demonstrate the use of each parameter passing mode:
29 MACRO T NAME,=NUMBER, ( LIST ) 30 NAME NUMBER 31 LIST 32 ENDMAC 33 34 T W,5+4,(COMMON A,5) +000026: 00000009 34 W 9 34 COMMON A,5 34 END 35 36 T W,1,(BIW -25):3 +00002A: 00000001 36 W 1 +00002E: FFFFFFE7 36 W -25 36 END 37 38 T W, A + 2, ("; NONESUCH"):2:6 +000032:+00000002 38 W A + 2 38 ; NONE 38 END
All lines in the listing with identical line numbers are the result of macro expansion. Normally, these would not be listed! The END directives are introduced automatically at the end of each macro body.
In the first call to the macro T, note that the expression "5+4" has been evaluated, and the value "9" has been substituted into the macro body. Similarly, in the second call, the expression "1" was evaluated and passed as "1". In the third call, the expression "A+2" could not be passed as a number because it is relocated relative to the common name "A", which was defined in the first call to the macro.
The following macro demonstrates the use of macros to hide an ugly aspect of SMAL. In this case, the strange syntax used to achieve halfword and word alignment in SMAL programs:
3 MACRO ALIGN =x 4 IF x > 1 5 IF (x & 1) = 1 6 ERROR odd parameter to ALIGN 7 ELSE 8 ALIGN (x>>1) 9 .=.+(ABS(.)&(x>>1)) 10 ENDIF 11 ENDIF 12 ENDMAC 13 14 ALIGN 1 ; no operation +000000: 01 15 B 1 16 ALIGN 2 ; align to a halfword boundary +000002: 0002 17 H 2 18 ALIGN 4 ; align to a word boundary +000004: 00000003 19 W 3 20 ALIGN 3 ; an improper alignment 20 ERROR odd parameter to ALIGN error message
This macro is recommended for achieving alignment, and in absolute assembly code, it will align to any power of 2; in relocatable assembly code, however, it can only be guaranteed to relocate to the nearest word unless corresponding alignment directives are included in the linker control file to align the relocation process. This example illustrates how conditional and macro assembly can be combined to perform complex computations; it illustrates a recursive macro, and it illustrates the use of the ERROR directive to report an imporper parameter to a macro.
The following two macros demonstrate the use of value parameters and
concatenation for
automatic label generation.
The macro "CATNUM" simply concatenates its three parameters, with the constraint
that the middle parameter is an expression. This is used to construct
new symbols by the second macro. The second macro maintains a counter, "LAB",
which is incremented once each time it is called. Thus, each time it is
called, it will be able to create new symbols.
40 MACRO CATNUM (PART1),=NUM,(PART2) 41 PART1'NUM'PART2 42 ENDMAC 43 44 LAB = 1 45 MACRO USER 46 LAB = LAB + 1 47 CATNUM (L),LAB,(:) 48 W 0 49 CATNUM (W L),LAB 50 ENDMAC 51 52 USER 52 LAB = LAB + 1 52 CATNUM (L),LAB,(:) 52 L2: 52 END +000036: 00000000 52 W 0 52 CATNUM (W L),LAB +00003A:+00000036 52 W L2 52 END 52 END 53 54 USER 54 LAB = LAB + 1 54 CATNUM (L),LAB,(:) 54 L3: 54 END +00003E: 00000000 54 W 0 54 CATNUM (W L),LAB +000042:+0000003E 54 W L3 54 END 54 END
Note that the symbol "LAB" could have been initialized in the macro "USER"
instead of in the main program. To do this, the initialization would have
to be assembled conditionally depending on whether or not "LAB" had been
previously defined.
The last example illustrates a macro similar to the built in ASCII directive. This is:
19 MACRO TEXT S,(PART1),(PART2) 20 IF LEN(S) > 0 21 TEXT ,(S):2:1,(S):3:LEN(S)-3 22 ELSEIF LEN(''PART1'') > 2 23 B ''PART1'' 24 TEXT ,(PART2):1:1,(PART2):2 25 ENDIF 26 ENDMAC 27 28 TEXT "CALCULATE" +00003E: 43 41 4C 43 29 55 4C 41 54 45
This example demonstrates the combination of conditional and macro assembly, the use of substring parameters, the use of the function LEN to detect missing parameters, and the use of recursion. Note also the use of doubled single quotes within the macro definition which result in single quotes at expansion time.
Unfortunately, this example also demonstrates a minor bug. When listing is turned off during macro expansion, the object code generated by the macro is not listed until the line after the macro call.