The anatomy of a computer (Part 4 of 4)

Completing the model

Jack Holland
Understanding computer science
8 min readMar 31, 2014

--

This is an ongoing series. Please check out the collection for the rest of the articles.

In the last post, I gave you an example of MIPS assembly code and how it translates into machine code:

MIPS assembly code
MIPS machine code

This post, I’d like to give a few more examples of assembly code to give you a better idea of what kinds of instructions processors have and then show you a basic diagram of the MIPS processor. Remember, the learning goal of this sequence is not to become an expert at processor design but to get a basic feel for how computers ultimately compute data.

As I mentioned last post, MIPS instructions come in three types: R, I, and J. R instructions take values from two registers, compute something with them, and store the result in the third register. The example above is an R-instruction; add takes two registers (in this case registers 10 and 8), adds their values together, and stores the sum in the third register (in this case register 13).

I-instructions take a register and an immediate value, compute something with the two values, and store the result in a second register. An immediate, or literal, value is a number typed directly into the code, rather than stored in a register. Here’s an example:

The I-instruction equivalent to add

In this case, addi (“add immediate”) adds the value stored in register 12 to the number 40 and stores the result in register 7. So if register 12 holds the value 20, then the processor computes 20 + 40 = 60, storing 60 in register 7. It works the same way as add, except one of the numbers it adds is stored immediately in the code rather than in a register.

Another basic instruction is and. It’s a lot like Cake’s and, except it works on each bit in a sequence rather than on a single Boolean like true or false. It’s called bitwise-and as opposed to logical-and. Take for instance, 1011 and 0110. Since bitwise-and compares each bit of the two sequences, it’s easier to to visualize when the two sequences are vertically aligned like this:

Computing the bitwise-and of 1011 and 0110 returns 0010. Why? Because each bit of 1011 is compared to each bit of 0110; if both bits are 1, then the resulting bit is 1; if one or both bits are 0, then the resulting bit is 0. In this example, the first bit of the first sequence is 1 and the first bit of the second is 0; the first bit of the new sequence is thus 0.

The same comparison is made for the second bits, third bits, etc. until the new sequence is the same length as the original ones’. A result of 0010 tells us that the third bit of both sequences is 1; every other pair of bits contains at least one 0, making the resulting bit 0.

A visualization of how bitwise-and works

Notice the similarity to logical-and? With logical-and, if both Booleans are true, the result is true; if one or both Booleans are false, then the result is false. In other words, bitwise-and treats 1 like true and 0 like false. Each bit of one sequence is compared to each bit of the other, and the result is a new sequence with 1's and 0's corresponding to and performed on each pair of bits. To tie this back to an earlier concept, you can view these bit sequences as arrays of Booleans, and bitwise-and as an instruction that accepts two arrays of Booleans and produces a new array of Booleans.

Moving on, another common MIPS instruction is jump. This tells the processor to jump to another part of the code. As you may have guessed, jump is a J-instruction. Instead of inputting and outputting registers, jump deals with addresses. What’s an address? A number that uniquely identifies some part of the code. Just like each element in an array gets a unique index, each instruction in a program gets a unique number that represents where it’s stored in memory.

We won’t go into the details of how this number is derived, so for now think of it like rooms in a hotel: each instruction gets its own room. Normally, the processor moves through the rooms sequentially; after Room 10 it goes to Room 11. But sometimes, the program wants to skip certain rooms and go straight to others. Take, for example,

A generic if-then-else expression

If x is true, y is computed; otherwise, z is computed. y and z are different instructions with different addresses, or room numbers. Depending on the value of x, the program needs to jump to one instruction or another. So this is where instructions like jump come in; they allow the processor to move to different parts of the program depending on the situation.

Unfortunately, we lack the prerequisite knowledge to cover every MIPS instruction. Rest assured, we’ll return to this topic in much more detail later. If your curiosity hasn’t been slaked, feel free to check out the full list.

As we learned with our original add example (at the top of this post and in the previous one), each assembly instruction is converted into machine code composed of a sequence of bits. How does the processor use these bit sequences to perform computations? In a simplified world, like this:

A highly simplified schematic of MIPS architecture

Please note that this is a very abridged schematic of MIPS architecture. However, its simplicity and elision makes it much more digestible and provides an intuitive basis for how the actual architecture is laid out. Let’s examine it in more detail.

First, let’s clarify what’s what. The gray boxes hide much of the complexity: they represent collections of electronic components wired together to serve some purpose. Some of the collections are simple and contain only a few dozen components (like the “+1") and others are quite complex (like the “memory”). Regardless of how complex and gray each box is, treat it as a black box — that is, a device whose purpose you understand but whose internal mechanisms you don’t. Down the road we will care about how memory is stored in circuitry, but for now view the “memory” box as a component that just works.

As you can see, each box is connected to other boxes via blue lines with arrows. These represent wires (or multiple wires bundled into a bus) and the arrows indicate in which direction the current flows. A clock regulates how often current flows through the architecture; in a simple, nonparallel processor the above diagram represents, one instruction is computed each cycle. The cycles-per-second are measured in Hertz. Modern computers achieve billions of cycles per second, giving them a “speed” in the range of gigahertz (GHz).

Now let’s examine each box individually. The cycle starts at the left, in the “address” box. This is the address we mentioned above — a number that uniquely identifies the current instruction. Normally, the address is incremented each cycle, as indicated by the wire connecting it to the “+1” box. However, if the current instruction is a J-instruction, the address is changed to the location specified by the instruction, as shown with the top-most line in the schematic. This wire comes from the “address -> instruction” box, which is a memory unit that holds the current program’s instructions. Give it an address and it returns the corresponding instruction.

Once the appropriate instruction has been retrieved, the “registers” box loads the values of any registers the instruction references. To clarify this, let’s reuse a previous example:

The values in registers 10 and 8 would be retrieved (register 13 will store the result, but this hasn’t been computed yet).

If you look to the right of the “registers” box, you’ll find two boxes: the “ALU” box and the “memory” box. ALU stands for arithmetic logic unit; as the name suggests, this box computes all of the arithmetic (addition, multiplication, etc.) and logical (and, or, etc.) operations that various instructions are designed to compute. In addition to being used for arithmetic instructions like add and addi, the ALU is also used to compute any addresses need to interact with main memory (main memory and its caches are represented by the “memory” box). We won’t get too much into how memory is loaded and stored, but recognize that the memory locations must be calculated and kept track of.

For memory-related instructions, the results from the ALU are stored in memory or loaded from memory into the register specified by the current instruction. For arithmetic and logical instructions, the ALU results are stored back into the registers; in the example above, register 13 would receive the result of the add instruction.

At this point, the cycle is complete and a new cycle with a new instructions begins, restarting the process. Even when it looks like your computer isn’t doing anything (e.g. you’re staring at your desktop wallpaper), the processor is executing billions of cycles a second in order to update the screen, run programs in the background, download updates, etc. The processor is always active, repeating this cycle until the computer is shut off.

It may look like nothing is going on, but the processor is cycling billions of times a second as long as the computer remains on

There is, of course, much more to discuss; this sequence was quite a macroscopic anatomy, barely scratching the surface of the inner workings. But hopefully you have a better idea of how computers operate and I can use this to catalyze our next topic: a brief survey of operating systems. While it may seem strange to discuss architecture and operating systems before even introducing an actual programming language, I think these survey sequences will help demystify many of the concepts I intend to introduce after.

Image credit: desktop

--

--