Next Chapter | Previous Chapter | Contents | Index
NASM contains a powerful macro processor, which supports conditional
assembly, multi-level file inclusion, two forms of macro (single-line and
multi-line), and a `context stack' mechanism for extra macro power.
Preprocessor directives all begin with a sign.
The preprocessor collapses all lines which end with a backslash (\) character into a single line. Thus:
%define THIS_VERY_LONG_MACRO_NAME_IS_DEFINED_TO \
THIS_VALUE
will work like a single-line macro without the backslash-newline sequence.
%define Single-line macros are defined using the
preprocessor directive. The definitions
work in a similar way to C; so you can do things like
%define ctrl 0x1F &
%define param(a,b) ((a)+(a)*(b))
mov byte [param(2,ebx)], ctrl 'D'
which will expand to
mov byte [(2)+(2)*(ebx)], 0x1F & 'D'
When the expansion of a single-line macro contains tokens which invoke another macro, the expansion is performed at invocation time, not at definition time. Thus the code
%define a(x) 1+b(x)
%define b(x) 2*x
mov ax,a(8)
will evaluate in the expected way to
, even though the macro
wasn't defined at the time of definition of
.
Macros defined with are case
sensitive: after , only
will expand to :
or will not. By
using instead of
(the `i' stands for `insensitive') you
can define all the case variants of a macro at once, so that
would cause
, ,
, and so on all
to expand to .
There is a mechanism which detects when a macro call has occurred as a result of a previous expansion of the same macro, to guard against circular references and infinite loops. If this happens, the preprocessor will only expand the first occurrence of the macro. Hence, if you code
%define a(x) 1+a(x)
mov ax,a(3)
the macro will expand once, becoming
, and will then expand no further. This
behaviour can be useful: see section
8.1 for an example of its use.
You can overload single-line macros: if you write
%define foo(x) 1+x %define foo(x,y) 1+x*y
the preprocessor will be able to handle both types of macro call, by
counting the parameters you pass; so will
become whereas
will become
. However, if you define
%define foo bar
then no other definition of will be
accepted: a macro with no parameters prohibits the definition of the same
name as a macro with parameters, and vice versa.
This doesn't prevent single-line macros being redefined: you can perfectly well define a macro with
%define foo bar
and then re-define it later in the same source file with
%define foo baz
Then everywhere the macro is invoked, it
will be expanded according to the most recent definition. This is
particularly useful when defining single-line macros with
(see section
4.1.5).
You can pre-define single-line macros using the `-d' option on the NASM command line: see section 2.1.12.
%xdefine To have a reference to an embedded single-line macro resolved at the
time that it is embedded, as opposed to when the calling macro is expanded,
you need a different mechanism to the one offered by
. The solution is to use
, or it's case-insensitive counterpart
.
Suppose you have the following code:
%define isTrue 1 %define isFalse isTrue %define isTrue 0 val1: db isFalse %define isTrue 1 val2: db isFalse
In this case, is equal to 0, and
is equal to 1. This is because, when a
single-line macro is defined using , it is
expanded only when it is called. As
expands to , the expansion will be the
current value of . The first time it is
called that is 0, and the second time it is 1.
If you wanted to expand to the value
assigned to the embedded macro at the time
that was defined, you need to change the
above code to use .
%xdefine isTrue 1 %xdefine isFalse isTrue %xdefine isTrue 0 val1: db isFalse %xdefine isTrue 1 val2: db isFalse
Now, each time that is called, it
expands to 1, as that is what the embedded macro
expanded to at the time that
was defined.
%+ Individual tokens in single line macros can be concatenated, to produce longer tokens for later processing. This can be useful if there are several similar macros that perform similar functions.
As an example, consider the following:
%define BDASTART 400h ; Start of BIOS data area
struc tBIOSDA ; its structure
.COM1addr RESW 1
.COM2addr RESW 1
; ..and so on
endstruc
Now, if we need to access the elements of tBIOSDA in different places, we can end up with:
mov ax,BDASTART + tBIOSDA.COM1addr
mov bx,BDASTART + tBIOSDA.COM2addr
This will become pretty ugly (and tedious) if used in many places, and can be reduced in size significantly by using the following macro:
; Macro to access BIOS variables by their names (from tBDA):
%define BDA(x) BDASTART + tBIOSDA. %+ x
Now the above code can be written as:
mov ax,BDA(COM1addr)
mov bx,BDA(COM2addr)
Using this feature, we can simplify references to a lot of macros (and, in turn, reduce typing errors).
%undef Single-line macros can be removed with the
command. For example, the following
sequence:
%define foo bar
%undef foo
mov eax, foo
will expand to the instruction ,
since after the macro
is no longer defined.
Macros that would otherwise be pre-defined can be undefined on the command-line using the `-u' option on the NASM command line: see section 2.1.13.
%assign An alternative way to define single-line macros is by means of the
command (and its case-insensitive
counterpart , which differs from
in exactly the same way that
differs from
).
is used to define single-line macros
which take no parameters and have a numeric value. This value can be
specified in the form of an expression, and it will be evaluated once, when
the directive is processed.
Like , macros defined using
can be re-defined later, so you can do
things like
%assign i i+1
to increment the numeric value of a macro.
is useful for controlling the
termination of preprocessor loops: see
section 4.5 for an example of this. Another use
for is given in
section 7.4 and
section 8.1.
The expression passed to is a critical
expression (see section 3.8), and
must also evaluate to a pure number (rather than a relocatable reference
such as a code or data address, or anything involving a register).
%strlen and %substr It's often useful to be able to handle strings in macros. NASM supports two simple string handling macro operators from which more complex operations can be constructed.
%strlen The macro is like
macro in that it creates (or redefines) a
numeric value to a macro. The difference is that with
, the numeric value is the length of a
string. An example of the use of this would be:
%strlen charcnt 'my string'
In this example, would receive the
value 8, just as if an had been used. In
this example, was a literal string
but it could also have been a single-line macro that expands to a string,
as in the following example:
%define sometext 'my string' %strlen charcnt sometext
As in the first case, this would result in
being assigned the value of 8.
%substr Individual letters in strings can be extracted using
. An example of its use is probably more
useful than the description:
%substr mychar 'xyz' 1 ; equivalent to %define mychar 'x' %substr mychar 'xyz' 2 ; equivalent to %define mychar 'y' %substr mychar 'xyz' 3 ; equivalent to %define mychar 'z'
In this example, mychar gets the value of 'y'. As with
(see section
4.2.1), the first parameter is the single-line macro to be created and
the second is the string. The third parameter specifies which character is
to be selected. Note that the first index is 1, not 0 and the last index is
equal to the value that would assign
given the same string. Index values out of range result in an empty string.
%macro Multi-line macros are much more like the type of macro seen in MASM and TASM: a multi-line macro definition in NASM looks something like this.
%macro prologue 1
push ebp
mov ebp,esp
sub esp,%1
%endmacro
This defines a C-like function prologue as a macro: so you would invoke the macro with a call such as
myfunc: prologue 12
which would expand to the three lines of code
myfunc: push ebp
mov ebp,esp
sub esp,12
The number after the macro name in the
line defines the number of parameters the
macro expects to receive. The use of
inside the macro definition refers to the
first parameter to the macro call. With a macro taking more than one
parameter, subsequent parameters would be referred to as
, and so on.
Multi-line macros, like single-line macros, are case-sensitive, unless
you define them using the alternative directive
.
If you need to pass a comma as part of a parameter to a multi-line macro, you can do that by enclosing the entire parameter in braces. So you could code things like
%macro silly 2
%2: db %1
%endmacro
silly 'a', letter_a ; letter_a: db 'a'
silly 'ab', string_ab ; string_ab: db 'ab'
silly {13,10}, crlf ; crlf: db 13,10
As with single-line macros, multi-line macros can be overloaded by defining the same macro name several times with different numbers of parameters. This time, no exception is made for macros with no parameters at all. So you could define
%macro prologue 0
push ebp
mov ebp,esp
%endmacro
to define an alternative form of the function prologue which allocates no local stack space.
Sometimes, however, you might want to `overload' a machine instruction; for example, you might want to define
%macro push 2
push %1
push %2
%endmacro
so that you could code
push ebx ; this line is not a macro call
push eax,ecx ; but this one is
Ordinarily, NASM will give a warning for the first of the above two
lines, since is now defined to be a macro,
and is being invoked with a number of parameters for which no definition
has been given. The correct code will still be generated, but the assembler
will give a warning. This warning can be disabled by the use of the
command-line option (see
section 2.1.18).
NASM allows you to define labels within a multi-line macro definition in
such a way as to make them local to the macro call: so calling the same
macro multiple times will use a different label each time. You do this by
prefixing to the label name. So you can invent
an instruction which executes a if the
flag is set by doing this:
%macro retz 0
jnz %%skip
ret
%%skip:
%endmacro
You can call this macro as many times as you want, and every time you
call it NASM will make up a different `real' name to substitute for the
label . The names NASM invents are of the
form , where the number 2345 changes
with every macro call. The prefix prevents
macro-local labels from interfering with the local label mechanism, as
described in section 3.9. You
should avoid defining your own labels in this form (the
prefix, then a number, then another period)
in case they interfere with macro-local labels.
Occasionally it is useful to define a macro which lumps its entire command line into one parameter definition, possibly after extracting one or two smaller parameters from the front. An example might be a macro to write a text string to a file in MS-DOS, where you might want to be able to write
writefile [filehandle],"hello, world",13,10
NASM allows you to define the last parameter of a macro to be greedy, meaning that if you invoke the macro with more parameters than it expects, all the spare parameters get lumped into the last defined one along with the separating commas. So if you code:
%macro writefile 2+
jmp %%endstr
%%str: db %2
%%endstr:
mov dx,%%str
mov cx,%%endstr-%%str
mov bx,%1
mov ah,0x40
int 0x21
%endmacro
then the example call to above will
work as expected: the text before the first comma,
, is used as the first macro
parameter and expanded when is referred to,
and all the subsequent text is lumped into and
placed after the .
The greedy nature of the macro is indicated to NASM by the use of the
sign after the parameter count on the
line.
If you define a greedy macro, you are effectively telling NASM how it
should expand the macro given any number of parameters from the
actual number specified up to infinity; in this case, for example, NASM now
knows what to do when it sees a call to
with 2, 3, 4 or more parameters. NASM will take this into account when
overloading macros, and will not allow you to define another form of
taking 4 parameters (for example).
Of course, the above macro could have been implemented as a non-greedy macro, in which case the call to it would have had to look like
writefile [filehandle], {"hello, world",13,10}
NASM provides both mechanisms for putting commas in macro parameters, and you choose which one you prefer for each macro definition.
See section 5.2.1 for a better way to write the above macro.
NASM also allows you to define a multi-line macro with a range of allowable parameter counts. If you do this, you can specify defaults for omitted parameters. So, for example:
%macro die 0-1 "Painful program death has occurred."
writefile 2,%1
mov ax,0x4c01
int 0x21
%endmacro
This macro (which makes use of the
macro defined in section 4.3.3) can be called
with an explicit error message, which it will display on the error output
stream before exiting, or it can be called with no parameters, in which
case it will use the default error message supplied in the macro
definition.
In general, you supply a minimum and maximum number of parameters for a macro of this type; the minimum number of parameters are then required in the macro call, and then you provide defaults for the optional ones. So if a macro definition began with the line
%macro foobar 1-3 eax,[ebx+2]
then it could be called with between one and three parameters, and
would always be taken from the macro call.
, if not specified by the macro call, would
default to , and
if not specified would default to .
You may omit parameter defaults from the macro definition, in which case
the parameter default is taken to be blank. This can be useful for macros
which can take a variable number of parameters, since the
token (see section
4.3.5) allows you to determine how many parameters were really passed
to the macro call.
This defsingle-line macros using the `-d' option on the NASM command line: see section 2.1.12.
%xdefine To have a reference to an embedded single-line macro resolved at the
time that it is embedded, as opposed to when the calling macro is expanded,
you need a different mechanism to the one offered by
. The solution is to use
, or it's case-insensitive counterpart
.
Suppose you have the following code:
%define isTrue 1 %define isFalse isTrue %define isTrue 0 val1: db isFalse %define isTrue 1 val2: db isFalse
In this case, is equal to 0, and
is equal to 1. This is because, when a
single-line macro is defined using , it is
expanded only when it is called. As
expands to , the expansion will be the
current value of . The first time it is
called that is 0, and the second time it is 1.
If you wanted to expand to the value
assigned to the embedded macro at the time
that was defined, you need to change the
above code to use .
%xdefine isTrue 1 %xdefine isFalse isTrue %xdefine isTrue 0 val1: db isFalse %xdefine isTrue 1 val2: db isFalse
Now, each time that is called, it
expands to 1, as that is what the embedded macro
expanded to at the time that
was defined.
%+ Individual tokens in single line macros can be concatenated, to produce longer tokens for later processing. This can be useful if there are several similar macros that perform similar functions.
As an example, consider the following:
%define BDASTART 400h ; Start of BIOS data area
struc tBIOSDA ; its structure
.COM1addr RESW 1
.COM2addr RESW 1
; ..and so on
endstruc
Now, if we need to access the elements of tBIOSDA in different places, we can end up with:
mov ax,BDASTART + tBIOSDA.COM1addr
mov bx,BDASTART + tBIOSDA.COM2addr
This will become pretty ugly (and tedious) if used in many places, and can be reduced in size significantly by using the following macro:
; Macro to access BIOS variables by their names (from tBDA):
%define BDA(x) BDASTART + tBIOSDA. %+ x
Now the above code can be written as:
mov ax,BDA(COM1addr)
mov bx,BDA(COM2addr)
Using this feature, we can simplify references to a lot of macros (and, in turn, reduce typing errors).
%undef Single-line macros can be removed with the
command. For example, the following
sequence:
%define foo bar
%undef foo
mov eax, foo
will expand to the instruction ,
since after the macro
is no longer defined.
Macros that would otherwise be pre-defined can be undefined on the command-line using the `-u' option on the NASM command line: see section 2.1.13.
%assign An alternative way to define single-line macros is by means of the
command (and its case-insensitive
counterpart , which differs from
in exactly the same way that
differs from
).
is used to define single-line macros
which take no parameters and have a numeric value. This value can be
specified in the form of an expression, and it will be evaluated once, when
the directive is processed.
Like , macros defined using
can be re-defined later, so you can do
things like
%assign i i+1
to increment the numeric value of a macro.
is useful for controlling the
termination of preprocessor loops: see
section 4.5 for an example of this. Another use
for is given in
section 7.4 and
section 8.1.
The expression passed to is a critical
expression (see section 3.8), and
must also evaluate to a pure number (rather than a relocatable reference
such as a code or data address, or anything involving a register).
%strlen and %substr It's often useful to be able to handle strings in macros. NASM supports two simple string handling macro operators from which more complex operations can be constructed.
%strlen The macro is like
macro in that it creates (or redefines) a
numeric value to a macro. The difference is that with
, the numeric value is the length of a
string. An example of the use of this would be:
%strlen charcnt 'my string'
In this example, would receive the
value 8, just as if an had been used. In
this example, was a literal string
but it could also have been a single-line macro that expands to a string,
as in the following example:
%define sometext 'my string' %strlen charcnt sometext
As in the first case, this would result in
being assigned the value of 8.
%substr Individual letters in strings can be extracted using
. An example of its use is probably more
useful than the description:
%substr mychar 'xyz' 1 ; equivalent to %define mychar 'x' %substr mychar 'xyz' 2 ; equivalent to %define mychar 'y' %substr mychar 'xyz' 3 ; equivalent to %define mychar 'z'
In this example, mychar gets the value of 'y'. As with
(see section
4.2.1), the first parameter is the single-line macro to be created and
the second is the string. The third parameter specifies which character is
to be selected. Note that the first index is 1, not 0 and the last index is
equal to the value that would assign
given the same string. Index values out of range result in an empty string.
%macro Multi-line macros are much more like the type of macro seen in MASM and TASM: a multi-line macro definition in NASM looks something like this.
%macro prologue 1
push ebp
mov ebp,esp
sub esp,%1
%endmacro
This defines a C-like function prologue as a macro: so you would invoke the macro with a call such as
myfunc: prologue 12
which would expand to the three lines of code
myfunc: push ebp
mov ebp,esp
sub esp,12
The number after the macro name in the
line defines the number of parameters the
macro expects to receive. The use of
inside the macro definition refers to the
first parameter to the macro call. With a macro taking more than one
parameter, subsequent parameters would be referred to as
, and so on.
Multi-line macros, like single-line macros, are case-sensitive, unless
you define them using the alternative directive
.
If you need to pass a comma as part of a parameter to a multi-line macro, you can do that by enclosing the entire parameter in braces. So you could code things like
%macro silly 2
%2: db %1
%endmacro
silly 'a', letter_a ; letter_a: db 'a'
silly 'ab', string_ab ; string_ab: db 'ab'
silly {13,10}, crlf ; crlf: db 13,10
As with single-line macros, multi-line macros can be overloaded by defining the same macro name several times with different numbers of parameters. This time, no exception is made for macros with no parameters at all. So you could define
%macro prologue 0
push ebp
mov ebp,esp
%endmacro
to define an alternative form of the function prologue which allocates no local stack space.
Sometimes, however, you might want to `overload' a machine instruction; for example, you might want to define
%macro push 2
push %1
push %2
%endmacro
so that you could code
push ebx ; this line is not a macro call
push eax,ecx ; but this one is
Ordinarily, NASM will give a warning for the first of the above two
lines, since is now defined to be a macro,
and is being invoked with a number of parameters for which no definition
has been given. The correct code will still be generated, but the assembler
will give a warning. This warning can be disabled by the use of the
command-line option (see
section 2.1.18).
NASM allows you to define labels within a multi-line macro definition in
such a way as to make them local to the macro call: so calling the same
macro multiple times will use a different label each time. You do this by
prefixing to the label name. So you can invent
an instruction which executes a if the
flag is set by doing this:
%macro retz 0
jnz %%skip
ret
%%skip:
%endmacro
You can call this macro as many times as you want, and every time you
call it NASM will make up a different `real' name to substitute for the
label . The names NASM invents are of the
form , where the number 2345 changes
with every macro call. The prefix prevents
macro-local labels from interfering with the local label mechanism, as
described in section 3.9. You
should avoid defining your own labels in this form (the
prefix, then a number, then another period)
in case they interfere with macro-local labels.
Occasionally it is useful to define a macro which lumps its entire command line into one parameter definition, possibly after extracting one or two smaller parameters from the front. An example might be a macro to write a text string to a file in MS-DOS, where you might want to be able to write
writefile [filehandle],"hello, world",13,10
NASM allows you to define the last parameter of a macro to be greedy, meaning that if you invoke the macro with more parameters than it expects, all the spare parameters get lumped into the last defined one along with the separating commas. So if you code:
%macro writefile 2+
jmp %%endstr
%%str: db %2
%%endstr:
mov dx,%%str
mov cx,%%endstr-%%str
mov bx,%1
mov ah,0x40
int 0x21
%endmacro
then the example call to above will
work as expected: the text before the first comma,
, is used as the first macro
parameter and expanded when is referred to,
and all the subsequent text is lumped into and
placed after the .
The greedy nature of the macro is indicated to NASM by the use of the
sign after the parameter count on the
line.
If you define a greedy macro, you are effectively telling NASM how it
should expand the macro given any number of parameters from the
actual number specified up to infinity; in this case, for example, NASM now
knows what to do when it sees a call to
with 2, 3, 4 or more parameters. NASM will take this into account when
overloading macros, and will not allow you to define another form of
taking 4 parameters (for example).
Of course, the above macro could have been implemented as a non-greedy macro, in which case the call to it would have had to look like
writefile [filehandle], {"hello, world",13,10}
NASM provides both mechanisms for putting commas in macro parameters, and you choose which one you prefer for each macro definition.
See section 5.2.1 for a better way to write the above macro.
NASM also allows you to define a multi-line macro with a range of allowable parameter counts. If you do this, you can specify defaults for omitted parameters. So, for example:
%macro die 0-1 "Painful program death has occurred."
writefile 2,%1
mov ax,0x4c01
int 0x21
%endmacro
This macro (which makes use of the
macro defined in section 4.3.3) can be called
with an explicit error message, which it will display on the error output
stream before exiting, or it can be called with no parameters, in which
case it will use the default error message supplied in the macro
definition.
In general, you supply a minimum and maximum number of parameters for a macro of this type; the minimum number of parameters are then required in the macro call, and then you provide defaults for the optional ones. So if a macro definition began with the line
%macro foobar 1-3 eax,[ebx+2]
then it could be called with between one and three parameters, and
would always be taken from the macro call.
, if not specified by the macro call, would
default to , and
if not specified would default to .
You may omit parameter defaults from the macro definition, in which case
the parameter default is taken to be blank. This can be useful for macros
which can take a variable number of parameters, since the
token (see section
4.3.5) allows you to determine how many parameters were really passed
to the macro call.
This defsingle-line macros using the `-d' option on the NASM command line: see section 2.1.12.
%xdefine To have a reference to an embedded single-line macro resolved at the
time that it is embedded, as opposed to when the calling macro is expanded,
you need a different mechanism to the one offered by
. The solution is to use
, or it's case-insensitive counterpart
.
Suppose you have the following code:
%define isTrue 1 %define isFalse isTrue %define isTrue 0 val1: db isFalse %define isTrue 1 val2: db isFalse
In this case, is equal to 0, and
is equal to 1. This is because, when a
single-line macro is defined using , it is
expanded only when it is called. As
expands to , the expansion will be the
current value of . The first time it is
called that is 0, and the second time it is 1.
If you wanted to expand to the value
assigned to the embedded macro at the time
that was defined, you need to change the
above code to use .
%xdefine isTrue 1 %xdefine isFalse isTrue %xdefine isTrue 0 val1: db isFalse %xdefine isTrue 1 val2: db isFalse
Now, each time that is called, it
expands to 1, as that is what the embedded macro
expanded to at the time that
was defined.
%+ Individual tokens in single line macros can be concatenated, to produce longer tokens for later processing. This can be useful if there are several similar macros that perform similar functions.
As an example, consider the following:
%define BDASTART 400h ; Start of BIOS data area
struc tBIOSDA ; its structure
.COM1addr RESW 1
.COM2addr RESW 1
; ..and so on
endstruc
Now, if we need to access the elements of tBIOSDA in different places, we can end up with:
mov ax,BDASTART + tBIOSDA.COM1addr
mov bx,BDASTART + tBIOSDA.COM2addr
This will become pretty ugly (and tedious) if used in many places, and can be reduced in size significantly by using the following macro:
; Macro to access BIOS variables by their names (from tBDA):
%define BDA(x) BDASTART + tBIOSDA. %+ x
Now the above code can be written as:
mov ax,BDA(COM1addr)
mov bx,BDA(COM2addr)
Using this feature, we can simplify references to a lot of macros (and, in turn, reduce typing errors).
%undef Single-line macros can be removed with the
command. For example, the following
sequence:
%define foo bar
%undef foo
mov eax, foo
will expand to the instruction ,
since after the macro
is no longer defined.
Macros that would otherwise be pre-defined can be undefined on the command-line using the `-u' option on the NASM command line: see section 2.1.13.
%assign An alternative way to define single-line macros is by means of the
command (and its case-insensitive
counterpart , which differs from
in exactly the same way that
differs from
).
is used to define single-line macros
which take no parameters and have a numeric value. This value can be
specified in the form of an expression, and it will be evaluated once, when
the directive is processed.
Like , macros defined using
can be re-defined later, so you can do
things like
%assign i i+1
to increment the numeric value of a macro.
is useful for controlling the
termination of preprocessor loops: see
section 4.5 for an example of this. Another use
for is given in
section 7.4 and
section 8.1.
The expression passed to is a critical
expression (see section 3.8), and
must also evaluate to a pure number (rather than a relocatable reference
such as a code or data address, or anything involving a register).
%strlen and %substr It's often useful to be able to handle strings in macros. NASM supports two simple string handling macro operators from which more complex operations can be constructed.
%strlen The macro is like
macro in that it creates (or redefines) a
numeric value to a macro. The difference is that with
, the numeric value is the length of a
string. An example of the use of this would be:
%strlen charcnt 'my string'
In this example, would receive the
value 8, just as if an had been used. In
this example, was a literal string
but it could also have been a single-line macro that expands to a string,
as in the following example:
%define sometext 'my string' %strlen charcnt sometext
As in the first case, this would result in
being assigned the value of 8.
%substr Individual letters in strings can be extracted using
. An example of its use is probably more
useful than the description:
%substr mychar 'xyz' 1 ; equivalent to %define mychar 'x' %substr mychar 'xyz' 2 ; equivalent to %define mychar 'y' %substr mychar 'xyz' 3 ; equivalent to %define mychar 'z'
In this example, mychar gets the value of 'y'. As with
(see section
4.2.1), the first parameter is the single-line macro to be created and
the second is the string. The third parameter specifies which character is
to be selected. Note that the first index is 1, not 0 and the last index is
equal to the value that would assign
given the same string. Index values out of range result in an empty string.
%macro Multi-line macros are much more like the type of macro seen in MASM and TASM: a multi-line macro definition in NASM looks something like this.
%macro prologue 1
push ebp
mov ebp,esp
sub esp,%1
%endmacro
This defines a C-like function prologue as a macro: so you would invoke the macro with a call such as
myfunc: prologue 12
which would expand to the three lines of code
myfunc: push ebp
mov ebp,esp
sub esp,12
The number after the macro name in the
line defines the number of parameters the
macro expects to receive. The use of
inside the macro definition refers to the
first parameter to the macro call. With a macro taking more than one
parameter, subsequent parameters would be referred to as
, and so on.
Multi-line macros, like single-line macros, are case-sensitive, unless
you define them using the alternative directive
.
If you need to pass a comma as part of a parameter to a multi-line macro, you can do that by enclosing the entire parameter in braces. So you could code things like
%macro silly 2
%2: db %1
%endmacro
silly 'a', letter_a ; letter_a: db 'a'
silly 'ab', string_ab ; string_ab: db 'ab'
silly {13,10}, crlf ; crlf: db 13,10
As with single-line macros, multi-line macros can be overloaded by defining the same macro name several times with different numbers of parameters. This time, no exception is made for macros with no parameters at all. So you could define
%macro prologue 0
push ebp
mov ebp,esp
%endmacro
to define an alternative form of the function prologue which allocates no local stack space.
Sometimes, however, you might want to `overload' a machine instruction; for example, you might want to define
%macro push 2
push %1
push %2
%endmacro
so that you could code
push ebx ; this line is not a macro call
push eax,ecx ; but this one is
Ordinarily, NASM will give a warning for the first of the above two
lines, since is now defined to be a macro,
and is being invoked with a number of parameters for which no definition
has been given. The correct code will still be generated, but the assembler
will give a warning. This warning can be disabled by the use of the
command-line option (see
section 2.1.18).
NASM allows you to define labels within a multi-line macro definition in
such a way as to make them local to the macro call: so calling the same
macro multiple times will use a different label each time. You do this by
prefixing to the label name. So you can invent
an instruction which executes a if the
flag is set by doing this:
%macro retz 0
jnz %%skip
ret
%%skip:
%endmacro
You can call this macro as many times as you want, and every time you
call it NASM will make up a different `real' name to substitute for the
label . The names NASM invents are of the
form , where the number 2345 changes
with every macro call. The prefix prevents
macro-local labels from interfering with the local label mechanism, as
described in section 3.9. You
should avoid defining your own labels in this form (the
prefix, then a number, then another period)
in case they interfere with macro-local labels.
Occasionally it is useful to define a macro which lumps its entire command line into one parameter definition, possibly after extracting one or two smaller parameters from the front. An example might be a macro to write a text string to a file in MS-DOS, where you might want to be able to write
writefile [filehandle],"hello, world",13,10
NASM allows you to define the last parameter of a macro to be greedy, meaning that if you invoke the macro with more parameters than it expects, all the spare parameters get lumped into the last defined one along with the separating commas. So if you code:
%macro writefile 2+
jmp %%endstr
%%str: db %2
%%endstr:
mov dx,%%str
mov cx,%%endstr-%%str
mov bx,%1
mov ah,0x40
int 0x21
%endmacro
then the example call to above will
work as expected: the text before the first comma,
, is used as the first macro
parameter and expanded when is referred to,
and all the subsequent text is lumped into and
placed after the .
The greedy nature of the macro is indicated to NASM by the use of the
sign after the parameter count on the
line.
If you define a greedy macro, you are effectively telling NASM how it
should expand the macro given any number of parameters from the
actual number specified up to infinity; in this case, for example, NASM now
knows what to do when it sees a call to
with 2, 3, 4 or more parameters. NASM will take this into account when
overloading macros, and will not allow you to define another form of
taking 4 parameters (for example).
Of course, the above macro could have been implemented as a non-greedy macro, in which case the call to it would have had to look like
writefile [filehandle], {"hello, world",13,10}
NASM provides both mechanisms for putting commas in macro parameters, and you choose which one you prefer for each macro definition.
See section 5.2.1 for a better way to write the above macro.
NASM also allows you to define a multi-line macro with a range of allowable parameter counts. If you do this, you can specify defaults for omitted parameters. So, for example:
%macro die 0-1 "Painful program death has occurred."
writefile 2,%1
mov ax,0x4c01
int 0x21
%endmacro
This macro (which makes use of the
macro defined in section 4.3.3) can be called
with an explicit error message, which it will display on the error output
stream before exiting, or it can be called with no parameters, in which
case it will use the default error message supplied in the macro
definition.
In general, you supply a minimum and maximum number of parameters for a macro of this type; the minimum number of parameters are then required in the macro call, and then you provide defaults for the optional ones. So if a macro definition began with the line
%macro foobar 1-3 eax,[ebx+2]
then it could be called with between one and three parameters, and
would always be taken from the macro call.
, if not specified by the macro call, would
default to , and
if not specified would default to .
You may omit parameter defaults from the macro definition, in which case
the parameter default is taken to be blank. This can be useful for macros
which can take a variable number of parameters, since the
token (see section
4.3.5) allows you to determine how many parameters were really passed
to the macro call.
This defsingle-line macros using the `-d' option on the NASM command line: see section 2.1.12.
%xdefine To have a reference to an embedded single-line macro resolved at the
time that it is embedded, as opposed to when the calling macro is expanded,
you need a different mechanism to the one offered by
. The solution is to use
, or it's case-insensitive counterpart
.
Suppose you have the following code:
%define isTrue 1 %define isFalse isTrue %define isTrue 0 val1: db isFalse %define isTrue 1 val2: db isFalse
In this case, is equal to 0, and
is equal to 1. This is because, when a
single-line macro is defined using , it is
expanded only when it is called. As
expands to , the expansion will be the
current value of . The first time it is
called that is 0, and the second time it is 1.
If you wanted to expand to the value
assigned to the embedded macro at the time
that was defined, you need to change the
above code to use .
%xdefine isTrue 1 %xdefine isFalse isTrue %xdefine isTrue 0 val1: db isFalse %xdefine isTrue 1 val2: db isFalse
Now, each time that is called, it
expands to 1, as that is what the embedded macro
expanded to at the time that
was defined.
%+ Individual tokens in single line macros can be concatenated, to produce longer tokens for later processing. This can be useful if there are several similar macros that perform similar functions.
As an example, consider the following:
%define BDASTART 400h ; Start of BIOS data area
struc tBIOSDA ; its structure
.COM1addr RESW 1
.COM2addr RESW 1
; ..and so on
endstruc
Now, if we need to access the elements of tBIOSDA in different places, we can end up with:
mov ax,BDASTART + tBIOSDA.COM1addr
mov bx,BDASTART + tBIOSDA.COM2addr
This will become pretty ugly (and tedious) if used in many places, and can be reduced in size significantly by using the following macro:
; Macro to access BIOS variables by their names (from tBDA):
%define BDA(x) BDASTART + tBIOSDA. %+ x
Now the above code can be written as:
mov ax,BDA(COM1addr)
mov bx,BDA(COM2addr)
Using this feature, we can simplify references to a lot of macros (and, in turn, reduce typing errors).
%undef Single-line macros can be removed with the
command. For example, the following
sequence:
%define foo bar
%undef foo
mov eax, foo
will expand to the instruction ,
since after the macro
is no longer defined.
Macros that would otherwise be pre-defined can be undefined on the command-line using the `-u' option on the NASM command line: see section 2.1.13.
%assign An alternative way to define single-line macros is by means of the
command (and its case-insensitive
counterpart , which differs from
in exactly the same way that
differs from
).
is used to define single-line macros
which take no parameters and have a numeric value. This value can be
specified in the form of an expression, and it will be evaluated once, when
the directive is processed.
Like , macros defined using
can be re-defined later, so you can do
things like
%assign i i+1
to increment the numeric value of a macro.
is useful for controlling the
termination of preprocessor loops: see
section 4.5 for an example of this. Another use
for is given in
section 7.4 and
section 8.1.
The expression passed to is a critical
expression (see section 3.8), and
must also evaluate to a pure number (rather than a relocatable reference
such as a code or data address, or anything involving a register).
%strlen and %substr It's often useful to be able to handle strings in macros. NASM supports two simple string handling macro operators from which more complex operations can be constructed.
%strlen The macro is like
macro in that it creates (or redefines) a
numeric value to a macro. The difference is that with
, the numeric value is the length of a
string. An example of the use of this would be:
%strlen charcnt 'my string'
In this example, would receive the
value 8, just as if an had been used. In
this example, was a literal string
but it could also have been a single-line macro that expands to a string,
as in the following example:
%define sometext 'my string' %strlen charcnt sometext
As in the first case, this would result in
being assigned the value of 8.
%substr Individual letters in strings can be extracted using
. An example of its use is probably more
useful than the description:
%substr mychar 'xyz' 1 ; equivalent to %define mychar 'x' %substr mychar 'xyz' 2 ; equivalent to %define mychar 'y' %substr mychar 'xyz' 3 ; equivalent to %define mychar 'z'
In this example, mychar gets the value of 'y'. As with
(see section
4.2.1), the first parameter is the single-line macro to be created and
the second is the string. The third parameter specifies which character is
to be selected. Note that the first index is 1, not 0 and the last index is
equal to the value that would assign
given the same string. Index values out of range result in an empty string.
%macro Multi-line macros are much more like the type of macro seen in MASM and TASM: a multi-line macro definition in NASM looks something like this.
%macro prologue 1
push ebp
mov ebp,esp
sub esp,%1
%endmacro
This defines a C-like function prologue as a macro: so you would invoke the macro with a call such as
myfunc: prologue 12
which would expand to the three lines of code
myfunc: push ebp
mov ebp,esp
sub esp,12
The number after the macro name in the
line defines the number of parameters the
macro expects to receive. The use of
inside the macro definition refers to the
first parameter to the macro call. With a macro taking more than one
parameter, subsequent parameters would be referred to as
, and so on.
Multi-line macros, like single-line macros, are case-sensitive, unless
you define them using the alternative directive
.
If you need to pass a comma as part of a parameter to a multi-line macro, you can do that by enclosing the entire parameter in braces. So you could code things like
%macro silly 2
%2: db %1
%endmacro
silly 'a', letter_a ; letter_a: db 'a'
silly 'ab', string_ab ; string_ab: db 'ab'
silly {13,10}, crlf ; crlf: db 13,10
As with single-line macros, multi-line macros can be overloaded by defining the same macro name several times with different numbers of parameters. This time, no exception is made for macros with no parameters at all. So you could define
%macro prologue 0
push ebp
mov ebp,esp
%endmacro
to define an alternative form of the function prologue which allocates no local stack space.
Sometimes, however, you might want to `overload' a machine instruction; for example, you might want to define
%macro push 2
push %1
push %2
%endmacro
so that you could code
push ebx ; this line is not a macro call
push eax,ecx ; but this one is
Ordinarily, NASM will give a warning for the first of the above two
lines, since is now defined to be a macro,
and is being invoked with a number of parameters for which no definition
has been given. The correct code will still be generated, but the assembler
will give a warning. This warning can be disabled by the use of the
command-line option (see
section 2.1.18).
NASM allows you to define labels within a multi-line macro definition in
such a way as to make them local to the macro call: so calling the same
macro multiple times will use a different label each time. You do this by
prefixing to the label name. So you can invent
an instruction which executes a if the
flag is set by doing this:
%macro retz 0
jnz %%skip
ret
%%skip:
%endmacro
You can call this macro as many times as you want, and every time you
call it NASM will make up a different `real' name to substitute for the
label . The names NASM invents are of the
form , where the number 2345 changes
with every macro call. The prefix prevents
macro-local labels from interfering with the local label mechanism, as
described in section 3.9. You
should avoid defining your own labels in this form (the
prefix, then a number, then another period)
in case they interfere with macro-local labels.
Occasionally it is useful to define a macro which lumps its entire command line into one parameter definition, possibly after extracting one or two smaller parameters from the front. An example might be a macro to write a text string to a file in MS-DOS, where you might want to be able to write
writefile [filehandle],"hello, world",13,10
NASM allows you to define the last parameter of a macro to be greedy, meaning that if you invoke the macro with more parameters than it expects, all the spare parameters get lumped into the last defined one along with the separating commas. So if you code:
%macro writefile 2+
jmp %%endstr
%%str: db %2
%%endstr:
mov dx,%%str
mov cx,%%endstr-%%str
mov bx,%1
mov ah,0x40
int 0x21
%endmacro
then the example call to above will
work as expected: the text before the first comma,
, is used as the first macro
parameter and expanded when is referred to,
and all the subsequent text is lumped into and
placed after the .
The greedy nature of the macro is indicated to NASM by the use of the
sign after the parameter count on the
line.
If you define a greedy macro, you are effectively telling NASM how it
should expand the macro given any number of parameters from the
actual number specified up to infinity; in this case, for example, NASM now
knows what to do when it sees a call to
with 2, 3, 4 or more parameters. NASM will take this into account when
overloading macros, and will not allow you to define another form of
taking 4 parameters (for example).
Of course, the above macro could have been implemented as a non-greedy macro, in which case the call to it would have had to look like
writefile [filehandle], {"hello, world",13,10}
NASM provides both mechanisms for putting commas in macro parameters, and you choose which one you prefer for each macro definition.
See section 5.2.1 for a better way to write the above macro.
NASM also allows you to define a multi-line macro with a range of allowable parameter counts. If you do this, you can specify defaults for omitted parameters. So, for example:
%macro die 0-1 "Painful program death has occurred."
writefile 2,%1
mov ax,0x4c01
int 0x21
%endmacro
This macro (which makes use of the
macro defined in section 4.3.3) can be called
with an explicit error message, which it will display on the error output
stream before exiting, or it can be called with no parameters, in which
case it will use the default error message supplied in the macro
definition.
In general, you supply a minimum and maximum number of parameters for a macro of this type; the minimum number of parameters are then required in the macro call, and then you provide defaults for the optional ones. So if a macro definition began with the line
%macro foobar 1-3 eax,[ebx+2]
then it could be called with between one and three parameters, and
would always be taken from the macro call.
, if not specified by the macro call, would
default to , and
if not specified would default to .
You may omit parameter defaults from the macro definition, in which case
the parameter default is taken to be blank. This can be useful for macros
which can take a variable number of parameters, since the
token (see section
4.3.5) allows you to determine how many parameters were really passed
to the macro call.
This defsingle-line macros using the `-d' option on the NASM command line: see section 2.1.12.
%xdefine To have a reference to an embedded single-line macro resolved at the
time that it is embedded, as opposed to when the calling macro is expanded,
you need a different mechanism to the one offered by
. The solution is to use
, or it's case-insensitive counterpart
.
Suppose you have the following code:
%define isTrue 1 %define isFalse isTrue %define isTrue 0 val1: db isFalse %define isTrue 1 val2: db isFalse
In this case, is equal to 0, and
is equal to 1. This is because, when a
single-line macro is defined using , it is
expanded only when it is called. As
expands to , the expansion will be the
current value of . The first time it is
called that is 0, and the second time it is 1.
If you wanted to expand to the value
assigned to the embedded macro at the time
that was defined, you need to change the
above code to use .
%xdefine isTrue 1 %xdefine isFalse isTrue %xdefine isTrue 0 val1: db isFalse %xdefine isTrue 1 val2: db isFalse
Now, each time that is called, it
expands to 1, as that is what the embedded macro
expanded to at the time that
was defined.