Arithmetic Logic Unit: ALU
Instead of explaining how to build an ALU, I will show you one.
Behold the glory of SN74LS181:


The output is purely feed-forward, series of transformations that given an input, perform certain operations and then produces an output. The symbols on the schematics are various gates, all signals flow from top to bottom. This ALU unit can perform 4 bit operations.
You select what function to use with S0-S4, M is used to choose between logical or arithmetic operations, A0-A3 is one input and B0-B3 is the other, the operation is done bit by bit A0 with B0, A1 with B1, A2 with B2, A3 with B3. You get the output from the bottom F0-F3.
Lets add 5 and 9.
5: 0101
9: 1001
Preparing the input:
A3: 0 B3: 1
A2: 1 B2: 0
A1: 0 B1: 0
A0: 1 B0: 1
M: 1 for arithmetic mode
S: 1001, for A + B
S3: 1
S2: 0
S1: 0
S0: 1
------
Output:
F3: 1
F2: 1
F1: 1
F0: 0
First let's do the addition by hand
3210
0101 (A: 5)
+ 1001 (B: 9)
-------
1110 (14)
||||
||| `-> 1 + 1 = 10, 0 and we carry 1
|||
||`---> 0 + 0 + 1(carry) = 1, nothing to carry
||
|`----> 1 + 0 = 1, nothing to carry
|
`-----> 0 + 1 = 1, nothing to carry
You can see in the diagram, how A0 with B0's carry gets to A1 and B1 and etc, in the end you can see A3 with B3's carry gets to the Cn+4 output, which can be put in the Cn input of a next chip, you can chain multiple 74181 ALU units to make operations on more bits.
This is a list of all the things this amazing chip can do:

I want to emphasize again, this is the first circuit that we discuss is not actually a loop, but a complete feed forward transformation. And you can see how with very few elements it can do so many different things! Every time I look at it I am amazed.
When we are building our hypothetical computer, we would connect 2 of those, one after the other, so that we can do 8 bit operations. And we will hook our registers to it. We will have an instructions which will make the control unit load data into registers, then the next instruction will make it use the registers data and pass it to the ALU, after which will take the output of the ALU and put it on the bus.

But how does it work? How can we do math through a feed forward stream of information?
The magic of the ALU is in the way it uses logic gates (AND, OR, NOR, XOR, NAND etc). You saw in the beginning of the chapter how to build SR Latches and Flip Flops and store bits of information, and now I will show you how to do addition, and you will see that subtraction is also addition, and multiplication is also addition and division is also addition.. and negative numbers are made up.
One and Zero are the only true numbers! MUAHAHAH.
The following circuit can perform addition of 2 bits A
and B
plus a carry
bit Cin
, and produce a result S
and a carry bit Cout
This is the NAND truth table, I will leave you to try out the circuit yourself, try to add 1 + 0 with carry 1, and 1 + 1 with carry 1.
| X | Y | Q = NAND(X,Y) |
|---|---|---------------|
| 0 | 0 | 1 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
We want to add numbers that can be represented by 32 or even 64 bits, so we can just chain a bunch of adders.
The carry out of one becomes the carry in of the next. In this image you can see the least significant bit, is on the right, and the most significant bit is on the left.
place: |8|4|2|1|
-------|-|-|-|-|
value: |1|0|0|1|
In this example, if we toggle the least significant bit, the number changes from
1001
to 1000
, or from 9
to 8
, but if we toggle the most significant bit,
1001
becomes 0001
, or from 9
to 1
.
You can see now how we can add numbers, but how can we subtract? A - B
is the
same as A + (-B)
, So we need a way to represent negative numbers -B
. Knowing if a
number is positive or negative is a piece of information that we need to have,
and since it has exactly two possible values: positive
or negative
, we can
use 1 bit to tell us that.
We call it the sign bit, if its 1 that means the number is negative, if its 0
that means its positive. You can see this is a huge cost, to reduce our possible
number by one whole bit, if we have a 32 bit integer our maximum value is
4294967295, but if we have 31 bit integer the maximum value is 2147483647, but
we can have negative values. That is why in C we have the unsigned
keyword, so
that we can create unsigned long
, int
, char
, etc data types, to allow to
decide when we want to pay the price of the sign bit, in go
you also have
uint
and int
, but in java
all primitive integers are signed.
You might think that we just put the bit on or off and thats enough, which seem to work when you look at it.
sign bit
|
v
7| 0111
6| 0110
5| 0101
4| 0100
3| 0011
2| 0010
1| 0001
0| 0000
-1| 1001
-2| 1010
-3| 1011
-4| 1100
-5| 1101
-6| 1110
-7| 1111
But, if you try to add 5
+ -5
you will see it does not work:
0101
1101
----
10010
^
this bit is cut off since we don't have space
in our 4 bit computer
So 5 - 5
is equal to 2
, which.. is not good, and would lead to the absolute
collapse of the universe if it were true. It is weird to think what holds our
universe together, but one of the things seems to be that 5 - 5
is 0
.
There is a way to make the math work out, by inverting the bits of the negative
numbers, so 1 becomes from 0001
to 1110
, and so on. There is a slight
weirdness with 0, we have it both as +0 and as -0, but at least the math checks
out.
sign bit
|
v
7| 0111
6| 0110
5| 0101
4| 0100
3| 0011
2| 0010
1| 0001
0| 0000
-0| 1111
-1| 1110
-2| 1101
-3| 1100
-4| 1011
-5| 1010
-6| 1001
-6| 1000
So 5 - 5
is:
0101
1010
----
1111
Which is -0, much better than +2
we had before, lets try another subtraction 5 - 3
.
0101
1100
----
10001
So the result is 1, which is again not amazing, but we just need to add 1 to it
to get to the right value. You can try it with other numbers and will see you
are always missing 1. This method is called One's complement
. So A + (-B)
is
(A + NOT(B)) + 1
, it works and some systems use it, but it is quite annoying
with this -0
business.
Most systems use even better method called Two's complement
The way we do A + (-B)
is using Two's Complement
, which just removes the -0
, and replaces it with -1
sign bit
|
v
7| 0111
6| 0110
5| 0101
4| 0100
3| 0011
2| 0010
1| 0001
0| 0000
-1| 1111
-2| 1110
-3| 1101
-4| 1100
-5| 1011
-6| 1010
-7| 1001
-8| 1000
To convert a number to its negative you need to do NOT(B) + 1
, so 3 becomes
0011
-> 1100
and we add 1 => 1101
.
This way everything works out just fine.
So 5 - 5
is:
0101
1011
----
10000
^
cut
Which is -0, much better than +2
we had before, lets try another subtraction 5 - 3
.
0101
1101
----
10010
^
cut
It works out to 2
. So negating a number is NOT(B) + 1
, and A - B
is A + (NOT(B) + 1)
.
Make sure you watch Ben Eater's Two's Complement video, I copied the examples from there so that you are familiar when you watch it.
But how would we do multiplication and division, and what about fractions? I will briefly discuss them because they can easilly take over the whole book. Multiplication and division by 2 are extremely natural, you can see that by just moving/shifting the bit pattern left we double the value and moving it right we halve it.
Halving:
4: 0100
2: 0010
1: 0001
Doubling:
1: 0001
2: 0010
4: 0100
If we want to multiply 2 * 6, we can multiply 2 * 2 (which is easy, just moving it to the left once) and then add it to 2 * 4 (which is also easy, just moving it to the left twice), but what about 7 * 3? Well we will just have to do 7 + 7 + 7.
There are dedicated circuits that specialze in multiplication, like 74LS384. Division however is another story (unless it is division by 2), it requires way more complicated logic and multiple chips and multiple clock cycles to get it done. Watch some videos of people building minecraft calculators and see their horror when they have to build the division logic with redstone.
What about fractions? There are two ways, we can do fixed point fractions, for example we dedicate few bits for the whole part, and few bits for the fraction part, in a 32 bit system we could say, 1 bit is for sign, 15 bits are for the whole part, and 16 bits are for fraction. Then we could have special instructions for adding and multiplying, and they will know exactly what to do.
Or we could use floating point numbers, which are more complicated but more flexible, 32 bit floating point numbers use 1 sign bit, 8 bit exponent and 23 bit mantissa (also called significand).
Again, we wont go into detail, but there is special circuits needed in order to efficiently do floating point math.
Since our computer only needs 1 instruction, and all it does is subtract, we could do that by using 74LS283 adder and 74LS04 inverter, or using 74LS181 ALU that we can configure to do subtraction, or we can build our own adder using NAND gates. Since I just love the 74LS181 chip, we will use it, and it also allows you to experiment and try other things.