By now you know that machine language is the pattern of bits a CPU uses to encode its instructions. Instructions are usually encoded as bitfields in order to pack a lot of information into a small number of bits.

MIPS uses a few different instruction formats in order to encode its instructions. No matter what format, all MIPS instructions are 32 bits.

The one you’ll be looking at today is the I-type format, where “I” stands for “immediate.” This is used to encode instructions with two registers and an immediate such as li, addi, lw, and beq.

Here’s how it looks:

So for example, addi t0, s1, 123 will be encoded as:


First: think about it

Using the diagram above, write yourself some notes to answer these questions.


Now: write a program about it

Right click this link and save it. It’s the skeleton/driver code for this lab.

The goal is to have your program output the following:

0x2228007b
0x1100fff8

opcode = 8
rs = 17
rt = 8
immediate = 123

opcode = 4
rs = 8
rt = 0
immediate = -8

Encoding instructions

The encode_instruction function takes four arguments in this order: opcode, rs, rt, and immediate. (Look at main to see how it’s called.)

It should:

This function isn’t too long. Once you write it, you might see this:

0x2228007b
0xfffffff8

The second instruction doesn’t quite look right. Step through and have a look at what value is in a3. It’s -8… negative numbers have a bunch of 1 bits at the beginning. That’s not good.

The immediate should only be 16 bits. So how can you “filter out” the low 16 bits of a3 and “turn off” the upper 16 bits? Do that in encode_instruction before ORing everything together, and you should now get the correct output:

0x2228007b
0x1100fff8

Decoding instructions

Decoding isn’t much more complicated, but you’ll be printing it out with strings, so that will make this function a bit longer.

decode_instruction takes 1 argument: the encoded instruction to be decoded.

It should do the following:

Pretty straightforward, but there are a few things to note.

You have to reuse a0, so, uh, hm.

The syscalls expect their arguments in a0, but this function takes an argument in a0.

Printing strings

I’ve given you 4 strings in the .data segment. To print a string, you use syscall 4, and you use la (not li or lw) to put the address of the string in a0:

	la	a0, some_string
	li	v0, 4
	syscall

The second instruction doesn’t decode properly…

You might get this:

opcode = 4
rs = 8
rt = 0
immediate = 65528

The immediate should be -8. What’s going on here?

It’s because it’s a negative number, but it needs to be sign-extended from 16 bits to 32 bits. Right now, it’s 0x0000FFF8; it needs to be 0xFFFFFFF8.

We can do this with a funky trick (assuming that the value to be sign-extended is in t0 to begin with):

	sll t0, t0, 16
	sra t0, t0, 16 # shift right *arithmetic*

sra is a new kind of shift, an arithmetic right shift. It’s kinda like sign-extension: instead of shifting 0s into the left side, it smears the top bit into the new places. Just like sign extension!

If you do this after ANDing the immediate value, you should now get -8 in the output!


Submitting

Make sure your file is named username_lab4.asm, like jfb42_lab4.asm.

Submit here.

Drag your asm file into your browser to upload. If you can see your file, you uploaded it correctly!

You can also re-upload if you made a mistake and need to fix it.