Project 2 · Arithmetic
Adders: from half adder to ALU
The gates from the previous page are enough; from here they are combined into things that compute: half adder → full adder → 16-bit adder → incrementer → ALU. The carry travels through all sixteen full adders, bit by bit.
HalfAdder half adder
sum = a ⊕ b, carry = a ∧ b
Truth table
| a | b | sum | carry |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
Structure
What it does
CHIP HalfAdder { IN a, b; OUT sum, carry; PARTS: Xor(a=a, b=b, out=sum); And(a=a, b=b, out=carry); }
Two gates are enough: sum is XOR, carry is AND.
FullAdder full adder
sum = a ⊕ b ⊕ c, carry = (a∧b) ∨ (c∧(a⊕b))
Truth table
| a | b | c | sum | carry |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
Structure
What it does
CHIP FullAdder { IN a, b, c; OUT sum, carry; PARTS: HalfAdder(a=a, b=b, sum=ab, carry=c1); HalfAdder(a=ab, b=c, sum=sum, carry=c2); Or(a=c1, b=c2, out=carry); }
Two half adders and an Or: the first adds a and b, the second adds c in.
Add16 16-bit adder
out = a + b (16 bits, overflow dropped)
Demo
Structure
What it does
CHIP Add16 { IN a[16], b[16]; OUT out[16]; PARTS: HalfAdder(a=a[0], b=b[0], sum=out[0], carry=carry1); FullAdder(a=a[1], b=b[1], c=carry1, sum=out[1], carry=carry2); FullAdder(a=a[2], b=b[2], c=carry2, sum=out[2], carry=carry3); FullAdder(a=a[3], b=b[3], c=carry3, sum=out[3], carry=carry4); FullAdder(a=a[4], b=b[4], c=carry4, sum=out[4], carry=carry5); FullAdder(a=a[5], b=b[5], c=carry5, sum=out[5], carry=carry6); FullAdder(a=a[6], b=b[6], c=carry6, sum=out[6], carry=carry7); FullAdder(a=a[7], b=b[7], c=carry7, sum=out[7], carry=carry8); FullAdder(a=a[8], b=b[8], c=carry8, sum=out[8], carry=carry9); FullAdder(a=a[9], b=b[9], c=carry9, sum=out[9], carry=carry10); FullAdder(a=a[10], b=b[10], c=carry10, sum=out[10], carry=carry11); FullAdder(a=a[11], b=b[11], c=carry11, sum=out[11], carry=carry12); FullAdder(a=a[12], b=b[12], c=carry12, sum=out[12], carry=carry13); FullAdder(a=a[13], b=b[13], c=carry13, sum=out[13], carry=carry14); FullAdder(a=a[14], b=b[14], c=carry14, sum=out[14], carry=carry15); FullAdder(a=a[15], b=b[15], c=carry15, sum=out[15], carry=carry16); }
Sixteen FullAdder chips chained, each passing its carry out into the carry in of the next.
Inc16 incrementer
out = in + 1
Demo
Structure
What it does
CHIP Inc16 { IN in[16]; OUT out[16]; PARTS: // or as one line: Add16(a=in, b[0]=true, b[1..15]=false, out=out); Add16(a=in, b[0]=true, b[1]=false, b[2]=false, b[3]=false, b[4]=false, b[5]=false, b[6]=false, b[7]=false, b[8]=false, b[9]=false, b[10]=false, b[11]=false, b[12]=false, b[13]=false, b[14]=false, b[15]=false, out=out); }
Just Add16 with b = 1.
Coda: why an adder can subtract
① turn back two hours (borrow subtraction: 3 − 2 = 1);
② turn forward ten hours (modular addition: 3 + 10 = 13 ≡ 1 mod 12).
Hardware hates borrowing and loves overflow. Set the dial to 65536 (the top-bit carry is dropped automatically) and subtracting X becomes adding
65536 − X.In binary, 65536 − X = (65535 − X) + 1. Since 65535 is all ones (0xFFFF), subtracting X from it is exactly inverting every bit (~X).
Negation = invert every bit, then add 1. That is two’s complement — it removes the need for a whole subtractor and lets the same Add16 handle both addition and subtraction.
In the ALU, subtraction needs only three control bits: nx=1 (invert x), f=1 (add), no=1 (invert the output) — the ALU page has the full derivation.