Macros are another method of implementing modularity and abstraction in Assembler (and C, spreadsheets, and many languages). Though both are abstraction techniques, macros are very different from a function in use.
use has the effect of automating the writing of the program.
Consider a typical scenario where one needs to do a number of different divisions of the Ax register by 10. The following lists the typical evolution of macro development and usage. The final result is the expansion of the macro in which the generated assembler code becomes part of the program. In the following example the macrouse simply inserts the three instructions of the macro definition.
| Instruction
Needed |
Real
Operations |
Macro
Definition |
Use in
Program |
Macro
Expansion |
Div 10 |
Mov Dx, 0 Mov Bx, 10 Div Bx |
_Div10 Macro Mov Dx, 0 Mov Bx, 10 Div Bx endm |
Call GetDec$ _Div10 Call PutDec$ |
Call GetDec$ Mov Dx, 0 Mov Bx, 10 Div Bx Call PutDec$ |
| Instruction
Needed |
Real
Operations |
Macro
Definition |
Use in
Program |
Macro
Expansion |
Div N |
Mov Dx, 0 Mov Bx, N Div Bx |
_Div Macro N Mov Dx, 0 Mov Bx, &N Div Bx endm |
Call GetDec$ _Div 34 Call PutDec$ |
Call GetDec$ Mov Dx, 0 Mov Bx, 34 Div Bx Call PutDec$ |
Labels - Labels must be unique. When used
in macros, multiple expansions of a macro can produce multiple definitions
of the same label. To see the problem consider the macro below to produce
the absolute value of a signed integer variable. For one use of the _Abs
macro, the
if:, then:, and endif: label definitions are unique,
two or more uses of the _Abs macro leads to multiple definitions of the
if:,
then:, endif: labels.
Local Symbols - The solution to
the label problem is to generate unique symbol(s) each time the macro is
used. The solution is to implement a counter that is incremented each time
a symbol defined to be local is encountered during macro expansion.
The following illustrates the use of local symbols to produce unique symbols
in place of if, then, and endif. The first table is correct
but produces labels that bear little connection to the original structure.
The second produces more readable code.
| Instruction
Needed |
Real
Operations |
Macro
Definition |
Use in
Program |
Macro
Expansion |
Abs X |
if: Cmp X, 0 Jl then Jmp endif then: Neg X endif: |
_Abs Macro X local if, then, endif if: Cmp &X, 0 Jl then Jmp endif then: Neg &X endif: endm |
Call GetDec$ _Abs Z Call PutDec$ |
Call GetDec$ $0001: Cmp Z, 0 Jl $0002 Jmp $0003 $0002: Neg Z $0003: Call PutDec$ |
| Instruction
Needed |
Real
Operations |
Macro
Definition |
Use in
Program |
Macro
Expansion |
Abs X |
if: Cmp X, 0 Jl then Jmp endif then: Neg X endif: |
_Abs Macro X local label if&label: Cmp &X, 0 Jl then Jmp endif then&label: Neg &X endif&label: endm |
Call GetDec$ _Abs Z Call PutDec$ |
Call GetDec$ if$0001: Cmp Z, 0 Jl then$0001 Jmp endif$0001 then$0001: Neg Z endif$0001: Call PutDec$ |
Debug equ 1 ; Debug is true : : Mul Bx If Debug ; Assemble when Debug is true Push Bx Mov Bh, 0 Call PutDec$ Pop Bx Endif Sub Cx, 12 |
Debug equ 0 ; Debug is false : : Mul Bx If Debug ; Assemble when Debug is true Endif Sub Cx, 12 |
Macro Messages
Errors can be signaled to the console during assembly using:
Testing for Blank Macro Parameters/Exit Expansion - It is possible to perform some simple tests for correctness of macro parameters, the simplest is to verify that all parameters are defined. Consider a macro _Max that returns the maximum of two unsigned integers in the Ax register. If either of the two values are missing the macro should display an error message to the screen but not expand to produce any program instructions.
| Instruction
Needed |
Real
Operations |
Macro
Definition |
Use in
Program |
Macro
Expansion |
Max X, Y |
Mov Ax, X if: Cmp Ax, Y Ja then Jmp else then: Mov Ax, X Jmp endif else: Mov Ax, Y endif: |
_Max Macro X, Y local L ifB <&Y> %outm Error exitm endif Mov Ax, &X if&L: Cmp Ax, &Y Ja then&L Jmp else&L then&L: Mov Ax, &X Jmp endif&L else&L: Mov Ax, &Y endif&L: endm |
Call GetDec$ _Max Z, 5 Call PutDec$ _Max Z |
Call GetDec$ _Max Z, 5 Mov Ax, Z if$01: Cmp Ax, 5 Ja then$01 Jmp else$01 then&01: Mov Ax, Z Jmp endif$01 else$01: Mov Ax, 5 endif$01: Call PutDec$ _Max Z No expansion due to missing parameter. |
Examples
| Modulus operator
Result parameter must be a variable or register other than Ax, Bx, or Dx |
Interchange operator
All parameters must be variables or registers other than Ax, Bx |
GCD operator
All parameters must be variables or registers other than Ax, Bx, Dx |
_Mod Macro N, Divisor, Result PushA Mov Ax, &N Mov Bx, &Divisor Mov Dx, 0 Div Bx Mov &Result, Dx PopA endm |
_Swap Macro A, B PushA Mov Ax, &A Mov Bx, &B Mov &A, Bx Mov &B, Ax PopA endm |
_Gcd Macro X, Y local L while&L: cmp &Y, 0 Jne do&L Jmp endwhile&L do&L: _Mod &X, &Y, &X _Swap &X, &Y Jmp while&L endwhile&L: endm |
_GCD A, B while$0001: cmp B, 0 Jne do$0001 Jmp endwhile$0001 do$0001:
Jmp while$0001 endwhile$0001: |
_OR - Performs a logical OR operation on the results of two relations. The Carry flag is set equal 1 if the result is true and the Carry flag is reset equal 0 if the result is false. For example:
Mov N, 12 ;; N = 12; Do: ;; Do Dec N ;; N = N - 1; While: _OR N, Jl, 0, N, Jg, 10 ;; While (N < 0) || (N > 10) Jc Do EndDo:would set the Carry flag equal 1 when 0 <= N <= 10. To set Carry flag equal 1 use STC instruction, reset Carry flag equal 0 use CLC instruction. The following macro will test that there are six actual parameters provided to the macro, if any parameters are missing the macro will exit without generating any code.
Definition
_OR Macro Opr1, Condition1, Opr2, Opr3, Condition2, Opr4 Local L IfB <&Opr4> %Outm "Missing operand in OR" exitm Endif if&L: Cmp &Opr1, &Opr2 &Condition1 then&L or&L: Cmp &Opr3, &Opr4 &Condition2 then&L Jmp else&L then&L: Stc Jmp endif&L else&L: Clc endif&L: Endm |
Expansion
_OR N, Jl, 0, N, Jg, 10 if$0001: Cmp N, 0 Jl then$0001 or$0001: Cmp N, 10 Jg then$0001 Jmp else$0001 then$0001: Stc Jmp endif$0001 else$0001: Clc endif$0001: |
.386 .model flat, stdcall include v:\common\user\c335\masm32\include\kernel32.inc include v:\common\user\c335\masm32\include\masm32.inc includelib v:\common\user\c335\masm32\lib\kernel32.lib includelib v:\common\user\c335\masm32\lib\masm32.lib PutDec proto
GetDec proto
.code
main Proc Near ; void main() {
Call Getdec ; cin >> eAx ;
Call Putdec ; cout << eAx ;
Push 0
Call ExitProcess
main Endp
End main
|