Lab 4: MIPS Memory Usage with Loops


Due Friday, February 5 at 11:59:59 PM

Goals for This Week

By the time you have completed this work, you should be able to:

Provided files: Documentation:

Step by Step Instructions

There are two tasks that you need to complete for this week, some with multiple steps. Strictly speaking, you may complete them in any order, though for this lab it is recommended to go in order. The tasks are listed below:

You may use any MIPS instructions or psuedoinstructions you want in order to implement these tasks. However, you will be somewhat restricted on exams. You may want to read the grading policies regarding MIPS assembly instructions for more information.

The initial step below describes how to get the files you will need into the appropriate place. These files are used for all the different tasks.

Initial Step: Create a Directory for This Lab and Copy Over the Files

After you log in, go into your cs64 directory that you created last time:

cd cs64

Then create a new directory for this lab: lab4:

mkdir lab4

Then go into that directory.

Now copy over all of the files necessary for this week's tasks:

cp ~kyledewey/public_html/cs64/labs/4/lab4funcs.asm ~kyledewey/public_html/cs64/labs/4/swap_array.asm ~kyledewey/public_html/cs64/labs/4/partner.txt .

Note the use of the trailing . in the above command, which stipulates that the specified files should be copied into the current directory.

Task 1: Basic Memory Operations

This tutorial is a sequence of memory operations, each slightly harder than the last one. In order to allow us to produce different test cases, the main code is separate from your code. We will supply a different main file depending on what we want the initial values to be.

Step 1: Familiarize Yourself With the Code

Because it is assembly, it is a little hard to get a high-level sense of what you are being asked to do. Open the file lab4funcs.asm. The first thing to look at is the global variables, declared in the .data section. There are four global integers: globalA, globalB, globalC, and globalD. Then there is an array: myArray. These are the variables you will be working with this week.

Now look at the code in lab4funcs.asm, particularly the functions (i.e., storevalues, storeregvalues, copyvalues, operations, arrays, and arraycalcs). There are a number of functions you need to fill in. We have not gone over how to properly call functions yet, so you might not know how to do all the steps necessary to write a function. As such, for this portion, you are restricted to use only certain registers (more on why this makes sense later in the class). Each function uses a different restricted set; for example, with storevalues, only registers $v0 - $v1, $t0 - $t7, and $a0 - $a3 may be used. Some of the functions receive input; any input received will be in registers $a0 - $a3. Additionally, the last line of each function may not be removed (which is always jr $ra).

Now look at the lines of code in comments. For each function, there are some lines of code written in high-level language. Translating those to assembly is your task. A description of function inputs, along with C-like code describing what the function does, is supplied in the table below. Like C, semicolon (;) should be viewed as a sequencing operator, meaning the statements should be performed in order, and previous statements can influence the results of later statements. For example, with operations, the second use of globalA refers to the value of globalA set in the previous line.

Your code should not trigger a processor-level exception if overflow occurs. With MIPS, this means that you should use the addu instruction instead of the add instruction for adding numbers together.

Function Name Function Number Input Description
storevalues 0 None
globalA = 6;
globalB = 6;
globalC = 30;
globalD = 30;
storeregvalues 1
  • $a0: New value for globalA
  • $a1: New value for globalB
  • $a2: New value for globalC
  • $a3: New value for globalD
globalA = $a0;
globalB = $a1;
globalC = $a2;
globalD = $a3;
copyvalues 2 None
globalA = globalC;
globalD = globalB;
operations 3 None
globalA = globalB + globalC;
globalD = globalA + globalB;
arrays 4
  • $a0: The new value of myArray[0]
  • $a1: The new value of myArray[1]
myArray[2] = globalA;
globalA = myArray[3];
myArray[0] = $a0;
myArray[1] = $a1;
arraycalcs 5 None
globalA = myArray[0] + myArray[1];

The code is also equipped with a main function, which calls the functions you will be writing. Each run of the program calls a single function. The name of the function to execute is specified with a number, supplied by the user. The table above specifies which number executes which function (e.g., a user-supplied 1 will call storeregvalues). The main function prints out the global variables and the first four array values before and after every function call, and, depending on the function selected, may also print out registers $a0 - $a3.

For example, below is a sample run testing storevalues, which is function number 0 from the preceding table. User input is shown in bold.

This program loads, stores, and operates on variables
Looking at the comments in the code, perform the operations specified.  Make sure you make changes to this code in order to test it.
You can change the initial values of variables as well as the inputs you test.
Global variable values are: 35, 39, 54, 90
Array values are: 537067794, 537002737, 269156513, 7032874
Enter a value: 0
0
Global variable values are: 6, 6, 30, 30
Array values are: 537067794, 537002737, 269156513, 7032874

As an aside, you may have noticed something a bit odd about the memory layout here: we have declared globalA and other global mutable variables in the .data section, whereas in class we discussed that global mutable variables should be in the .bss section. The reason why is specific to limitations of SPIM; on a real processor, these should be separated out.

Step 2: Implement the Functions

Look for the words “TODO” in the file. (If you are using vi, you can search for those words by using “/TODO”.) Make sure you test often. After implementing each function, make sure you run it using the supplied main function.

Make sure your code does not depend on initial values. Remember that the program we run it on will have different global declarations.

A Note About add/addi versus addu/addiu

Recall that add/addi will raise a processor-level exception (for the purposes of this class, a fatal error) in the event that the addition sets the overflow bit. In contrast, addu/addiu (where “u” is short for “unsigned”) will still set the overflow bit, but they will ignore its value. The behavior of add/addi makes sense if we are dealing with signed numbers in two's complement, and the behavior of addu/addiu makes sense if we are dealing with unsigned numbers, which do not have a concept of overflow (that is, it's impossible to get a result of an incorrect sign in a context where everything is implicitly either zero or positive).

The above distinction is important when dealing with memory. Memory addresses are inherently unsigned; there is no such thing as a negative address. As such, when you manipulate memory addresses (as you would need to do in order to translate myArray[x] to MIPS assembly), you should be using addu/addiu, not add/addi. The signed variants (add/addi) will often produce the same result, but if we start dealing with very large addresses, then suddenly the processor might experience an overflow error in a nonsensical context.

It is quite possible, and even common, to need to have both the signed and unsigned variants of add in the same program. For example, consider the following snippet of C-like code, where semantically we should provide an error if signed integer overflow occurs:

int* arr = ...; // initialize arr to some valid value      
int temp = arr[x] + arr[y];

In the above snippet of code, we would need the unsigned variants of add (that is, addu/addiu) in order to grab both arr[x] and arr[y]. However, in order to perform the addition itself (that is, +), we need to use the signed variant of add (that is, add/addi). To help clarify this, keep in mind that arr[x] is shorthand for *(arr + x), where arr is a pointer (it holds a memory address). As such, arr + x is adding together items of types int* (a pointer) and x (an unsigned integer), which denotes the calculation of a new memory address. As this is a calculation for a memory address, and memory addresses are unsigned, we use addu/addiu. However, for the addition that takes place in the original code (that is, arr[x] + arr[y]), this is adding together two values of type int, a signed integer. As such, in this case, we are dealing with signed integer arithmetic, hence we use add/addi for this addition.

Task 2: Swapping Array Contents

In this task, you will swap values around from within an array. In order to allow us to produce different test cases, the main code is separate from your code. We will supply a different main file depending on what we want the initial values to be.

Step 1: Familiarize Yourself With the Code

Open the file swap_array.asm. In the .data section, a number of prompts have been defined, along with an array named myArray which you will modify the contents of. In the .text section, some code for running tests is in place, along with a function stub named doSwap, which you will define. As with the previous task, we have not discussed functions yet, and so this might be a little confusing. The restrictions are that you may only use registers $v0 - $v1, $t0 - $t7, and $a0 - $a3. Additionally, the last line of each function may not be removed (which is always jr $ra).

Step 2: Implement doSwap

Now look at the stub for doSwap, which contains a snippet of C code in the comments which you must implement. This snippet is duplicated below for convenience:

unsigned int x = 0;
unsigned int y = 8;

while (x != 4) {
  int temp = myArray[x];
  myArray[x] = myArray[y];
  myArray[y] = temp;
  x++;
  y--;
}

Your assembly code must do the same thing as the above C code. Make sure your code does not depend on initial values. While you may assume that myArray will always contain exactly 9 elements, you can make no assumptions regarding what values these are. Remember that the program we run it on will have different global declarations.

In addition, the point made regarding the practical distinction between add/addi and addu/addiu in the previous task is still relevant for this task. Correct implementations must utilize both forms, and in the proper manner (that is, addu/addiu for memory addresses and other unsigned things, and add/addi for signed values).

For testing, the code in main will ultimately use your doSwap implementation, and it features a built-in correctness check. If you would like to test on other array contents, feel free to change the values of myArray (keeping sure to keep it exactly 9 elements long). If you do change the values in myArray, be sure to also change the corresponding values in expectedMyArray, which holds what the result should be. When it comes to your implementation, do not simply programmatically copy the values in expectedMyArray into myArray; while this will appear to work on your end, on our end the values in expectedMyArray will not sync up with myArray as it does in your code, so this would end up always giving the wrong result on our end.

Turn in Everything Using turnin

If you partnered with someone, record the email address they are using for the class in partner.txt. For example, if your partner had the email address foo@bar.com, then the contents of partner.txt should be the following (and only the following):

Partner: foo@bar.com

If you did not partner with anyone, you do not need to (and should not) edit partner.txt.

Assuming you are in the cs64/lab4 directory you created at the beginning, you can send in your answers via the following command:

turnin lab4@cs64 lab4funcs.asm swap_array.asm partner.txt

You may turn in the same assignment up to 100 times, which is useful if you are working on it incrementally. Note that only the last version you submitted will be graded.

Even if you did not partner with anyone, you should still turn in partner.txt, which should not have been modified.


Prepared for Computer Science 64 by Diana Franklin, with slight adaptation by Kyle Dewey.