HomeARM CourseDay 12
DAY 12 · THE INSTRUCTION SET

Branches, Loops & Control Flow

By EcrioniX · Updated Jun 6, 2026

Code isn't just a straight line — it jumps, loops and calls. Today we meet the branch instructions and use them (with the flags from Day 11) to build every if, while and for you've ever written.

1. The branch instructions

A branch changes the PC (Day 3) to continue somewhere else. ARM has three:

InstrDoesUsed for
B labeljump to label (PC = label)goto, loops, if/else
BL labelsave return addr in LR, then jumpcall a subroutine (Day 15)
BX Rnjump to address in a register; can switch ARM/Thumbreturn (BX LR), function pointers

Add a condition code (Day 11) to branch only when the flags agree: BEQ, BNE, BGT, BHI… An unconditional B is just BAL (always).

2. The pattern: compare, then branch

All control flow is the same two steps — set the flags, then branch on them:

CMP r0, r1 ; set flags from r0 - r1 BEQ equal ; go to 'equal' if r0 == r1 BLT smaller ; go to 'smaller' if r0 < r1 (signed)

3. Building an if / else

; if (r0 > 5) r1 = 100; else r1 = 200; CMP r0, #5 BLE else_part ; if NOT (r0 > 5), skip the if-body MOV r1, #100 ; if-body B endif else_part: MOV r1, #200 ; else-body endif:

Note the trick: you branch when the condition is false (here BLE) to skip the if-body. For a short if/else like this, conditional execution (Day 11) would avoid both branches entirely.

4. Loops

Counting loop (for / down-counter)

; for (i = 10; i != 0; i--) { ... } MOV r2, #10 ; counter loop: ; ... loop body ... SUBS r2, r2, #1 ; decrement and SET flags BNE loop ; branch back while r2 != 0

The S on SUBS sets Z when the counter hits zero; BNE loops until then. Down-counting to zero is the cheapest loop on ARM — the decrement is the test.

While loop (test at the top)

; while (r0 < r1) { ... } while: CMP r0, r1 BGE done ; exit when condition fails ; ... body ... B while done:

Do-while loop (test at the bottom)

do: ; ... body runs at least once ... CMP r0, #0 BNE do

5. CBZ / CBNZ — the Thumb shortcut

On Thumb/Cortex-M, two handy instructions test a register against zero and branch in one go — no separate CMP:

CBZ r0, target ; branch if r0 == 0 (e.g. null check / loop exit) CBNZ r0, target ; branch if r0 != 0

Great for the very common "is it zero / is the pointer null?" pattern.

6. A complete example — sum an array

; sum r2 words at address r1 into r0 MOV r0, #0 ; sum = 0 sum_loop: LDR r3, [r1], #4 ; load element, advance pointer (Day 10) ADD r0, r0, r3 ; sum += element SUBS r2, r2, #1 ; count down, set flags BNE sum_loop ; repeat while count != 0

Load/store addressing (Day 10) + a down-counter loop + a conditional branch — a complete, idiomatic ARM loop.

✅ The mental model

All control flow = set flags → branch. B jumps, BL calls (saves LR), BX returns/switches. Loops are a label + body + conditional branch back; the cheapest is a SUBS down-counter with BNE.

🎯 Day 12 takeaways

Quick check

  1. Which branch instruction saves a return address, and where?
  2. Why is a down-counting loop to zero efficient on ARM?
  3. What does CBZ r0, exit do?

FAQ

B vs BL vs BX?

B jumps; BL jumps and saves the return address in LR (for calls); BX jumps to a register address and can switch ARM/Thumb (BX LR returns).

How do you write a loop?

Label + body + a conditional branch back. A counter with SUBS then BNE is the classic down-counting loop.

What are CBZ/CBNZ?

Thumb instructions that compare a register to zero and branch in one step — ideal for null checks and loop exits.

Previous
← Day 11: Conditional execution

← Back to the full course roadmap