9.7.2 Testing for Argument Type and Environment

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