Similarly to the C preprocessor, NASM allows sections of a source file to be assembled only if certain conditions are met. The general syntax of this feature looks like this:
%if<condition>
; some code which only appears if <condition> is met
%elif<condition2>
; only appears if <condition> is not met but <condition2> is
%else
; this appears if neither <condition> nor <condition2> was met
%endif
The %else clause is optional, as is the %elif clause. You can
have more than one %elif clause as well.
Beginning a conditional-assembly block with the line %ifdef MACRO will assemble the subsequent code if, and only if, a
single-line macro called MACRO is defined. If not, then the
%elif and %else blocks (if any)
will be processed instead.
For example, when debugging a program, you might want to write code such as
; perform some function
%ifdef DEBUG
writefile 2,"Function performed successfully",13,10
%endif
; go and do something else
Then you could use the command-line option -D DEBUG to
create a version of the program which produced debugging messages, and remove the option
to generate the final release version of the program.
You can test for a macro not being defined by
using %ifndef instead of %ifdef. You can also
test for macro definitions in %elif blocks by using
%elifdef and %elifndef.
The %ifmacro directive operates in the same
way as the %ifdef directive, except that it checks for the
existence of a multi-line macro.
For example, you may be working with a large project and not have control over the macros in a library. You may want to create a macro with one name if it doesn’t already exist, and another name if one with that name does exist.
The %ifmacro is considered true if defining a macro with
the given name and number of arguments would cause a definitions conflict. For
example:
%ifmacro MyMacro 1-3
%error "MyMacro 1-3" causes a conflict with an existing macro.
%else
%macro MyMacro 1-3
; insert code to define the macro
%endmacro
%endif
This will create the macro MyMacro 1-3 if no macro
already exists which would conflict with it, and emits a warning if there would be a
definition conflict.
You can test for the macro not existing by using the %ifnmacro instead of
%ifmacro. Additional tests can be performed in %elif blocks by using %elifmacro and %elifnmacro.
The conditional-assembly construct %ifctx
ctxname will cause the subsequent code to be assembled if and only if the top
context on the preprocessor’s context stack has the name ctxname. As with %ifdef, the inverse and
%elif forms %ifnctx, %elifctx and %elifnctx are also supported.
For more details of the context stack, see Раздел 3.7. For
a sample use of %ifctx, see Раздел 3.7.5.
The conditional-assembly construct %if
expr will cause the subsequent code to be assembled if and only if the value of
the numeric expression expr is non-zero. An example of the
use of this feature is in deciding when to break out of a %rep preprocessor loop: see Раздел 3.5 for a detailed example.
The expression given to %if, and its counterpart
%elif, is a critical expression (see Раздел 2.8).
%if extends the normal NASM expression syntax, by
providing a set of relational operators which are not normally available in expressions.
The operators =, <, >, <=,
>= and <> test equality, less-than,
greater-than, less-or-equal, greater-or-equal and not-equal respectively. The C-like
forms == and != are supported as alternative forms of
= and <>. In addition,
low-priority logical operators &&, ^^ and || are
provided, supplying logical
AND, logical XOR and
logical OR. These work like
the C logical operators (although C has no logical XOR), in that they always return
either 0 or 1, and treat any non-zero input as 1 (so that ^^, for example, returns 1 if exactly one of its inputs is zero, and 0
otherwise). The relational operators also return 1 for true and 0 for false.
The construct %ifidn text1,text2 will
cause the subsequent code to be assembled if and only if text1 and text2, after expanding
single-line macros, are identical pieces of text. Differences in white space are not
counted.
%ifidni is similar
to %ifidn, but is case-insensitive.
For example, the following macro pushes a register or number on the stack, and allows
you to treat IP as a real register:
%macro pushparam 1
%ifidni %1,ip
call %%label
%%label:
%else
push %1
%endif
%endmacro
Like most other %if constructs, %ifidn has a counterpart %elifidn, and negative forms %ifnidn
and %elifnidn. Similarly, %ifidni has
counterparts %elifidni, %ifnidni and %elifnidni.
Some macros will want to perform different tasks depending on whether they are passed a number, a string, or an identifier. For example, a string output macro might want to be able to cope with being passed either a string constant or a pointer to an existing string.
The conditional assembly construct %ifid, taking one parameter (which may be
blank), assembles the subsequent code if and only if the first token in the parameter
exists and is an identifier. %ifnum works similarly, but tests for the
token being a numeric constant; %ifstr tests for it being a string.
For example, the writefile macro defined in Раздел 3.3.3 can be extended to take
advantage of %ifstr in the following fashion:
%macro writefile 2-3+
%ifstr %2
jmp %%endstr
%if %0 = 3
%%str: db %2,%3
%else
%%str: db %2
%endif
%%endstr: mov dx,%%str
mov cx,%%endstr-%%str
%else
mov dx,%2
mov cx,%3
%endif
mov bx,%1
mov ah,0x40
int 0x21
%endmacro
Then the writefile macro can cope with being called in
either of the following two ways:
writefile [file], strpointer, length
writefile [file], "hello", 13, 10
In the first, strpointer is used as the address of an
already-declared string, and length is used as its length;
in the second, a string is given to the macro, which therefore declares it itself and
works out the address and length for itself.
Note the use of %if inside the %ifstr: this is to detect whether the macro was passed two arguments (so
the string would be a single string constant, and db %2
would be adequate) or more (in which case, all but the first two would be lumped together
into %3, and db %2,%3 would be
required).
The usual %elifXXX, %ifnXXX and %elifnXXX versions exist for each of %ifid,
%ifnum and %ifstr.
The preprocessor
directive %error will cause NASM to report an error if it occurs in assembled
code. So if other users are going to try to assemble your source files, you can ensure
that they define the right macros by means of code like this:
%ifdef SOME_MACRO
; do some setup
%elifdef SOME_OTHER_MACRO
; do some different setup
%else
%error Neither SOME_MACRO nor SOME_OTHER_MACRO was defined.
%endif
Then any user who fails to understand the way your code is supposed to be assembled will be quickly warned of their mistake, rather than having to wait until the program crashes on being run and then not knowing what went wrong.