Chapter 3: Programming With Macros¶
Macros (or macro instructions) are an extremely important tool provided by the assembler. Properly utilized, they will increase the efficiency of programming and the readability of programs. It is strongly suggested that the user become familiar with the use of macros and utilize them to tailor programming to suit his specific needs.
What Are Macros?¶
A macro is a means of specifying to the assembler that a symbol (the macro name) appearing in the code field of a statement actually stands for a group of instructions. Both the macro name and the instructions for which it stands are chosen by the programmer.
Consider a simple macro which shifts the contents of the accumulator one bit position to the right, while a zero is shifted into the high-order bit position. We will call this macro SHRT, and define it by writing the following instructions in the program:
Label Code Operand
SHRT MACRO
RRC ; Rotate accumulator
; right
ANI 7FH ; Clear high-order bit
ENDM
We can now reference the macro by placing the following instructions later in the same program:
Label Code Operand
LDA TEMP ; Load accumulator
SHRT
which would be equivalent to writing:
Label Code Operand
LDA TEMP ; Load accumulator
RRC
ANI 7FH
The example above illustrates the three aspects of a macro: the definition, the reference, and the expansion.
The definition specifies the instruction sequence that is to be represented by the macro name. Thus:
Label Code Operand
SHRT MACRO
RRC
ANI 7FH
ENDM
is the definition of SHRT, and specifies that SHRT stands for the two instructions:
RRC
ANI 7FH
Every macro must be defined once and only once in a program.
The reference is the point in a program where the macro is referenced. A macro may be referenced in any number of statements by inserting the macro name in the code field of the statements:
Label Code Operand
LDA TEMP
SHRT ; Macro reference
STA TEMP
The expansion of a macro is the complete instruction sequence represented by the macro reference:
Label Code Operand
LDA TEMP
RRC ; Macro reference
ANI 7FH
STA TEMP
The macro expansion will not be present in a source program, but its machine language equivalent will be generated by the assembler in the object program.
Now consider a more complex case, a macro that shifts the accumulator right by a variable number of bit positions specified by the D register contents. This macro is named SHV, and defined as follows:
Label Code Operand
SHV MACRO
LOOP: RRC ; Rotate right once
ANI 7FH ; Clear the high-order bit
DCR D ; Decrement shift counter
JNZ LOOP ; Return for another shift
ENDM
The SHV macro may then be referenced as follows:
Label Code Operand
LDA TEMP
MVI D,3 ; Specify 3 right shifts
SHV
STA TEMP
The above instruction sequence is equivalent to the expression:
Label Code Operand
LDA TEMP
MVI D,3
LOOP: RRC
ANI 7FH
DCR D
JNZ LOOP
STA TEMP
Note that the D register contents will change whenever the SHV macro is referenced, since it is used to specify shift count.
A better method is to write a macro which uses an arbitrary register and loads its own shift amount using macro parameters. Such a macro is defined as follows:
Label Code Operand
SHV MACRO REG,AMT
MVI REG,AMT ; Load shift count
; into register
; specified
; by REG
LOOP: RRC ; Perform right rotate
ANI 7FH ; Clear high-order bit
DCR REG ; Decrement shift
; counter
JNZ LOOP
ENDM
SHV may now be referenced as follows:
Label Code Operand
LDA TEMP
; Assume Register C is free, and a 5-place shift is needed
SHV C,5
the expansion of which is given by:
Label Code Operand
MVI C,5
LOOP: RRC
ANI 7FH
DCR C
JNZ LOOP
Here is another example of an SHV reference:
Label Code Operand
; Assume Register E is free, and a 2-place shift is needed
SHV E,2
and the equivalent expansion:
Label Code Operand
MVI E,2
LOOP: RRC
ANI 7FH
DCR E
JNZ LOOP
While the preceding examples will provide a general idea of the efficiency and capabilities of macros, a rigorous description of each aspect of macro programming is given in the next section.
Macro Terms and Use¶
The previous section explains how a macro must be defined, is then referenced, and how every reference has an equivalent expansion. Each of these three aspects of a macro will be described in the following subsections.
Macro Definition¶
Format:
name MACRO plist
.
. macro body
.
ENDM
Description: The macro definition produces no assembled data in the object program. It merely indicates to the assembler that the symbol “name” is to be considered equivalent to the group of statements appearing between the pseudo instructions MACRO and ENDM (see Chapter 2 - MACRO and ENDM Macro Definition). This group of statements, called the macro body, may consist of assembly language instructions, pseudo-instructions (except MACRO or ENDM), comments, or references to other macros.
“plist” is a list of expressions (usually unquoted character strings) which indicate parameters specified by the macro reference that are to be substituted into the macro body. These expressions, which serve only to mark the positions where macro parameters are to be inserted into the macro body, are called dummy parameters.
Example:
The following macro takes the memory address of the label specified by the macro reference, loads the most significant 8 bits of the address into the C register, and loads the least significant 8 bits of the address into the B register. (This is the opposite of what the instruction LXI B,ADDR would do).
Label Code Operand
LOAD MACRO ADDR
MVI C,ADDR SHR 8
MVI B,ADDR AND 0FFH
ENDM
.
.
LABEL: --
.
.
INST: --
The reference:
Code Operand
LOAD LABEL
is equivalent to the expansion:
Code Operand
MVI C,LABEL SHR 8
MVI B,LABEL AND 0FFH
The reference:
Code Operand
LOAD INST
is equivalent to the expansion:
Code Operand
MVI C,INST SHR 8
MVI B,INST AND 0FFH
The MACRO and ENDM statements inform the assembler that when the symbol LOAD appears in the code field of a statement, the characters appearing in the operand field of the statement are to be substituted everywhere the symbol ADDR appears in the macro body, and the two MVI instructions are to be inserted into the statements at that point of the program and assembled.
Macro Reference Or Call¶
Format:
Label Code Operand
name plist
“name” must be the name of a macro; that is, “name” appears in the label field of a MACRO pseudo-instruction.
“plist” is a list of expressions. Each expression is substituted into the macro body as indicated by the operand field of the MACRO pseudo-instruction. Substitution proceeds left to right; that is, the first string of “plist” replaces every occurrence of the first dummy parameter in the macro body, the second replaces the second, and so on.
If fewer parameters appear in the macro reference than in the definition, a null string is substituted for the remaining expressions in the definition.
If more parameters appear in the reference than the definition, the extras are ignored.
Example:
Given the macro definition:
Label Code Operand
MAC1 MACRO P1,P2,COMMENT
XRA P2
DCR P1 COMMENT
ENDM
The reference:
Code Operand
MAC1 C,D, ; DECREMENT
; REG C'
is equivalent to the expansion:
Code Operand
XRA D
DCR C ; DECREMENT REG C
Todo
The MAC1 reference is transcribed as printed (scans/page-50.png). There
is a closing quote but no opening one, and the comment is split over two
lines. The intended operand was probably C,D,'; DECREMENT REG C'.
The reference:
Code Operand
MAC1 E,B
is equivalent to the expansion:
Code Operand
XRA B
DCR E
Macro Expansion¶
The result obtained by substituting the macro parameters into the macro body is called the macro expansion. The assembler assembles the statements of the expansion exactly as it assembles any other statements. In particular, every statement produced by expanding the macro must be a legal assembler statement.
Example:
Given the macro definition:
Label Code Operand
MAC MACRO P1
PUSH P1
ENDM
the reference:
MAC B
will produce the legal expansion:
PUSH B
but the reference:
MAC C
will produce the illegal expansion:
PUSH C
which will be flagged as an error.
Scope of Labels and Names Within Macros¶
In this section, the terms global and local are important. For our purposes, they will be defined as follows: A symbol is globally defined in a program if its value is known and can be referenced by any statement in the program, whether or not the statement was produced by the expansion of a macro. A symbol is locally defined if its value is known and can be referenced only within a particular macro expansion.
Instruction Labels: Normally a symbol may appear in the label field of only one instruction. If a label appears in the body of a macro, however, it will be generated whenever the macro is referenced. To avoid multiple-label conflicts, the assembler treats labels within macros as local labels, applying only to a particular expansion of a macro. Thus, each “jump to LOOP” instruction generated in the first example of the chapter refers uniquely to the label LOOP generated in the local macro expansion.
Conversely, if the programmer wishes to generate a global label from a macro expansion, he must follow the label with two colons in the macro definition, rather than one. Now, this global label must not be generated more than once, since it is global and therefore must be unique in the program.
For example, consider the macro definition:
Label Code Operand
TMAC MACRO
LOOP: --
.
.
JMP LOOP
ENDM
If two references to TMAC appear in a program, the label LOOP will be a local label and each JMP LOOP instruction will refer to the label generated within its own expansion:
If in the macro definition, LOOP had been followed by two successive colons, LOOP would be generated as a global label by the first reference to TMAC, while the second reference would be flagged as an error.
“Equate” Names: Names on equate statements within a macro are always local, defined only within the expansion in which they are generated.
For example, consider the following macro definition:
Label Code Operand
EQMAC MACRO
VAL EQU 8
DB VAL
ENDM
The following program section is valid:
Label Code Operand Assembled Data
VAL EQU 6
.
DB1: DB VAL 06
.
EQMAC
VAL EQU 8
DB VAL 08
.
DB2: DB VAL 06
VAL is first defined globally with a value of 6. Therefore the reference to VAL at DB1 produces a byte equal to 6. The macro reference EQMAC generates a symbol VAL defined only within the macro expansion with a value of 8; therefore the reference to VAL by the second statement of the macro produces a byte equal to 8. Since this statement ends the macro expansion, the reference to VAL at DB2 refers to the global definition of VAL. The statement at DB2 therefore produces a byte equal to 6.
“Set” Names: Suppose that a “set” statement is generated by a macro. If its name has already been defined globally by another set statement, the generated statement will change the global value of the name for all subsequent references. Otherwise, the name is defined locally, applying only within the current macro expansion. These cases are illustrated as follows:
Consider the macro definition:
Label Code Operand
STMAC MACRO
SYM SET 5
DB SYM
ENDM
The following program section is valid:
Label Code Operand Assembled Data
SYM SET 0
.
DB1: DB SYM 00
.
STMAC
SYM SET 5
DB SYM 05
.
DB2: DB SYM 05
SYM is first defined globally with a value of zero, causing the reference at DB1 to produce a byte of 0. The macro reference STMAC resets this global value to 5, causing the second statement of the macro to produce a value of 5. Although this ends the macro expansion, the value of SYM remains equal to 5, as shown by the reference at DB2.
Using the same macro definition as above, the following program section is invalid:
Label Code Operand Assembled Data
STMAC
SYM SET 5
DB SYM 05
.
DB3: DB SYM **ERROR**
Since in this case SYM is first defined in a macro expansion, its value is defined locally. Therefore the second (and final) statement of the macro expansion produces a byte equal to 5. The statement at DB3 is invalid, however, since SYM is unknown globally.
Macro Parameter Substitution¶
The value of macro parameters is determined and passed into the macro body at the time the macro is referenced, before the expansion is produced. This evaluation may be delayed by enclosing a parameter in quotes, causing the actual character string to be passed into the macro body. The string will then be evaluated when the macro expansion is produced.
Example:
Suppose that the following macro MAC4 is defined at the beginning of the program:
Label Code Operand
MAC4 MACRO P1
ABC SET 14
DB P1
ENDM
Further suppose that the statement:
ABC SET 3
has been written before the first reference to MAC4, setting the value of ABC to 3.
Then the macro reference:
MAC4 ABC
will cause the assembler to evaluate ABC and to substitute the value 3 for parameter P1, then produce the expansion:
ABC SET 14
DB 3
If, however, the user had instead written the macro reference:
MAC4 'ABC'
the assembler would evaluate the expression ‘ABC,’ producing the characters ABC as the value of parameter P1. Then the expansion is produced, and, since ABC is altered by the first statement of the expansion, P1 will now produce the value 14.
Expansion produced:
ABC SET 14
DB ABC ; Assembles as 14
Reasons for Using Macros¶
The use of macros is an important programming technique that can substantially ease the user’s task in the following ways:
Often, a small group of instructions must be repeated many times throughout a program with only minor changes for each repetition.
Macros can reduce the tedium (and resultant increased chance for error) associated with these operations.
If an error in a macro definition is discovered, the program can be corrected by changing the definition and reassembling. If the same routine had been repeated many times throughout the program without using macros, each occurrence would have to be located and changed. Thus debugging time is decreased.
Duplication of effort between programmers can be reduced. Once the most efficient coding of a particular function is discovered, the macro definition can be made available to all other programmers.
As has been seen with the SHRT (shift right) macro, new and useful instructions can be easily simulated.
Useful Macros¶
Load Indirect Macro¶
The following macro, LIND, loads register RI indirect from memory location INADD.
That is, location INADD will be assumed to hold a two-byte memory address (least significant byte first) from which register RI will be loaded.
Example:
If the address of INADD is 134CH, register RI will be loaded from the address held in memory locations 134CH and 134DH, which is 1350H.
Macro definition:
Label Code Operand Comment
LIND MACRO RI,INADD
LHLD INADD ; Load indirect address
; into H and L registers
MOV RI,M ; Load data into RI
ENDM
Macro reference:
Label Code Operand
; Load register C indirect with the contents of memory
; location LABEL.
LIND C,LABEL
Macro expansion:
Label Code Operand
LHLD LABEL
MOV C,M
Other Indirect Addressing Macros¶
Refer to the LIND macro definition in the last section. Only the MOV RI,M instruction need be altered to create any other indirect addressing macro. For example, substituting MOV M,RI will create a “store indirect” macro. Providing RI is the accumulator, substituting ADD M will create an “add to accumulator indirect” macro.
As an alternative to having load indirect, store indirect, and other such indirect macros, we could have a “create indirect address” macro, followed by selected instructions. This alternative approach is illustrated for indexed addressing in the next section.
Create Indexed Address Macro¶
The following macro, IXAD, loads registers H and L with the base address BSADD, plus the 16-bit index formed by register pair RP (RP=B,D,H, or SP).
Macro definition:
Label Code Operand Comment
IXAD MACRO RP,BSADD
LXI H,BSADD ; Load the base address
DAD RP ; Add index to base
; address
ENDM
Macro reference:
Label Code Operand
; The address created in H and L by the following macro
; call will be Label + 012EH
MVI D,1
MVI E,2EH
IXAD D,LABEL
Macro expansion:
Label Code Operand
MVI D,1
MVI E,2EH
LXI H,BSADD
DAD D
Todo
The IXAD expansion is transcribed as printed (scans/page-53.png). Since
the reference passes LABEL as BSADD, the expansion should read
LXI H,LABEL. This looks like a misprint in the original.