Immediate values
mov rax, 42The instruction contains the actual value that should be used. No memory access is involved in this example.
NASM • MEMORY & STACK
Understand how NASM programs locate data in memory, construct effective addresses, work with arrays and buffers, and use the x86-64 stack for temporary storage, procedure calls, local variables, and stack frames.
THE CORE IDEA
One of the most important concepts in assembly programming is understanding the difference between a value, an address, and the memory location represented by that address.
Registers provide the processor with very fast working storage. Main memory provides a much larger space for storing instructions and data. Assembly instructions can operate directly on registers, use immediate values, or access data stored in memory.
Consider the following instruction:
mov rax, rbxThis copies the value currently held in RBX into RAX. It does not access the memory location whose address might happen to be stored in RBX.
Now compare it with:
mov rax, [rbx]Here the square brackets indicate a memory operand. The value in RBX is interpreted as an address, and the processor reads the data stored at that memory location.
← Return to the complete NASM guideVALUES & ADDRESSES
An address tells the processor where something is located. A value is the data stored there or the data being manipulated.
A value is the actual data being used by the program. It might represent an integer, character, pointer, flag, part of an object, or some other piece of information.
mov rax, 42Here, the immediate value 42 is placed directly into RAX.
An address identifies a location in memory. A register can contain such an address and then be used as part of a memory operand.
mov rax, [rbx]In this example, the contents of RBX are treated as the address from which data should be loaded.
ADDRESSING FORMS
x86-64 provides flexible ways to calculate effective addresses. These forms are particularly useful when working with arrays, structures, buffers, pointers, and stack frames.
mov rax, 42The instruction contains the actual value that should be used. No memory access is involved in this example.
mov rax, rbxThe instruction operates directly on values held in registers.
mov rax, [rbx]The register contains an address, and the square brackets tell NASM to access the memory located at that address.
mov rax, [rbx + 8]An offset is added to a base address to access a different memory location.
mov rax, [rbx + rcx]Two registers can contribute to the effective address, which is useful when traversing data structures.
mov rax, [rbx + rcx*8]A scaled index is particularly useful when accessing arrays of elements with a fixed element size.
NASM SYNTAX
In NASM syntax, square brackets are a critical visual clue that the instruction is accessing memory rather than simply using the value contained in a register.
mov rax, rbx
mov rax, [rbx]MOV RAX, RBX copies the value contained in RBX into RAX.
MOV RAX, [RBX] uses the contents of RBX as a memory address and loads the value stored there.
This distinction is fundamental. Many early assembly mistakes come from treating a pointer value as though it were the data stored at that pointer.
EFFECTIVE ADDRESS
A memory operand can be constructed from several components, allowing programs to calculate the location of an element efficiently.
A common x86-64 addressing form can be represented conceptually as:
base + index × scale + displacementThe base register provides a starting address. The index register can represent an element position. The scale accounts for the size of each element, and the displacement provides an additional fixed offset.
mov rax, [rbx + rcx*8]If RBX represents the beginning of an array and RCX represents an element index, multiplying the index by eight is appropriate when each element occupies eight bytes. The resulting effective address identifies the requested element.
ARRAYS
Arrays provide one of the clearest practical examples of why base, index, and scale addressing are useful.
Suppose an array contains 64-bit integer values. Each element occupies eight bytes. If a register contains the address of the first element, the address of another element can be calculated using its index multiplied by eight.
; RBX = address of array
; RCX = element index
mov rax, [rbx + rcx*8]If RCX is zero, the first element is accessed. If RCX is one, the address advances by eight bytes. If RCX is two, it advances by sixteen bytes, and so on.
This pattern appears throughout low-level programming and is particularly useful when studying arrays, pointers, compiled code, and data structures.
LEA
LEA, or Load Effective Address, is frequently misunderstood because its name suggests a memory load. It actually calculates an address and places that address into a register.
lea rax, [rbx + rcx*8]The instruction above calculates the effective address represented by the expression inside the brackets and places that address into RAX. It does not load the value stored at that address.
This makes LEA useful for address calculations and certain arithmetic expressions where the addressing hardware can efficiently construct the desired result.
mov rax, [rbx + rcx*8]
lea rdx, [rbx + rcx*8]The first instruction accesses memory and retrieves the value. The second calculates the address itself. Understanding this distinction is essential when reading compiler-generated assembly.
OPERAND SIZE
A memory address alone does not always tell an instruction whether it should access one byte, two bytes, four bytes, or eight bytes.
Assembly programs frequently work with values of different widths. The instruction and its operands must therefore provide enough information for the processor to determine the intended data size.
| Common width | Typical register form | Example concept |
|---|---|---|
| 8-bit | AL | Byte-sized data |
| 16-bit | AX | Word-sized data |
| 32-bit | EAX | Doubleword-sized data |
| 64-bit | RAX | Quadword-sized data |
Understanding operand size becomes particularly important when working with arrays, structures, strings, binary data, and mixed-width calculations.
THE X86-64 STACK
The stack provides temporary storage and plays a central role in procedure calls, saved state, return addresses, local variables, and stack frames.
Unlike a general-purpose data structure, the stack follows a particular access discipline. Data is typically added and removed according to a last-in, first-out pattern.
In x86-64, RSP is the stack pointer. It tracks the current stack position, while instructions such as PUSH and POP adjust the stack as data is added or removed.
push rax
push rbx
pop rbx
pop raxIn the example, the values are restored in reverse order. The last value pushed is the first value popped.
STACK COMPONENTS
These concepts should be learned together because they describe different parts of the same procedure and stack-management mechanism.
The stack pointer identifies the current stack position and changes as data is pushed onto or removed from the stack.
RBP can provide a stable reference point within a stack frame and is commonly used when explaining local variables and procedure layouts.
PUSH places a value on the stack and updates the stack pointer accordingly.
POP retrieves a value from the stack and updates the stack pointer accordingly.
CALL transfers execution to a procedure and works with the stack to preserve a return address.
RET returns execution to the saved return location associated with a procedure call.
STACK OPERATIONS
PUSH and POP provide a straightforward mechanism for saving and restoring values during program execution.
mov rax, 100
mov rbx, 200
push rax
push rbx
pop rcx
pop rdxThe values are pushed in the order RAX and then RBX. Because the stack is last-in, first-out, POP places RBX's saved value into RCX and RAX's saved value into RDX.
This simple pattern demonstrates why stack order matters when saving registers or temporary values.
PROCEDURES
Procedure calls require the program to remember where execution should continue after the procedure finishes.
call calculate
; execution continues here
; after calculate returns
calculate:
; procedure instructions
retCALL transfers execution to the procedure and preserves a return location. RET then uses the saved return information to continue execution after the original CALL.
The exact stack layout around a procedure depends on the architecture, calling convention, compiler, optimisation choices, and the procedure's own instructions. This is why understanding the stack is so important when debugging or analysing compiled programs.
STACK FRAMES
A stack frame provides a useful conceptual model for understanding local variables, saved registers, parameters, and return information associated with a procedure.
A traditional stack-frame model often uses RBP as a stable reference point and RSP to track the current top of the stack. Local variables and saved state can then be accessed at known offsets.
push rbp
mov rbp, rsp
; procedure body
mov rsp, rbp
pop rbp
retThis is a simplified educational model of a procedure prologue and epilogue. Modern compilers may omit the frame pointer or organise the stack differently, especially when optimisation is enabled.
Nevertheless, understanding this conventional model provides a strong foundation for reading assembly and examining stack frames in a debugger.
LOCAL DATA
Once a stack frame has been established, fixed offsets can be used to access data associated with the current procedure.
push rbp
mov rbp, rsp
sub rsp, 16
mov qword [rbp-8], 42
mov rax, [rbp-8]
mov rsp, rbp
pop rbp
retThe example reserves stack space and stores a value at an offset relative to RBP. The value is then loaded back into RAX.
The exact layout of a real compiled function can be more complicated, but the example demonstrates the fundamental relationship between a stack frame, an address, and a local memory location.
COMMON MISTAKES
Most difficulties come from mixing up values, addresses, operand sizes, and stack state.
Confusing MOV RAX, RBX with MOV RAX, [RBX] changes whether the instruction copies a register value or accesses memory.
Array addressing depends on element size. An incorrect scale can cause the program to access the wrong memory location.
Reading or writing the wrong number of bytes can corrupt data or produce results that do not match expectations.
Every stack operation changes the stack state. An incorrect push/pop sequence can make procedure returns and saved registers fail.
RBP is useful for learning stack frames, but optimised programs may use it differently or omit a traditional frame pointer altogether.
LEA calculates an effective address. It does not retrieve the value stored at that address.
PUTTING IT TOGETHER
The same concepts appear repeatedly in real programs: addresses identify data, registers hold working values, and the stack provides temporary storage and procedure state.
push rbp
mov rbp, rsp
sub rsp, 16
mov qword [rbp-8], 25
mov rax, [rbp-8]
add rax, 5
mov rsp, rbp
pop rbp
retThis simplified example combines several concepts from the guide. A stack frame is established, local space is reserved, a value is stored in memory, that value is loaded into a register, arithmetic is performed, and the stack frame is removed before returning.
Being able to trace each of these steps is a key milestone in understanding x86-64 assembly.
CONTINUE LEARNING
The remaining guides connect memory and stack concepts with registers, Linux system calls, and debugging.
Return to the main NASM resource for the broader x86-64 programming roadmap.
Open the complete NASM guide →Review general-purpose registers, flags, data movement, arithmetic, comparisons, jumps, and bitwise instructions.
Review registers and instructions →Learn how NASM programs interact with Linux and how tools can be used to inspect execution.
Explore system calls and debugging →ACADEMIC & TECHNICAL WORK
Memory addressing and stack management are fundamental topics in computer architecture, operating systems, systems programming, cybersecurity, and reverse engineering.
Academic work involving NASM may require students to explain how data is represented in memory, demonstrate addressing techniques, trace stack operations, analyse procedure calls, or document how local variables and parameters are handled.
These concepts also provide a foundation for understanding compiled programs and low-level behaviour during debugging and binary analysis.
ProjectAssignments provides structured technical and academic guidance for complex computing work, with an emphasis on understanding the concepts and reasoning behind the work.
Explore our technical academic services →Let's make your work clearer
Tell us what you're researching, building, or trying to understand. We'll help you find the clearest ethical next move.