I’ll be honest — the first time I looked back at Assembly code I’d written just two weeks earlier, I couldn’t remember what half of it did. Register names don’t explain intent, and a raw sequence of MOV, CMP, and JNZ instructions tells you what the CPU is doing but never why. That’s exactly the gap comments are meant to fill, and in Assembly — more than almost any other language — comments aren’t optional polish. They’re often the difference between maintainable code and a write-only mess.
Table of Contents
- What Are Comments in Assembly?
- Why Comments Matter More in Assembly Than in High-Level Languages
- Comment Syntax Across Assemblers
- Types of Comments and When to Use Them
- x86/x86-64 Comment Examples
- ARM Comment Examples
- How the Assembler Handles Comments (Mermaid Diagram)
- Comments and the Assembly Process
- Comments in Debugging and Reverse Engineering
- Performance Considerations
- Comparison: Good vs. Bad Commenting Practices
- Best Practices
- Common Mistakes
- FAQs
- Summary and Key Takeaways
- References
What Are Comments in Assembly?
A comment is text in the source file that the assembler completely ignores — it exists purely for human readers. Comments carry no runtime effect whatsoever; once assembled, all trace of a comment is gone from the resulting machine code.
MOV EAX, 5 ; initialize counter to 5
Everything from the ; to the end of the line is a comment on x86 assemblers like NASM and MASM — the assembler simply skips over it during tokenization.
Why Comments Matter More in Assembly Than in High-Level Languages
In a high-level language, a line like total = price * quantity; is nearly self-explanatory. In Assembly, the equivalent might be:
MOV EAX, [price]
IMUL EAX, [quantity]
MOV [total], EAX
Nothing about MOV, IMUL, or register names inherently conveys business logic. Assembly operates at the level of registers, flags, and raw memory — there’s no structural hint (like meaningful function or variable names in a typical high-level codebase) baked into the syntax itself unless you deliberately choose good labels and comments. This is precisely why comments are disproportionately important here: they bridge the semantic gap between “what the CPU does” and “what the programmer intended.”
Comment Syntax Across Assemblers
| Assembler | Single-line Comment | Block Comment |
|---|---|---|
| NASM | ; comment | Not natively supported (use ; per line) |
| MASM | ; comment | COMMENT directive with custom delimiter |
| GAS (x86) | # comment or // comment | /* comment */ |
ARM (GNU as) | @ comment or // comment | /* comment */ |
Types of Comments and When to Use Them
- Explanatory comments — describe why something is done, not just what (the instruction already shows what).
- Section header comments — mark logical divisions (initialization, main loop, cleanup).
- Register usage comments — document what each register currently holds, since Assembly has no variable names to rely on.
- Algorithm/context comments — explain the broader logic a block of instructions implements (e.g., “Bresenham’s line algorithm — see reference”).
- Warning/TODO comments — flag known limitations, edge cases, or unfinished work.
x86/x86-64 Comment Examples
; ===========================================
; Function: sum_array
; Purpose: Adds up all integers in an array
; Input: ESI = pointer to array, ECX = count
; Output: EAX = sum
; ===========================================
sum_array:
XOR EAX, EAX ; EAX will hold the running sum
.loop:
ADD EAX, [ESI] ; add current element to sum
ADD ESI, 4 ; move pointer to next 32-bit element
LOOP .loop ; decrement ECX, loop while ECX != 0
RET
In GAS (AT&T) syntax:
# Function: sum_array
# ESI holds the array pointer, ECX the element count
sum_array:
xor %eax, %eax # clear running sum
.loop:
add (%esi), %eax # accumulate element
add $4, %esi # advance pointer
loop .loop
ret
ARM Comment Examples
@ Function: sum_array
@ R0 = array pointer, R1 = count, returns sum in R0
sum_array:
MOV R2, #0 @ running sum
loop:
LDR R3, [R0], #4 @ load element, post-increment pointer
ADD R2, R2, R3 @ accumulate
SUBS R1, R1, #1 @ decrement counter, update flags
BNE loop
MOV R0, R2 @ move result into return register
BX LR
How the Assembler Handles Comments
flowchart TD
A[Raw Source Line] --> B[Lexer / Tokenizer Scans Line]
B --> C{Comment Delimiter Found?}
C -->|Yes| D[Discard Remainder of Line]
C -->|No| E[Tokenize as Instruction/Directive/Label]
D --> F[Continue to Next Line]
E --> F
F --> G[Proceed with Assembly: Symbol Table, Encoding, etc.]
Comments and the Assembly Process
Comments are stripped during lexical analysis, before the assembler even attempts to identify instructions, directives, or labels. This means comments have zero influence on the symbol table, location counter, or generated machine code — they are purely a source-level, human-facing artifact removed at the very first stage of processing.
Comments in Debugging and Reverse Engineering
- Well-placed comments dramatically reduce the time needed to debug a crash, since you can immediately see the intended behavior of a block versus its actual behavior.
- When reverse-engineering unfamiliar binaries (with no original comments available), analysts often reconstruct comments themselves in disassemblers like IDA Pro or Ghidra, annotating register usage and function purpose as they go — proving just how critical this kind of documentation is for anyone reading Assembly, including its original author months later.
- Comments referencing the algorithm or external documentation (RFCs, datasheets, CPU manuals) are enormously valuable when working with hardware-specific or protocol-specific Assembly code.
Performance Considerations
Comments have absolutely zero runtime cost. They’re removed entirely before instructions are encoded, so there is no trade-off between “well-commented” and “fast” code — none whatsoever. The only “cost” is a marginal increase in source file size and, at most, a fraction of a second of extra assembler parsing time, which is negligible even for huge codebases.
Comparison: Good vs. Bad Commenting Practices
| Bad Commenting | Good Commenting |
|---|---|
MOV EAX, EBX ; move EBX into EAX (restates the obvious) | MOV EAX, EBX ; EAX now holds the frame pointer for cleanup (explains intent) |
| No comments on register usage in a long function | Comment block at function start listing what each register holds |
| Outdated comment that no longer matches the code | Comments updated alongside every code change |
| One giant comment block with no per-line context | Mix of section headers and targeted inline comments |
Documentation-Style Comment Blocks
For any function of real significance, I’ve found it worth adopting a consistent header format — something close to what you’d see in professional driver or kernel code:
;-------------------------------------------------------------------
; Function: binary_search
; Description: Performs iterative binary search on a sorted array
; Input:
; ESI = pointer to sorted array of 32-bit integers
; ECX = number of elements
; EDX = target value to search for
; Output:
; EAX = index of target if found, -1 if not found
; Clobbers:
; EBX, EDI (caller must save if needed)
; Notes:
; Array must be sorted ascending; behavior undefined otherwise
;-------------------------------------------------------------------
binary_search:
; ... implementation ...
RET
This kind of header pays for itself many times over the life of a project. Six months later, I don’t need to trace through the entire function body just to remember which registers are safe to reuse after calling it — the comment tells me immediately.
Comments vs. Self-Documenting Code: A False Dichotomy in Assembly
In high-level languages, there’s an ongoing debate about whether well-named variables and functions make comments redundant (“self-documenting code”). In Assembly, this debate mostly doesn’t apply the same way, because the language itself offers very little semantic room for self-documentation beyond label names. A label like validate_checksum: helps, but it can’t convey argument types, register conventions, edge-case handling, or algorithmic complexity the way a well-named function signature in C or Python can. Comments aren’t competing with expressive syntax in Assembly — they’re often the only available channel for that information.
Comments as a Debugging Aid During Development
Beyond long-term documentation, comments are genuinely useful as scratch notes while actively developing and debugging Assembly:
MOV EAX, [ECX+8] ; TEMP: verify this offset once struct layout is confirmed
; TODO: handle the case where ECX is NULL
CMP EAX, 0
JE .skip
TODO/FIXME/TEMP style tags are just as useful in Assembly as in any other language, and searching a codebase for these tags before a release is a quick way to catch unfinished work. Since these are ordinary comments with no special assembler meaning, they cost nothing and integrate naturally into any existing comment style.
Commenting Data Structures and Memory Layouts
Comments earn their keep especially heavily around hand-laid-out data structures, where there’s no compiler to enforce or display field boundaries for you:
SECTION .data
; struct Player { uint32 health; uint32 mana; char name[16]; } -- total size: 24 bytes
player:
DD 100 ; offset 0: health
DD 50 ; offset 4: mana
DB "Hero", 0 ; offset 8: name (16-byte field, null-terminated)
TIMES 11 DB 0 ; padding to fill the fixed-size name field
Without the leading comment describing the equivalent high-level struct and per-field offsets, this block is nearly unreadable to anyone who didn’t design it — and even the original author will likely forget the exact byte layout within weeks. This is a case where a comment isn’t just helpful, it’s functionally the only documentation of the data format that exists anywhere in the codebase.
Best Practices
- Comment why, not just what — the instruction mnemonic already tells the reader what operation runs.
- Document register usage at the start of every non-trivial function, since Assembly has no named variables.
- Keep comments updated when you change the code; a wrong comment is often worse than no comment.
- Use section headers for large files to visually separate initialization, main logic, and cleanup.
- Reference external documentation (datasheets, RFCs, CPU manuals) when implementing something non-obvious like a hardware register write or a cryptographic primitive.
Comments in Open-Source and Collaborative Assembly Projects
When Assembly code is shared publicly or worked on by more than one person, comments take on an even more important role, because they’re often the only way a new contributor can understand design intent without a lengthy walkthrough from the original author. Well-known open-source projects with hand-written Assembly — parts of the Linux kernel, FFmpeg’s hand-optimized SIMD routines, various cryptographic libraries — tend to have unusually dense, careful commenting exactly because the code has to survive review from people who didn’t write it and don’t have the author’s mental context. If you look at FFmpeg’s assembly-optimized codecs, you’ll typically find comments explaining not just what a block does, but why a particular instruction sequence was chosen over an obvious alternative — often referencing specific CPU microarchitecture quirks that justify a non-obvious ordering of instructions.
A Practical Commenting Checklist
Before committing a nontrivial block of Assembly, I run through a short mental checklist:
- Does every non-trivial function have a header explaining inputs, outputs, and clobbered registers?
- Are there comments on any instruction whose purpose isn’t obvious from the mnemonic alone (bit tricks, magic constants, non-standard calling conventions)?
- Have I removed or updated comments that no longer match the current code after refactoring?
- Are section headers present in longer files to mark logical divisions?
- Have I flagged any incomplete or fragile logic with
TODO/FIXMEso it’s not mistaken for finished, trustworthy code?
This is a small habit, but consistently applying it is what turns a working prototype into Assembly code that other people (including a future version of yourself) can actually maintain.
Common Mistakes
- Letting comments go stale after refactoring, so they actively mislead readers.
- Over-commenting trivial, self-evident instructions while leaving complex logic completely undocumented.
- Using comments to explain bad code instead of simply fixing the code and writing a comment on the design decision behind the good version.
- Forgetting that comment syntax differs between assemblers (
;in NASM/MASM,#///in GAS,@///in ARM’s GNU assembler) when porting code.
The Real Cost-Benefit of Commenting
It’s worth stating plainly: there is no scenario in Assembly where skipping a comment saves you anything meaningful. It doesn’t save execution time, binary size, or even much typing time compared to the hours a missing comment can cost you (or a teammate) later when tracing a bug. I’ve come to treat under-commented Assembly the same way I’d treat undocumented API endpoints — technically functional, but a liability the moment anyone other than the original author needs to touch it, and often a liability even for the original author a few weeks later. Given that the “cost” of a comment is effectively zero at every level that matters — runtime, binary size, or assembly time — treating comments as optional polish rather than a core part of writing correct, maintainable Assembly is, in my experience, one of the most common and most avoidable mistakes beginners make.
Comments and Code Review
When Assembly code goes through peer review — whether on a team or in an open-source pull request — comments often become the actual subject of the review discussion as much as the instructions themselves. A reviewer unfamiliar with a specific optimization trick will frequently ask “why this instead of the obvious approach?”, and a good preemptive comment answers that question before it’s even asked, saving a review round-trip. I’ve noticed that Assembly pull requests with strong commenting tend to get approved faster precisely because the reviewer doesn’t have to reconstruct the author’s reasoning from scratch — the comment already did that work.
FAQs
Do comments affect the size of the compiled binary? No. Comments are completely stripped before machine code is generated and have zero effect on binary size or content.
Can comments span multiple lines? In NASM and MASM, no native block-comment syntax exists (each line needs its own ;), though MASM supports a COMMENT directive with a custom delimiter. GAS and ARM’s GNU assembler support C-style /* ... */ block comments.
Is heavy commenting a sign of bad Assembly code? No — quite the opposite. Given Assembly’s low-level nature, thorough commenting is generally considered a sign of professional, maintainable code, not a crutch for poor design.
Should I comment every single line? Not necessarily every line, but every non-obvious one — especially anything involving register reuse, flag-dependent branches, or non-standard calling conventions.
Summary and Key Takeaways
- Comments in Assembly exist purely for human readers and are completely discarded during lexical analysis — they have zero runtime cost.
- Because Assembly lacks the built-in semantic clarity of high-level languages (no named variables, no structured control flow keywords), comments are disproportionately valuable.
- Good commenting practice documents intent, register usage, and algorithmic context — not just a restatement of the instruction.
- Comment syntax varies across assemblers:
;(NASM/MASM),#/////* */(GAS),@/////* */(ARM GNU assembler). - Comments are essential for debugging, maintenance, and reverse engineering, making them one of the most cost-free investments you can make in Assembly code quality.
References
- NASM Documentation — nasm.us/doc
- GNU Assembler (GAS) Manual — sourceware.org/binutils/docs/as
- ARM Assembler Reference Guide — developer.arm.com/documentation
- Intel® 64 and IA-32 Architectures Software Developer’s Manual — intel.com/sdm