This chapter contains the following sections:
Introduction
Macro Operations
Macro Definition
Macro Calls
Dummy Argument Operators
Dummy Argument Concatenation Operator - \
Return Value Operator - ?
Return Hex Value Operator - %
Dummy Argument String Operator - "
Macro Local Label Operator - ^
DUP, DUPA, DUPC, DUPF Directives
Conditional Assembly
This chapter describes the macro operations and conditional assembly.
The macro preprocessor is implemented in the assembler.
Programming applications frequently involve the coding of a repeated pattern or group of instructions. Some patterns contain variable entries which change for each repetition of the pattern. Others are subject to conditional assembly for a given occurrence of the instruction group. In either case, macros provide a shorthand notation for handling these instruction patterns. Having determined the iterated pattern, the programmer can, within the macro, designate selected fields of any statement as variable. Thereafter by invoking a macro the programmer can use the entire pattern as many times as needed, substituting different parameters for the designated variable portions of the statements.
When the pattern is defined it is given a name. This name becomes the mnemonic by which the macro is subsequently invoked (called). If the name of the macro is the same as an existing assembler directive or mnemonic opcode, the macro will replace the directive or mnemonic opcode, and a warning will be issued.
The macro call causes source statements to be generated. The generated statements may contain substitutable arguments. The statements produced by a macro call are relatively unrestricted as to type. They can be any processor instruction, almost any assembler directive, or any previously-defined macro. Source statements resulting from a macro call are subject to the same conditions and restrictions that are applied to statements written by the programmer.
To invoke a macro, the macro name must appear in the operation code field of a source statement. Any arguments are placed in the operand field. By suitably selecting the arguments in relation to their use as indicated by the macro definition, the programmer causes the assembler to produce in-line coding variations of the macro definition.
The effect of a macro call is to produce in-line code to perform a predefined function. The code is inserted in the normal flow of the program so that the generated instructions are executed with the rest of the program each time the macro is called.
An important feature in defining a macro is the use of macro calls within the macro definition. The assembler processes such nested macro calls at expansion time only. The nesting of one macro definition within another definition is permitted. However, the nested macro definition will not be processed until the primary macro is expanded. The macro must be defined before its appearance in a source statement operation field.
The definition of a macro consists of three parts: the header, which assigns a name to the macro and defines the dummy arguments; the body, which consists of prototype or skeleton source statements; and the terminator. The header is the MACRO directive, its name, and the dummy argument list. The body contains the pattern of standard source statements. The terminator is the ENDM directive.
The header of a macro definition has the form:
The required name is the symbol by which the macro will be called. The dummy argument list has the form:
The dummy arguments are symbolic names that the macro processor will replace with arguments when the macro is expanded (called). Each dummy argument must obey the same rules as global symbol names. Dummy arguments are separated by commas.
When a macro call is executed, the dummy arguments within the macro definition (NMUL,AVEC,BVEC,OFFSET,RESULT in the example below) are replaced with the corresponding argument as defined by the macro call.
All local label definitions within a macro which use the local label operator are made unique for this macro call. This is done by appending a unique postfix to every local label, making the scope of the label local to the module. This mechanism allows the programmer to freely use local labels within a macro definition without regard to the number of times that the macro is expanded. Labels without the local label operator are considered to be normal labels and thus cannot occur more than once unless used with the SET directive (see Chapter 6 , Assembler Directives).
The macro:
N_R_MUL MACRO NMUL,AVEC,BVEC,OFFSET,RESULT ;header MOV.B R0L,#NMUL ;body MOV.W R4,RESULT MOV.W R1,SOF(AVEC)+OFFSET MOV.W R2,SOF(BVEC)+OFFSET ^again: MOV.B R3L,[R1+] MOV.B R3H,[R2+] MULU.B R3H,R3L ADD [R4],R3 DJNZ.B R0L,^again ENDM ;terminator N_R_MUL 10H,_obj1,_obj2,10H,_RESULT
expands to: (note the different handling of again and _RESULT)
MOV.B R0L,#10H MOV.W R4,_RESULT MOV.W R1,SOF(_obj1)+10H MOV.W R2,SOF(_obj2)+10H again__M_L000001: MOV.B R3L,[R1+] MOV.B R3H,[R2+] MULU.B R3H,R3L ADD [R4],R3 DJNZ.B R0L,again__M_L000001
When a macro is invoked the statement causing the action is termed a macro call. The syntax of a macro call consists of the following fields:
The argument field can have the form:
The macro call statement is made up of three besides the comment field: the label, if any, will correspond to the value of the location counter at the start of the macro expansion; the operation field which contains the macro name; and the operand field which contains substitutable arguments. Within the operand field each calling argument of a macro call corresponds one-to-one with a dummy argument of the macro definition. For example, the N_R_MUL macro defined earlier could be invoked for expansion (called) by the statement:
N_R_MUL CNT+1,VEC1,VEC2,OFFS,OUT
where the operand field arguments, separated by commas and taken left to right, correspond to the dummy arguments NMUL through RESULT, respectively. These arguments are then substituted in their corresponding positions of the definition to produce a sequence of instructions.
Macro arguments consist of sequences of characters separated by commas. Although these can be specified as quoted strings, to simplify coding the assembler does not require single quotes around macro argument strings. However, if an argument has an embedded comma or space, that argument must be surrounded by single quotes ('). An argument can be declared null when calling a macro. However, if must be declared explicitly null. Null arguments can be specified in four ways:
- by writing the delimiting commas in succession with no intervening spaces;
- by terminating the argument list with a comma and omitting the rest of the argument list;
- by declaring the argument as a null string;
- by simply omitting some or all of the arguments.
A null argument will cause no character to be substituted in the generated statements that reference the argument. If more arguments are supplied in the macro call than appear in the macro definition, a warning will be issued by the assembler.
The assembler macro processor provides for text substitution of arguments during macro expansion. In order to make the argument substitution facility more flexible, the assembler also recognizes certain text operators within macro definitions which allow for transformations of the argument text. These operators can be used for text concatenation, numeric conversion, and string handling.
Dummy arguments that are intended to be concatenated with other characters must be preceded by the concatenation operator, '\' to separate them from the rest of the characters. The argument may precede or follow the adjoining text, but there must be no intervening blanks between the concatenation operator and the rest of the characters. To position an argument between two alphanumeric characters, place a backslash both before and after the argument name. For example, consider the following macro definition:
SWAP_MEM MACRO REG1,REG2 ;swap memory contents XCH R\REG1, R\REG2 ENDM
If this macro were called with the following statement,
SWAP_MEM 0,1
then for the macro expansion, the macro processor would substitute the character '0' for the dummy argument REG1, and the characters '1' for the dummy argument REG2. The concatenation operator (\) indicates to the macro processor that the substitution characters for the dummy arguments are to be concatenated in both cases with the character R. The resulting expansion of this macro call would be:
XCH R0, R1
Another macro definition operator is the question mark (?) that returns the value of a symbol. When the macro processor encounters this operator, the ?symbol sequence is converted to a character string representing the decimal value of the symbol. For example, consider the following modification of the SWAP_MEM macro described above:
SWAP_MEM MACRO REG1,REG2 ;swap memory contents XCH R\?REG1, R\?REG2 ENDM
If the source file contained the following SET statements and macro call,
AREG SET 1 BREG SET 2 SWAP_MEM AREG,BREG
then the sequence of events would be as follows: the macro processor would first substitute the characters AREG for each occurrence of REG1 and BREG for each occurrence of REG2. For discussion purposes (this would never appear on the source listing), the intermediate macro expansion would be:
XCH R\?AREG, R\?BREG
The macro processor would then replace ?AREG with the character '1' and ?BREG with the character '2', since 1 is the value of the symbol AREG and 2 is the value of BREG. The resulting intermediate expansion would be:
XCH R\1, R\2
Next, the macro processor would apply the concatenation operator (\), and the resulting expansion as it would appear on the source listing would be:
XCH R1, R2
The percent sign (%) is similar to the standard return value operator except that it returns the hexadecimal value of a symbol. When the macro processor encounters this operator, the %symbol sequence is converted to a character string representing the hexadecimal value of the symbol. Consider the following macro definition:
GEN_LAB MACRO LAB,VAL,STMT LAB\%VAL: STMT ENDM
This macro generates a label consisting of the concatenation of the label prefix argument and a value that is interpreted as hexadecimal. If this macro were called as follows,
NUM SET 10 GEN_LAB HEX,NUM,'NOP'
the macro processor would first substitute the characters HEX for LAB, then it would replace %VAL with the character A, since A is the hexadecimal representation for the decimal integer 10. Next, the macro processor would apply the concatenation operator (\). Finally, the string 'NOP' would be substituted for the STMT argument. The resulting expansion as it would appear in the listing file would be:
HEXA: NOP
The percent sign is also the character used to indicate a binary constant. If a binary constant is required inside a macro it may be necessary to enclose the constant in parentheses or escape the constant by following the percent sign by a backslash (\).
Another dummy argument operator is the double quote ("). This character is replaced with a single quote by the macro processor, but following characters are still examined for dummy argument names. The effect in the macro call is to transform any enclosed dummy arguments into literal strings. For example, consider the following macro definition:
STR_MAC MACRO STRING ASCII "STRING" ENDM
If this macro were called with the following macro expansion line,
STR_MAC ABCD
then the resulting macro expansion would be:
ASCII 'ABCD'
Double quotes also make possible DEFINE directive expansion within quoted strings. Because of this overloading of the double quotes, care must be taken to insure against inappropriate expansions in macro definitions. Since DEFINE expansion occurs before macro substitution, any DEFINE symbols are replaced first within a macro dummy argument string:
DEFINE LONG 'short' STR_MAC MACRO STRING MSG 'This is a LONG STRING' MSG "This is a LONG STRING" ENDM
If this macro were invoked as follows,
STR_MAC sentence
then the resulting expansion would be:
MSG 'This is a LONG STRING' MSG 'This is a short sentence'
It may be desirable to pass a name as a macro argument to be used as a local address reference within the macro body. If a circumflex (^) precedes an identifier then the macro preprocessor will perform name mangling on that label so the label is used literally in the resulting macro expansion. Here is an example:
LOAD MACRO ADDR MOV R0, ^ADDR ENDM
The macro ^-operator performs name mangling on the ADDR argument. Consider the following macro call:
_LOCAL: LOAD _LOCAL
With the local label in the macro definition the macro LOAD would expand to the something like this:
_LOCAL: MOV R0, _LOCAL__M_L000001
This would result in an assembly error as the label LOCAL__M_L000001 is nowhere defined. Without the local label operator in the macro definition (as shown above) the macro LOAD would expand, as expected, to this:
_LOCAL: MOV R0, _LOCAL
This will assemble correctly.
The DUP, DUPA, DUPC, and DUPF directives are specialized macro forms. They can be thought of as a simultaneous definition and call of an unnamed macro. The source statements between the DUP, DUPA, DUPC, and DUPF directives and the ENDM directive follow the same rules as macro definitions, including (in the case of DUPA, DUPC, and DUPF) the dummy operator characters described previously. For a detailed description of these directives, refer to Chapter 6 , Assembler Directives.
Conditional assembly facilitates the writing of comprehensive source programs that can cover many conditions. Assembly conditions may be specified through the use of arguments in the case of macros, and through definition of symbols via the DEFINE, SET, and EQU directives. Variations of parameters can then cause assembly of only those parts necessary for the given conditions. The built-in functions of the assembler provide a versatile means of testing many conditions of the assembly environment (see section 4.5 for more information on the assembler built-in functions).
Conditional directives can also be used within a macro definition to ensure at expansion time that arguments fall within a range of allowable values. In this way macros become self-checking and can generate error messages to any desired level of detail.
The conditional assembly directive IF has the following form:
IF expression . . [ELSE] ;(the ELSE directive is optional) . . ENDIF
A section of a program that is to be conditionally assembled must be bounded by an IF-ENDIF directive pair. If the optional ELSE directive is not present, then the source statements following the IF directive and up to the next ENDIF directive will be included as part of the source file being assembled only if the expression had a nonzero result. If the expression has a value of zero, the source file will be assembled as if those statements between the IF and the ENDIF directives were never encountered. If the ELSE directive is present and expression has a nonzero result, then the statements between the IF and ELSE directives will be assembled, and the statement between the ELSE and ENDIF directives will be skipped. Alternatively, if expression has a value of zero, then the statements between the IF and ELSE directives will be skipped, and the statements between the ELSE and ENDIF directives will be assembled.