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:

Two TMAC expansions, each JMP LOOP to its own LOOP

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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:

INADD at 134C holds address 1350 of the data FF

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.