You have only 16 registers, but programs nest calls and juggle far more data. The answer is the stack — a last-in-first-out scratchpad in memory, pointed to by SP. Today: how it works, which way it grows, and the PUSH/POP idioms behind every function.
The stack is a region of RAM used as a LIFO (last-in, first-out) scratchpad, addressed by the Stack Pointer, SP (R13) from Day 3. Programs use it to:
A stack of plates. You push a new plate on top and pop the top one off. The last plate on is the first off. SP always marks the top plate.
Two questions define a stack: which way does it grow, and does SP point at the last full slot or the next empty one? ARM's standard is full-descending:
So PUSH = decrement SP, then store. POP = load, then increment SP.
PUSH and POP take a register list and do the SP bookkeeping for you:
Notice the elegant trick on the way out: pushing lr and popping it directly into pc restores the registers and performs the function return in one instruction. Registers are always stored in a fixed order regardless of how you list them, so a matching POP unwinds correctly.
From Day 10, PUSH/POP are just friendly names for load/store multiple with the full-descending stack mode and writeback:
| You write | Really is |
|---|---|
| PUSH {regs} | STMFD sp!, {regs} |
| POP {regs} | LDMFD sp!, {regs} |
FD = Full Descending. The sp! means "update SP afterwards" (writeback). Other modes exist (EA/ED/FA) but full-descending is the ARM standard you'll almost always use.
Each function call typically carves out a stack frame: a slice of stack holding its saved registers, return address and locals. As calls nest, frames stack up; as functions return, frames are popped off. This is exactly what a debugger walks to show you a call stack / backtrace.
The stack has a finite size. If it grows too far — deep/infinite recursion, or huge local arrays — it runs past its reserved memory and corrupts whatever is next. That's a stack overflow: a classic, serious bug (and the namesake of a certain website). Keep recursion bounded, locals modest, and size the stack for the worst-case call depth.
The stack is a LIFO scratchpad in RAM at SP, growing down (full-descending). PUSH saves registers, POP restores them — and PUSH {…, lr} / POP {…, pc} brackets a function and returns in one move. It's how a chip with 16 registers runs arbitrarily deep call chains.
PUSH {…, lr} + POP {…, pc} saves/restores and returns in one go.POP {r4-r6, pc} a return as well as a restore?A LIFO region of RAM at SP (R13) used to save registers, return addresses and locals across function calls.
ARM's convention: it grows downward and SP points at the last pushed item. PUSH decrements then stores; POP loads then increments.
PUSH/POP are aliases for STMFD sp! / LDMFD sp! — store/load multiple in full-descending mode with writeback.