Part 1 of 10

The Physical Foundation

How electricity, transistors, and logic gates become a machine that computes — the bedrock everything else stands on.

Ch. 0

Electricity and Binary: The Alphabet

Everything in computing starts with one simple idea: electricity can be ON or OFF. We label these states 1 and 0 — a single bit, the smallest unit of information that can exist.

Analogy: A bit is like a yes-or-no question. Alone it's trivial, but ask 20 in sequence and you can identify almost anything — that's the logic behind "20 Questions." Enough bits combined can represent anything.

Why binary? Humans use base 10 because we have ten fingers. Computers use binary (base 2) because transistors have exactly two reliable electrical states: ON or OFF. It's not an arbitrary choice — two states are simply the most stable thing to engineer in hardware.

Bit Groups

Four bits form a nibble (16 values). Eight bits form a byte (256 values). The byte is the smallest addressable unit of memory in most architectures — you can't read a single bit from RAM, you read at least one byte. A byte can encode a number 0–255, a character like 'A' (ASCII value 65 = 01000001), or one channel of a color value.

Analogy: Bits are like Morse code. Just two symbols — dot and dash — encode the entire English language. 0s and 1s can encode everything: text, images, video, music, programs.

Representing Negative Numbers — Two's Complement

A raw byte holds 0–255 (unsigned). Programs also need negative numbers. The standard encoding is two's complement: the most-significant bit (MSB) acts as a sign bit, and the pattern wraps around so that the same adder circuit handles both signed and unsigned arithmetic without modification.

To negate any value: flip all bits, then add 1.

  • 5 = 0000 0101
  • Flip bits: 1111 1010
  • Add 1: 1111 1011 = −5

Why this works: 5 + (−5) in hardware produces 0000 0000 — the carry out of the MSB is discarded and the result is zero. No separate subtraction circuit needed.

Integer overflow happens when arithmetic exceeds the representable range. For a signed 8-bit value, adding 1 to 127 wraps to −128. The hardware doesn't raise an error — bits just roll over — which is why C programs can produce silently wrong results. Languages like Rust and Swift make overflow a compile-time or checked runtime error by default.

Floating Point — IEEE 754

Integers are exact but limited in range. Floating-point numbers trade exactness for the ability to represent both very large and very small values. The IEEE 754 standard (used by every modern CPU and language) defines two formats:

The value is: ± 1.mantissa × 2^(exponent − bias)

The critical consequence: most decimal fractions have no exact binary representation. 0.1 + 0.2 evaluates to 0.30000000000000004 in every language that uses IEEE 754 doubles. This isn't a language bug — it's fundamental to binary floating point. This is why financial software uses fixed-point arithmetic or decimal types (e.g., Python's Decimal, Java's BigDecimal), never floats.

Special values encoded in IEEE 754 include +Infinity, −Infinity, and NaN (Not a Number, result of 0/0 or sqrt(−1)).

Text Encoding — From ASCII to Unicode

Early computers assigned each character a 7-bit number (ASCII: 128 characters — English letters, digits, punctuation, control codes). 8-bit bytes allowed 256-character extensions, but different regions adopted incompatible extensions, producing encoding chaos.

Unicode defines a universal code space of over 143,000 characters covering all human writing systems. UTF-8 is the dominant encoding: ASCII characters use 1 byte unchanged; other code points use 2–4 bytes. It is backward-compatible with ASCII and is the default for HTML, HTTP, JSON, and most modern file formats.

When you read a file or network stream and see "garbled text," the root cause is almost always an encoding mismatch — the bytes are being interpreted with the wrong encoding table.

Endianness

Multi-byte values must be laid out as a sequence of bytes in memory. The ordering convention is called endianness:

  • Little-endian: least-significant byte stored first. Intel x86/x64 and ARM (in LE mode) use this. The 32-bit value 0x01020304 is stored as 04 03 02 01 in memory.
  • Big-endian: most-significant byte first. Used by most network protocols (TCP/IP, HTTP headers) — also called "network byte order."

When writing network code or parsing binary file formats, you must convert between host byte order and network byte order (htons, htonl in C; ByteOrder in Java). Ignoring endianness is a common source of subtle bugs in serialization and protocol code.

Hexadecimal

Binary strings are hard to read. Hexadecimal (base 16) compresses them: every four bits map to one hex digit (0–9, then A–F). A 16-bit address shrinks from 16 digits to 4.

Ch. 1

Transistors: The Tiny Switches

A transistor is a microscopic electronic switch. Apply a small voltage to its gate and it conducts electricity (ON / 1). Remove that voltage and it stops (OFF / 0).

Analogy: A transistor is like a water faucet: a tiny turn of the handle (gate signal) controls a much larger flow of water (current). This amplification property is also why transistors were originally used in radios and hearing aids — a small input signal controls a big output.

Scale and Manufacturing

Modern CPUs pack tens of billions of transistors. An Apple M2 has roughly 20 billion, each about 5 nanometers wide — approximately 20 atoms. These are manufactured using photolithography: patterns are projected onto a silicon wafer with extreme-ultraviolet (EUV) light (wavelength ~13 nm), etched, and built up in dozens of layers. A single wafer yields hundreds of chips.

CMOS — How Real Gates Are Built

Production chips use CMOS (Complementary MOS) logic. Each gate is implemented with two complementary transistor types: a PMOS (p-type) transistor that pulls the output high, and an NMOS (n-type) transistor that pulls it low. Exactly one conducts at a time; the output is always driven to a definite voltage.

The key efficiency property: CMOS draws significant current only while switching, not while holding a stable value. Static power draw is near zero. This is why CMOS dominates digital logic — it can pack billions of gates and still be battery-friendly.

The downside: as transistors shrink below ~10 nm, quantum tunneling causes leakage current even when the gate is "off," increasing static power and generating heat regardless of workload.

Moore's Law and the End of the Free Lunch

Moore's Law (1965) observed that transistor density doubles roughly every two years. For decades, this translated directly into faster, cheaper chips with no software changes required.

Around 2005, the "free lunch" ended. Smaller transistors no longer automatically run faster because: (1) clock speeds above ~4 GHz generate too much heat to dissipate, (2) leakage current grows, and (3) quantum effects become significant. The industry responded by shifting to multi-core CPUs, specialized silicon (GPUs, TPUs, NPUs), 3D stacking (stacking die vertically), and heterogeneous architectures.

Understanding Moore's Law's slowdown explains why modern performance gains come from parallelism, specialization, and memory hierarchy optimization — not just faster clocks.

By arranging transistors in specific patterns, we build logic gates — circuits capable of making Boolean decisions.

Ch. 2

Logic Gates: The Decision Makers

A logic gate takes one or more binary inputs and produces one binary output according to a fixed rule. Four gates are fundamental:

NAND and NOR are worth singling out. NAND is AND followed by NOT; NOR is OR followed by NOT. More importantly, NAND is functionally complete — every other gate can be built from NANDs alone (and similarly for NOR). Real hardware exploits this for manufacturing uniformity: a fab can optimize a single NAND cell deeply and build everything else from it.

Analogy: Logic gates are like nightclub bouncers. AND: "both of you need IDs." OR: "at least one of you." NOT: inverts the rule entirely. XOR: "exactly one of you — not both." XNOR (the inverse): "same outcome for both of you."

Boolean Algebra

Just as ordinary algebra manipulates numbers, Boolean algebra manipulates true/false values. The rules let you simplify and transform logic expressions — essential for minimizing circuit area.

Key identities:

  • A AND 0 = 0 — anything AND false collapses to false
  • A AND 1 = A — AND with true is transparent
  • A OR 0 = A — OR with false is transparent
  • A OR 1 = 1 — anything OR true is always true
  • A AND (NOT A) = 0 — a contradiction
  • A OR (NOT A) = 1 — a tautology

De Morgan's Laws convert between AND and OR forms — essential for pushing NOT gates inward or turning NOR/NAND into more convenient forms:

  • NOT (A AND B) = (NOT A) OR (NOT B)
  • NOT (A OR B) = (NOT A) AND (NOT B)

Analogy: "It's not the case that I want tea AND coffee" means the same as "I don't want tea, OR I don't want coffee."

Propagation Delay and the Critical Path

Gates are not instantaneous. Every gate introduces a propagation delay — the time for an input change to appear at the output (typically 10–100 picoseconds per gate in modern CMOS). Chain many gates and the delays accumulate.

The critical path is the longest gate chain between any two flip-flops in a circuit. It determines the maximum safe clock frequency: the clock period must be longer than the critical path delay. CPU architects spend enormous effort shortening critical paths — via Boolean minimization, gate sizing, and circuit restructuring — because every picosecond shaved translates directly to MHz gained.

This is also why overclocking can cause data corruption: if you run the clock faster than the critical path, flip-flops capture results before the computation finishes.

Ch. 3

Building Up from Gates: Arithmetic Circuits

Combinational circuits wire gates together so that output depends only on current inputs — no memory, no clock. These are the building blocks of everything the CPU computes.

Adders

The simplest arithmetic circuit is a half adder: add two single bits and produce a Sum and a Carry-out.

A full adder adds three inputs — two operands plus a Carry-In from the column to the right. Chain eight full adders together and you have an 8-bit adder that processes two bytes column-by-column.

Analogy: This mirrors decimal long addition exactly — column by column, carrying overflow to the next column when the sum exceeds the base. The circuit does the same in base 2.

The ripple-carry problem: in an N-bit ripple-carry adder, each column must wait for the carry from the column to its right before it can produce a result. A 64-bit ripple-carry adder has a critical path through 64 sequential carry stages — far too slow for a modern CPU.

Carry-lookahead adders (CLA) solve this by precomputing — in parallel — whether each position will generate a carry (both bits are 1) or propagate a carry (exactly one bit is 1). This reduces carry computation from O(n) serial stages to O(log n). All modern CPUs use carry-lookahead or a variant (carry-select, Han-Carlson).

Comparators

A comparator checks whether two values are equal, or which is larger (A == B, A > B, A < B). Its output is a single control bit. This is how the CPU evaluates conditions: if (x > y) compiles to a compare instruction that sets a flag register, which subsequent branch instructions read.

Other Essential Combinational Circuits

  • Multiplexer (MUX): selects one of N inputs based on a binary selector — like a channel selector. Used extensively in CPUs to route data between units.
  • Demultiplexer (DEMUX): routes one input to one of N outputs based on a selector.
  • Decoder: maps an N-bit binary input to one of 2^N active output lines — used in memory chips to select a specific row or column from an address.
  • ALU (Arithmetic Logic Unit): combines adders, subtractors, comparators, and logic units into a single circuit. An opcode input selects which operation to perform. The ALU is the CPU's computational engine — every arithmetic and logical operation passes through it.

Analogy: The ALU is a Swiss Army knife. You pick the blade (operation), hand it two operands, and it returns the result. The CPU picks which blade to open each clock cycle based on the instruction being executed.

Ch. 4

Memory: Remembering Things

Everything so far is combinational — output depends only on current inputs. The moment inputs change, output changes. Computers also need sequential logic: circuits that remember past state.

The SR Latch — Simplest Memory

Cross-connect two NOR gates and the circuit becomes bistable — it has two stable states and retains whichever it was last driven into.

Analogy: An SR latch is like a light switch. Flip it up and it stays up. Flip it down and it stays down — without any ongoing signal.

D Flip-Flop and the Clock

The SR latch has a problem: it responds to inputs immediately, so any glitch or race condition on the inputs corrupts state. The D flip-flop adds a clock input: the output only updates on the rising edge of a clock pulse. All other times, it holds its value regardless of input changes.

The clock is a signal that oscillates between 0 and 1 at a fixed frequency — the processor's heartbeat.

A 3 GHz processor ticks 3 billion times per second. Each tick: data flows through the combinational logic, and the results are captured in flip-flops.

Setup time and hold time are the constraints that make this work. The input must be stable for at least t_setup before the clock edge and t_hold after it. Violate these windows and the flip-flop enters metastability — its output is neither 0 nor 1 for an unpredictable time, potentially propagating garbage through the rest of the circuit.

This is the real reason overclocking causes crashes: at too high a frequency, the combinational logic doesn't finish computing before the flip-flop tries to capture the result.

Analogy: The clock is a drumbeat for a marching band. Every musician moves in sync. Without it, instruments would update at different times, producing chaos.

Registers

Groups of flip-flops wired together store a multi-bit value — 32 or 64 bits in modern CPUs. Registers are the fastest storage in the computer because they are physically inside the CPU die, a few gate-delays from the ALU. A typical CPU has 16–32 general-purpose registers. The entire visible state of a running program (what assembly code manipulates) lives in registers.

The Memory Hierarchy

Registers are fast but tiny. As storage needs grow, we descend a hierarchy where each tier is slower but larger:

SRAM (used for caches) builds each bit from 6 transistors in a latch — fast (1–40 cycles) but expensive (~50× cost per bit vs DRAM).

DRAM (main RAM) stores each bit in a single capacitor. Cheap and dense, but: (1) capacitors leak charge and must be electrically refreshed every ~64 ms (during which the memory controller is unavailable), (2) random access latency is ~100 ns, roughly 100× slower than an L1 cache hit.

NAND Flash (SSDs, phones) stores charge on a floating-gate transistor that retains state without power. Reads are fast; writes require erasing an entire block (128KB–4MB) first, then writing — making writes 10–100× slower than reads. Each cell wears out after 1,000–100,000 program/erase cycles, which is why SSDs use wear leveling: a controller spreads writes evenly across all blocks to extend lifetime.

Analogy: SRAM is a whiteboard — holds content until erased. DRAM is writing in wet sand — fades unless constantly re-traced. Flash is carving into stone — persists without power, but each carving gradually wears the stone down.

Cache Lines and the Cost of a Miss

Caches don't load individual bytes — they load cache lines (typically 64 bytes) aligned to 64-byte boundaries. When the CPU reads byte 40 of an array, it loads bytes 0–63 into a cache line. The next read at byte 44 is free — it's already in cache (cache hit). Reading a pointer that jumps to an unrelated memory location is a cache miss and stalls the CPU while the next tier is fetched.

Real latency numbers:

  • L1 cache hit: ~1–4 cycles (~0.5 ns)
  • L2 cache hit: ~10–12 cycles (~3 ns)
  • L3 cache hit: ~40 cycles (~12 ns)
  • RAM access: ~200 cycles (~60 ns)
  • SSD read: ~100 µs (100,000 ns)
  • HDD read: ~10 ms (10,000,000 ns)

This is why data structure choice matters enormously. Iterating a dense array is often 10–100× faster than traversing a linked list of the same elements — the array exploits spatial locality (each cache line covers many adjacent elements), while the linked list pointer-chases across random memory addresses, causing constant misses.

Write policies determine when modified data is written back:

  • Write-through: write to cache and to the next tier simultaneously. Simple; cache and memory always in sync. But every write pays full latency.
  • Write-back: write only to cache; the "dirty" line is flushed to the next tier only when evicted. Faster, but requires tracking which lines have been modified and careful handling on multi-core systems where other cores share memory.

Locality — Why the Hierarchy Works

Two principles explain why caches are effective despite being tiny:

  • Temporal locality: data accessed recently is likely to be accessed again soon (e.g., a loop counter)
  • Spatial locality: data near recently-accessed data is likely to be accessed soon (e.g., the next element in an array)

Caches exploit both: they keep recently-used lines (temporal) and load entire lines per access (spatial). Programs written with locality in mind — sequential access patterns, compact data structures, avoiding pointer indirection — naturally benefit from the cache hierarchy without any explicit caching code.