MIPS Assembly for CS Students
MIPS Assembly for CS Students
Write the utility function Avg_of_Avgs in MIPS assembly language which takes 6 input values
(3 in $a0, $a1 and $a2, and 3 on the stack), and returns one single-precision floating point
output value in $fv0. $a0, $a1 and $a2 each contain the starting address in main memory of an
array (A, B, and C, respectively), where each item in the arrays is an 32-bit floating point
number in IEEE 754 format. The 3 top locations on the stack (TOS, TOS-1 and TOS-2) contain
the values lengthA, lengthB and lengthC, which are the numbers of values in each array.
Your Avg_of_Avgs routine will find the average of averages, and should return that value in
$fv0. It will randomly select two of the arrays (either A & B, A & C, or B & C), find the average
of the floating point numbers in each of the 2 chosen arrays, and then take the average of these
averages. It should make use of calls to Choose1 and to FPAvg, both of which are library
functions that are written, debugged and available to you.
int Choose1 (int x, y, z) is the C header for a routine that takes 3 integers as inputs, randomly
chooses one of them, and returns that value. How it is implemented is unknown to you, but you
know that it obeys the MIPS convention for the use of registers.
float FPAvg (float &Array, int length) is the C header for a routine that takes as 1 input an
address of an array of floating point values, and takes as the other input the length of the array. It
calculates the average of all the values in the array, and returns this floating point average as its
return value. Again, you do not know how it is implemented, but you know that it obeys the
MIPS convention for the use of registers.
To convert an integer into a floating point number, you may use the following sequence
mtc1 $a0, $ft0 # move the integer into a floating point register
cvt.s.w $fv0, $ft0 # convert it into a single-precision FP value
--Your Avg_of_Avgs should obey the MIPS convention for the use of registers, just as the calling
program, Choose1 and FPAvg will obey it.
--Your code for Avg_of_Avgs may use pseudo-instructions, but you may not use psuedo-syntax.
--Code performance is important, but correctness is more important!
--Explain your code with comments and a header.
Use the outline of the code below, to help guide you as you write the MIPS.
# initialization of registers
move $s0, $a0 # put address of A in $s0
move $s1, $a1 # put address of B in $s1
move $s2, $a2 # put address of C in $s2
2
# find the average of the 2 averages
findAvg: add.s $fs0, $fs0, $fv0 # add the two average values together
addi $t0, $0, 2 # put 2 into $t0
mtc1 $t0, $ft0 # move the 2 into $ft0
cvt.s.w $fv0, $ft0 # convert it into a single-precision FP value
div.s $fv0, $fs0, $fv0 # divide the sum of averages by 2; put result in $fv0
# return to caller
jr $ra
In the following MIPS code, there may be inefficiencies that cause it to execute slowly,
violations of the MIPS convention for register usage, and errors that will cause it execute
incorrectly. Find and fix all such problems in the space next to the given code. Use only real
MIPS instructions.
Q1: addi $sp, $sp, -12
sw $t0, -8 ($sp) // no need to push a t-register; must be +8
sw $s0, -4 ($sp) // don’t use $s0--this push can be skipped; +4
sw $s1, 0 ($sp) // useless push, since $s1 is not used
Top: addi $s0, $a0, 0 // useless transfer, just use $a0! (But if done, do
// it before the loop, since it initializes $s0)
mul $t0, $s0, $a1
add $v0, $v0, $t0 // $v0 is used here with first initializing it
subi $s0, $s0, 1 // no such instruction “subi”; just use $s0
bne $s0, $0, Top // just use $a0
3
Top: mul $t0, $a0, $a1
add $v0, $v0, $t0
addi $a0, $a0, -1
bne $a0, $0, Top
jr $ra
Question 2 (MT #1 Spring 2016) MIPS Programming for Floating Point Average
Write the utility function FPaverage in MIPS assembly language which takes 2 input values in
$a0 and $a1, gets n integer inputs from main memory, and returns one double-precision floating
point output value in $fv0.
$a0 contains the starting address in main memory of an array, where each item in the array is an
integer. $a1 contains the value n, which is the number of integers in the array.
Your FPaverage routine will obtain n double-precision floating point numbers with the help of
intToFP, and use those floating point values in making its calculation. FPaverage should return
in $fv0 the average of the n floating point numbers.
intToFP takes 1 input argument: it receives an integer in $a0. It converts that integer into its
floating point representation, and returns the double-precision floating point result in $fv0.
Assume intToFP is already written, debugged, and available to you as a library function.
intToFP: mtc1 $a0, $ft0 # move the integer into a floating point register
cvt.d.w $fv0, $ft0 # convert it into a double-precision FP value
jr $ra # return to caller, with the result in $fv0
--Your FPaverage should obey the MIPS convention for the use of registers, just as the calling
program and intToFP will obey it.
--Your code for FPaverage may use pseudo-instructions, but you may not use psuedo-syntax.
--Code performance is important, but correctness is more important!
--Explain your code with comments and a header.
FPaverage: # push the needed registers onto stack, before changing them
addi $sp, $sp, -24
sdc1 $fs0, 16($sp) # push $fs0, to store double-precision Sum
sw $s2, 12($sp) # $s2 will store pointer to memory array
sw $s1, 8($sp) # $s1 will store n
sw $s0, 4($sp) # $s0 will store count
sw $ra, 0($sp) # save $ra since FPaverage is non-leaf
# initialization of registers
mtc1 $0, $fs0 # move 0 into a floating point register
4
cvt.d.w $fs0, $fs0 # convert 0 into a double-precision FP Sum
addi $s0, $0, 0 # count = 0
move $s1, $a1 # n is now stored in $s1
move $s2, $a0 # memory array pointer is stored in $s1
Top: lw $a0, 0($s2) # get next integer value from array in memory
jal intToFP # call intToFP to convert it to floating point
add.d $fs0, $fs0, $fv0 # add new FP value into Sum
addi $s0, $s0, 1 # increment count
addi $s2, $s2, 4 # increment pointer to next integer’s address
bne $s0, $s1, Top # until count = n, keep iterating the loop
# return to caller
jr $ra
Note that the code can be speeded up by avoiding the call to intToFP in “calculate the FP
average”. It is faster to do it directly:
mtc1 $s1, $fv0 # move n into a floating point register
cvt.d.w $fv0, $fv0 # convert n into a double-precision FP
This way uses only 2 instructions, instead of 5 (mov, jal, and the 3 in the intToFP routine)
Another way to improve performance is to eliminate the loop counter, by pre-calculating the
final address of the array (Max), and making the loop test compare the pointer to that Max+4 .
The improved loop is now:
Top: lw $a0, 0($s2) # get next integer value from array in memory
jal intToFP # call intToFP to convert it to floating point
add.d $fs0, $fs0, $fv0 # add new FP value into Sum
addi $s2, $s2, 4 # increment pointer to next integer’s address
bne $s2, $s0, Top # until pointer = Max+4, keep iterating the loop
5
This way the loop only contains 5 instructions (but one of them is a call to intToFP). It looks like
1 instruction, but actually 4 are performed, so the total is 8 per loop iteration.
The ultimate speedup would be to eliminate the call to intToFP inside the loop by “in-lining” the
function. In-lining brings the body of the function to the location of the call, eliminating the jal
and the jr instructions (and possibly other instructions also). The resulting loop is like this:
Top: lw $a0, 0($s2) # get next integer value from array in memory
mtc1 $a0, $fv0 # move $a0 into a floating point register
cvt.d.w $fv0, $fv0 # convert n into a double-precision FP
add.d $fs0, $fs0, $fv0 # add new FP value into Sum
addi $s2, $s2, 4 # increment pointer to next integer’s address
bne $s2, $s0, Top # until pointer = Max+4, keep iterating the loop
With this “in-lining”, the number of instructions per loop iteration is now 6. However the
problems specification required the n calls to intToFP, so even though it is faster, this was not
allowed in this question.
Write a utility function copy in MIPS assembly language that copies an ASCII string from one
memory location into another. It receives the address of the starting location of the string in $a0,
and the address of the location to copy it to in $a1. There is no return value from the copy
function.
copy: lbu $t0, 0($a0) // get byte from string in 1st memory location
sb $t0, 0($a1) // put copy of byte into 2nd memory location
addi $a0, $a0, 1 // move address pointer to next byte in 1st location
addi $a1, $a1, 1 // move address pointer to next byte in 2nd location
bne $t0, $zero, copy // if byte != Null, go get next byte
jr $ra // return to point of call when done
Write a main program in MIPS assembly language that will copy the ASCII string “Hello Will!”
from the static (a.k.a. global) data segment into the heap part of the dynamic data segment. Your
program should obtain space in heap, call copy to perform the actual string copy, and then end
cleanly by exiting to the operating system. Assume that the label for the “Hello Will!” string’s
location in static (a.k.a. global) memory is Greeting:
6
move $a1, $v0 //put address of dynamic memory string into $a1
jal copy // call copy to do the string copy
For the following MIPS code sequence and given that $a0 = 1 initially, show the contents of the
program counter (PC), register $ra, and the memory stack after the execution of each of the
instructions marked below. Point 1 is given for you. You may use the given labels to indicate the
program execution point.
Write a short MIPS assembly language program which implements the C code shown below:
7
void switchplus5 (int z[ ], int i, int j) {
int temp
plus5 (&z[i]);
temp = z[i];
z[i] = z[j];
z[j] = temp;
}
Your assembly language routine switchplus5 should be a callable function, which has an entry
label “switchplus5:”, and which returns to the place from which it was called at the end of
execution. It is both callee and caller. You may assume that the address of z[0] is passed in $a0,
and the value of i is passed in $a1 and the value of j is passed in $a2. Your code should obey the
software convention for the use of MIPS registers.
[You don't have to write plus5, just assume it exists, and call it. The routine plus5 takes the
address of item z[i], finds the value of that element, and adds 5 to it. It works correctly when
called with the parameter which is the address of z[i]. You can safely assume that plus5 obeys
that software convention also.]
Solution:
switchplus5: addi $sp, $sp, -12 # make room for 3 new items on stack
sw $ra, 0($sp) # push $ra
sw $s0, 4($sp) # push $s0
sw $s1, 8($sp) # push $s1
8
Question 1 (Quiz #1 Sec2 Spring 2016)
Correct the errors in the MIPS code given below. For each error, identify if it is a syntax
error, or a semantic error, or an inefficiency error.
Note that push $s0 (addi + sw) and pop $s0 (lw + addi) are not
necessary, since $s0 isn’t used. Thus, the 1st two lines and last 2 lines
before jr $v0 are inefficiency errors as well as semantic.
In the code below, identify each pseudo instruction, and change it into one or more actual
MIPS instructions, writing the actual MIPS code to the right of the pseudo instruction.
9
rol $t4, $t4, $t5
bge $a0, $a1, LoopTop
mul $v0,$t0, $t1 [Since the Green Card does not list mul, it is ok to
identify this as a pseudo, but in fact it is part of
the actual MIPS architecture now. It does
mult $t0, $t1 then mflo $v0]
mflo $s0
move $s0, $a0 Pseudo: add $s0, $a0, $zero (or addi $s0, $a0, 0)
li $t3, 65538 Pseudo: lui $at, 1, followed by ori $t3, $at, 2
slti $t2, $t3, -1
sll $t3,$t3, 2
rol $$t4, $t4, $t5 Pseudo: addi $at, $zero, 32, then sub $at, $at, $t5,
then srlv $at, $t4, $at, then sllv $t4, $t4, $t5,
then or $t4, $t4, $at
bge $a0, $a1, LoopTop
Pseudo: slt $at, $a0, $a1, then beq $at, $0, LoopTop
Write the shortest possible MIPS assembly language program which implements the C
code shown below:
Your code should use the fewest possible number of registers. You should make the
standard assumptions about which registers to use for y and z, and for the return value.
Your code should obey the software convention regarding the use of MIPS registers.
The shortest possible solution calculates y (z + 8) in two steps (add first, then multiply),
then returns to the caller. We use the mul instruction here, which avoids having to write
mult, then mflo $v0.
To minimize the use of registers, use the $v0 register instead of $t or other registers.
10
Note: the first instruction could have also been addi $a1,$a1,8, since as callee we have
no obligation to save or protect the $a registers. Then the 2nd instruction would be mul
$v0,$a1,$a0.
Write the utility function Sum_of_averages for finding the total of the average values found in
each 10-element group of a 1-dimensional array of element values. Sum_of_averages should be
a callable function that performs the task and returns to the calling program with the result value.
The address of the first element in the array is given by the caller in $a0. The length of the array
is not known, but it is known that the array’s last element is followed by a terminal value (similar
to the way the NULL character serves as a terminator for ASCII strings). The return value will
be in $v0. Sum_of_averages ignores any leftover elements that are not part of a 10-element
group. For example: if there are 34 values in the array, and the averages of the 1st 10, 2nd 10 and
3rd 10 are 5, -2 and 11 respectively, your Sum_of_averages function should return 14 to the
calling function. But if there are just 8 values in the array, it should return the largest negative
integer (-MAXInt) as the return value. Besides returning the sum-of-averages in a register, it
should also return that same value on the stack, putting it where it will be on the top of stack
after the return, for the caller to find it there.
Your Sum_of_averages routine should call the routine Avg_of_next_10 as many times as it
needs, and use the average values that are returned in its calculation. You can assume that
Avg_of_next_10 is already written, debugged, and available to you as a library function.
Avg_of_next_10 takes as an input argument in $a0 the first address in memory of a group of
consecutive elements. If there are 10 elements which have non-terminal values, then
Avg_of_next_10 returns the average of these 10 element values in $v0. But if there are less than
10 elements left and the terminal value is encountered instead, then the return value in $v0 will
be largest negative integer
(-MAXInt). In summary, Avg_of_next_10 finds and returns in $v0 the average of the next 10
array element values (or -MAXInt if there are not 10 more array elements). [Reminder: you
don’t need to write Avg_of_next_10, it already is available.]
You should write the MIPS assembly language code for Sum_of_averages. You may use real
MIPS instructions and pseudo-instructions, but you may not use psuedo-syntax. You should
explain your code with comments and a header. Code performance is important, but correctness
is more important! IMPORTANT REMINDER: as with any correct MIPS assembly code, both
the calling program and Avg_of_next_10 will obey the MIPS convention for the use of registers.
Therefore your code, Sum_of_averages, must also obey this convention, so that it can work
correctly with these other MIPS codes.
Solution:
########################################################
## Sum_of_averages: calculates and returns the sum of average-of-10 values,
## from an array, taken 10 elements at a time
11
##
## The algorithm does 6 things:
## a. it pushes ra, s0, s1 and s2 values onto stack to protect them
## b it initializes and finds the average value of the 1st 10 elements
## c. looping, it repeatedly calls Avg_of_next_10 to find the average of next 10
## elements, and accumulates it into c
## d. transfer the sum of averages into v0, as the return value
## e. it pops ra, s0, s1, and s2 values from stack and restores them to registers
## f. it returns to the calling program
##
##
## Register Usage
## a0 holds the address of the array, and of each remaining sub-array
## $s0 holds the starting address of the array, and of each remaining sub-array
## $s1 holds the sum of the average-of-10 values
## $s2 holds the –MAXint, the largest possible negative integer: 1000000…….
## $v0 holds the return values (avg of 10, and sum of averages)
## $ra holds the return address
##
#######################################################
Median_avg:
## push onto stack to protect (part a of the algorithm)
addi $sp, $sp, -16 ## make room for 4 new items on top of stack
sw $ra, 12($sp) ## push return address
sw $s0, 8($sp) ## push s0
sw $s1, 4($sp) ## push s1
sw $s2, 0($sp) ## push s2
## loop to repeatedly find average of 10, and accumulate it (part c of the algorithm)
addi $s1, $s1, $v0 ## accumulate $v0 into the total
addi $s0, $s0, 40 ## address of rest of array = last address + 40 bytes
mov $a0, $s0 ## put starting address of remaining array into $a0
jal Avg_of_next_10 ## the average value is returned in $v0
bne $v0, $s2, Loop ## if not –MAXint, iterate loop again
12
## pop from stack to restore registers (part d of the algorithm)
End: lw $s0, 8($sp) ## pop s0
lw $s1, 4($sp) ## pop s1
lw $s2, 0($sp) ## pop s2
addi $sp, $sp, 12 ## restore stack pointer to original value +4
lw $ra, 0($sp) ## restore the ra register value
sw $v0, 0($sp) ## put the result value onto the top of stack
Write the utility routine Median_avg for finding the average of the median values in 1-dimensional
arrays. Median_avg should be a callable function that performs the task and returns to the calling
program. The address of the first element in the array is given in $a0, and the length of the array is
given in $a1. The return value in $v0 is (Median(lower_halfarray) + Median(upper_halfarray)) / 2.
You may assume that the array contains at least 2 elements. If the number of elements is odd, then it
doesn’t matter which half-array is bigger in size by 1. Besides returning the average of the 2 half-
array’s median values in a register to the calling program, it should also put it in heap. Upon return,
the caller program will have 2 copies of the computed value: in a register, and in a dynamic memory
location (the address of that location should be returned in $v1).
Your Median_avg routine should call the routine find_Median twice, and use the median values that
are returned in its calculation. You can assume that find_Median is already written, debugged, and
available to you as a library function. Like Median_avg, it expects to find the address of the half-
array in $a0, and the length of it in $a1. Using these, it finds and returns the median value in $v0.
[Reminder: you don’t need to write find_Median, it already is available.]
You should write the MIPS assembly language code for Median_average. You may use real MIPS
instructions and pseudo-instructions, but you may not use psuedo-syntax. You should explain your
code with comments and a header. Code performance is important, but correctness is more
important!
########################################################
## Median_avg: calculates and returns the average of 2 median values,
## from 2 half-arrays. Signed 32-bit integer values are assumed.
##
## The algorithm does 8 things:
## a. it pushes ra, s0, s1 and s2 values onto stack to protect them
## b it calculates the starting address of the 2nd half-array (byte address, of
## course) and the lengths of both half-arrays, in items
## c. it calls the find_Median function for the 1st half-array
13
## d. it calls the find_Median function for the 2nd half-array
## e. it calculates the average of the 2 median values found in c and d
## f. it obtains a location in heap and puts the average there, and into v0
## g. it pops ra, s0, s1, and s2 values from stack and restores them to registers
## h. it returns to the calling program
##
## Register Usage
## a0 holds the address of the 1-dimenstional array, of the 1st half-array, and
## of the 2nd half-array
## a1 holds the array length, 1st half-array length, & 2nd half-array length
## $t0 holds the length, in bytes, of the 1st half-array
## $s0 holds the address of the 2nd half-array
## $s1 holds the length of the 2nd half-array
## $s2 holds the 1st median value, and the median average value
## $v0 holds the 1st median value, the 2nd median value, and the Median_avg
## return value
## $v1 holds the address of the value stored in heap, the other return value
## $ra holds the return address
##
#######################################################
Median_avg:
## push onto stack to protect (part a of the algorithm)
addi $sp, $sp, -16 ## make room for 4 new items on top of stack
sw $ra, 12($sp) ## push return address
sw $s0, 8($sp) ## push s0
sw $s1, 4($sp) ## push s1
sw $s2, 0($sp) ## push s2
## calculate values for 1st and 2nd calls to find_Median (part b above)
srl $s1, $a1, 1 ## find half-length of array, in items ( = a1 / 2)
sub $a1, $a1, $s1 ## find other half-length of array, in items
sll $t0, $a1, 2 ## convert length to bytes (i.e multiply by 4)
add $s0, $a0, $t0 ## find starting address of 2nd half-array (byte address)
14
## calculate the average of the median values (part e of the algorithm)
add $s2, $s2, $v0 ## add the two median values from the 2 half-arrays
sra $s2, $s2, 1 ## sra 1 divides by 2 to get the average value
## put the value into the heap, and into the return register (part f above)
li $a0, 4 ## ask for 4 bytes in heap (to store 1 integer value)
li $v0, 9 ## syscall 9 is sbrk, to allocate space in heap
syscall ## ask OS to do it
move $v1, $v0 ## transfer the address into $v1 for return
sw $s2, 0($v1) ## put the Median_avg value into the heap location
move $v0, $s2 ## transfer Median_avg into $v0 for return
Write the utility program insert for linked lists. It should be a callable function that performs the
task and returns to the calling routine. You may use real MIPS instructions and pseudo-
instructions. You should explain your code with comments and a header. Code performance is
important, but correctness is more important! The linked list elements have two parts: first a 32-
bit address portion, and after that a 32-bit data portion. These are located in adjacent words of
memory, in heap. You should consider the head element of the list to be the 1st element (N = 1)
The insert function puts a new element into the linked list at postion N+1, inserting it after the
Nth element. To keep things simple, you can assume that the input N is valid, i.e that there are at
least N elements in the list, and that N > 0.. The address of the head of the list is given in $a0, the
address of the element to be added is given in $a1, and N is given in $a2.
Again, you should explain your code with comments and a header.
########################################################
## insert: to insert a new item into the linked list after the Nth element
##
## The algorithm does 3 things:
## a. it finds the location of list element N, moving to the next element repeatedly with a loop
## b it modifies the pointers of element N and the new element, so that it becomes part of
15
## the list at (new) position N+1
## c. it returns to the calling program
##
## In the comments below, notation “N(address) means the address portion of list element N”
##
## Each element’s address part is assumed to point to the next element, to the address part.
## As described in the problem statement, the address part (containing the pointer to the next
## element)comes first, and the data part (containing the value) comes next, i.e. 4 bytes later
## in memory.
## The model of linked list used is shown below:
## Note that if the address part of element N were to point to the data part of element N+1,
## it would not affect the algorithm at all. It would only affect the implementation of the
## loop code in a minor way.
##
## Register Usage
## t0 as loop counter in the loop part, and later as a temp for the pointer changing part
## a0 holds the address of element #N . Initially, the head element’s address is in a0
## a1 holds the address of the new element to be inserted
## a2 holds the number N of the element to be searched for in the loop
## ra holds the return address
##
#######################################################
insert:
## initialization of loop (part a of the algorithm)
li $t0, 1 ## the loop counter is initially 1, since it holds the element number and
## a0 initially contains the address of element 1, the head element.
## Keeping t0 and a0 synchronized avoids “off by 1” errors
j Test ## enter the loop the first time at the test, not at the top
lw $t0, 0($a0) ## make a copy in temp of N(address) the address part of element N
sw $a1, 0($a0) ## store the address of the new element into N(address)
sw $t0, 0($a1) ## store the temp into New(address), which is now N+1(address)
a) Write a callable MIPS assembly language routine named word_count which counts the
number of words in a document of ASCII characters, using 2 utility routines: skip_spaces and
sentance_words. Your word_count routine returns the number of words in the document.
word_count is kept simple by repeated calls to the 2 utilities, which are pre-written library
functions. Since they are already written, you just use them in word_count.
skip_spaces receives as input the address of the location in an ASCII string pointing to the first
space character after a non-space character. It moves the pointer, skipping over all spaces (there
can be 1 or more than 1). Its return value is the address of the next non-space character (if there
is one) or 0, if the end-of-string is encountered.
sentance_words receives as input the address of the first character of the first word of the
sentance, searches for the end of the sentance (marked by a period . a question mark ? or an
exclamationpoint !) and returns two output values: a) the address of the first space after the
sentance (or 0, if the end-of-string is encountered) and the number of words in the sentance.
[Assume that there are no partial incomplete sentances in the document.]
Give the MIPS code for word_count. Use only real MIPS instructions.
Counting words in a document will be done by initializing count, then in a loop repeatedly
counting the words in the next sentance, adding that to the total word count, skipping spaces till
the next sentance, etc until the NULL character is encounterd. The total count is the return value
of this procedure. The stack must be used to preserve the value of $ra since word_count is non-
leaf, meaning it calls sub-routines (skip_spaces and sentance_words). And it must preserve the
value of an $s-register, which holds the count value. A $t-register can not be used for count,
since they aren’t preserved across the sub-routine calls.
17
word_count: addi $sp, $sp, -8 // open space for 2 values on the stack
sw $s0, 0($sp) // save old s0 value, in order to use s0 register
sw $ra, 4($sp) // push ra, since it will be changed by jal calls
addi $s0, $0, 0 // initialize count = 0
loop_top jal sentance_words // get number of words in next sentance
add $s0, $s0, $v1 // accumulate the sentance total into count
beq $v0, $0, end // test for end-of-text (if v0=0 NULL was found)
add $a0, $0, $v0 // move address pointer into place for next call
jal skip_spaces // skip over spaces between sentances
beq $v0, $0, end // test for end-of-text (if v0=0 NULL was found)
add $a0, $0, $v0 // move address pointer into place for next call
j loop_top
end: add $v0, $0, $v0 // put total word count into return value register
lw $s0, 0($sp) // restore s0 register’s old value
lw $ra, 4($sp) // restore ra register’s old value
addi $sp, $sp, -8 // return the stack pointer to original value
jr $ra // return to callee
a) Write a MIPS routine named fact which efficiently and quickly computes and returns the
factorial of a non-negative integer input value. Use only real MIPS instructions. Your code
should seek to minimize the number of multiplications (since 32-bit multiplication is a slow,
multi-cycle, operation) and of course should minimize the total number of instructions executed.
fact = 1
if (n == 0)
return (1)
18
temp = 0
else do {temp++
fact = fact * temp
while (temp != n)
return (fact)
[Note1: The above solution works correctly for the cases n=0 and n>0, but it can be
made faster by making the n=1 case an initial test, thus avoiding one loop iteration and a
multiply operation.
[Note2: Taking the above improvement a step further, the code could be modified further
to make n=2 as an additional initial case, further reducing loop iterations and number of
multiplications by 1.
b) Analyze your code from part a) to determine the performance of computing fact (n). Fill in
the values in the table below.
For the first code given above, the table values are:
n | # of instr. executed | # of multiplications
0 | 3 | 0
1 | 7 | 1
2 | 10 | 2
19
x | 3x + 4 | x
[Note1: For the second code given above, the table values are:
n | # of instr. executed | # of multiplications
0 | 3 | 0
1 | 4 | 0
2 | 8 | 1
x | 3x + 2 | x-1
As expected, there is a slight speedup: 2 less instructions, & 1 less multiply ]
[Note3: For the recursive factorial code on page 331, the table values are:
n | # of instr. executed | # of multiplications
0 | 9 | 0
1 | 9 | 0
2 | 22 | 1
x | 13x - 4 | x-1
3x is more than 4 times faster than 13x, so recursive code is slow code!! In addition to
the number of instruction being greatly bloated due to the overhead of calls and returns and
stack operations, each memory access (the stack pushes and pops) is a potential cache miss,
which can create stalls and further slow down the execution. From a performance perspective, it
is hard to find anything good to say about recursive implementations.]
(a) Write a MIPS code fragment that is equivalent to the code below which writes the maximum
value to the memory location labeled M. For maximum credit, include comments and use
minimal number of instructions. Only real MIPS instructions are allowed, but you may use a
pseudo-syntax with the load and store instructions.
Answer:
Label Instruction Comment
.data
A: .word 4, -1, 3, ..., 6, 17 # int A[100]={4,-1,3,..., 6,17}
M: .alloc 1 # max value will be stored here
.text
lw $t1, A($0) # initialize max=A[0]
addi $t0, 0, 4 # initialize i = 1
slti $t2, $t0, 400 # check loop exit test
beq $t2, $0, Exit # if i>=100, exit
Loop: lw $t3, A($t0) # read in current element
slt $t2, $t1, $t3 # is max < A[i]?
beq $t2, $0, Skip # if not, skip update
addi $t1, $t3, 0 # else new max = A[i]
Skip: addi $t0, $t0, 4 # update i
slti $t2, $t0, 400 # check loop exit test
bne $t2, $0, Loop # continue looping
Exit: sw $t1, M($0) # store maximum
a) A MIPS single-cycle processor runs the following code, but performance is too slow. Instead
of buying an optimizing compiler, the company hires you to “hand tune” the code, in order to
achieve maximum performance and yet still compute the same thing. Be sure to use only real
MIPS instructions, part of the instruction set (not pseudo-instructions). Give the new code, after
“hand tuning”, in the space below.
Compile the two C routines below into MIPS assembly code. Use no pseudo-instructions; use
only real MIPS instructions, which are part of the instruction set architecture. Your code should
follow the MIPS convention for register usage and function calls.
a) int problem_1a(void) {
int x = 0;
for (j=0; j!=5; j++)
x = x + do_it(j) <<3;
return (x - 13);
}
Answer
problem_1a: addi $Sp, $sp, -16 # make room to push 4 items onto stack
sw $s0, 0($sp) # must preserve s-registers before using
sw $s1, 4($sp) # must use s-registers because t-registers
sw $s2, 8($sp) # would be changed by do_it
sw $ra, 12($sp) # preserve $ra since this routine calls do_it
addi $s0, $0, 0 # x = 0 in $s0
addi $s1, $0, 0 # j = 0 in $s1
addi $s2, $0, 5 # hold 5 in $s2
# loop is bottom-tested, no need for j Test this time
22
# since 5 != 0
Top: addi $a0, $s1, 0 # put argument in $a0 before the call
jal do_it # call do_it
sll $t0, $v0, 3 # shift return value by 3
add $s0, $s0, $t0 # accumulate into x
addi $s1, $s1, 1 # j++
Test: bne $s1, $s2, Top # test if j != 5
addi $v0, $s0, -13 # return x - 13
lw $s0, 0($sp) # pop stack to undo pushes and restore the
lw $s1, 4($sp) # 4 original values
lw $s2, 8($sp)
lw $ra, 12($sp)
addi $Sp, $sp, 16 # restore stack pointer
jr $ra # return to point of call
b) For this part, PORTD = 0xFFFF0000 and TRISD = 0xFFFF0004. Other conditions are the
same as in part a).
Answer
problem_1b: addi $t0, $0, 0xFF00 # put 0xFF00 into $t0
lui $t1, 0xFFFF # PORTD address = 0xFFFF0000, put into $t1
sw $t0, 4($t1) # TRISD address (=FFFF0004) gets 0xFF00
sw $a0, 0($t1) # PORTD gets $a0, which contains a
spin: lw $t2, 0($t1) # get PORTD value
and $t3, $t2, $t0 # PORTD & 0xFF00
beq $t3, $0, spin # spin while PORTD & 0xFF00 = 0
addi $v0, $t2, 0 # put PORTD value into $v0 for return value
jr $ra # return to point of call
Translate the C function below into MIPS assembly language. Pay particular attention to
properly saving and restoring registers across function calls, and obeying the MIPS convention
for register usage. Clearly comment your code. Keep local variable a in $s0. You may use
pseudo-instructions if they are well-known (or obvious).
23
int q1(int n, int m) {
int a;
a = m + 2;
if (n == 0) a = 5;
else a = a + (n * n) + q1(n – 1, m + 1);
return a * m;
}
Solution:
#####################################################
## MIPS code for function q1
##
## $a0 contains the first input argument: n
## $a1 contains the 2nd input argument: m
## $s0 contains local variable a
## $t0 is used as a temporary register at 1 place
## $v0 contains the return value from function calls to q1
## $ra contains the return address
##
## q1 is a non-leaf procedure, since it makes a call to invoke a
## routine (in fact, it is recursive, since it calls itself)
##
## q1 has an entry label “q1” , the name of the function in C
## q1 has a single point of return, since the C code has a single return
#####################################################
24
# return value will be in $v0
Write a MIPS program to find the median value in a list of unsorted integers. Example: in the list
[1 3 6 7 2 8 4] the median value is 4. In the list [1 4 3 2], 2 is the median value, not 3.
Your program main should follow the outline given below, calling GetList and Sort when
necessary to help it do its job. It should output the median value with a short explanatory
message, then end, exiting to the operating system. Your program must obey the MIPS
convention for register usage. Your program should be a short as possible, and use as few
registers as possible. You may use pseudo-instructions if they are well-known (or obvious).
GetList is an existing library I/O utility. When called, it dialogs with the user to determine the
size of the list, and gets the members of the list, making sure that each is an integer value
representable in 32 bits. It acquires memory space from the OS to store the list elements in
sequence as an array. Upon return, the starting address of the array is in $v0 and the number of
elements in the array is in $v1.
Sort is an existing library utility function which takes the starting address of the unsorted list of
integers (in $a0) and the number of integers in the list (in $a1). It sorts the list values into
increasing order, and returns the address (in $v0) of the beginning of the sorted list.
.text
main: jal GetList # get list values from user
move $s0, $v1 # save the number of elements, since $v1 is not preserved
move $a0, $v0
move $a1, $v1
25
lw $s0, 0($v0) # get median value
.data
exp_msg: .asciiz “The median value of the list is “
b) Consider the following if statements and their implementation in MIPS. Some of these
implementations can be incorrect. Analyze each of them and state whether the
implementation is correct or incorrect. Assume that $19 is used for i and $20 is used for j.
For incorrect one(s), if any, provide the corrected version(s) without using
pseudoinstructions.
i) if(i > j) go to L1; $19 > $20 ==> $20 < $19
slt $1, $19, $20 ==> slt $1, $20, $19
bne $1, $0, L1
is not correct
ii) if(i >= j) go to L1; $19 >= $20 ==> not ($19 < $ 20)
slt $1, $20, $19 ==> slt $1, $19, $20
beq $1, $0, L1
is not correct
26
d) The following program tries to copy bytes from the address in register $t3 to the address in
$t4, one byte at a time, counting the number of bytes copied in register $t2. The program
stops copying when it finds a byte with a value equal to 0 (note that 0 is not copied).
i) Complete the missing parts of the following program. If you need you may introduce
labels (such as L1, L2) to branch to. Provide a comment for each line.
li $t1, 0 # Clear $t1
li $t2, 0 # Initialize the counter
L1: lb $t1, 0($t3) # Get the byte pointed by $t3 to $t1
beq $t1, $zero, L2 # If it is 0 exit loop
sb $t1, 0($t4) # Store byte to location pointed by $t4
addi $t2, $t2, 1 # Increment the counter
addi $t3, $t3, 1 # Increment from pointer
addi $t4, $t4, 1 # Increment to pointer
b L1
L2:
ii) Assume that for the above code segment we have the following data segment
.data # Starts at memory location 1001 000016
from: .word -1, -2, -3, -4, -5, 0
to: .space 30
Assume that, before executing the code segment given in section-i, $t3 and $t4 are
initialized as follows.
la $t3, from
la $t4, to
What will be the contents of the following registers (shown below) after executing the code
segment given above? (Give the values in hexadecimal. To get credit for this section you
have to the give the contents of the registers for the code segment which is completed in the
correct way.)
$t3: 10 01 00 14
$t4: 10 01 00 2C
$t2: 00 00 00 14
$t1: 00 00 00 00
a) Write a short MIPS subroutine to conditionally exchange 2 integer values in memory, if and
only if the first one is greater than the second one. The address of the first value is passed in $a0,
and the address of the second value is passed in $a1. The return value should indicate if the
exchange was made: 0 = not swapped; 1 = swapped. The entry point of your subroutine is the
label cond_exch: Your code should follow the MIPS convention for register usage. You may use
pseudo-instructions.
27
cond_exch: lw $t0, 0($a0)
lw $t1, 0($a1)
bgt $t0, $t1, Swap
NoSwap: li $v0, 0
j Return
Swap: sw $t0, 0(a1)
sw $t1, 0($a0)
li $v0, 1
Return: jr $ra
Note: cond_exch can be done with 8 lines of MIPS code; the 9-line version here is given for
clarity and understandability
b) Analyze the code below, and determine the values of the given registers just before executing
the instructions at A: B: and C: Assume that Start: is at address 0x0050000. Write your
answers in the box provided.
Note that la and bgt are both pseudoinstructions that take 8 bytes; all other instructions take 4
bytes. The address of Exit: is therefore 56 bytes (38 in hex) after Start:
28
iii) give the value of $t1 at C: -1
a. Consider the following code segment. Note that MIPS code segment begins at memory
location 00 40 00 00 (hex.) and data segment begins at memory location 10 00 00 00 (hex.).
.data
array: .word 0, 1, 3, 5, 10, 20
n: .word 6
a: .word … # its value is specified below in the subsections of the question
.text
.globl main
main: lw $t0, a
la $t1, array
lw $t2, n
li $t4,0
L1: beq $t2, $0, L2
lw $t3, 0($t1)
addi $t1, $t1, 4
addi $t2, $t2, -1
bgt $t3, $t0, L1
add $t4, $t3, $t4
b L1
L2:
i. What will be the contents of the following registers (in hex.) when we reach L2 during
execution time? Assume that the value of a is 0.
$t1 (2 pts.)
10 00 00 18
$t2 (1 pt.)
00 00 00 00
$t3 (1 pt.)
00 00 00 14
$t4 (4 pts.)
00 00 00 00
ii. What will be the contents of the following registers (in hex.) when we reach L2 during
execution time? Assume that the value of a is 10.
29
$t1 (2 pts.)
10 00 00 18
$t2 (1 pt.)
00 00 00 00
$t3 (1 pt.)
00 00 00 14
$t4 (4 pts.)
00 00 00 13
The main( ) does the necessary initializations and performs a jump and link instruction to
come to the method. The method PartB returns the result by following the MIPS
programming conventions. In your implementation you are not allowed to use pseudo
instructions like move, etc.
ii. What will be the contents of $5 (in hex) after executing the above code segment?
30
Note that the instruction at memory location "main" is loaded into $5.
8C E8 00 64
-2000
Summary of syscalls
Service Code in $v0 Arguments
print integer 1 $a0 = integer to print
print float 2 $f12 = float to print
print double 3 $f12 double to print
print string 4 $a0 = address of null-terminated string to print
a) Trace the execution of the following program, to determine the final values in $t1 and in $v0
at time it return. Assume the initial value in $a0 is 31
.text // a0 is initially 31
Q6_a: la $t3, A // t3 contains the address A
lw $t0, 0($t3) // t0 now contains -4
srl $a0, $a0, 2 // a0 now contains 7 (31 shifted right 2 places)
$v0 = 0x00000002 (or 2 in decimal, since $t1’s final value is positive, (5 pts)
b) Write a short function in MIPS assembly language named get_exp which takes a single-
precision floating point number in $f0 as input, and returns the actual value of the exponent
(without bias) in $v0. The function must be callable, have no unexpected side effects, and obey
the MIPS convention for register usage. Your routine should be a short as possible, and use as
few registers as possible.
In this problem, you will write the MIPS assembly code for 2 different subroutines. Your MIPS
routines must follow the MIPS convention for register usage, concerning those which must be
preserved across a call and return. You may use pseudo-instructions. To get full credit, your
code should be fully correct, and it should be efficient.
The subroutine max_diff takes 3 integer values as input, and returns the maximum difference
between any 2 input values. For example, if -8, 5 and 2 are the inputs, the 6 differences are -13, -
10, 13, 3, 10, -3, but the maximum difference is 13. The 3 input arguments are given in $a0, $a1,
and $a2, and the output value is returned in $v0. You must implement max_diff in a way that
makes 3 calls to abs_diff, and compares these differences to find the maximum difference.
Solution:
max_diff: addi $sp, $sp, -20 # make room on the stack to save 5 important regs
sw $ra, 0($sp) # $ra must be saved, since jal calls change it
sw $s0, 4($sp) # $s-registers must be saved, since they are changed
sw $s1, 8($sp) # when written to, at various places in the code
sw $s2, 12($sp)
sw $s3, 16($sp) # $s3 will be used to contain the largest abs_diff
32
move $s0, $a0 # put the 3 input parameters in a safe place (s-regs)
move $s1, $a1 # since $a-registers AREN’T saved/preserved across
move $s2, $a2 # call & return. They can be changed by the callee
The subroutine abs_diff takes two integer values as inputs and returns the absolute value of their
difference. It uses the $a0 and $a1 registers for input arguments, and $v0 to output the value of
the absolute difference. abs_diff does its job without calling any subroutines.
Solution:
abs_diff: sub $v0, $a0, $a1 # take the difference between the two inputs
abs $v0, $v0 # take the absolute value of the difference
jr $ra # return to the point of call
Compile this high-level language (HLL) code into MIPS assembly code, by hand. You should
follow the MIPS convention for register usage. Assume that routines func and min exist, and
33
work correctly to do their functions. Make your loop bottom-tested for performance. You may
use pseudo-instructions.
int Q1_code ( ) {
int x, y, z;
x = 0;
for (z = 0, z ≤ 10, z ++)
{ x = func (z, x);
y = func (x, z);
}
return (min (x, y));
}
The MIPS assembly program is started for you on the next page, including the header and the
stack pushing and popping. Following the given header and its use of registers, complete the rest
of the program in the space provided.
Solution: (in italic, the rest was provided at the time of the exam)
## In this program, the following register assignments are made:
##
## S-registers are used to protect values across the call-returns
## $s0 conatains x $s1 contains y $s2 contains z
##
## $a0 and $a1 are used in their normal way, for input parameters
## $v0 is used in its normal way, for output values
## $ra is used for the return address, in the normal way
## $sp is used for stack pointer address, in the normal way
##
Q1_code: addi $sp, $sp, -16 # push ra and s-registers onto stack
sw $ra, 0($sp)
sw $s0, 4($sp)
sw $s1, 8($sp)
sw $s2, 12($sp)
34
jal func
addi $s0, $v0, 0 # move $v0 result into x
j = choice;
k = length - choice;
35
quicksort(z,i-1,choice-1);
}
z[i] = j + k;
useless(z,j,k);
}
}
The C code for the useless function is given above, where x[] is an array of 32-bit integers to be
used, and where length and choice are integer values. Translate the HLL function into a callable
MIPS assembly language routine by the same name. Put your useless MIPS code below. You do
not need to write code for quicksort, just call it & assume that it performs the sorting. Be sure to:
--use only real MIPS instructions, except for relative comparison branching (blt, etc)--you may
use pseudoinstructions ONLY for these branches
--make all your loops top-tested, not bottom tested
--implement using index addressing, not pointers
--use the given header and code outline to guide you
--create the code as a non-optimizing compiler would, directly from the C code. As directly
as possible, you are to “hand compile” from C into MIPS assembly
Starting with the partially complete header which is given, list all registers used, and what they
contain. Please write your MIPS code legibly, so that it can be read and understood. Note that
plenty of space is given, including the back of the page.
#############################################################
## useless routine: wastes time doing nothing in particular, using an array z of 32-bit integers
##
## $a0: input argument containing base address of array z
## $a1: input argument containing # of integers in array z
## $a2: input argument containing choice, a parameter used for useless purposes
## $s0: contains i
## $s1: contains j
## $s2: contains k
##
## Since useless is non-leaf, calls to itself and to quicksort may change
## the values in a0, a1 and a2. Therefore, these need to be in safe storage
## $s3: contains base address of array z (safe storage for $a0)
## $s4: contains length (safe storage for $a1)
## $s5: contains choice (safe storage for $a2)
##
## $t0, $t1: temporary storage locations for addresses and values in lw and sw operations
##
## The assembly code follows the C code in structure—there are 4 levels:
## -at the outer level: an overall for1 loop, which is implemented with initialization,
## then top-test, then body, then at the bottom incrementaion and goto the top
36
## -at the middle level: an if statement, a while1 loop, an assignment and a call to useless
## -at the almost-inner level: the if contains an assignment of z[] and a while1 loop.
## the while2 loop contains j--, the for2 loop, and a call to q-sort
## -at the very inner level are the statements in the bodies of while1 and for2
## All together, there are 2 while loop, 2 for loops and1 if statement. All of these are
## top-tested in MIPS, which means that the logic of the test (since it branches out and
## doesn’t execute) must be opposite of the test logic in C (which stays in and executes).
##
#############################################################
useless: # entry point for the routine
## do stack pushing, to preserve values of $s-registers (since we use and change them ) ###
## and to preserve $ra (since useless is non-leaf) ###
## Values are moved into $s-registers here, to be preserved across the many function calls,
## including recursive ones. Here, we do the initial 2 statements assigning j and k
add $s3, $a0, $0 # move address of x[] into safe storage ($s3)
add $s4, $a1, $0 # move length into safe storage ($s4)
add $s5, $a2, $0 # move choice into safe storage ($s5)
## do the for1 loop, which compares i and length. If the condition is met, the for1 loop will run.
## But if i > length, then the for1 loop isn’t needed, and the useless routine exits via Exit..
## do the if test, which compares i+j to k+length. If the condition is met, the if clause
37
## has work to do: the z[length] assignment and the while1 loop. But if i+j<=k+length,
## then the if is skipped, and the for1 loop continues with the while2 loop
## Note that at this point, $t1 contains the address of z[length] , from assign_zlen above ##
lw $t1, 0($t1) # load z[length] value into $t1 (and use it
# repeatedly in the while1 loop
while1test: sll $t0, $s1, 2 # byte offset = j*4
add $t0, $t0, $s3 # address of z[j] = byte offset + base addr
lw $t0, 0($t0) # load z[j] value into $t0
bge $t1, $t0, while2test # if z[length] < z[j] do the while1 loop; if not,
# go to while2loop, since while1, and the if which
# contains it, are both finished
addi $s1, $s1, -1 # j--
addi $s2, $s2, 1 # k++
j while1test # end of while1 loop, goto its re-entry point
# for top-testing
## do the while2 loop, which gets the memory value from z[0] to compare in the test. ##
## Failing the test will cause exit from the loop. The loop body decrements j, does the for2 ##
## loop and calls quicksort.
38
Quicksort: addi $a0, $s3, 0 # put z[ ] into $a0
addi $a1, $s0, -1 # put i-1into $a1
addi $a2, $s5, -1 # put choice-1 into $a2
jal quicksort # call quicksort
## Finish up the rest of the actions in the for1 loop: assign z[i], then call useless, then do ##
## the loop increment, then goto the for1test, the top-of-loop test ##
## ##
## The call to useless is preceded by 3 moves, to get the correct values into the 3 argument ##
## registers. The reason to load all 3 input arguments each time is that a-registers are ##
## NOT protected (i.e “saved”) by the callee, and may be changed by the callee. Since ##
## useless (which calls quicksort) may change a0, a1 and a2, we must not trust that ##
## “they won’t be changed” (i.e “bir sey olmaz” ) ! ##
## do stack popping, to restore the values of the $s-registers (since we use and change them) ##
## and to restore $ra (since calls to useless and to quicksort have changed it) ##
39
Question 2 (Midterm Spring 2013)
if(first<last){
pivot=first;
i=first;
j=last;
while(i<j){
while(x[i]<=x[pivot] && i<last)
i++;
while(x[j] > x[pivot])
j--;
if(i<j)
swap (x[], i, j) // swaps x[i] and x[j]
}
}
}
The C code for quicksort is given above, where x[] is an array of 32-bit integers to be sorted, first
and last are the indexes of the first and last position in the array. Write a callable MIPS assembly
language routine quicksort, which will be called from a main program (or other higher level
routine). Put your quicksort MIPS code below. You do not need to write code for swap, just call
it and assume that it performs the swap indicated.
Be sure to:
--use only real MIPS instructions, no pseudoinstructions
--use the given header and code outline to guide you
--create the code as a non-optimizing compiler would, directly from the C code, Use index
addressing, top-tested loops, etc. As directly as possible, you are to “hand compile” from C into
MIPS assembly
Use the outline of the code given below to guide you as you write your MIPS assembly language
program. In your code, you should use a header and line comments (in English). In the header,
you should state what the routine is doing, and you should list each register used and what it
contains. You should provide comments for your code to make it understandable.
40
#############################################################
## quicksort routine: sorts an array of 32-bit integers, putting them in ascending order
##
## $a0: base address of array x
## $a1: index of first element to be sorted in array (or partitioned sub-array), or
## index of first of 2 elements to be swapped
## $a2: index of last element to be sorted in array (or partitioned sub-array), or
## index of second of 2 elements to be swapped
## $s0: i
## $s1: j
## $s2: pivot
## +
## $s3: base address of array x (safe storage for input parameter in $a0)
## $s4: first (safe storage for input parameter in $a1)
## $s5: last (safe storage for input parameter in $a2)
##
## $t0, $t1: temporary storage locations for addresses and value in lw and sw operations
##
## The assembly code follows the C code in structure—there are 3 levels:
## -at the outer level: an overall if, which skips everything if there are < 2 items to sort
## -at the middle level: initializations, then a while loop, then 3 call statements
## -at the inner level: inner while 1, inner while 2 and inner if
## All together, there are 3 while loops and 2 if statements. All of these are
## top-tested in MIPS, which means that the logic of the test (since it branches out and
## doesn’t execute) must be opposite of the test logic in C (which stays in and executes).
##
#############################################################
## do stack pushing, to preserve values of $s-registers (since we use and change them)###
## and to preserve $ra (since quicksort is non-leaf) ###
## do the outer if test, which compares i and j. If the condition is met, the quicksort routine
## has work to do. But if i >= j, then the sorting isn’t needed, and the routine exits via Return.
## Values are moved into $s-registers here, to be preserved across the many function calls,
41
## including recursive ones.
If_outer: slt $at, $a1, $a2 # if first<last do the following code; if not,
beq $at, $0, Exit # then go to return, since sorting isn’t needed
# (equivalent to bge $a1, $a2, Exit)
add $s2, $a1, $0 # move pivot <= first
add $s0, $a1, $0 # move i first
add $s1, $a2, $0 # move j last
add $s3, $a0, $0 # move address of x into safe storage ($s3)
add $s4, $a1, $0 # move first into safe storage ($s4)
add $s5, $a2, $0 # move last into safe storage ($s5)
While_middle: slt $at, $s0, $s1 # while i<j do the following code
beq $at, $0, Finish_up # (equivalent to bge $s0, $s1, Finish_up)
## do first inner while loop, which gets the memory value from x[pivot] to compare with the ##
## memory value of x[i] in the first test and also compares i and last in the second test. ##
## Failing either test will cause exit from the loop. The loop body just increments i ##
## Note that x[pivot] only needs to be fetched once, and can be used also in While_inner2 ##
## do second inner while loop, which gets the memory value from x[j ] to compare in the test.
## Failing the test will cause exit from the loop. The loop body just decrements j.
42
j While_inner2 # end of While_inner2, goto its top for
# “top”-testing
## Finish up the rest of the actions in the outer-if: swap, then quicksort, the quicksort again ##
## Each call is preceded by 3 moves, to get the correct values into the 3 argument registers ##
## The reason to load all 3 input arguments each time is that a-registers are NOT protected ##
## (“saved”) by the callee, and may be changed by the callee. Since swap (and quicksort ##
## which calls swap) may change a0, a1 and a2, we must not trust that “they won’t be ##
## changed” (i.e “bir sey olmaz” ) ! ##
## do stack popping, to restore the values of the $s-registers (since we use and change them) ##
## and to restore $ra (since calls to swap and to quicksort have changed it) ##
43
lw $s4, 8($sp)
lw $s5, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 28
jr $ra # return to caller
Write a callable MIPS routine named find_char which receives the address of the null-terminated
ASCII character string to be searched, and the ASCII code for the character to be searched for,
and returns +1 if that character is found in the string, and -1 otherwise. The routine find_char
should make repeated use of a subroutine named is_match, which takes two character codes as
inputs (in $a0 and $a1) and returns a value in $v0 telling if the two characters were the same
($v0 = 1) or different ($v0 = 0). Give the MIPS code for find_char and for is_match.
find_char: addi $sp, $sp, -12 # make room to push 3 items on stack
sw $ra, 0($sp) # push $ra, since jal changes it
sw $s0, 4($sp) # push $s0, since 1st move changes it
sw $s1, 8($sp) # push $s1, since 2nd move changes it
is_match: li $v0,1
beq $a0, $a1, End
44
not_match: li $v0, 0
End: jr $ra
a) Given the following C code with nested loops, compile it by hand into the equivalent
MIPS assembly code. Make your loops top-tested.
Top Tested
nestedloops: addi $s0, $zero, 1
outertop: bgt $s0, $a0, Done
addi $s1, $zero, $a1
innertop: ble $s1, $a0, End_inner
body_of_inner_loop
addi $s1, $s1, -1
j innertop
End_inner: addi $s0, $s0, 1
j outertop
Done: ...
Bottom Tested
nestedloops: addi $s0, $zero, 1
j outertest
outertop: addi $s1, $zero, $a1
j innertest
innertop: body_of_inner_loop
addi $s1, $s1, -1
innertest: bgt $s1, $a0, innertop
addi $s0, $s0. 1
outertest: ble $s0, $a0, outertop
Done: .
45
B. Practice converting array-index-style MIPS assembly code into pointer-style MIPS assembly
code.
Convert the MIPS assembly code given below into a more efficient pointer-style code that
executes faster. As you convert it, try to use the smallest possible number of temporary registers.
How much faster will your new code execute vs. the given code?
46
Since the loop only has 6 statements now instead of 8 (before), the loop will run 8/6 = 1.33 times
faster.
Write a MIPS program that gets two integer arrays from the user, computes their intersection and
outputs that intersection back for the user to read. You can assume that array sizes are fixed as 5
for first array and 7 for second array. The output should be given, then a message containing
your names and Bilkent ID numbers. Assume for simplicity that the user enters correct input (i.e.
no need for error checking).
The main program will handle the input from the terminal (including prompt messages, reading
the input, forming the values into arrays, etc), and then call the subroutine. Also, the main
program will do the output operations. Note that array sizes are 5 and 7. Integers can be positive
or negative or 0.
A MIPS subroutine handles the actual computation. It receives from the main program the
address of two arrays of integers, computes their intersection and puts these values in an array,
then returns the address of that array to the main program. This subroutine (also called routine,
procedure, method, function) must follow the MIPS convention for register usage (see p. 115 of
the textbook and other possible related sections). In particular, if you use any of the 12 “saved”
registers ($s0-$s7, $fp, $gp, $sp, and $ra), you must ensure that they are returned to the caller
with exactly the same values that were received at the time of call. [Hint: the intersection is
much easier to find if the arrays are sorted.]
47
Example of terminal input and output:
Welcome to our program (AA and ZZ ).
Please enter your inputs for array 1 (5 integers) followed by the Enter key.
Please enter your inputs for array 2 (7 integers) followed by the Enter key.
10
This program was created by Ali Aydın and Ziya Zafer. We hope you liked it, and
will use it often . Iyi günler !
Solution:
.data
first: .word 0 : 5
second: .word 0 : 7
result: .word 0 : 5 #max 5 integer can be common
48
fSize: .word 5
sSize: .word 7
.text
la $a0,start
li $v0,4
syscall
#-------------------------1-------------------------------#
li $v0,4
syscall
la $t5, fSize
lw $t5, 0($t5) #loading value from the address
la $s0, first
j TestF
SFir: sll $t1, $t0, 2
add $t2, $s0, $t1
49
syscall
sw $v0, 0($t2)
#-------------------------2-------------------------------#
la $a0,s_pro
li $v0,4
syscall
la $t5, sSize
lw $t5, 0($t5) #loading value from the address
la $s1, second
j TestS
#--------------CONTROL--PART-----------------#
la $a2, fSize
lw $a2, 0($a2) #loading value from the address
la $a1, sSize
lw $a1, 0($a1) #loading value from the address
50
la $s2, result
j TestC1
c1: sll $t1, $t0, 2
add $t2, $s0, $t1
lw $t3, 0($t2)
#------------------print------------------#
la $a0,p_pro
li $v0,4
syscall
j TestP
print: sll $t1, $t0, 2
add $t2, $s2, $t1
lw $t3, 0($t2)
la $a0, ($t3)
li $v0,1
syscall
addi $t0, $t0, 1
la $a0,space
li $v0,4
syscall
51
TestP: blt $t0, $s3, print #checks whether there are more integers to print out
la $a0,end
li $v0,4
syscall
Write the MIPS code which corresponds to the following C code. Let x be in $f0 & $f1, let y be
in $f30 & $f31.
double x, y;
int i;
for (i=0; i<10; i++)
{
if (x>y)
x=x-y;
else
x=x+y;
}
x=x*y;
Solution:
52
addi $t0, $t0, 1 # increment i, the loop counter
slti $t1, $t0, 10 # this and the next instruction together make the i < 10 test
bne $t1, $zero, Loop # if i<10 we repeat the loop
mul.d $f0, $f0, $f30 # once finished with the loop, we multiply x * y
Translate the following C routines to MIPS assembly language. Write the MIPS code in the
boxes provided. Use only real MIPS instructions in your code, but put an asterisk * next to lines
where pseudoinstructions would either make the code easier to write, or easier to understand.
return funcB(a, b)
}
#################################################
# isBigger is called with a in $a0 and b in $a1
# its result value is returned in $v0
# $sp contains stack pointer
# $ra contains return address
##################################################
isBigger:
* addi $sp, $sp, -4
* sw $ra, 0($sp) # must push $ra, since this is a non-leaf procedure
jal functB
* lw $ra, 0($sp) # need to pop and restore $ra before return since it was
# changed by jal call
* addi $sp, $sp, 4
jr $ra
[push and pop pseudo-instructions would be helpful ]
#################################################
# functB is called with c in $a0 and d in $a1
# its result value is returned in $v0
# $at contains temporary value used by the assembler
# $ra contains return address
##################################################
53
functB:
* slt $at, $a1, $a0
* bne $at, $zero, put_c # d < c means c > d, so take the branch to put_c
* addi $v0, $zero, -1 # else put -1 and return
jr $ra
put_c:
* add $v0, $zero, $a0 # put c and return
jr $ra
[bgt, li, and move pseudo-instructions would be helpful ]
Convert the following code segment into MIPS assembly language. Your loop should be
optimized for fast performance (Hint: use bottom-testing and pointer-addressing)
lw $t0, 0($a0)
addi $t1,$zero,1
Top: bge $t1,$a1,Done
sll $t2, $t1, 2
add $t2, $a0, $t2
lw $t2, 0($t2)
ble $t0, $t2, Skip
move $t0, $t2
54
Skip: addi $t1, $t1, 1
j Top
Done: …
Optimized Version:
##############################################################
## ##
## Registers used: ##
## $a0 contains the beginning address of array ##
## $a1 contains the size of array(at the beginning) ##
## then contains size x 4, as part of the calculation ##
## $t0 contains current minimum element ##
## $t1 contains the pointer to the current array location ##
## $t2 contains the current element of the array at location $t1 ##
##############################################################
lw $t0, 0($a0)
sll $a1, $a1, 2
add $a1, $a0, $a1
move $t1, $a0
j Skip
Top: lw $t2, 0($t1)
ble $t0,$t2, Skip
move $t0, $t2 // min <-a[i]
Skip: addi $t1,$t1,4
blt $t1, $a1, Top // i<size
The un-optimized solution has 8 instructions in the loop, but the optimized version has only 5.
The j Top instruction is saved by converting to bottom testing, and 2 instructions are saved by
converting to pointer addressing (since pointer = pointer + 4 does the work of 3 instructions: a
shift to multiply the index by 4, an addition to add base + 4*i to get the address, and i = i + 1 to
increment the index).
A special sequence of numbers is defined as follows. The first two numbers in the sequence are 0
and 1, and each subsequent number is 4 times the previous number minus the one before that.
In other words, let Ax be the x-th number in sequence. Then,
Ax = (4 × Ax-1) - Ax-2
55
Write a program that calculates the x-th number in the sequence. The program asks the user for
the value of x. If the value of x is smaller than 1, the program asks the user to enter a number
greater than 0. After calculating the x-th number in sequence, your program prints the result
along with both your names, surnames and Bilkent IDs. (For example: “the 6 th number in the
sequence is 209 -- Ali Aytek 12345678 and Bora Bilgin 87654321”.) You may assume that user
enters only integers. (This is for simplicity—so there is no need to check for floating point
numbers).
Solution:
.data
aseq:.word 0 : 15 # "array" of words to contain numbers
# Loop to compute each number in sequence using the previous two numbers.
56
addi $t0, $t0, 4 # increment address to now-known number storage
addi $t1, $t1, -1 # decrement loop counter
bgtz $t1, loop # repeat while not finished
subi $t6, $s5, 1 # to calculate the offset of the last element of the array
sll $t6, $t6, 2 # multiply by 4
add $t5, $t5, $t6 # add beginning address and the offset
lw $a0, 0($t5) # get the last value from array
beq $s5, 1, one # if x = 1, we should load s0 with zero offset
end:
li $v0, 10
syscall # bye
Write a program similar to Program 1, including error-checking on the input value. But this time,
take the x value in the main program and then pass x value to a procedure. The called procedure
calculates the first x numbers in the sequence and writes them to an array in memory. After that,
57
the procedure returns the beginning address of the array to the main program. The main program
then accesses the x-th element of the array and prints it out, with both your names, surnames and
Bilkent IDs. (For example: “In position 8, the value is 2911 -- Ali Aytek 12345678 and Bora
Bilgin 87654321”.) You may assume that user enters only integers. (This is for simplicity—so
there is no need to check for floating point numbers). Please follow the MIPS convention for
writing procedures (see p. 115 of the textbook and other related sections). In particular, you
must preserve the value of the $s registers, and the $fp, $gp, $sp, and $ra registers in any called
procedure, so that they are returned to the caller with exactly the same values that were received
at the time of call.
Solution
.data
aseq:.word 0 : 15 # "array" of words to contain numbers
add $t5, $v0, $0 # proc returned the beginning address of the array
la $a0, result1 # load address of result1 for syscall
li $v0, 4 # specify Print String service
syscall # print the prompt string
58
subi $t6, $s5, 1 # to calculate the offset of the last element of the array
sll $t6, $t6, 2 # multiply by 4
add $t5, $t5, $t6 # add beginning address and the offset
lw $a0, 0($t5) # Get the last value from array
beq $s5, 1, one # if x = 1, we should load s0 with zero offset
end:
li $v0, 10
syscall # bye
proc:
addi $sp,$sp,-4 # moving stack pointer
sw $t3, 0($sp) # store previous value
# Procedure Body
la $t0, aseq # load address of array
li $t2, 0 # 0 is the first number in sequence
sw $t2, 0($t0) # A[0] = 0
li $t2, 1 # 1 is the second number in sequence
sw $t2, 4($t0) # A[1] = 1
beq $a0, 1, ret # if x = 1
beq $a0, 2, ret # if x = 2
addi $t1, $a0, -2 # counter for loop, will execute (size-2) times
# Loop to compute each number in sequence using the previous two numbers.
59
ret: lw $t3, 0($sp) # load previous value
addi $sp,$sp,4 # moving stack pointer
jr $ra # return (copy $ra to PC)
Since no restriction was made on the use of pseudoinstructions, we are free to use them. The
most useful one will be ble regX, regY, Target (branch less than or equal). [Without using
pseudoinstructions, this can be implemented with slt $t0, regY, regX followed by beq $t0, $zero,
Target]
Remembering that in HLL“if (A + B> C)” must be implemented in assembly as ble A+B, C,
Else , we write
60