100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Data Movement Instructions (MOV, PUSH, POP)

Learn how MOV, PUSH, POP, XCHG, and LEA move data between registers and memory in x86 assembly without performing arithmetic.

Instructions & DataBeginner9 min readJul 10, 2026
Analogies

Data Movement Instructions (MOV, PUSH, POP)

MOV is the most frequently used x86 instruction: it copies a value between two registers, between a register and memory, or an immediate constant into a register or memory location. Critically, MOV never touches the FLAGS register — no matter what value is copied, the Zero, Carry, Sign, and Overflow flags remain exactly as they were before the instruction executed. This makes MOV purely a data-transfer instruction, distinct from arithmetic instructions like ADD or SUB which both compute a result and update flags.

🏏

Cricket analogy: Like a fielder relaying the ball from deep third man to the wicketkeeper without altering its speed or spin, MOV simply relocates a value from register to register or memory without touching any arithmetic flags, such as the Zero or Carry flag.

The MOV Instruction and Addressing Modes

Despite its flexibility, MOV has one strict limitation: it can never move data directly between two memory operands. If you need to copy a value from one memory address to another, you must first load it into a register with MOV reg, [mem1] and then store it with MOV [mem2], reg. Operand sizes must also match exactly — MOV al, ebx is illegal because a 1-byte destination cannot receive a 4-byte source without an explicit narrowing instruction, and the assembler will reject it at compile time.

🏏

Cricket analogy: A captain can't simultaneously field at both slip and gully, just as x86 forbids MOV [mem1], [mem2] — you need an intermediate register, much like a fielder must relay the ball through a second player to cover two positions.

nasm
; MOV, PUSH, POP demonstration
section .data
    num1 dd 10
    num2 dd 20

section .text
global _start
_start:
    mov eax, [num1]      ; eax = 10 (load from memory)
    mov ebx, [num2]      ; ebx = 20
    mov ecx, eax          ; ecx = eax (register to register)
    mov [num1], ebx       ; store ebx back into num1

    push eax               ; save eax on the stack
    push ebx               ; save ebx on the stack
    pop  ecx                ; ecx = ebx (most recently pushed)
    pop  edx                ; edx = eax

    mov eax, 1
    int 0x80

PUSH, POP, and the Stack Pointer

PUSH and POP manage the call stack using the ESP register as a pointer to its current top. PUSH first decrements ESP by the operand's size (4 bytes for a 32-bit register) and then stores the value at the new address; POP reads the value at [ESP] first and then increments ESP by the same amount. Because the stack is last-in-first-out, the most recently pushed value is always the first one popped, which is why saving and restoring registers must use PUSH and POP in exactly reversed order.

🏏

Cricket analogy: Each time a batsman is dismissed, the scoreboard's wicket count is decremented in reverse order of dismissal when reviewing the innings, similar to how PUSH decrements ESP before storing a value and POP retrieves it before incrementing ESP back up.

PUSH and POP always operate on the full width of the general-purpose register in use (16 bits with PUSH ax, 32 bits with PUSH eax, or 64 bits with PUSH rax) -- the stack pointer moves by exactly that many bytes, so mixing widths mid-routine misaligns ESP.

XCHG, LEA, and Sign/Zero Extension

Beyond MOV, several related instructions handle special transfer cases. XCHG atomically swaps the contents of two operands in a single instruction, which is why it's used with the LOCK prefix for simple synchronization primitives. LEA (Load Effective Address) computes an address expression like [ebx+ecx*4] without dereferencing memory, distinguishing it from MOV eax, [ebx+ecx*4] which would actually read from that address. MOVZX and MOVSX widen smaller operands into larger registers, zero-extending or sign-extending respectively so signed negative values don't get misread as large positive numbers.

🏏

Cricket analogy: Swapping two batsmen's positions on strike after a single run mirrors XCHG eax, ebx swapping the contents of two registers atomically in one instruction.

MOV never has two memory operands and never moves an immediate directly into a segment register on most assemblers; more importantly, every PUSH must be matched by exactly one POP (or an equivalent ADD esp, N) before RET executes, or the CPU will pop a corrupted address into EIP and jump to garbage, crashing the program.

  • MOV copies data between registers, memory, and immediates but never directly between two memory operands.
  • MOV does not affect the FLAGS register, unlike arithmetic instructions such as ADD or SUB.
  • PUSH decrements ESP by the operand size and then stores the value; POP loads the value first and then increments ESP.
  • The stack follows a last-in-first-out (LIFO) discipline, so the most recently PUSHed value is the first one POPped.
  • LEA computes an effective address without accessing memory, while MOV [reg] actually dereferences it.
  • MOVZX and MOVSX extend smaller operands into larger registers with zero-extension or sign-extension respectively.
  • Unbalanced PUSH/POP pairs corrupt ESP and, at function boundaries, can overwrite the return address, causing a crash on RET.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#AssemblyLanguageStudyNotes#DataMovementInstructionsMOVPUSHPOP#Data#Movement#Instructions#MOV#StudyNotes#SkillVeris#ExamPrep