MIPS Instruction Set
MIPS Instruction Set
17, 2016
In today’s lecture we will look at two ”co-processors”, namely the floating point processor (called
CP1 or the FPU) and the kernel processor (called CP0 or the ’system control’ processor). We will
look at the MIPS assembly language instructions for this processor.
This is the last lecture above MIPS programming. After this, we will go back to the circuits
and connect the general ideas about circuits to the particular instructions we have seen in MIPS,
mostly CPU instructions but occasionally CP0 too.
You can only read from the product register. You cannot manipulate it directly. In MARS, the
product register is shown as two 32 bit registers, HI and LO. If the HI register has all 0’s, then the
product that is computed can be represented by 32 bits (what’s in the LO register). Otherwise, we
have a number that is bigger than the maximum int and it would need to be treated separately.
Details are omitted here.
What about division? To understand division, we need to recall some terminology. If we divide
one positive integer by another, say 78/21, or more generally ”dividend/divisor” then we get a
quotient and a remainder, i.e.
e.g. 78 = 3 ∗ 21 + 15
In MIPS, the divide instruction also uses the HI and LO registers, as follows:
mfc1
register integer floating point
arithmetic register arithmetic
$0,..,$31 division mtc1 $f0,.. $f31 divison
multiplication multiplication
logical ops int−float convert
sw lwc1
lw swc1
Memory
(2^32 bytes)
The MIPS instructions for adding and subtracting floating point numbers are of the form:
add.s $f1, $f0, $f1 # single precision add
sub.s $f0, $f0, $f2 # single precision sub
add.d $f2, $f4, $f6 # double precision add
sub.d $f2, $f4, $f6 # double precision sub
Having a separate FPU takes some getting used to. For example, the following instructions have
incorrect syntax and are not allowed.
add.s $s0, $s0, $s1 # NOT ALLOWED (add.s expects FPU registers)
add $f0, $f2, $f2 # NOT ALLOWED (add expects CPU registers)
1
by “chip”, I mean the silicon-based electronics which contains the combinational and sequential circuits
similarly for double precision, except now we must use an even number register for the argument,
Note that the memory address is held in a CPU register, not in a float register!
To load/store double precision, we could use two operations to load/store the two words. It is
easier though to just use a pseudoinstruction:
Example 1
Let’s say we have an integer, say -8 in $s0, and we want to convert it to a single precision float and
put the result in $f0. First, we move the integer from the CPU to FPU (c1). Then we convert.
addi $s0, $0, -8 # $s0 = 0xfffffff8
mtc1 $s0, $f0 # from $s0 to $f0 -> $f0 = 0xfffffff8
cvt.s.w $f0, $f0 # from w to s -> $f0 = 0xc1000000
As an exercise, recall from the first few lectures of the course why the registers have the coded
values shown on the right.
Similarly, suppose we have a single precision float (in $f0) and we want to convert it to an
integer and put it into $s0. Here is how we could do it.
cvt.w.s $f2, $f0 # we use $f2 as a temp here
mfc1 $s0, $f2
Example 2
int i = 841242345 // This value in binary would be more than 23
// bits, but less than 32 bits. (Specifically
// we cannot write it exactly as a float.)
Example 3
double x = -0.3;
double y = (float) x;
Here it is in MIPS:
.data
d1 : .double -0.3
.text
l.d $f0, d1 # $f0 = 0xbfd3333333333333 <- these two...
cvt.s.d $f2, $f0 # $f2 = 0xbe9999a
cvt.d.s $f4, $f2 # $f4 = 0xbfd3333340000000 <- ... are different
c. .s $f2, $f4
Here the “ ” is any comparison operator such as: eq, neq, lt, le, ge, gt. These compare
operations are R format instructions.
The programmer doesn’t have access to the result of the comparison. It is stored in a special
D flip-flop, which is written to by the comparison instruction, and read from by the following
conditional branch instruction:
The same one bit register is used when comparisons are made using double precision numbers.
c. .d $f2, $f4
System call
I did not mention it in the slides, but you also can print and read float and double from the
console, using syscall. Rather than printing from or reading to an argument register, it uses a
particular coprocesor 1 register shown in table below.
• EPC: ($14) The exception program counter contains a return address in the program. It is
the address in the program that follows the instruction that caused the exception.
• Cause: ($13) contains a code for what kinds of exception occured. ( invalid operation, division
by zero, overflow )
• BadVaddr: ($8) holds the address that led to an exception if the user program tried to access
an illegal address e.g. above 0x80000000.
• Status: ($12): says whether the processor is running in kernel mode or user mode. Says
whether the processor can be interrupted by various possible interrupts (We will discuss
interrupts in a few weeks).
This is the opposite problem. Now I am trying to load from the text (instruction) segment. Note
that the MARS assembler cannot detect this problem, since the problem only occurs at runtime,
once it is determined that $s0 contains an address in the text segment, not the data segment.
Example 4: Overflow
addi $s0, $0, 1
sll $s0, $s0, 30 # $s0 = 2^30
mtc1 $s0, $f0
cvt.s.w $f0, $f0 # $f0 = 2^30 = 1.00000000000000000000000 x 2^30
mul.s $f0, $f0, $f0 # $f0 = 2^60
mul.s $f0, $f0, $f0 # $f0 = 2^120
mul.s $f0, $f0, $f0 # $f0 = 2^240 -> overflow
Interesting, no exception occurs here. Instead, if you look at the result that ends up in $f0, namely
0x7f80 0000, you will find that it represents +∞, namely 0 for the sign bit, 11111111 for the
exponent, and then 23 0’s for the significand.
Example 6: 0/0
mtc1 $0, $f1
div.s $f2, $f1, $f1 # 0/0