In this section, we introduce the instruction set architecture (ISA) of the MIPS processor. Figure 2.1.1 shows a conceptual overview over a computer with a MIPS processor. Of course, a concrete implementation is much more complex but we are focussing on the parts that are relevant for learning machine code programming. So from this very high-level perspective, a MIPS processor contains an arithmetic logical unit (ALU) that carries out all the computations. It also contains a register file, a small but fast storage facility where the programmer can store 32 values of 32 bits each. Register 0 cannot be written to and always reads 0. This is a “trick” to provide the constant 0 that is frequently used. Like all the other bus participants, the processor is connected to the bus via which it can send and receive data to the rest of the system, including the main memory. The address space of the system we are looking at here is 32-bit, which means that it contains the addresses from \(0\) to \(2^{32}-1\text{.}\)
Remark2.2.1.Registers/Memory.
Why do we need registers if we have main memory to store data? There mainly two reasons for having registers:
Speed.
Retrieving data from main memory is quite slow in comparison to the speed of the processor. For technical reasons, it can take several dozens to hundreds of clock cycles. So it is unpractical for a CPU to retrieve the data it operates on from the main memory constantly.
Addressability.
The address of a datum in main memory is 32-bits wide. If the CPU operated on main memory directly, a simple addition would need to invest three times 32 bits to describe the operands and the target memory cell which sums up to 96 bits. This makes the instruction word quite large in comparison to the three times 5 bits needed to address the register file.
Remark2.2.2.Caches.
If instructions are fetched from main memory and main memory accesses take so long compared to the speed of the processor, you may wonder why the processor's speed is actually higher than the one of main memory. The reason is that concrete systems use a hierarchy of so-called cache memories between the CPU and the actual main memory. These caches trade-off speed versus size and are able to provide data quicker, cache recently-accessed memory cells and also prefetch data from lower hierarchies that is “likely” to be used next. This has the effect that the instructions that will be fetched in the near future reside very closely to the CPU in the cache hierarchy.
Caches are mostly transparent to the programmer. We only need to care about them when we want to optimize our program for speed or are concerned with multi-processor systems that can execute several threads of a program simultaneously and therefore need to share data consistently. Both topics are out of the scope of this chapter.
A processor executes instructions. These instructions are just data that is stored in the memory of the computer. The program counter (PC) is a register inside the CPU that contains the address of the instruction that is currently executed. In MIPS (and many other modern ISAs), each instruction word is 32 bits wide. The vast majority of instructions fall in one of the following three categories:
Computation.
These instructions take two operands of which one is read from the register file and the other either also from the register file or directly from the instruction word. In the latter case, the operand is called an immediate. Immediates allow for embedding constants into the instruction word which is needed quite often when programming (think of adding 1 to a number for example). The arithmetic-logical unit (ALU) performs all the operations we have discussed in Chapter 1 (addition, subtraction, and, or, xor, and more) and writes the result back to the register file. The MIPS processor we look at has a word size of 32 bit which means that the ALU inputs and outputs are 32 bits wide.
Memory Access.
These instructions compute an address based on the contents of a register and an optional immediate and use this address to either load or store data from or to the main memory.
Control Flow.
When the processor executes a computation or memory instruction, it increments the program counter by 4 to let it point to the instruction right after the one that is currently been executed. Control flow instructions can influence the value of the program counter in various different ways that we discuss below. Essentially, they serve the purpose to alter the program's flow which is important to implement repeated and conditional execution of code. Without that, computers would not be very powerful because they only could execute a simple list of instructions.
Instruction Encoding.
Let us look at an example of how instruction words look like. As mentioned before, an instruction is just a 32-bit word. The bits in the word encode the operation that the processor shall carry out, the registers it accesses, and potential immediates. Let us consider the word 0x00641021. It consists of individual bit fields that represent the different properties mentioned above.
opcode, funct
Specifies the operation to be carried out. Here, opcode=0 funct=33 means that the ALU shall add the numbers.
rs, rt, rd
Specifies the registers of the first and second operand and the register where the result shall be placed.