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 Section 4.7. For a
sample use of %ifctx
, see Section 4.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 Section 4.5 for a detailed example.
The expression given to %if
, and its counterpart
%elif
, is a critical expression (see Section 3.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 Section 4.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.