=================================== 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: .. code-block:: none 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: .. code-block:: none Label Code Operand LDA TEMP ; Load accumulator SHRT which would be equivalent to writing: .. code-block:: none 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: .. code-block:: none Label Code Operand SHRT MACRO RRC ANI 7FH ENDM is the definition of SHRT, and specifies that SHRT stands for the two instructions: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none Label Code Operand LDA TEMP MVI D,3 ; Specify 3 right shifts SHV STA TEMP The above instruction sequence is equivalent to the expression: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none Label Code Operand MVI C,5 LOOP: RRC ANI 7FH DCR C JNZ LOOP Here is another example of an SHV reference: .. code-block:: none Label Code Operand ; Assume Register E is free, and a 2-place shift is needed SHV E,2 and the equivalent expansion: .. code-block:: none 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: .. code-block:: none 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). .. code-block:: none Label Code Operand LOAD MACRO ADDR MVI C,ADDR SHR 8 MVI B,ADDR AND 0FFH ENDM . . LABEL: -- . . INST: -- The reference: .. code-block:: none Code Operand LOAD LABEL is equivalent to the expansion: .. code-block:: none Code Operand MVI C,LABEL SHR 8 MVI B,LABEL AND 0FFH The reference: .. code-block:: none Code Operand LOAD INST is equivalent to the expansion: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none Label Code Operand MAC1 MACRO P1,P2,COMMENT XRA P2 DCR P1 COMMENT ENDM The reference: .. code-block:: none Code Operand MAC1 C,D, ; DECREMENT ; REG C' is equivalent to the expansion: .. code-block:: none 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-block:: none Code Operand MAC1 E,B is equivalent to the expansion: .. code-block:: none 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: .. code-block:: none Label Code Operand MAC MACRO P1 PUSH P1 ENDM the reference: .. code-block:: none MAC B will produce the legal expansion: .. code-block:: none PUSH B but the reference: .. code-block:: none MAC C will produce the illegal expansion: .. code-block:: none 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: .. code-block:: none 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: .. scan-figure:: 51 0.08 0.68 0.49 0.95 :alt: 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: .. code-block:: none Label Code Operand EQMAC MACRO VAL EQU 8 DB VAL ENDM The following program section is valid: .. code-block:: none 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: .. code-block:: none Label Code Operand STMAC MACRO SYM SET 5 DB SYM ENDM The following program section is valid: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none Label Code Operand MAC4 MACRO P1 ABC SET 14 DB P1 ENDM Further suppose that the statement: .. code-block:: none ABC SET 3 has been written before the first reference to MAC4, setting the value of ABC to 3. Then the macro reference: .. code-block:: none MAC4 ABC will cause the assembler to evaluate ABC and to substitute the value 3 for parameter P1, then produce the expansion: .. code-block:: none ABC SET 14 DB 3 If, however, the user had instead written the macro reference: .. code-block:: none 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: .. code-block:: none 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: (a) 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. (b) 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. (c) 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. (d) 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: .. scan-figure:: 53 0.08 0.1 0.48 0.31 :alt: 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: .. code-block:: none 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: .. code-block:: none Label Code Operand ; Load register C indirect with the contents of memory ; location LABEL. LIND C,LABEL Macro expansion: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none 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: .. code-block:: none 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.