# MIPS Floating Point Architecture using PCSPIM

Working with MIPS Floating Point Architecture using PCSPIM

Until now we have checked only integer computations  in PCSPIM from our last tutorials, but in reality computations also involve floating point arithmetic. For this purpose MIPS supports various floating point instructions along with having 32 special floating point registers \$f0 − \$f31. Some very interesting facts about Floating Point (hereafter called FP) organization and FP programming are given below.  Understand these as they will help you in working with floating point numbers in the MIPS architecture.

1. There are thirty two 32-bit registers for floating point operation. These registers are separate from the integer registers (R0 – R31) that you have worked with earlier.
2. F0 is like any other register unlike R0 which is always zero.  F0 can be used as a destination.
3. You cannot mix FP registers and Integer registers in one instruction (except for FP Load/Store instructions).
4. Address to a FP number (in memory) is stored in an INT register (address or pointer itself cannot be FP, a pointer is always a 32-bit integer address).
5. It is not recommended to compare two FP values for equality in programs.
6. In MIPS, floating point arithmetic is performed by a separate hardware chip, called Coprocessor 1 by tradition. The FP coprocessor has its own FP Arithmetic Unit and 32 floating-point registers, named \$f0-\$f31, as mentioned above.  All these registers are stored in a separate register file.  This means that both ALU and FPU can work on different instructions independently, thus increasing the throughput of the system.
7. MIPS floating point hardware supports both single- and double-precision values.  Just like integer registers, floating point registers are 32 bits long.  Each register can hold a single-precision value or they can be combined to hold 16 double-precision numbers: \$f0-\$f1, \$f2-\$f3, …, \$f30-\$f31.  However, in this lab we will only work with Single Precision FP Numbers.
8. Floating-point programming conventions: Just like integer registers, code written for floating point needs to follow specific conventions (such as for procedure calls).
1. Floating-point (input) arguments are placed in registers \$f12-\$f15
2. Floating-point return values go into \$f0-\$f1
3. Registers \$f0- \$f19 are caller-saved
4. Registers \$f20-\$f31 are callee-saved
9. All FP (single precision) instructions end with .s suffix in assembly. For example,
the instruction add.s \$f0, \$f1, \$f2 for FP numbers, adds the contents of FP registers \$f1 and \$f2 and places the result in FP register \$f0. Notice ‘.s’ is for single precision.
10. Product of two FP numbers fits in a single 32-bit FP register unlike integer multiplication where the product fits in 64-bits

### MIPS FLOATING POINT ASSEMBLY  INSTRCUTIONS

Floating-point MIPS instructions are similar to the integer instructions, both in structure and syntax.  Many instructions can be used directly, but with the ‘.s’ extension. For example,

mul.s    \$f7, \$f9, \$f21             # FP Multiplication

li.s        \$f5,  3.14159              # FP Load Immediate

### 2.1 Memory accesses  MIPS FLOATING POINT ASSEMBLY  INSTRCUTIONS

There are two instructions for data transfer.  The format is:

l.s \$f2, 0(\$a0)              # Loading from Memory in to a FP Register: \$f2=MEM[\$a0]
s.s \$f4, 4(\$s0)              # Storing from a FP Register into Memory: MEM[\$s0+4]=\$f4

Note: The Base Address is always provided in an integer register since the address is an integer.

### 2.2 Moving Integer values to FP  IN MIPS FLOATING POINT ASSEMBLY  INSTRCUTIONS

There are two steps to transfer data to and from Integer registers and FP registers.

STEP-1: Instructions to transfer raw data between integer registers and floating-point ones.

mtc1 \$t0, \$f0        # move to Coprocessor1          result: \$f0 = \$t0

mfc1 \$t0, \$f0        # move from Coprocessor1      result: \$t0 = \$f0

STEP-2:  The above instruction has copied bits but the result is still not in proper 32-bit Single Precision FP format. Instructions to convert contents of the source register to the destination register in the correct format are:

cvt.s.w \$f2, \$f2     # convert from word (stored in \$f2) to single-precision (in \$f2)

cvt.w.s \$f2, \$f2     # convert from single-precision (stored in \$f2) to word (in \$f2)

2.3 Branches

Conditional branches are performed in two steps.  First, perform the comparison.  Second, branch depending on the result.  This is quite similar to the behavior of SLT instructions.  For example, the instruction branch to LOOP if \$f2 < \$f4 can be written as:

c.lt.s \$f2, \$f4               # if (\$f2 < \$f4) set bit comp to 1, else set it to 0

bc1t LOOP                 # if (comp == 1) go to LOOP

Note that the first instruction can use any of the three possible comparisons (eq, lt, le) and can compare.  Also, the name of the branch instruction is the abbreviation of “branch if the condition code of coprocessor1 is true” where true means set to 1.

Recall that the familiar set-if instructions use a programmer-specified register to store the result of the comparison.  But since the result is either 0 or 1, one bit suffices to store the result.  The branch instruction then uses the result produced by the previous instruction to decide what to do next.

Exercise 1: Reading a Floating Point Value using SYSCALL and examining
the Single Precision Binary Floating Point (32-bit) format
:

Read the value of π = 3.1415 using SYSCALL (\$v0 = 6, returns the value in \$f0) as given in the reference table on Page 8. The code is given below:

Code:

```.data

Mesg1: .asciiz  “Input a FP Number (Do not forget the dot):  “

.text

.globl main

main:

la \$a0, Mesg1

li \$v0, 4

syscall       # Print the string “Mesg1”

li \$v0, 6     # Place 6 in \$v0 to read a floating number

syscall       # FP number returned in \$f0

End_Prog:

li \$v0, 10

syscall```

After running this program and entering 3.1415 as input. Make the register display for FP as HEX and note the value of sign (MSB), EXP (8-bit biased) and mantissa (23-bit with implicit 1). Fill the following:

Hex Number in \$f0: 0x40490e56

Its Binary value: 0 10000000 10010010000111001010110

Sign: 0, Exponent: 10000000, Fraction: 10010010000111001010110

Similarly, run this program again and this time enter −0.0005 as the input number and repeat the above task.

Hex Number in \$f0: 0xba03126f

Its Binary value: 1 01110100 00000110001001001101111

Sign: 1, Exponent: 01110100, Fraction: 00000110001001001101111

Exercise 2: Procedure Call for a FP Program:

Write a MIPS code that asks for Fahrenheit temperature and returns the Celsius value which is printed on the console. The conversion formula is:

Celsius = (Fahrenheit − 32) ÷ 1.8

Write a procedure (function) “T_Convert” for this temperature conversion.  Fahrenheit value is to be passed into the function as single precision data and Celsius value is to be returned in single precision format.  Remember to follow all the floating point procedure conventions mentioned in Part-1, Point-8 (a,b,c,d). Give the Celsius values for the following Fahrenheit values:

 Temperature in Fahrenheit Temperature in Celsius 32 0.00000000 212 100.00000000 98.6 37.00000000 122 50.00000000 149 65.00000000

Code of the Program:

```.data

Input:              .asciiz "\nPlease Enter the Temperature in Fahrenheit: "

Output:            .asciiz "\nThe Temperature in Celsius is: "

.text

.globl main

main:

la \$a0, Input

li \$v0, 4

syscall                          # Print Input String

li \$v0, 6

syscall                          # Read Floating Point Number in \$f0

li.s \$f4, 0.0                  # Initialize \$f4 with 0.0

add.s \$f12, \$f4, \$f0    # Move input number into \$f12 as input parameter

jal T_Convert            # Call Procedure T_Convert with input in \$f12

li.s \$f4, 0.0                  # Initialize \$f4 with 0.0

add.s \$f12, \$f4, \$f1    # Procedure returns result in \$f1, move it to \$f12

la \$a0, Output

li \$v0, 4

syscall                          # Print Output String

li \$v0, 2

syscall                          # Print result contained in \$f12

End_Prog:

li \$v0, 10

syscall                          # Exit to System

T_Convert:                            # Procedure T_Convert

li.s \$f5, 1.8                  # Load 1.8 in \$f5

li.s \$f6, 32.0                # Load 32.0 in \$f6

sub.s \$f1, \$f12, \$f6     # \$f1 =  Fahrenheit(\$f12) - 32

div.s \$f1, \$f1, \$f5       # Celsius (\$f1) = (Fahrenheit - 32) / 1.8

Exercise 3: Average of a FP Array: Write a program to find and print the average value of a floating point array in the console area of PCSPIM. The assembler directive .float is used to initialize floating point numbers. Initial framework is given below:

```.data

myArray: .float 200.86, -100.0, 300.0, -150.7258, 400.68562,
-200.5686, 54.6789, 481.9827, -682.4012, 586.4512

.text

.globl main

main:

la \$s0, myArray    # FP Array Base Address in integer register \$s0

li \$t1, 0                        # Loop index in integer register \$t1

li \$t2, 10                      # Loop limit in integer register \$t2

li.s \$f12, 0.0                # Initialize FP register \$f12 with 0.0

li.s \$f13, 10.0              # Initialize FP register \$f13 with 10.0

loop:

sll \$t3, \$t1, 2               # Index × 4

l.s \$f2, 0(\$s1)              # Load FP Array Number into \$f2

add.s \$f12, \$f12, \$f2  # Accumulate the loaded FP Number in \$f12

addi \$t1, \$t1, 1           # Loop Index++

bne \$t1, \$t2, loop        # Branch to label loop if loop limit not reached

div.s \$f12, \$f12, \$f13 # Divide the accumulated value by 10 (number of elements)

la \$a0, Result

li \$v0, 4

syscall                          # Print string Result

li \$v0, 2

syscall                          # Print the result (Average Value of 10 FP Numbers)

End_Prog:

li \$v0, 10

syscall       # Exit to System```

# Average Value is: 89.09628296 (As displayed on the Console Window)

Exercise 4: Printing the MIN of 12 user defined FP Numbers: Write a MIPS program to find the MIN of twelve floating point numbers.  The user should be asked to input 12 FP numbers and the program should print the MIN value (SYSCALL with \$v0 = 2 and FP number in \$f12 – see the table on Page 7). You are not required to store these 12 values in any array, just take them as input from the user through the console and print the MIN.

Code:

```.data

Msg1:     .asciiz "\nPlease enter the FP Number: "

Msg2:     .asciiz "\nThe Smallest FP Number is: "

.text

.globl main

main:

li \$t0, 0                        # Loop index in integer register \$t0

li \$t1, 12                      # Loop limit in integer register \$t1

li.s \$f2, 0.0                  # The FP register \$f2 will be used to hold MIN, Initialize to 0.0

li.s \$f3, 0.0                  # Initialize FP register \$f3 with 0.0

Input_Loop:

beq \$t0, \$t1, Print       # Branch to label Print if loop limit reached

addi \$t0, \$t0, 1           # Loop Index++

li \$v0, 4                       # Place 4 in \$v0

syscall                          # Print String Msg1

li \$v0, 6                       # Place 6 in \$v0

syscall                          # Read FP Number into FP register \$f0

c.lt.s \$f2, \$f0               # if (\$f2 < \$f0) set bit comp to 1, else set it to 0

bc1t Input_Loop       # if (comp == 1) go to Input_Loop (No need to update MIN)

add.s \$f2, \$f0, \$f3      # else move new MIN from \$f0 to \$f2

Print:

add.s \$f12, \$f2, \$f3    # Move final MIN value from \$f2 to \$f12

li \$v0, 4                       # Place 4 in \$v0

syscall                          # Print String Msg2

li \$v0, 2                       # Place 2 in \$v0

syscall                          # Print the contents of \$f12 (MIN)

End_Prog:

li \$v0, 10

syscall```