An effective address is any operand to an instruction which references memory. Effective addresses, in NASM, have a very simple syntax: they consist of an expression evaluating to the desired address, enclosed in square brackets. For example:
wordvar dw 123 mov ax,[wordvar] mov ax,[wordvar+1] mov ax,[es:wordvar+bx]
Anything not conforming to this simple system is not a valid memory reference in NASM,
for example es:wordvar[bx]
.
More complicated effective addresses, such as those involving more than one register, work in exactly the same way:
mov eax,[ebx*2+ecx+offset] mov ax,[bp+di+8]
NASM is capable of doing algebra on these effective addresses, so that things which don’t necessarily look legal are perfectly all right:
mov eax,[ebx*5] ; assembles as [ebx*4+ebx] mov eax,[label1*2-label2] ; ie [label1+(label1-label2)]
Some forms of effective address have more than one assembled form; in most such cases
NASM will generate the smallest form it can. For example, there are distinct assembled
forms for the 32-bit effective addresses [eax*2+0]
and
[eax+eax]
, and NASM will generally generate the latter on
the grounds that the former requires four bytes to store a zero offset.
NASM has a hinting mechanism which will cause [eax+ebx]
and [ebx+eax]
to generate different opcodes; this is
occasionally useful because [esi+ebp]
and [ebp+esi]
have different default segment registers.
However, you can force NASM to generate an effective address in a particular form by
the use of the keywords BYTE
, WORD
, DWORD
and NOSPLIT
. If you need
[eax+3]
to be assembled using a double-word offset field
instead of the one byte NASM will normally generate, you can code [dword eax+3]
. Similarly, you can force NASM to use a byte offset for a
small value which it hasn’t seen on the first pass (see Section 3.8 for an example of
such a code fragment) by using [byte eax+offset]
. As special
cases, [byte eax]
will code [eax+0]
with a byte offset of zero, and [dword
eax]
will code it with a double-word offset of zero. The normal form, [eax]
, will be coded with no offset field.
The form described in the previous paragraph is also useful if you are trying to access data in a 32-bit segment from within 16 bit code. In particular, if you need to access data with a known offset that is larger than will fit in a 16-bit value, if you don’t specify that it is a dword offset, NASM will cause the high word of the offset to be lost.
Similarly, NASM will split [eax*2]
into [eax+eax]
because that allows the offset field to be absent and space to
be saved; in fact, it will also split [eax*2+offset]
into
[eax+eax+offset]
. You can combat this behaviour by the use
of the NOSPLIT
keyword: [nosplit
eax*2]
will force [eax*2+0]
to be generated
literally.
In BITS 64
mode, displacements, for the most part, remain
32 bits and are sign extended prior to use. The exception is one restricted form of the
mov instruction: between an AL
, AX
, EAX
, or RAX
register and a 64-bit absolute address (no registers are allowed in
the effective address, and the address cannot be RIP-relative). In NASM syntax, use of
the 64-bit absolute form requires QWORD
. Examples in NASM
syntax:
mov eax, [1] ; 32 bit, with sign extension mov al, [rax-1] ; 32 bit, with sign extension mov al, [qword 0x1122334455667788] ; 64-bit absolute mov al, [0x1122334455667788] ; truncated to 32-bit (warning)
In 64-bit mode, a new form of effective addressing is available to make it easier to
write position-independent code. Any memory reference may be made RIP
relative
(RIP
is the instruction pointer register, which contains the
address of the location immediately following the current instruction).
In NASM syntax, there are two ways to specify RIP-relative addressing:
mov dword [rip+10], 1
stores the value 1 ten bytes after the end of the instruction. 10
can also be a symbolic constant, and will be treated the same way. On
the other hand,
mov dword [symb wrt rip], 1
stores the value 1 into the address of symbol symb
. This
is distinctly different than the behavior of:
mov dword [symb+rip], 1
which takes the address of the end of the instruction, adds the address of
symb
to it, then stores the value 1 there. If symb
is a variable, this will not
store the value 1 into the symb
variable!
Yasm also supports the following syntax for RIP-relative addressing. The REL
keyword makes it produce RIP
-relative addresses, while the
ABS
keyword makes it produce non-RIP
-relative addresses:
mov [rel sym], rax ; RIP-relative mov [abs sym], rax ; not RIP-relative
The behavior of mov [sym], rax
depends on a mode set by
the DEFAULT
directive (see Section 5.2), as follows. The default
mode at Yasm start-up is always ABS
, and in REL
mode, use of registers, a FS
or
GS
segment override, or an explicit ABS
override will result in a non-RIP-relative effective address.
default rel mov [sym], rbx ; RIP-relative mov [abs sym], rbx ; not RIP-relative (explicit override) mov [rbx+1], rbx ; not RIP-relative (register use) mov [fs:sym], rbx ; not RIP-relative (fs or gs use) mov [ds:sym], rbx ; RIP-relative (segment, but not fs or gs) mov [rel sym], rbx ; RIP-relative (redundant override) default abs mov [sym], rbx ; not RIP-relative mov [abs sym], rbx ; not RIP-relative mov [rbx+1], rbx ; not RIP-relative mov [fs:sym], rbx ; not RIP-relative mov [ds:sym], rbx ; not RIP-relative mov [rel sym], rbx ; RIP-relative (explicit override)