Overview
Every abstraction in software bottoms out in the same small machine: bits, gates that
compute boolean functions of them, an arithmetic unit built from those gates, and a
processor that endlessly fetches, decodes, and executes instructions against a memory
hierarchy. Knowing this layer isn't nostalgia — it explains integer overflow, 0.1 + 0.2,
why cache-friendly code is 100× faster than big-O predicts, and what a compiler is
actually lowering your code to. It is the floor the whole stack stands on.
Key points
-
Everything is bits, meaning is interpretation: the same 32 bits are an unsigned int, a signed int, a float, four characters, or an instruction — only context decides. Hexadecimal is byte-notation shorthand (one hex digit = one nibble), not a different number system in spirit. Word size (32/64-bit), endianness (byte order in memory — little-endian won), and alignment (why structs have padding) are the conventions underneath every binary format and network protocol.
-
Two's complement is why integer arithmetic works — and overflows: negation is "flip the bits, add one", so the same adder circuit handles signed and unsigned; the price is asymmetric range (−2³¹ … 2³¹−1) and silent wraparound —
INT_MAX + 1is negative,abs(INT_MIN)overflows, and languages differ on whether that's defined behavior, a wrap, or an exception. -
Floating point is scientific notation in base 2 (IEEE 754): sign, exponent, mantissa. Most decimals (0.1!) have no finite binary representation — hence
0.1 + 0.2 ≠ 0.3; comparison needs epsilons, addition isn't associative, and money never goes in a float (use integer cents or decimal types). Special values (±0, ±Inf, NaN ≠ NaN) and the precision cliff of mixing magnitudes are part of the contract every language inherits. -
Boolean logic → gates → an ALU: AND/OR/NOT (all buildable from NAND alone) are transistors in circuits; wire a XOR and an AND together and you have a half adder; chain full adders and you're doing arithmetic. Combinational logic computes, sequential logic (flip-flops latching on a clock edge) remembers — registers and memory are just that. A CPU is nothing but these two kinds of circuit, composed.
-
The processor loop: fetch (at the program counter) → decode → execute → writeback, against a small set of registers. The instruction set architecture (x86-64, ARM, RISC-V) is the hardware/software contract — the stored-program idea (code is data in the same memory, the von Neumann architecture) is why compilers, loaders, and JITs can exist at all.
flowchart LR F["Fetch (at the program counter)"] --> D[Decode] D --> E[Execute] E --> W["Writeback (to registers)"] W --> F -
Modern CPUs cheat relentlessly: pipelining overlaps the stages; superscalar cores issue several instructions per cycle; out-of-order execution reorders around stalls; branch prediction speculates past every
if— a mispredict costs ~15–20 cycles, which is why sorted data can be faster to scan than unsorted, and why speculation leaking through caches became Spectre/Meltdown. -
The memory hierarchy is the performance story: registers (~0 cycles) → L1/L2/L3 caches (~4/12/40 cycles) → RAM (~100+ cycles) → SSD (~100 µs, orders of magnitude again). Data moves in 64-byte cache lines, so spatial and temporal locality — arrays over pointer-chasing, structs sized to lines, false sharing avoided — decide real speed; "mechanical sympathy" is writing code shaped like the hierarchy.
flowchart TB R["Registers (~0 cycles)"] --> C["L1 / L2 / L3 caches (~4 / 12 / 40 cycles)"] C --> M["RAM (~100+ cycles)"] M --> S["SSD (~100 microseconds, orders of magnitude again)"] -
To explore: virtual memory and the MMU (the bridge to the OS layer), interrupts and how I/O actually happens, SIMD and why GPUs are shaped differently, what the compiler emits (read some
-O2disassembly on godbolt.org).
Practice
- "Code" by Charles Petzold (source) — the canonical read-first: relays to gates to a working computer, no prior hardware knowledge assumed.
- Nand2Tetris (part I) (source) — build a computer from NAND gates up through an ALU, CPU, and assembler; the whole tower made concrete in ~40 hours.
- CS:APP Data Lab (source) — bit-twiddling puzzles that force two's complement and IEEE 754 into your fingers (the companion book is the reference for this whole note).
- Ben Eater's 8-bit breadboard CPU (source) — watch (or build) fetch-decode-execute happen on real wires, one clock pulse at a time.
- CHIP-8 emulator (source) — write the fetch-decode-execute loop yourself; the classic first emulator, a weekend project.
- One Billion Row Challenge (exercise) — the memory hierarchy as a competitive sport: cache lines, branch prediction, and mechanical sympathy measured in the wall clock.
Related
- Unicode & text encoding — the "bytes and their interpretation" layer applied to text.
- Time, timezones & datetime — the sibling fundamental.
- Linux deep dive — the OS layer that sits directly on top of this one.
- Algorithms and data structures — big-O meets cache lines: where the constant factors come from.