6. Program Counter
Assemblers and Compilers
Machine code is the binary representation of assembly. We encode each instruction as a bitfield. MIPS has three instruction formats. The opcode (and funct) field identifies which instruction it is.
All R types have an opcode of 0. shamt is shift amount which is used for the shift instructions (it makes sense to only shift 31 bits so we need 5 bits for this field).
I types include lw, sw, branches, lui, ori, etc. rt is the like the rd, target is similar to desitaiton.
J types are for jumps, like going to a function or a loop.
How it does it:
How is it Made?
We know the assembler converters assembly to machine code, but its as clear as how it actually works.
The assembler is actually pretty simple, just have a couple of mappings to each instructions with some extras.
What about jumps and labels?
If a label doesn't exist, throw an error. Now we have machine code! Then packaged it into a casing called an object file. Then the object files are linked and then you get an executable program.
Compilers are... hard...
For this course, it's just a grinder, throw a .c into the compiler then object files come out.
Jumps and Branches
Control flow is divided into two groups.
Each jump or branch has a target (where it goes to). We would like to be able to encode any target address. But we have a fixed number of bits to encode our instructions.
Jump targets can be quite far away since we use them for functions. While loops and if statements for branches, they are most closer together.
Jumps are absolute and branches are relative. Relative jumps allow you to move functions in memory without issues.
To save some more space, since every MIPS instructions is 4 bytes and they are aligned on addresses of multiples of 4 and multiples of 4 end in 00 in binary, we save two bits by just not storing them.
Instruction Fetching
So we know how a program moves, we can implement the hardware to do this.
The easiest is just moving ahead by 1 instruction each cycle.
Now... what about jumps! Well they put a constant value into the PC called the jump target. We have two choices, how do we choose, use a mux.
Wait, types have 26 bits for the target, but the PC is 32 bits! So what do we do?!?!?!?
Well, we don't need to store the lower 2 bits because of alignment. Also, most programs are nowhere near big enough to need 32-bit addresses. So in MIPS, jumps only change the lower 28 bits of the program counter.
The process of getting the instruction is to set the opcode to 00010. Then get the absolute address, ignore the leftmost 4 bits and the rightmost 2 bits throw that into the target.
To go back, just shift the instruction 2 bits to the left and set it to the program counter.
Do we really need the full 32-bit address? Well, on rare cases. For example, booting your computer sometimes jumps a huge amount in memory.
If you want to jump far, then you can use the jr instruction.
Again, this is very rare.
For relative branches, think of a number line.
In MIPS the PC points to the next instruction to run, so things are a little different.
The actual offset stored in the bitfield, is not 8, but actually 2 since we shifted to the left by 2 bits.
So all in all: