Skip to content

Appendix D — C ↔ Assembly and compiler behaviour

Purpose

Make C understandable as generated machine behavior rather than a separate layer of magic.

Comparison method

For a small function: 1. write the behavior in AVR assembly; 2. write a clear C equivalent; 3. compile at selected optimization levels; 4. disassemble; 5. compare instructions, register allocation, memory accesses, calls and size; 6. explain differences rather than assuming shorter is always better.

ABI

Track argument registers, return-value registers, call-used versus call-saved registers, the zero-register convention and stack use. Hand-written assembly called from C must obey the ABI.

Topics to inspect

  • constants and arithmetic;
  • loops and branches;
  • pointers and arrays;
  • 16/32-bit operations on an 8-bit CPU;
  • structs;
  • function calls;
  • ISR entry/exit;
  • inline versus out-of-line code.

volatile

volatile tells the compiler that accesses are observable and must not be optimized away as ordinary memory operations. It is important for MMIO and some shared state, but it does not make multi-byte access atomic and is not a general concurrency solution.

Optimization

Compare at least an unoptimized/debug-friendly build with an optimized build. Source order, variable visibility and instruction count can change substantially while program semantics remain equivalent.

Rule

Always ask: which AVR instructions, registers, memory locations and peripherals implement this C construct?