Lesson 09-Loop Programming – Count and Condition Control

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:

  1. Decrements ECX (or CX in 16-bit mode, RCX in 64-bit mode) by 1.
  2. 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, LOOP doesn’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. LOOP does it for you.
  • Bug: If your loop body needs ECX for 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: LOOP uses CX (16-bit counter)
  • 32-bit mode: LOOP uses ECX (32-bit counter)
  • 64-bit mode: LOOP uses RCX (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:

  1. LOOP: Decrement counter, jump if counter != 0 (ZF doesn’t matter).
  2. LOOPE (also LOOPZ): Decrement counter, jump if counter != 0 AND ZF = 1.
  3. LOOPNE (also LOOPNZ): 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.

Membership Required

You must be a member to access this content.

View Membership Levels

Already a member? Log in here

Share your love