Chapter 6 Key Takeaways: The NASM Assembler

The 12 Most Important Points from This Chapter


1. NASM uses Intel syntax: destination first, no register sigils, brackets for memory. mov rax, rbx copies rbx into rax. mov rax, [rbx] loads from the address in rbx into rax. mov [rbx], rax stores rax at the address in rbx. This is the opposite operand order from GAS/AT&T syntax, which puts the source first. Intel syntax is what NASM, MASM, and Windows assembly use; AT&T syntax is what GCC uses by default.


2. Sections separate code, read-only data, initialized data, and BSS. .text for executable instructions, .rodata for constants and strings (read-only), .data for initialized globals (read-write), .bss for zero-initialized globals (no space in ELF file). Switch sections with section .name. The linker combines same-named sections from multiple object files.


3. Labels are addresses. Global labels are visible to the linker; dot-labels are local. my_func: is a global-scope label; declare global my_func to make it callable from other files. .loop: is a local label, scoped to the enclosing non-dot label. Reuse .loop:, .done:, .start: freely in every function without conflicts.


4. db/dw/dd/dq define bytes/words/dwords/qwords; resb/resw/resd/resq reserve BSS space. The "define" directives emit bytes into the current section (use in .data or .rodata). The "reserve" directives allocate space in .bss. Mixing resb into .data works but generates a NASM warning; always use resb in .bss.


**5. $ is the current assembly position; $$` is the current section start.** `msglen equ $ - msg` computes the number of bytes defined since `msg`. The padding idiom `times (N - ($ - $$) % N) % N db 0 pads to the next N-byte boundary within the section. These are compile-time calculations — zero runtime overhead.


6. EQU creates compile-time numeric constants; %define creates textual substitutions. SYS_WRITE equ 1 defines a numeric constant usable anywhere. %define NEWLINE 10 creates a textual 10 that substitutes wherever NEWLINE appears. EQU can reference positions ($) and labels; %define is preprocessor text replacement. Both produce no runtime code or data.


7. %macro name N ... %endmacro defines N-argument macros; %%label prevents label conflicts. Multi-line macros are invoked like name arg1, arg2. %1, %2, etc. refer to the arguments. %%loop inside a macro creates a unique label for each invocation, preventing "multiply defined symbol" errors when a macro is used more than once. Variable-argument macros use %0 (count) and %{0} (all arguments).


8. %if, %ifdef, %ifndef, %elif, %else, %endif enable conditional assembly. Code between %ifdef DEBUG and %endif is only assembled if -DDEBUG was passed to NASM on the command line. This is the mechanism for debug/release builds, platform-specific code, and feature flags. %error "message" generates an assembly error in the false branch of a condition — useful for requiring certain defines.


9. %include "file.inc" pastes the file's content textually at that point. %include is preprocessor text inclusion, not compilation of a separate module. The included file's macros, constants, and data declarations become part of the current file. For separate compilation (object files linked together), use global/extern and ld — that's a different mechanism from %include.


10. nasm -f bin produces flat binary output — no ELF headers. For bootloaders, firmware, and raw machine code, nasm -f bin produces just the bytes. Combined with ORG 0x7C00 in the source, the assembler resolves addresses as if the code loads at 0x7C00. The MinOS bootloader uses this format: 512 bytes of raw code that the BIOS loads at 0x7C00.


11. AT&T syntax (GCC/GAS default) uses % on registers, $ on immediates, and source-before-destination. movq %rbx, %rax in AT&T means mov rax, rbx in Intel (copy rbx to rax). cmpq %rcx, %rax computes rax - rcx (the right operand is the destination for comparison purposes). Add -M intel to objdump and set disassembly-flavor intel in .gdbinit to read Intel syntax everywhere.


12. Common NASM errors and their causes: - "operation size not specified": add QWORD/DWORD/WORD/BYTE to ambiguous memory operands - "symbol is multiply defined": use .local labels instead of global ones in functions - "invalid combination of opcode and operands": memory-to-memory move doesn't exist; wrong operand types - "times count must be a constant": can't use runtime variables in times — it's assembled at build time - "uninitialised space declared in non-BSS section": use resb/resw/resd/resq only in .bss


Visual Summary: Data Declaration Quick Reference

Section     NASM              Size    Description
─────────────────────────────────────────────────────────
.data/.rodata
            db value          1 byte  Byte(s), char literals, strings
            dw value          2 bytes Little-endian 16-bit
            dd value          4 bytes Little-endian 32-bit, or float
            dq value          8 bytes Little-endian 64-bit, or double
            times N db 0      N bytes Repeated initialization

.bss
            resb N            N bytes Reserve N bytes (zero at runtime)
            resw N            2N bytes Reserve N words
            resd N            4N bytes Reserve N dwords
            resq N            8N bytes Reserve N qwords

.text
            (assembly instructions only — no data declarations here)

Anywhere (no section):
            name equ value    0 bytes Compile-time constant (no memory)

Visual Summary: NASM vs AT&T Syntax at a Glance

Operation              NASM (Intel)              GAS (AT&T)
─────────────────────────────────────────────────────────────
Copy rbx to rax        mov rax, rbx              movq %rbx, %rax
Load from [rdi] to rax mov rax, [rdi]            movq (%rdi), %rax
Store rax to [rdi]     mov [rdi], rax            movq %rax, (%rdi)
Load immediate 42      mov rax, 42               movq $42, %rax
Array: rax = arr[i*8]  mov rax, [rdi + rsi*8]   movq (%rdi,%rsi,8), %rax
Compare rax with rbx   cmp rax, rbx              cmpq %rbx, %rax
                       (flags = rax - rbx)        (flags = rax - rbx)
Add 8 to RSP           add rsp, 8                addq $8, %rsp