Macros can check the type of arguments and generate different code depending on what they find. For example, you can use the OPATTR operator to determine if an argument is a constant, a register, or a memory operand.
If you discover a constant value, you can often optimize the code. In some cases, you can generate better code for 0 or 1 than for other constants. If the argument is a memory operand, you know nothing about the value of the operand, since it may change at run time. However, you may want to generate different code depending on the operand size and on whether it is a pointer. Similarly, if the operand is a register, you know nothing of its contents, but you may be able to optimize if you can identify a particular register with the IFDIFI or IFIDNI directives.
The following example illustrates some of these techniques. It loads a specified address into a specified offset register. The segment register is assumed to be DS.
load MACRO reg:REQ, adr:REQ
IF (OPATTR (adr)) AND 00010000y ;; Register
IFDIFI reg, adr ;; Don't load register
mov reg, adr ;; onto itself
ENDIF
ELSEIF (OPATTR (adr)) AND 00000100y
mov reg, adr ;; Constant
ELSEIF (TYPE (adr) EQ BYTE) OR (TYPE (adr) EQ SBYTE)
mov reg, OFFSET adr ;; Bytes
ELSEIF (SIZE (TYPE (adr)) EQ 2
mov reg, adr ;; Near pointer
ELSEIF (SIZE (TYPE (adr)) EQ 4
mov reg, WORD PTR adr[0] ;; Far pointer
mov ds, WORD PTR adr[2]
ELSE
.ERR <Illegal argument>
ENDIF
ENDM
A macro may also generate different code depending on the assembly environment. The predefined text macro @Cpu can be used to test for processor type. The following example uses the more efficient constant variation of the PUSH instruction if the processor is an 80186 or higher.
IF @Cpu AND 00000010y
pushc MACRO op ;; 80186 or higher
push op
ENDM
ELSE
pushc MACRO op ;; 8088/8086
mov ax, op
push ax
ENDM
ENDIF
Note that the example generates a completely different macro for the two cases. This is more efficient than testing the processor inside the macro and conditionally generating different code. With this macro, the environment is checked only once; if the conditional were inside the macro it would be checked every time the macro is called.
You can test the language and operating system using the @Interface text macro. The memory model can be tested with the @Model, @DataSize, or @CodeSize text macros.
You can save the contexts inside macros with PUSHCONTEXT and POPCONTEXT. The options for these keywords are:
Option | Description |
RADIX | Saves segment register information |
LIST | Saves listing and CREF information |
CPU | Saves current CPU and processor |
ALL | All of the above |