An Introduction to MACROS

Macro -- An alternative to a procedure for achieving modularity in assembly 
language programming.

Macro: 
       A name that stands for a block of text
       Defined once by giving the name and the text
       Then the name can be used as many times as we wish
       Each reference to the macro name causes text it stands for to be inserted at the place the name is used
           The assembler does this text insertion
           It's called "expanding" the macro

Intel 80X86 (MASM) Macro syntax:
     
       name    MACRO  [list of arguments]   ;arguments are optional placeholders
               statement_1
               statement_2     ;these form the body of the macro (the text it stands for)
               ...  
               statement_n
               ENDM 

Example:
       putchar, a macro to output a character to the screen     

putchar  macro      ;here no argument -- programmer must load character into DL before invoking macro
         mov AH,2   ;DOS single character output service
         int 21h
         endm


Using the putchar macro (This code will output an A and a 0 to the screen)

        ...
        mov  DL,'A'
        putchar
        ...
        ...
        mov  DL,30h  ;ASCII code for a '0'
        putchar
        ...
        ...
        

This code would be expanded (.LST file) as follows
(The assembler generates the 1's to show that these are macro statements):

        mov  DL,'A'
        putchar
1           mov AH,2  ;DOS single character output service
1           int 21h
        ...
        ...
        mov  CL,30h   ;ASCII code for a '0'
        putchar
1           mov AH,2  ;DOS single character output service
1           int 21h
        ...
        ...

Note that when the macro is expanded by the assembler, the text it stands for is 
blindly inserted.

Using macro arguments:

    Macro arguments are symbols to be replaced by values when macro is expanded
    They are just placeholders separated by commas
    Values given when macro is invoked will replace the arguments in the same order

Example -- putchar with a single argument:

putchar  macro  char      ;char is the argument
         mov    AH,2
         mov    DL,char
         int    21h
         endm

Using the new putchar macro:

        ...
        putchar 'A'        ;note the value of the argument here is 'A' 
1           mov    AH,2
1           mov    DL,'A'  ;an exact text replacement of char by 'A'
1           int    21h
        ...
        ...
        mov      AL,30h
        putchar  AL        ;here the value of the argument is AL
1           mov    AH,2
1           mov    DL,AL   ;exact text replacemt of char by AL
1           int    21h
        ...
        ...    


Careful with macros containing labels --
Example -- Macro to determine absolute value:

abs     macro     x
        cmp       x,0
        jge       done
        neg       x       ;reverse sign if negative
done:
        endm

Let's use this macro in the following code (source and expansion):
   
        ...
        mov     AX,-5
        abs     AX        ;x will be replaced by AX  
1           cmp    AX,0
1           jge    done
1           neg    AX
1 done:
        ...
        ...
        mov     BL,2
        abs     BL        ;x will be replaced by BL
1           cmp     BL,0 
1           jge     done
1           neg     BL
1 done:                   ;*** this is an error -- done is a duplicate label!!
        ...

If our macro is to be invoked more than once and has labels, we need to have the 
assembler generate label names.  Do this with the 'local' declaration. This causes 
the assembler to generate a new label name each time the macro is invoked.

Same example using local:

abs     macro     x
        local     done    ;this is the label name -- could have several separated by commas
        cmp       x,0
        jge       done
        neg       x       ;reverse sign if negative
done:
        endm

Using this new abs macro (soure and expansion):

        ...
        mov     AX,-5
        abs     AX         ;x will be replaced by AX  
1           cmp    AX,0
1           jge    ??0000  ;label name done replaced with ??0000 generated by assembler
1           neg    AX
1 ??0000:
        ...
        ...
        mov     BL,2
        abs     BL         ;x will be replaced by BL
1           cmp     BL,0
1           jge    ??0001  ;note new label name ??0001 generated by assembler
1           neg    BL
1 ??0001:                  ;*** No duplicate labels, no error!!
        ...


Nested Macros -- can have any level of nesting, but inner macros must be defined first

Example: DisplayStr  (Displays a string of characters and uses the putchar macro)

putchar     macro   char      ;char is the argument
            mov     AH,2
            mov     DL,char
            int     21h
            endm
DisplayStr  macro   str, lng  ;arguments are offset and length of string to be displayed
            local   top
            mov     SI,0      ;index of next character in string
            mov     CX,lng
top:        putchar str[SI]   ;output current character
            inc     SI        ;point to next character 
            loop    top       ;repeat
            endm

See example program (source and lst files) to see how these macros are used and 
expanded (http://www.cs.binghamton.edu/~reckert/220/macros.htm)


Using macros to generate new instructions:

putcharif -- a macro to display a character if a certain condition is true:

Examples of how it would be used:

putcharif  BL, E, 0, 'Z'   ; Read this as: displaycharif BL is Equal to 0, character 'Z'
                            This would display a Z if BL contains a 0

The macro definition:

putcharif  macro  op1,  cond, op2, char   ;op1, op2 are operands to be compared, cond is the ...
           local  doit, done    ;condition to be tested, char is the character to be displayed
           cmp    op1,  op2
           j&cond doit          ;& is the concatination operator -- append value of cond to j
           jmp    done
doit:      putchar char
done:
           endm

Example use of the putcharif macro:

          putcharif SI, L, BX, 'W'  ; display a W if SI < BX
1            cmp     SI, BX
1            jL      ??0000   ; note L concatenated to j and ??0000 label generated by assembler
1            jmp     ??0001
1 ??0000     putchar    'W'
  2            mov     AH,2   ; note level 2 nesting of macros generated by assembler
  2            mov     DL,'W'
  2            int     21h
1 ??0001:


Macros to emulate a for loop in assembly language:

for i = first_i to last_i   ;pseudo-code for a high level language for loop
   ...
   ...  ;body of loop
   ...
next i

We'll use one macro (formac) to emulate the 'for' statement and another 
(endfor) to emulate the 'next' statement.

Flow chart (The logic for the macros is enclosed in thick rectangles):



Note from the flow chart that these macros are not independent. The formac macro 
must have a label (start_L) that will be jumped to from the endfor macro. and the 
endfor macro must have a label (end_L) that will be branched to conditionally from 
the formac macro. One way of doing this is to include the two labels as arguments 
in each of the two macros. Here they are:

formac     macro    i, first_i, last_i, start_L, end_L
           mov      i, first_1
start_L    cmp      i, last_i
           ja       end_L
           inc      i
           endm

endfor     macro    start_L, end_L
           jmp      start_L
end_L:
           endm


Let us now use these macros to display 3 A's to the screen:

        formac   SI, 1, 3, ps, pf
           mov      AH,2
           mov      DL,'A'  ;output an A      
           int      21h
        endfor   ps, pf

The assembler will expand these macros and generate code as follows:

                           formac   SI, 1, 3, ps, pf
1          mov      SI, 1
1 ps:      cmp      SI, 3
1          ja       pf
1          inc      SI
             mov      AH,2
             mov      DL,'A'  ;output an A      
             int      21h
                           endfor   ps, pf
1          jmp      ps
1 pf:


If you trace execution of the resulting code you will see that the body of the loop 
does indeed repeat itself three times.

Macros could be used to implement many new and powerful instructions in a low-level 
language like assembly language. As an example see the WHILE/WEND macros that emulate 
a high level language while loop (http://www.cs.binghamton.edu/~reckert/220/whilemac.htm)          


Macros versus Procedures:

Advantage of macros: Faster than procedures -- the code is actually inserted in 
the program; there is no need to call the code so the overhead of using the stack 
for the return address is avoided. In other words, the pushes and pops of the 
stack that are required to store and retrieve the return address each time the 
procedure is invoked are eliminated.

Disadvantage of macros: Longer programs -- Each invocation of the macro causes the 
code to be inserted in the program. So if your code invokes the macro many times, 
the size of the program could become very large because of all the inserted macro code.