Core NASM Instructions: Moves, Arithmetic, Branching, and String Operations
Data Movement
mov
Moves data between registers and/or memory.
; register to register
a mov eax, ebx
; memory to register (base + displacement)
mov edx, [ecx + 8]
; register to memory
mov [edi + 4], eax
- Only one operand may be a memory location (memory-to-memory moves are not allowed with
mov).
movsx / movzx
Extend a smaller operand to a larger size.
- movsx: sign-extend the source into the destination
- movzx: zero-extend the source into the destination
; sign-extend 16-bit to 32-bit
movsx eax, word [esi] ; if [esi] = 0xFF80, eax = 0xFFFF8080
; zero-extend 8-bit to 32-bit
movzx ecx, byte [edx] ; if [edx] = 0x80, ecx = 0x00000080
lea (Load Effective Address)
Computes an address expression and stores the result.
; if ebx = 0x00403A40, result becomes 0x00403A48
lea eax, [ebx + 8]
; scaled-index addressing example
lea edi, [esi + ecx*4 + 12]
Increment / Decrement
inc destination ; destination = destination + 1
dec destination ; destination = destination - 1
; examples
inc ecx
dec dword [ebp - 4]
Addition / Subtraction
add destination, source
sub destination, source
; valid forms include:
; - register ← register
; - register ← memory
; - memory ← register
; - register ← immediate
; - memory ← immediate
; memory ← memory is not allowed
; examples
add eax, 5
sub [esp + 16], ebx
add edx, [edi]
Add/Subtract with Carry
adc (Add with Carry)
Adds source + destination + CF.
; generate a carry and then propagate it
mov al, 0xFF
add al, 0x01 ; AL = 0x00, CF = 1
mov dl, 0
adc dl, 0 ; DL = 1 because CF = 1
sbb (Subtract with Borrow)
Subtracts source + CF from destination.
; force a borrow, then subtract with borrow
mov ebx, 0
sub ebx, 5 ; CF = 1 (borrow occurred)
mov ecx, 20
sbb ecx, 0 ; ECX = 19 because CF = 1
Multiplication
mul (unsigned) / imul (signed)
Implicit-operand forms place the full product in a pair of registers:
- AL × r/m8 → AX
- AX × r/m16 → DX:AX
- EAX × r/m32 → EDX:EAX
- RAX × r/m64 → RDX:RAX
; unsigned 8-bit multiply
mov al, 25 ; AL = 25
mov bl, 10 ; BL = 10
mul bl ; AX = 250 (0x00FA)
; signed 32-bit multiply
mov eax, -7
imul dword [multiplier32] ; EDX:EAX = eax * [mem]
Division
div (unsigned) / idiv (signed)
Dividend is taken from a fixed register pair; quotient and remainder are returned as follows:
- (AX) ÷ r/m8 → AL (quotient), AH (remainder)
- (DX:AX) ÷ r/m16 → AX (quotient), DX (remainder)
- (EDX:EAX) ÷ r/m32 → EAX (quotient), EDX (remainder)
- (RDX:RAX) ÷ r/m64 → RAX (quotient), RDX (remainder)
; unsigned 16-bit divide by byte
mov dx, 0 ; clear high half of dividend
mov ax, 1000
mov bl, 24
div bl ; AL = 41, AH = 16 (1000 = 41*24 + 16)
Comparison
cmp
Subtracts source from destination and sets flags; operands are unchanged.
cmp eax, ebx
je .equal
jne .notequal
Unconditional Jump
jmp target_label
Condisional Jumps
Use after cmp or any flag-setting instruction. Two common families exist: signed and unsigned comparisons.
Signed comparisons
- JE/JZ → equal (ZF = 1)
- JNE/JNZ → not equal (ZF = 0)
- JG/JNLE → greater than (ZF = 0 and SF = OF)
- JGE/JNL → greater or equal (SF = OF)
- JL/JNGE → less than (SF ≠ OF)
- JLE/JNG → less or equal (ZF = 1 or SF ≠ OF)
Unsigned comparisons
- JE/JZ → equal (ZF = 1)
- JNE/JNZ → not equal (ZF = 0)
- JA/JNBE → above (CF = 0 and ZF = 0)
- JAE/JNB → above or equal (CF = 0)
- JB/JNAE → below (CF = 1)
- JBE/JNA → below or equal (CF = 1 or ZF = 1)
Single-flag and special jumps
- JO/JNO → overflow set/clear (OF = 1/0)
- JS/JNS → sign set/clear (SF = 1/0)
- JC/JNC → carry set/clear (CF = 1/0)
- JP/JPE → parity even (PF = 1)
- JNP/JPO → parity odd (PF = 0)
- JECXZ/JRCXZ → jump if ECX/RCX == 0
loop
loop label decremants ECX/RCX, jumps if the result is not zero.
mov ecx, 5
.repeat:
; ... body ...
loop .repeat ; runs 5 times
Bitwise and Test
AND destination, source
OR destination, source
XOR destination, source
NOT destination
TEST destination, source ; like AND for flags, result is not stored
; examples
and eax, 0xFF
or edx, [mem]
xor ecx, ecx ; set ECX to 0
test eax, eax ; sets ZF if eax == 0
Shifts and Rotates
- SHL/SHR: logical left/right (zero-filled)
- SAL/SAR: arithmetic left/right (SAR preserves sign bit)
- ROL/ROR: rotate left/right
- RCL/RCR: rotate through CF (includes carry)
- SHLD/SHRD: double-precision shift between two registers
; logical shift right
shr eax, 1
; arithmetic shift right (preserve sign)
sar ebx, 3
; rotate left by 1
rol edx, 1
; shift left double-precision: EDX:EAX << CL, result in EAX
shld eax, edx, 4
String and Block Operations
movsX (movsb/movsw/movsd/movsq)
Copies from [RSI/ESI] to [RDI/EDI]. Element size is 1/2/4/8 bytes. Direction is controlled by DF (0 = increment, 1 = decrement).
; copy 10 bytes from srcBuf to dstBuf
cld ; DF = 0 (increment)
lea esi, [srcBuf]
lea edi, [dstBuf]
mov ecx, 10
rep movsb
cld / std
- cld: clear direction flag (DF = 0)
- std: set direction flag (DF = 1)
rep prefixes
- rep → repeat while ECX/RCX > 0
- repe/repz → repeat while ECX/RCX > 0 and ZF = 1
- repne/repnz → repeat while ECX/RCX > 0 and ZF = 0
; scan for zero byte in a buffer (stop when found or count exhausted)
cld
mov edi, buf
mov ecx, buf_len
mov al, 0
repne scasb ; stop when AL == [EDI] (ZF=1) or ECX==0
cmpsX (cmpsb/cmpsw/cmpsd/cmpsq)
Compares [RSI/ESI] with [RDI/EDI], updates flags, advances pointers per DF. Often used with repe/repne.
; compare two blocks for equality
cld
lea esi, [blockA]
lea edi, [blockB]
mov ecx, length
repe cmpsb ; stop on mismatch (ZF=0) or ECX==0
scasX (scasb/scasw/scasd/scasq)
Compares AL/AX/EAX/RAX against [RDI/EDI], updates flags, advances per DF. Useful for searching.
stosX (stosb/stosw/stosd/stosq)
Stores AL/AX/EAX/RAX into [RDI/EDI], advances per DF.
; fill 32-bit values
d cld
mov edi, buffer
mov ecx, count
mov eax, 0xDEADBEEF
rep stosd
lodsX (lodsb/lodsw/lodsd/lodsq)
Loads from [RSI/ESI] into AL/AX/EAX/RAX, advances per DF.
Stack and Control Transfer
push / pop
Manipulate the stack via RSP/ESP. RBP/EBP is commonly used as a frame pointer.
push eax
push ebx
; ...
pop ebx
pop eax
call / ret
call pushes the return address and transfers control to the target. ret pops the saved address into IP/EIP/RIP.
call my_function
; execution resumes here after ret
my_function:
; ... work ...
ret