CS ATLAS A map of computing

The CPU

CPU: wiring the control bits

The ALU computes, registers remember, PC advances. The CPU introduces no new parts — it does exactly one thing:wire the 16 bits of an instruction straight onto the control pins of those parts. “Decoding” is just wiring.

01What the CPU does: 16 bits onto control pins

The CPU invents nothing. It does one thing: split the fetched 16-bit instruction into control signals and drive the control pins of the ALU, registers, memory and PC.

Mental model: the CPU is an automatic railway switchyard driven by the instruction
· Tracks: the 16-bit data bus, carrying 16-bit cars;
· The factory: the ALU is the only workshop on the line;
· The 16-bit instruction: not a magic spell but a work order for an automatic switch operator —the a bit throws the input switch (send A or M into the factory); c1~c6 select the arithmetic or logic job; d1~d3 throw the output switch (unload into A, D, or through Memory into RAM); j1~j3 read the sign and zero flags of the result and decide whether the PC track takes the default next stop (+1) or turns to a new station (a jump).

The format (how A and C instructions are written) is on the machine language page; what the machine promises externally is on the Hack computer page. This page cares about one thing: how those bits become wires.

CPU16inM16instruction1reset16outM1writeM15addressM15pcfromData MemoryfromInstruction Memoryfromthe user
instruction (hex)0xFDD0
mnemonicD=M+1
1111110111010000
111ac1c2c3c4c5c6d1d2d3j1j2j3
y is
M (RAM[A])
ALU computes
M+1 (control bits 110111)
Written to
D
Jump
no jump

02Where the six control bits come from

Bits 11–6 of a C instruction are in exactly the same order as the ALU’s six control bits:

Instruction bitWired toMeaning
bit 12(a)Mux16.selwhether the ALU’s y takes A or M
bit 11ALU.zxzero x
bit 10ALU.nxinvert x
bit 9ALU.zyzero y
bit 8ALU.nyinvert y
bit 7ALU.f1 = add, 0 = and
bit 6ALU.noinvert the result

That is why the ALU page presents them in the order zx nx zy ny f no: it matches the instruction exactly, so wiring is one bit to one pin with no conversion.

03Datapath: x is always D, y follows the a bit

The ALU’s two inputs are not arbitrary:

  • x = the D register: hard-wired, so every instruction implicitly computes with D.
  • y = A or M: chosen by the a bit. When it picks M, M is RAM[A] — A doubles as the address.

Writing back uses three independent lines: d1 writes A, d2 writes D, d3 writes M (each gated by “this is a C instruction”).So the number of combinations on the left of = in D=M+1 is the number of control lines.

CPU pinWired to
instruction[16]where the control bits come from
inM[16]RAM[A], that is M
addressM[15]the low 15 bits of A (the address)
outM[16]the ALU output
writeM“is a C instruction” AND d3
pc[15]the low 15 bits of PC (next instruction address)
resetthe PC reset

04Jumps: three bits plus two flags

The last three bits j1 j2 j3 mean “less / equal / greater”. ANDed with the ALU flags ng (negative) and zr (zero), they produce “jump or not”:

jjjMnemonicCondition
001JGTresult > 0
010JEQresult = 0
011JGEresult ≥ 0
100JLTresult < 0
101JNEresult ≠ 0
110JLEresult ≤ 0
111JMPalways

Note: 0;JMP has no destination — the result is only tested, not written back anywhere.

05One instruction, step by step

StageA instruction (@x)C instruction (D=M+1;JGT)
Fetch16 bits are read from the address in PC and fed to instruction
Top bit0: a constant1: a computation
Computenothing; the ALU output is ignoredx=D, y=M, the six control bits compute M+1
WriteA ← the constantD ← the result (d2=1)
Jumpno jumpif the result > 0, PC loads A; otherwise PC+1

One pass per clock tick. A “program” is just these 16-bit words lined up in memory.

06How PC gets written

PC is not a register programs write directly; it has three inputs, in priority order:

SignalEffectPriority
resetPC = 0highest (power-on / reset)
load (from the jump)PC = A (the jump target)middle
incPC = PC + 1lowest (the default)

So a jump is two wires in hardware: send A to the PC input, then raise load.An A instruction puts the target address into A first; only then can a jump mean anything.

The jump condition is the three jump bits ANDed/ORed with the ALU’s zr / ng — load goes high only when it holds.When it does not hold, load=0 and PC still increments.

07Writing it in HDL

// Hack CPU: A and D registers + ALU + PC
CHIP CPU {
  IN inM[16], instruction[16], reset;
  OUT outM[16], writeM, addressM[15], pc[15];

  PARTS:
  // A input: an A-instruction writes a constant, a C-instruction writes the ALU result
  Mux16(a=instruction, b=aluOut, sel=instruction[15], out=aIn);
  Not(in=instruction[15], out=notInstruction);

  // A register: always written by an A-instruction; a C-instruction looks at d1
  Or(a=notInstruction, b=instruction[5], out=loadA);
  ARegister(in=aIn, load=loadA, out=aOut, out[0..14]=addressM);

  // ALU y: the a bit picks A or M
  Mux16(a=aOut, b=inM, sel=instruction[12], out=yIn);

  // ALU control: on an A-instruction, pass D through (zy=ny=1, f=0 → D ∧ -1)
  And(a=instruction[11], b=instruction[15], out=zx);
  And(a=instruction[10], b=instruction[15], out=nx);
  Or(a=instruction[9],  b=notInstruction, out=zy);
  Or(a=instruction[8],  b=notInstruction, out=ny);
  And(a=instruction[7], b=instruction[15], out=f);
  And(a=instruction[6], b=instruction[15], out=no);

  ALU(x=dOut, y=yIn,
      zx=zx, nx=nx, zy=zy, ny=ny, f=f, no=no,
      out=outM, out=aluOut, zr=zr, ng=ng);

  // write memory: only a C-instruction with d3
  And(a=instruction[15], b=instruction[3], out=writeM);

  // D register: only a C-instruction with d2
  And(a=instruction[15], b=instruction[4], out=loadD);
  DRegister(in=aluOut, load=loadD, out=dOut);

  // jump: j1 = less, j2 = equal, j3 = greater
  Or(a=zr, b=ng, out=notPos);
  Not(in=notPos, out=pos);
  And(a=instruction[0], b=pos, out=jgt);
  And(a=instruction[1], b=zr,  out=jeq);
  And(a=instruction[2], b=ng,  out=jlt);
  Or(a=jgt, b=jeq, out=j1);
  Or(a=j1, b=jlt, out=jCond);
  And(a=jCond, b=instruction[15], out=jump);

  // PC: load A on a jump, otherwise increment
  PC(in=aOut, load=jump, reset=reset, inc=true, out[0..14]=pc);
}

A uses ARegister and D uses DRegister: these are the course’s observable registers, and the test files read their state. A plain Register works the same, but the DRegister column in the compare file stays 0.

Three things to notice in this code: every control bit comes from instruction[..] (a few are gated), and there is no separate decoder; A is both data and address; the PC load comes from the jump logic.