An exploration into how your Processor works.
In previous posts I have described how computers store information and how the special ALU chip can run elementary operations on that information. In this post I will endeavor to bring it all together into a cohesive overview of how your processor works. Before I dive into the details, I want to step back and talk about the overall architecture.
If we take a look at CPU it actually appears simple, and in some ways it is! We have the program which is written by the programmer. Think of the program like a list of commands. This list of commands is fed to the ALU which then executes the command that it is given. More than likely, that command will have something to do with data that has been stored in our data container – at which point the ALU will interact with the data and update it as needed. Once the command has been executed it moves to the next command or line in the program and begins executing that command. This brilliant architecture was invented by Von Neumann in the 1940s. It is such an effective design that it is still being utilized today. My current computer is running a Ryzen AMD processor that operates at peaks of 3.6GHZ. Which means that it is running 3.6 billion cycles per second.
In exploring the CPU I feel like the best part to start is the program. As I said before, you can think of the program as a series of instructions for the ALU. Since this CPU is built on a 16-bit architecture those commands come in the form of a 16-bit number. The following is a set of seven instructions to ALU:
Now that might not be very helpful and is definitely not human readable, so lets see if we can break it down into something more understandable. It turns out that these codes are encoded in the following way.
A or C Instruction
As you can see, there are 5 different groups of instructions packed into this 16 digit binary number. The bit on the far left dictates whether the instruction is a C instruction or an A instruction. If it is that the bit is set to one, then the instruction is a C instruction; otherwise the instruction is an A instruction. Don’t worry too much about the difference between an A and C instruction. An easy way to think about it is that if it is an A instruction, then it is referring to an address. In which case, none of the groups matter because the rest of the number is to be used as an address to get to a particular place in memory. On the other hand, if that bit is set to one, then the rest of the bits have instructions encoded into them. In fact, with just this knowledge you can look at the binary instructions I gave above and note that there are two A instructions and the rest appear to be C instructions. As a bonus, those A instructions are just zero, which means it is referencing the very first address in the RAM. As another teaser, that very first register of RAM is where the stack pointer resides. I won’t get into what exactly that does but I’ll leave that as an advertisement for a future post.
Write to the A or M Register
You’ll notice that we have two bits that are discarded. Now remember, if this system was built with a 64 bit architecture we could be sending 64 instructions to the CPU which could hold much more data than our current instructions. But as it turns out, 16-bit is more than sufficient for our purposes and in fact we end up not needing two of those bits, so those will just be ignored. That gets us to the fourth bit from the left signified by the grey box. There isn’t anything complicated about this bit. The state of this bit dictates whether the instruction will operate on the A register or the M register. What is the A and M register? Well, without getting too far into the weeds, the A register is the address of the currently selected RAM and the M register is the contents of the currently selected RAM. Probably easier to picture a bank vault that has many safety deposit boxes. Well when you pull a safety deposit box out to inspect it, the A register gets set to whatever number that box is. At the same time the M register is set to the contents of that safety deposit box.
ALU Function Code
The next 6 bits which have been grouped in blue are the meat of what the ALU is supposed to do with whatever number it is working on. Now if you read my post explaining how the ALU works, you will remember that part of the ALU’s construction was that it had six inputs. That is exactly how those bits are used. Here is how those bits are mapped:
Well, almost exactly. With one little catch that is actually ingenious. You use the above table in combination with the A or M register chip to produce the following behavior:
So in general, if that A or M register bit is 0 then the ALU will operate on the A and the D register. Otherwise it will operate on the D and M register. What is this D register? Well it is the third and only other register used by the CPU. Whereas the A register holds the address of the selected RAM and M holds the contents of the selected RAM, the D register is just a general purpose register where you can hold whatever you want in it. It comes in really useful when updating variables and things such as that. But that also is information that belongs in a separate post. From the above image you can see the usefulness of the CPU starting to show itself.
Now lets look at the yellow Destination bits. The value of these bits dictates which register, if any, should receive that value that the ALU will output after completing the function. As I said before, there are only three registers that store information in the CPU. There are the A, D and M registers. So based on whatever values you set those destination bits to be, it will then either write to all three registers or to none of them.
So say we wanted the ALU to perform the function 3+2, we then wanted to store those values in the M and D registers. We would give the appropriate ALU instruction then we would set the destination bits to be “011”.
Last but not least, lets look at the jump bits. Like the destination bits, we have only three values. But these three bits are a little more tricky than the destination bits in that they are conditional. Having this jump bit functionality provides the programmer with the ability to jump to any “line” or instruction. Say for example you were writing a program in which, when an error was encountered, you would want to jump to a safe part of the program. In order to do that, you would need to employ these three bits to achieve the desired effect. Having the ability to jump from one place in code to another place in code opens up huge opportunities like loops, switches and if-then logic. Lets take a look at the associated table that specifies the behavior.
So unlike the destination bits, these bits are set and then for the most part their behavior is dictated by the output of the ALU. It is important to note that you can set the bits to zero (this is most often the case) to indicate no jump or set all the bits to one for an unconditional jump. The word “out” in the diagram represents the output of the ALU. When using a “jump” statement it is assumed that the place where you want to jump to has been set in the A (address) Register. If the condition for jumping has been met, then the program will jump to the address currently stored in the A register.
Now with all this knowledge in our heads, lets decipher the cryptic instructions that were at the beginning of this post. Lets take the first instruction which was:
Now lets drop it into our diagram to make it easier to read.
The first thing we see is that this command is a C instruction, so the following bits will not be associated with an address. Secondly, we note that the A or M register is set to zero. With that in mind, we refer to our ALU chart to find the command “101010.” What we find is that, when translated, those bits are telling the ALU to output a zero. Then looking to the destination bits we can figure out where we are sending that zero. Consulting the chart we find that “010” is setting the D register. So now we know that the first instruction was to set the D register to zero. Congratulations, you have just deciphered your first binary command!
The second command was all zeros which, as I pointed out before, sets the A register to zero. Which happens to be the address where we store our Stack Pointer, which I will discuss later.
Lets do one more and I will leave the last there for you to decipher at your leisure. The next command was:
Again we note that this is a C instruction that wants to operate on the M (contents of the currently selected RAM) register. Looking at the ALU code “110000” we match it up with function that outputs the contents of the M register. Now looking at the destination bits we see that “100” = A register. So effectively we are setting the A (address) register to whatever the contents of the M register are. If we remember the command previous to this was to select the RAM where our stack pointer was. So essentially what happened in those two lines of code was to select the Stack Pointer and then “go to” whatever address was stored in the stack pointer. But more on that when we look into Machine Language and how to actually write programs our new computer can understand. Here are the instructions left to be decoded.