Describing the Syntax of an Assembly Language Instruction

Describe the syntax of an Assembly language instruction

When I first tried reading x86 Assembly and ARM Assembly side by side, I got genuinely confused — the same operation looked completely different depending on which syntax convention I was looking at. It wasn’t until I broke instruction syntax down into its actual grammatical components — label, mnemonic, operands, and comment — that everything started clicking into a consistent pattern, regardless of which architecture or assembler I was reading.

Table of Contents

  • The General Structure of an Assembly Instruction
  • Breaking Down Each Component
  • Intel Syntax vs. AT&T Syntax
  • x86/x86-64 Syntax Examples
  • ARM Syntax Examples
  • Instruction Syntax Grammar (Mermaid Diagram)
  • Operand Order and Direction
  • Instruction Prefixes and Suffixes
  • Whitespace, Case Sensitivity, and Line Structure
  • Common Instruction Formats Table
  • Performance Considerations
  • Comparison: Syntax Styles Across Assemblers
  • Best Practices
  • Common Mistakes
  • FAQs
  • Summary and Key Takeaways
  • References

The General Structure of an Assembly Instruction

Nearly every Assembly instruction, regardless of architecture or assembler, follows this general template:

[label:]   mnemonic   [operand1[, operand2[, operand3]]]   [; comment]

Every part except the mnemonic is optional. A bare NOP is a complete, valid instruction on its own; a fully decorated line might have a label, a three-operand instruction, and a trailing comment all on the same line.

Breaking Down Each Component

ComponentRoleOptional?
LabelNames the address of this instructionYes
MnemonicThe operation to performNo — required
Operand(s)Data or references the operation acts onDepends on the instruction
CommentHuman-readable explanation, ignored by assemblerYes
loop_start:   ADD   EAX, EBX, 4      ; label, mnemonic, operands, comment

(Note: x86 ADD only takes two operands; this illustrative line is closer to how a three-operand ARM instruction would look. Real x86 syntax is shown in the next section.)

Intel Syntax vs. AT&T Syntax

x86 Assembly has two competing syntax conventions, and confusing them is one of the most common early mistakes.

FeatureIntel Syntax (NASM, MASM)AT&T Syntax (GAS, default on Linux)
Operand orderdest, srcsrc, dest
Register prefixNone (EAX)% prefix (%eax)
Immediate prefixNone (5)$ prefix ($5)
Memory operand[EBX+4]4(%ebx)
Instruction size suffixImplicit from operand sizeExplicit suffix: b, w, l, q

Intel syntax example:

MOV EAX, [EBX+4]

Equivalent AT&T syntax:

movl 4(%ebx), %eax

Both lines do exactly the same thing: load the 32-bit value at address EBX + 4 into EAX. The direction of data flow is just written in opposite order.

x86/x86-64 Syntax Examples

; NASM (Intel syntax)
start:
    MOV     EAX, 10          ; two operands: dest, src
    ADD     EAX, EBX          ; register-to-register
    MOV     [result], EAX     ; register-to-memory (direct addressing)
    CMP     EAX, 0             ; comparison, sets flags
    JE      end_label           ; single operand: jump target
    NOP                          ; zero operands
end_label:
    RET
# GAS (AT&T syntax)
start:
    movl    $10, %eax        # source first, destination second
    addl    %ebx, %eax
    movl    %eax, result
    cmpl    $0, %eax
    je      end_label
    nop
end_label:
    ret

ARM Syntax Examples

ARM syntax (used consistently by both the GNU and ARM’s own toolchains, unlike x86’s split) generally follows a uniform format:

[label:]   mnemonic{cond}{S}   Rd, Rn, Operand2   [; comment]

Where {cond} is an optional condition code (like EQ, NE, GT), and {S} optionally indicates the instruction should update the status flags.

loop:
    ADDS    R0, R1, R2        @ R0 = R1 + R2, updates flags (S suffix)
    CMP     R0, #10
    BLE     loop                @ Branch if Less than or Equal
    MOVEQ   R3, #1                @ Conditional MOV: only if EQ flag is set

Here, ADDS combines the mnemonic ADD with the S suffix (update flags), and MOVEQ combines MOV with the EQ condition code — a distinctly ARM feature where nearly every instruction can be conditionally executed.

Instruction Syntax Grammar

flowchart TD
    A[Start of Line] --> B{Optional Label?}
    B -->|Yes| C[label:]
    B -->|No| D[Mnemonic]
    C --> D
    D --> E{Operands Required?}
    E -->|Zero| F[No Operand Field]
    E -->|One| G[Single Operand]
    E -->|Two or Three| H[Comma-Separated Operand List]
    F --> I{Trailing Comment?}
    G --> I
    H --> I
    I -->|Yes| J[; or # or @ Comment Text]
    I -->|No| K[End of Line]
    J --> K

Operand Order and Direction

This is the single most common source of confusion when switching between syntaxes:

SyntaxOrderExampleMeaning
Inteldestination, sourceMOV EAX, EBXEBX’s value goes into EAX
AT&Tsource, destinationmovl %ebx, %eaxSame operation, reversed order
ARMdestination, source(s)ADD R0, R1, R2R0 = R1 + R2

Instruction Prefixes and Suffixes

  • x86 prefixes: LOCK (atomic bus lock), REP/REPE/REPNE (string instruction repetition), segment override prefixes (CS:, DS:).
  • x86-64 REX prefix: enables access to 64-bit registers and the extended register set (R8R15).
  • AT&T size suffixes: b (byte), w (word), l (long/32-bit), q (quad/64-bit) — appended to the mnemonic since AT&T syntax doesn’t always infer size from operands.
  • ARM condition codes: EQ, NE, GT, LT, GE, LE, etc., appended directly to the mnemonic to make execution conditional.
  • ARM S suffix: appended to update the condition flags as a side effect of the instruction.

Whitespace, Case Sensitivity, and Line Structure

  • Whitespace (spaces/tabs) between fields is generally flexible — most assemblers don’t care about exact alignment, though consistent indentation greatly improves readability.
  • Case sensitivity varies: NASM and GAS are case-sensitive for labels by default but often case-insensitive for mnemonics; always check your specific assembler’s documentation.
  • Each instruction typically occupies one line, though line-continuation and multi-instruction-per-line syntax (separated by ; in some assemblers, though this conflicts with NASM’s comment character) exist in select toolchains.

Common Instruction Formats Table

FormatExample (x86 Intel)Example (ARM)
Zero operandNOPNOP
One operandINC EAXPUSH {R0}
Two operandMOV EAX, EBX— (rare, ARM favors three-operand)
Three operand— (rare on classic x86)ADD R0, R1, R2
Memory operandMOV EAX, [EBX+4]LDR R0, [R1, #4]
Immediate operandMOV EAX, 5MOV R0, #5

Performance Considerations

Syntax choice itself (Intel vs. AT&T, for instance) has no effect on performance — both compile down to identical machine code for the same logical instruction, since syntax is purely a textual representation chosen by the assembler front-end. What does matter for performance is the actual instruction and operand types chosen (register vs. memory operand, immediate vs. computed value), which is a semantic decision independent of which syntax convention you’re reading or writing in.

Comparison: Syntax Styles Across Assemblers

AssemblerDefault SyntaxOperand OrderRegister Notation
NASMInteldest, srcEAX
MASMInteldest, srcEAX
GAS (default)AT&Tsrc, dest%eax
GAS (.intel_syntax noprefix)Inteldest, srceax
ARM as/armasmARM (unified)dest, src(s)R0

A Complete Annotated Program

Seeing the full instruction grammar applied across an entire small program makes the pattern much easier to internalize. Here’s a complete NASM program that prints “Hello, World!” on Linux x86-64, with every line following the [label:] mnemonic [operands] [; comment] template:

SECTION .data
msg     DB "Hello, World!", 10       ; string literal + newline
msg_len EQU $ - msg                   ; EQU: computed constant, not an instruction

SECTION .text
GLOBAL  _start                         ; directive, not an instruction — no operand comma needed

_start:                                ; label with no instruction on the same line
    MOV     RAX, 1                     ; syscall number for sys_write
    MOV     RDI, 1                     ; file descriptor: stdout
    MOV     RSI, msg                    ; pointer to the string
    MOV     RDX, msg_len                 ; length of the string
    SYSCALL                              ; zero-operand instruction

    MOV     RAX, 60                     ; syscall number for sys_exit
    XOR     RDI, RDI                     ; exit code 0 (XOR is a common idiom for "set to zero")
    SYSCALL

Every non-blank, non-directive line here fits the grammar exactly: an optional label, a required mnemonic, zero to three operands, and an optional trailing comment.

Pseudo-Instructions That Look Like Real Instructions

Some “instructions” you’ll encounter aren’t real opcodes at all — they’re assembler shorthand that expands into one or more actual instructions, similar in spirit to macros but built into the assembler itself.

Pseudo-InstructionArchitectureExpands To
LDR Rd, =valueARMLoads a 32-bit constant via a literal pool when it can’t fit in a normal immediate field
PUSH/POP (with multiple registers)ARM{R0-R3} expands into multiple internal store/load operations
MOVZX/MOVSXx86Zero/sign-extending move, a genuine opcode but often explained as “MOV plus extension”
CALL/RETx86Genuine opcodes, but conceptually “PUSH address + JMP” and “POP address + JMP” respectively

Recognizing these helps when reading architecture reference manuals, since they’ll sometimes explicitly note “this is a pseudo-instruction” rather than a true single-opcode operation — useful context when you’re trying to predict exactly how many bytes or cycles something will cost.

How Assemblers Report Syntax Errors

Understanding the grammar also makes assembler error messages far easier to interpret. A typical NASM error like:

error: parser: instruction expected

almost always means the assembler expected a mnemonic where it found something else — often a misplaced comma, a missing colon after a label, or a stray operand left over from editing. Recognizing which grammatical slot (label, mnemonic, operand, comment) the parser was in when it failed makes debugging syntax errors dramatically faster than guessing.

Syntax Consistency Across a Growing Codebase

As an Assembly project grows past a single file, syntax consistency stops being a stylistic preference and starts being a practical necessity. A shared style guide — even an informal one — covering indentation width, capitalization of mnemonics and registers, comment placement, and label naming pays off enormously once more than one file (or more than one contributor) is involved. I’ve found it useful to settle these choices explicitly at the very start of a project rather than letting each file drift into its own conventions, since reformatting Assembly retroactively is far more error-prone than reformatting a high-level language, precisely because whitespace and alignment carry no semantic meaning the assembler will catch if you get it wrong — an accidentally deleted operand or misplaced comma can silently change behavior rather than throwing an obvious error.

Best Practices

  • Pick one syntax convention per project and stay consistent — mixing Intel and AT&T syntax in the same codebase invites confusion and bugs.
  • When reading unfamiliar Assembly, first identify the syntax style (look for % register prefixes as a quick AT&T indicator) before interpreting operand order.
  • Use consistent indentation for mnemonics and operands to keep large files readable.
  • Comment non-obvious operand choices, especially when register reuse makes intent unclear.

Converting Between Syntaxes in Practice

Because Intel and AT&T syntax coexist in real-world tooling, it’s worth knowing how to move between them without introducing bugs. GDB, by default on Linux, disassembles in AT&T syntax, but this can be switched:

(gdb) set disassembly-flavor intel
(gdb) disassemble

Similarly, objdump supports a -M intel flag:

objdump -d -M intel program.o

I switch to Intel-flavor output constantly when debugging, simply because I find mov eax, [ebx+4] easier to reason about at a glance than movl 4(%ebx), %eax — but knowing both is essential, since a lot of existing Linux tooling, inline assembly in C (asm volatile(...)), and GCC-generated .s files default to AT&T syntax.

Inline Assembly Syntax: A Related but Distinct Grammar

It’s worth flagging that when Assembly is embedded inside a C/C++ file via GCC’s extended inline assembly, there’s an additional layer of syntax on top of the underlying AT&T grammar — output/input operand constraints and clobber lists:

int result;
__asm__ (
    "addl %%ebx, %%eax"
    : "=a" (result)      // output operand
    : "a" (10), "b" (20) // input operands
    :                      // clobbered registers
);

This isn’t a different Assembly instruction syntax so much as a C-level wrapper describing how C variables map onto the registers referenced inside the Assembly string — but it’s a common source of confusion for people learning standalone Assembly syntax first and inline Assembly second, since the underlying addl %ebx, %eax line is still ordinary AT&T syntax once you strip away the surrounding C constraint syntax.

Common Mistakes

  • Assuming operand order is universal — swapping source and destination is one of the most common bugs when porting code between Intel and AT&T syntax.
  • Forgetting AT&T’s % register prefix and $ immediate prefix, causing assembly errors.
  • Missing size suffixes in AT&T syntax (movl vs. mov) when the operand size can’t be inferred.
  • Misreading ARM condition codes and S suffixes as separate instructions rather than modifiers on the base mnemonic.

The Grammar Is Smaller Than It Looks

Looking back at everything covered here, I think the most reassuring realization for anyone new to Assembly is that the actual grammar — label, mnemonic, operands, comment — is genuinely small. What makes Assembly feel complicated at first isn’t the sentence structure; it’s the sheer number of mnemonics, operand combinations, and architecture-specific quirks layered on top of that simple four-part template. Once the underlying grammar stops requiring conscious thought, reading unfamiliar Assembly — even in a syntax or architecture you haven’t used before — becomes a matter of looking up unfamiliar mnemonics rather than re-learning how to parse a line at all.

FAQs

Which syntax should I learn first, Intel or AT&T? Intel syntax (via NASM) is generally considered more approachable for beginners due to its more intuitive dest, src order and cleaner memory operand notation; AT&T is worth learning afterward since it’s the Linux/GCC toolchain default.

Does ARM have an equivalent Intel/AT&T split? Not really — ARM assembly syntax is largely unified across GNU and ARM’s own toolchains, though minor directive differences exist.

Can I mix syntaxes in one file? Generally no, though GAS supports a .intel_syntax noprefix directive to switch modes within a single file if truly necessary.

Why do ARM instructions often have three operands while x86 mostly has two? This reflects ARM’s RISC “load-store” design philosophy, where the destination register can differ from either source operand without needing an extra MOV, unlike x86’s traditional two-operand, destructive-operation style.

Summary and Key Takeaways

  • Every Assembly instruction generally follows: [label:] mnemonic [operands] [; comment].
  • x86 has two competing syntaxes — Intel (dest, src) and AT&T (src, dest) — that produce identical machine code but read very differently.
  • ARM syntax is largely unified, favors a three-operand format, and supports rich condition codes and flag-update suffixes directly on the mnemonic.
  • Syntax choice is a textual convention with zero performance impact; what matters for performance is the underlying instruction and operand type chosen.
  • Recognizing these structural patterns makes it far easier to read, write, and port Assembly code across different tools and architectures.

References

  • Intel® 64 and IA-32 Architectures Software Developer’s Manual — intel.com/sdm
  • NASM Documentation — nasm.us/doc
  • GNU Assembler (GAS) Manual — sourceware.org/binutils/docs/as
  • ARM Architecture Reference Manual — developer.arm.com/documentation
Total
0
Shares

Leave a Reply

Previous Post
Differentiate between high-level languages and Assembly language

High-Level Languages vs Assembly Language: A Complete Comparison for Programmers

Next Post
What is the purpose of comments in Assembly code

What Is the Purpose of Comments in Assembly Code?

Related Posts