%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
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
%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.
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.
%ifmacro is considered true if defining a macro with
the given name and number of arguments would cause a definitions conflict. For
%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
The conditional-assembly construct
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
%elifnctx are also supported.
The conditional-assembly construct
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.
<> test equality, less-than,
greater-than, less-or-equal, greater-or-equal and not-equal respectively. The C-like
!= are supported as alternative forms of
<>. In addition,
low-priority logical operators
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.
%ifidn text1,text2 will
cause the subsequent code to be assembled if and only if
text2, after expanding
single-line macros, are identical pieces of text. Differences in white space are not
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
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
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
would be adequate) or more (in which case, all but the first two would be lumped together
db %2,%3 would be
%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.