Expressions in NASM are similar in syntax to those in C.
NASM does not guarantee the size of the integers used to evaluate expressions at compile time: since NASM can compile and run on 64-bit systems quite happily, don’t assume that expressions are evaluated in 32-bit registers and so try to make deliberate use of ((integer overflow)). It might not always work. The only thing NASM will guarantee is what’s guaranteed by ANSI C: you always have at least 32 bits to work in.
NASM supports two special tokens in expressions,
allowing calculations to involve the current assembly position: the $
and $$
tokens. $
evaluates to the assembly position at the beginning of the line containing the
expression; so you can code an infinite loop using JMP $
. $$
evaluates to the beginning of the current section; so you can tell
how far into the section you are by using ($-$$)
.
The arithmetic operators provided by NASM are listed here, in increasing order of precedence.
The |
operator gives a bitwise OR, exactly as performed by the OR
machine instruction. Bitwise OR is the lowest-priority arithmetic operator supported by
NASM.
<<
gives a bit-shift to the left,
just as it does in C. So 5<<3
evaluates to 5 times 8,
or 40. >>
gives a bit-shift to the right; in NASM,
such a shift is always unsigned, so that the bits
shifted in from the left-hand end are filled with zero rather than a sign-extension of
the previous highest bit.
*
is the multiplication operator. /
and //
are both division operators: /
is unsigned division and //
is signed division. Similarly,
%
and %%
provide unsigned and
signed modulo operators
respectively.
NASM, like ANSI C, provides no guarantees about the sensible operation of the signed modulo operator.
Since the %
character is used extensively by the macro
preprocessor, you should ensure that both the signed and unsigned modulo operators are
followed by white space wherever they appear.
The highest-priority operators in NASM’s expression
grammar are those which only apply to one argument. -
negates its operand, +
does nothing (it’s provided for
symmetry with -
), ~
computes
the one’s complement of
its operand, and SEG
provides the segment address of its operand (explained in more
detail in Section 3.6.8).
When writing large
16-bit programs, which must be split into multiple segments, it is often necessary to be able to refer to
the segment part of the address of a symbol. NASM supports the SEG
operator to perform
this function.
The SEG
operator returns the preferred segment base of a symbol, defined as the segment
base relative to which the offset of the symbol makes sense. So the code
mov ax, seg symbol mov es, ax mov bx, symbol
will load es:bx
with a valid pointer to the symbol
symbol
.
Things can be more
complex than this: since 16-bit segments and groups may overlap, you might occasionally want to refer to some symbol
using a different segment base from the preferred one. NASM lets you do this, by the use
of the WRT
(With Reference To) keyword. So you can do things like
mov ax, weird_seg ; weird_seg is a segment base mov es, ax mov bx, symbol wrt weird_seg
to load es:bx
with a different, but functionally
equivalent, pointer to the symbol symbol
.
NASM supports far (inter-segment) calls and jumps by means of the syntax call segment:offset
, where segment
and
offset
both represent immediate values. So to call a far
procedure, you could code either of
call (seg procedure):procedure call weird_seg:(procedure wrt weird_seg)
(The parentheses are included for clarity, to show the intended parsing of the above instructions. They are not necessary in practice.)
NASM supports the syntax
call far procedure
as a synonym for the first of the above
usages. JMP
works identically to CALL
in these examples.
To declare a far pointer to a data item in a data segment, you must code
dw symbol, seg symbol
NASM supports no convenient synonym for this, though you can always invent one using the macro processor.