The Netwide Assembler: NASM

Next Chapter | Previous Chapter | Contents | Index

Chapter 4: The NASM Preprocessor

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.

4.1 Single-Line Macros

4.1.1 The Normal Way: %define

Single-line macros are defined using the %define 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 mov ax,1+2*8, even though the macro b wasn't defined at the time of definition of a.

Macros defined with %define are case sensitive: after %define foo bar, only foo will expand to bar: Foo or FOO will not. By using %idefine instead of %define (the `i' stands for `insensitive') you can define all the case variants of a macro at once, so that %idefine foo bar would cause foo, Foo, FOO, fOO and so on all to expand to bar.

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 a(3) will expand once, becoming 1+a(3), 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 foo(3) will become 1+3 whereas foo(ebx,2) will become 1+ebx*2. However, if you define

%define foo bar

then no other definition of foo 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 foo is invoked, it will be expanded according to the most recent definition. This is particularly useful when defining single-line macros with %assign (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.

4.1.2 Enhancing %define: %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 %define. The solution is to use %xdefine, or it's case-insensitive counterpart %xidefine.

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, val1 is equal to 0, and val2 is equal to 1. This is because, when a single-line macro is defined using %define, it is expanded only when it is called. As isFalse expands to isTrue, the expansion will be the current value of isTrue. The first time it is called that is 0, and the second time it is 1.

If you wanted isFalse to expand to the value assigned to the embedded macro isTrue at the time that isFalse was defined, you need to change the above code to use %xdefine.

%xdefine isTrue  1 
%xdefine isFalse isTrue 
%xdefine isTrue  0 

val1:    db      isFalse 

%xdefine isTrue  1 

val2:    db      isFalse

Now, each time that isFalse is called, it expands to 1, as that is what the embedded macro isTrue expanded to at the time that isFalse was defined.

4.1.3 Concatenating Single Line Macro Tokens: %+

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).

4.1.4 Undefining macros: %undef

Single-line macros can be removed with the %undef command. For example, the following sequence:

%define foo bar 
%undef  foo 

        mov     eax, foo

will expand to the instruction mov eax, foo, since after %undef the macro foo 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.

4.1.5 Preprocessor Variables: %assign

An alternative way to define single-line macros is by means of the %assign command (and its case-insensitive counterpart %iassign, which differs from %assign in exactly the same way that %idefine differs from %define).

%assign 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 %assign directive is processed.

Like %define, macros defined using %assign can be re-defined later, so you can do things like

%assign i i+1

to increment the numeric value of a macro.

%assign is useful for controlling the termination of %rep preprocessor loops: see section 4.5 for an example of this. Another use for %assign is given in section 7.4 and section 8.1.

The expression passed to %assign 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).

4.2 String Handling in Macros: %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.

4.2.1 String Length: %strlen

The %strlen macro is like %assign macro in that it creates (or redefines) a numeric value to a macro. The difference is that with %strlen, 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, charcnt would receive the value 8, just as if an %assign had been used. In this example, 'my string' 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 charcnt being assigned the value of 8.

4.2.2 Sub-strings: %substr

Individual letters in strings can be extracted using %substr. 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 %strlen (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 %strlen would assign given the same string. Index values out of range result in an empty string.

4.3 Multi-Line Macros: %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 1 after the macro name in the %macro line defines the number of parameters the macro prologue expects to receive. The use of %1 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 %2, %3 and so on.

Multi-line macros, like single-line macros, are case-sensitive, unless you define them using the alternative directive %imacro.

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

4.3.1 Overloading Multi-Line Macros

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 push 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 -w-macro-params command-line option (see section 2.1.18).

4.3.2 Macro-Local Labels

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 RET if the Z 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 %%skip. The names NASM invents are of the form ..@2345.skip, 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.

4.3.3 Greedy Macro Parameters

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 writefile above will work as expected: the text before the first comma, [filehandle], is used as the first macro parameter and expanded when %1 is referred to, and all the subsequent text is lumped into %2 and placed after the db.

The greedy nature of the macro is indicated to NASM by the use of the + sign after the parameter count on the %macro 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 writefile 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 writefile 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.

4.3.4 Default Macro Parameters

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 writefile 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 %1 would always be taken from the macro call. %2, if not specified by the macro call, would default to eax, and %3 if not specified would default to [ebx+2].

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 %0 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.

4.1.2 Enhancing %define: %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 %define. The solution is to use %xdefine, or it's case-insensitive counterpart %xidefine.

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, val1 is equal to 0, and val2 is equal to 1. This is because, when a single-line macro is defined using %define, it is expanded only when it is called. As isFalse expands to isTrue, the expansion will be the current value of isTrue. The first time it is called that is 0, and the second time it is 1.

If you wanted isFalse to expand to the value assigned to the embedded macro isTrue at the time that isFalse was defined, you need to change the above code to use %xdefine.

%xdefine isTrue  1 
%xdefine isFalse isTrue 
%xdefine isTrue  0 

val1:    db      isFalse 

%xdefine isTrue  1 

val2:    db      isFalse

Now, each time that isFalse is called, it expands to 1, as that is what the embedded macro isTrue expanded to at the time that isFalse was defined.

4.1.3 Concatenating Single Line Macro Tokens: %+

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).

4.1.4 Undefining macros: %undef

Single-line macros can be removed with the %undef command. For example, the following sequence:

%define foo bar 
%undef  foo 

        mov     eax, foo

will expand to the instruction mov eax, foo, since after %undef the macro foo 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.

4.1.5 Preprocessor Variables: %assign

An alternative way to define single-line macros is by means of the %assign command (and its case-insensitive counterpart %iassign, which differs from %assign in exactly the same way that %idefine differs from %define).

%assign 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 %assign directive is processed.

Like %define, macros defined using %assign can be re-defined later, so you can do things like

%assign i i+1

to increment the numeric value of a macro.

%assign is useful for controlling the termination of %rep preprocessor loops: see section 4.5 for an example of this. Another use for %assign is given in section 7.4 and section 8.1.

The expression passed to %assign 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).

4.2 String Handling in Macros: %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.

4.2.1 String Length: %strlen

The %strlen macro is like %assign macro in that it creates (or redefines) a numeric value to a macro. The difference is that with %strlen, 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, charcnt would receive the value 8, just as if an %assign had been used. In this example, 'my string' 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 charcnt being assigned the value of 8.

4.2.2 Sub-strings: %substr

Individual letters in strings can be extracted using %substr. 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 %strlen (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 %strlen would assign given the same string. Index values out of range result in an empty string.

4.3 Multi-Line Macros: %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 1 after the macro name in the %macro line defines the number of parameters the macro prologue expects to receive. The use of %1 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 %2, %3 and so on.

Multi-line macros, like single-line macros, are case-sensitive, unless you define them using the alternative directive %imacro.

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

4.3.1 Overloading Multi-Line Macros

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 push 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 -w-macro-params command-line option (see section 2.1.18).

4.3.2 Macro-Local Labels

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 RET if the Z 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 %%skip. The names NASM invents are of the form ..@2345.skip, 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.

4.3.3 Greedy Macro Parameters

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 writefile above will work as expected: the text before the first comma, [filehandle], is used as the first macro parameter and expanded when %1 is referred to, and all the subsequent text is lumped into %2 and placed after the db.

The greedy nature of the macro is indicated to NASM by the use of the + sign after the parameter count on the %macro 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 writefile 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 writefile 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.

4.3.4 Default Macro Parameters

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 writefile 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 %1 would always be taken from the macro call. %2, if not specified by the macro call, would default to eax, and %3 if not specified would default to [ebx+2].

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 %0 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.

4.1.2 Enhancing %define: %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 %define. The solution is to use %xdefine, or it's case-insensitive counterpart %xidefine.

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, val1 is equal to 0, and val2 is equal to 1. This is because, when a single-line macro is defined using %define, it is expanded only when it is called. As isFalse expands to isTrue, the expansion will be the current value of isTrue. The first time it is called that is 0, and the second time it is 1.

If you wanted isFalse to expand to the value assigned to the embedded macro isTrue at the time that isFalse was defined, you need to change the above code to use %xdefine.

%xdefine isTrue  1 
%xdefine isFalse isTrue 
%xdefine isTrue  0 

val1:    db      isFalse 

%xdefine isTrue  1 

val2:    db      isFalse

Now, each time that isFalse is called, it expands to 1, as that is what the embedded macro isTrue expanded to at the time that isFalse was defined.

4.1.3 Concatenating Single Line Macro Tokens: %+

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).

4.1.4 Undefining macros: %undef

Single-line macros can be removed with the %undef command. For example, the following sequence:

%define foo bar 
%undef  foo 

        mov     eax, foo

will expand to the instruction mov eax, foo, since after %undef the macro foo 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.

4.1.5 Preprocessor Variables: %assign

An alternative way to define single-line macros is by means of the %assign command (and its case-insensitive counterpart %iassign, which differs from %assign in exactly the same way that %idefine differs from %define).

%assign 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 %assign directive is processed.

Like %define, macros defined using %assign can be re-defined later, so you can do things like

%assign i i+1

to increment the numeric value of a macro.

%assign is useful for controlling the termination of %rep preprocessor loops: see section 4.5 for an example of this. Another use for %assign is given in section 7.4 and section 8.1.

The expression passed to %assign 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).

4.2 String Handling in Macros: %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.

4.2.1 String Length: %strlen

The %strlen macro is like %assign macro in that it creates (or redefines) a numeric value to a macro. The difference is that with %strlen, 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, charcnt would receive the value 8, just as if an %assign had been used. In this example, 'my string' 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 charcnt being assigned the value of 8.

4.2.2 Sub-strings: %substr

Individual letters in strings can be extracted using %substr. 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 %strlen (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 %strlen would assign given the same string. Index values out of range result in an empty string.

4.3 Multi-Line Macros: %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 1 after the macro name in the %macro line defines the number of parameters the macro prologue expects to receive. The use of %1 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 %2, %3 and so on.

Multi-line macros, like single-line macros, are case-sensitive, unless you define them using the alternative directive %imacro.

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

4.3.1 Overloading Multi-Line Macros

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 push 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 -w-macro-params command-line option (see section 2.1.18).

4.3.2 Macro-Local Labels

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 RET if the Z 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 %%skip. The names NASM invents are of the form ..@2345.skip, 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.

4.3.3 Greedy Macro Parameters

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 writefile above will work as expected: the text before the first comma, [filehandle], is used as the first macro parameter and expanded when %1 is referred to, and all the subsequent text is lumped into %2 and placed after the db.

The greedy nature of the macro is indicated to NASM by the use of the + sign after the parameter count on the %macro 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 writefile 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 writefile 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.

4.3.4 Default Macro Parameters

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 writefile 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 %1 would always be taken from the macro call. %2, if not specified by the macro call, would default to eax, and %3 if not specified would default to [ebx+2].

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 %0 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.

4.1.2 Enhancing %define: %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 %define. The solution is to use %xdefine, or it's case-insensitive counterpart %xidefine.

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, val1 is equal to 0, and val2 is equal to 1. This is because, when a single-line macro is defined using %define, it is expanded only when it is called. As isFalse expands to isTrue, the expansion will be the current value of isTrue. The first time it is called that is 0, and the second time it is 1.

If you wanted isFalse to expand to the value assigned to the embedded macro isTrue at the time that isFalse was defined, you need to change the above code to use %xdefine.

%xdefine isTrue  1 
%xdefine isFalse isTrue 
%xdefine isTrue  0 

val1:    db      isFalse 

%xdefine isTrue  1 

val2:    db      isFalse

Now, each time that isFalse is called, it expands to 1, as that is what the embedded macro isTrue expanded to at the time that isFalse was defined.

4.1.3 Concatenating Single Line Macro Tokens: %+

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).

4.1.4 Undefining macros: %undef

Single-line macros can be removed with the %undef command. For example, the following sequence:

%define foo bar 
%undef  foo 

        mov     eax, foo

will expand to the instruction mov eax, foo, since after %undef the macro foo 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.

4.1.5 Preprocessor Variables: %assign

An alternative way to define single-line macros is by means of the %assign command (and its case-insensitive counterpart %iassign, which differs from %assign in exactly the same way that %idefine differs from %define).

%assign 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 %assign directive is processed.

Like %define, macros defined using %assign can be re-defined later, so you can do things like

%assign i i+1

to increment the numeric value of a macro.

%assign is useful for controlling the termination of %rep preprocessor loops: see section 4.5 for an example of this. Another use for %assign is given in section 7.4 and section 8.1.

The expression passed to %assign 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).

4.2 String Handling in Macros: %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.

4.2.1 String Length: %strlen

The %strlen macro is like %assign macro in that it creates (or redefines) a numeric value to a macro. The difference is that with %strlen, 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, charcnt would receive the value 8, just as if an %assign had been used. In this example, 'my string' 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 charcnt being assigned the value of 8.

4.2.2 Sub-strings: %substr

Individual letters in strings can be extracted using %substr. 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 %strlen (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 %strlen would assign given the same string. Index values out of range result in an empty string.

4.3 Multi-Line Macros: %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 1 after the macro name in the %macro line defines the number of parameters the macro prologue expects to receive. The use of %1 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 %2, %3 and so on.

Multi-line macros, like single-line macros, are case-sensitive, unless you define them using the alternative directive %imacro.

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

4.3.1 Overloading Multi-Line Macros

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 push 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 -w-macro-params command-line option (see section 2.1.18).

4.3.2 Macro-Local Labels

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 RET if the Z 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 %%skip. The names NASM invents are of the form ..@2345.skip, 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.

4.3.3 Greedy Macro Parameters

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 writefile above will work as expected: the text before the first comma, [filehandle], is used as the first macro parameter and expanded when %1 is referred to, and all the subsequent text is lumped into %2 and placed after the db.

The greedy nature of the macro is indicated to NASM by the use of the + sign after the parameter count on the %macro 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 writefile 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 writefile 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.

4.3.4 Default Macro Parameters

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 writefile 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 %1 would always be taken from the macro call. %2, if not specified by the macro call, would default to eax, and %3 if not specified would default to [ebx+2].

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 %0 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.

4.1.2 Enhancing %define: %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 %define. The solution is to use %xdefine, or it's case-insensitive counterpart %xidefine.

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, val1 is equal to 0, and val2 is equal to 1. This is because, when a single-line macro is defined using %define, it is expanded only when it is called. As isFalse expands to isTrue, the expansion will be the current value of isTrue. The first time it is called that is 0, and the second time it is 1.

If you wanted isFalse to expand to the value assigned to the embedded macro isTrue at the time that isFalse was defined, you need to change the above code to use %xdefine.

%xdefine isTrue  1 
%xdefine isFalse isTrue 
%xdefine isTrue  0 

val1:    db      isFalse 

%xdefine isTrue  1 

val2:    db      isFalse

Now, each time that isFalse is called, it expands to 1, as that is what the embedded macro isTrue expanded to at the time that isFalse was defined.

4.1.3 Concatenating Single Line M