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.
A branch changes the PC (Day 3) to continue somewhere else. ARM has three:
| Instr | Does | Used for |
|---|---|---|
| B label | jump to label (PC = label) | goto, loops, if/else |
| BL label | save return addr in LR, then jump | call a subroutine (Day 15) |
| BX Rn | jump to address in a register; can switch ARM/Thumb | return (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).
All control flow is the same two steps — set the flags, then branch on them:
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.
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.
On Thumb/Cortex-M, two handy instructions test a register against zero and branch in one go — no separate CMP:
Great for the very common "is it zero / is the pointer null?" pattern.
Load/store addressing (Day 10) + a down-counter loop + a conditional branch — a complete, idiomatic ARM loop.
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.
SUBS + BNE) is the cheapest; while = test top, do-while = test bottom.CBZ r0, exit do?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).
Label + body + a conditional branch back. A counter with SUBS then BNE is the classic down-counting loop.
Thumb instructions that compare a register to zero and branch in one step — ideal for null checks and loop exits.