CS ATLAS A map of computing

The ALU

Arithmetic logic unit

The ALU is not a new part: it is an adder and a bitwise AND placed side by side, with a few control bits choosing which one runs. It has two 16-bit inputs, one 16-bit output, six 1-bit controls and two flags.

00What the ALU looks like

ALUsix control bitsx[16]y[16]zxnxzynyfnoout[16]zr / ng

01The six control bits

BitControlsWhen 1
zxxzero x
nxxinvert x (after zx)
zyyzero y
nyyinvert y (after zy)
foperation1 = add, 0 = bitwise AND
nooutputinvert the result

The order matters: zero first, then invert, so zx=1, nx=1 gives all ones (−1), not 0.

The elegant bit: how does the ALU subtract x − y?
By two’s complement, inversion is ~A = −A − 1. Set nx=1 (invert x), f=1 (add) and no=1 (invert the output):
· add inside: ~x + y = (−x − 1) + y = (y − x) − 1;
· invert the output: ~((y − x) − 1) = −((y − x) − 1) − 1 = x − y!
No dedicated subtract line is needed: invert, add, invert again — subtraction falls out of parts that were already there.

All six bits are 1 bit wide — narrow control, wide data. They carry no information; they only decide.

02The two flags

zr (zero): 1 when the output is all zeros. The CPU uses it for “the values are equal”.

ng (negative): the top bit of the output, that is the sign bit. The CPU uses it for “less than zero”.

Note that ng is just out[15], no extra logic; zr must look at all 16 bits — which is why reduction gates exist.

03Eighteen functions, eighteen switch combinations

Control bits
out0x00055
zr 0ng 0

0000 0000 0000 0101

Functionzx nx zy ny f noResultHexFlags
010101000x0000zr
111111110x0001
-1111010-10xFFFFng
x00110020x0002
y11000030x0003
!x001101-30xFFFDng
!y110001-40xFFFCng
-x001111-20xFFFEng
-y110011-30xFFFDng
x+101111130x0003
y+111011140x0004
x-100111010x0001
y-111001020x0002
x+y00001050x0005
x-y010011-10xFFFFng
y-x00011110x0001
x&y00000020x0002
x|y01010130x0003

Click any row and the control bits set themselves — the 18 functions are 18 combinations of six switches.

CHIP ALU {
  IN x[16], y[16], zx, nx, zy, ny, f, no;
  OUT out[16], zr, ng;
  PARTS:
  Mux16(a=x, b=false, sel=zx, out=x1);    // zx: zero x
  Not16(in=x1, out=nx1);
  Mux16(a=x1, b=nx1, sel=nx, out=x2);     // nx: invert x
  Mux16(a=y, b=false, sel=zy, out=y1);    // zy
  Not16(in=y1, out=ny1);
  Mux16(a=y1, b=ny1, sel=ny, out=y2);     // ny
  And16(a=x2, b=y2, out=andxy);
  Add16(a=x2, b=y2, out=addxy);
  Mux16(a=andxy, b=addxy, sel=f, out=o1); // f: 0 = AND, 1 = add
  Not16(in=o1, out=no1);
  Mux16(a=o1, b=no1, sel=no, out=out, out[0..7]=lo, out[8..15]=hi, out[15]=msb);  // no: invert the result
  Or8Way(in=lo, out=z1);                  // zr: all-zero test (reads internal nets, not out)
  Or8Way(in=hi, out=z2);
  Or(a=z1, b=z2, out=z);
  Not(in=z, out=zr);
  And(a=msb, b=true, out=ng);             // ng: sign bit
}

All six control bits go to Mux16 selectors. Since out is an output pin, it cannot be read back, so it is also wired to the internal nets lo / hi / msb, and the flags read those nets.