Introduction: The Beautiful Iteration
Let me tell you about the exact moment I realized loops were the most beautiful concept in programming. I was sitting in a computer architecture class, and the professor asked: “How do you sum an array of a million numbers?”
My hand shot up. “Well, you add the first to the second, then that result to the third, then—”
“Stop,” he said. “That’s not a loop. That’s a million instructions. Now, how do you sum an array of N numbers, where N could be 10 or 10 million?”
That’s when it hit me. Loops are how we tame infinity. They’re how we say “do this thing N times” without writing N instructions. They’re the difference between a program that can sum 10 numbers and a program that can sum 10 million.
In this article — the ninth in our series — we’re going to master loop programming in assembly. We’ll dive deep into the LOOP instruction (and why you might not want to use it), learn count-controlled and condition-controlled loops, explore loop unrolling, manage nested loops, and implement break and continue in assembly. We’ll also build a complete matrix multiplication program using nested loops.
This is where assembly programming starts to feel powerful. Let’s dive in.
LOOP Instruction: The Classic 8086 Loop — ECX as Counter, Implicit DEC ECX + JNZ
Let’s start with the most iconic loop instruction in x86 assembly: LOOP. This instruction has been around since the 8086 (1978), and it’s equal parts elegant and problematic.
What Does LOOP Do?
The LOOP instruction does two things in one:
- Decrements
ECX(orCXin 16-bit mode,RCXin 64-bit mode) by 1. - If the result is non-zero, jumps to the target label.
In other words, LOOP label is equivalent to:
DEC ECX ; Decrement ECX
JNZ label ; Jump if ECX != 0
Basic Usage of LOOP
Here’s the classic LOOP example: summing the numbers 1 through 10.
; ============================================
; Sum numbers 1-10 using LOOP
; ============================================
.DATA
sum DD 0
fmt DB 'Sum: %d', 10, 0
.CODE
main PROC
MOV EAX, 0 ; EAX = sum (initialize to 0)
MOV ECX, 10 ; ECX = loop counter (10 iterations)
loopStart:
ADD EAX, ECX ; sum += i (ECX is the current number)
LOOP loopStart ; ECX--, jump to loopStart if ECX != 0
; When we get here, EAX = 1+2+3+...+10 = 55
MOV sum, EAX
; Print result
PUSH sum
PUSH OFFSET fmt
CALL printf
ADD ESP, 8
PUSH 0
CALL exit
main ENDP
END main
Let’s trace through this:
- Iteration 1: ECX = 10, EAX += 10 → EAX = 10
- Iteration 2: ECX = 9, EAX += 9 → EAX = 19
- …
- Iteration 10: ECX = 1, EAX += 1 → EAX = 55
- Iteration 11: ECX = 0,
LOOPdoesn’t jump, we exit the loop.
The Implicit Use of ECX
Here’s the thing you must remember about LOOP: it always uses ECX as the counter. You can’t use EBX or EDX or any other register. It’s ECX or nothing.
This is both a feature and a bug:
- Feature: You don’t have to explicitly decrement and compare.
LOOPdoes it for you. - Bug: If your loop body needs
ECXfor something else (like a counter for an array index), you’re in trouble.
LOOP in 16-bit, 32-bit, and 64-bit Modes
The size of the counter register depends on the mode:
- 16-bit mode:
LOOPusesCX(16-bit counter) - 32-bit mode:
LOOPusesECX(32-bit counter) - 64-bit mode:
LOOPusesRCX(64-bit counter)
; 16-bit mode (DOS)
MOV CX, 100 ; Loop 100 times
loopStart:
; ... do something ...
LOOP loopStart ; Uses CX
; 32-bit mode (Windows/Linux)
MOV ECX, 100 ; Loop 100 times
loopStart:
; ... do something ...
LOOP loopStart ; Uses ECX
; 64-bit mode (modern Linux/macOS/Windows)
MOV RCX, 100 ; Loop 100 times
loopStart:
; ... do something ...
LOOP loopStart ; Uses RCX
LOOP Variants: LOOPE (LOOPZ) and LOOPNE (LOOPNZ)
The x86 architecture actually has three LOOP instructions:
LOOP: Decrement counter, jump if counter != 0 (ZF doesn’t matter).LOOPE(alsoLOOPZ): Decrement counter, jump if counter != 0 AND ZF = 1.LOOPNE(alsoLOOPNZ): Decrement counter, jump if counter != 0 AND ZF = 0.
These are rarely used in practice, but they exist. Here’s an example of LOOPE:
; Search for zero in an array
MOV ESI, OFFSET array
MOV ECX, arrayLen
MOV EAX, 0 ; Value we're searching for (0)
searchLoop:
CMP [ESI], EAX ; Compare array[i] with 0
LEAVE ; (Just kidding, this is for demonstration)
; Actually, LOOPE checks ZF AFTER the CMP
LOOPE searchLoop ; Continue if ECX > 0 AND ZF=1 (found zero)
; If we exit because ECX=0, we didn't find zero
; If we exit because ZF=0, we found a non-zero element
Real talk: I’ve been writing assembly for 15 years, and I’ve never used LOOPE or LOOPNE in production code. They’re niche. Focus on understanding LOOP, and you’ll be fine.



