Skip to content

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.

alt text

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:

alt text

How is it Made?

We know the assembler converters assembly to machine code, but its as clear as how it actually works.

alt text

The assembler is actually pretty simple, just have a couple of mappings to each instructions with some extras.

alt text

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...

alt text

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.

alt text

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.

alt text

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.

alt text

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.

alt text

Instruction Fetching

alt text

So we know how a program moves, we can implement the hardware to do this.

alt text

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.

alt text

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.

alt text

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.

alt text

If you want to jump far, then you can use the jr instruction.

alt text

alt text

Again, this is very rare.

For relative branches, think of a number line.

alt text

In MIPS the PC points to the next instruction to run, so things are a little different.

alt text

The actual offset stored in the bitfield, is not 8, but actually 2 since we shifted to the left by 2 bits.

alt text

So all in all:

alt text

Next Page →