CSCI 206 Computer Organization & Programming

Lab 3

Introduction to MARS and MIPS assembly

Goals

Learn to use theMARS IDE (Mips Assembly and Runtime Simulator)

Gain experience with MIPS assembly

Practice using Git for revision control

Setup

The Mips Assembly and Runtime Simulator (MARS) is a tool for students to explore the MIPS architecture, which is jointly developed by Pete Sanderson (Otterbein University) and Ken Vollmar (Missouri State University). It is distributed as a single Java .jar file that is ready for you to use at ~cs206/bin/Mars.jar — there is no need to copy it to one of your local directories. Before proceeding, start MARS up from the command prompt with:

1

$ java -jar ~cs206/bin/Mars.jar

Be sure you recognize that ~cs206 is a shortcut to the overall course folder /home/accounts/COURSES/cs206, not your own csci206 in your home directory (that you set up in Lab01, in /home/accounts/…/<your username>/csci206). Also recognize that despite the formatting it’s java -(dash)jar , then ~(tilde)cs206.

Since it is a bit of a pain to start up a program with such a long command line, here’s something to make things easier in the future. Close MARS for now; you will get back to it in a little while. Use a text editor to open your ~/.bashrc file – you are about to tweak it to make trivial to start up MARS in the future. (In Linux and its variation, a file which name starts with a dot ‘.’ is a hidden file, one that you will not see if you use a simple ls command. To see all files including hidden ones, you need to use the command ls -a.) What you need here is a Unixalias, that is, something that defines a friendlier nickname for MARS; mars works nicely. Add the following line to your ~/.bashrc file:

1

alias mars='java -jar ~cs206/bin/Mars.jar'

This alias tells the shell that when you type mars, it should understand that you meant instead java -jar ~cs206/bin/Mars.jar. Save the file and exit your text editor. Your running shell will not be ready to understand the new alias, yet: the ~/.bashrc file is normally read when you first start a shell and this instance of the shell is already running. If you don’t want to exit the shell and start it up again, you can force the running shell to reread the ~/.bashrc file with:

1

source ~/.bashrc

The source command will get your shell to read in all the definitions in your ~/.bashrc file, so that you can start using them immediately. Now, when you type simply mars, the simulator will start up again. Here’s a word of caution for when you try to work with MARS remotely (via ssh): since the simulator provides a GUI, you will need to remember to run an X server locally and to tunnel the X11 protocol through your connection. However, all of this can be rather slow depending on your internet connection. You can optionally install MARS on your local computer from the website http://courses.missouristate.edu/KenVollmar/MARS/. If you run into trouble doing this to work on your assignments, ask for help!

The MARS window should look like what you see below. Note that you should be able to see the panels on the right (Registers) and at the bottom (Mars Messages) – if you don’t see them, resize your window so that they appear.

Exercise 1: Mars warm up

Copy the file ~cs206/Labs/Lab03/mystery.asmto your Lab03 folder and open it in MARS. The .asm in this filename indicates that the file contains assembly language. Your screen should look like this:

You are going to practice using MARS to figure out what this mystery program does. At this point, read the entire assembly file from start to finish. You might not be familiar with everything in this file, yet. However, based on what you do know about MIPS, you should be able to make educated guesses as to what each line does.

Notice that the author of this file sometimes refers to registers by numbers (e.g., $8) and other times by names ($t0). To make this program easier to understand the author should have used one naming convention consistently. Your first task is to convert each register number to the corresponding register name. For reference, use the registers pane (on the right side of the MARS window), which shows both the name and the number for each of the 32 registers. To earn full credit in this problem, you need to make sure that your submission has no reference to register numbers.

After cleaning up the register numbers, you will run the program to see what happens. As with C code, you first have to compile (or in this case assemble) your assembly code to machine code before you can run it. You start the process in MARS by clicking the wrench/screwdriver icon, Run->Assemble (indicated in the red circle in the figure below), or by pressing F3.

After you assemble the code, your screen will switch to the Execute tab. You can go back to edit mode by clicking the Edit tab at the top. Note that if you modify your source code, you have assemble it again so that you can execute the changed program.

If there are any errors in your assembly code, they will be highlighted in the Mars Messages box. In the case of this program, the assembly should complete successfully. While on the Execute tab, notice that you can see the contents of memory (in the Data Segment), and the source code per instruction (in the Text Segment) and registers. MARS is nice enough to initialize all memory and registers for you. This is not typically the case on a real machine. The programmer is usually responsible for initialization. Since no one wants to look a bunch of random data in the simulator, MARS takes care of initialization for us.

In the Data Segment, you should see that the first two words are 0xdeadbeef and 0x59. Where do these two values come from? If you are not sure, switch back to the Edit tab and read through the source code to identify what might be setting these particular values. The registers are initialized to zero (except $sp, and pc, which have special initialization values).

When you start our program, it will run to completion, unless you force it to stop at a certain point to examine variables (registers or memory). These stopping points are called breakpoints. They can be enabled by checking the Bkpt box to the left of the instruction in the Text Segment where you want to stop execution. You will not use this feature just yet. First, you will run the program to completion without interruption. To do this, click thegreen circle with a white arrow button, Run->Go (indicated by the green circle in the figure above), or press F5.

In a flash, you should see “— program is finished running —” appear in the Run I/O box at the bottom. This program doesn’t generate any output, but it does modify memory. You should see that a few registers have changed, as well as the second item in the memory Data Segment where the value of 0x59 was. If you didn’t see it at first, press F3 to reset the simulation: take note of the second word in the Data Segment and then press F5 to re-run the program. Notice that the the second value is different, now.

Your goal is to understand what is happening in this simple program. Instead of running the program to completion, you can single step the program so that you can observe the effect of one instruction at a time. Again, reset the simulation by pressing F3. You should see the first line in the text segment change to a yellow background. That indicates the next line to be executed. Press F7 to single step the program to the next line and the effect of the previous line will be applied. Add comments to the .asm source file that demonstrate your understanding of what is happening. For every line in the assembly language source file, you should add one or more comment lines describing what the assembly line does and why.

Exercise 2: C to the rescue

After working through this simple program in assembly, you should be missing the relatively high-level nature of the C programming language. For practice, open up your text editor and create the file c_mystery.c. Inside this file, write your translation of mystery.asm to the C language. You obviously need variables to store the value for unused and x. To mimic the registers needed for operations, you may use extra variables in C. Since there is no output you have no way to verify this program works as expected… or do you?

The output of this program is stored in variable x, which is located in the Data Segment. After running your C program, if you find the same result as that produced by the assembly version, you can have some confidence that you implemented the correct algorithm. To do this, you use gdb to inspect your C program’s memory.

Make sure that you compile your C code with the -g flag, before your try to run your code in gdb as indicated below:

1

$ gcc -g c_mystery.c -o c_mystery

Then, load your program into gdb with:

1

$ gdb ./c_mystery

gdb will not allow you to to examine a program’s memory after it has finished running, but you can get around that limitation easily. gbd allows you to define a breakpoint on any line of our source C program. This will cause the program to stop just before the line at a break point is executed. Use the list command in gdb to peek at the source file that corresponds to your compiled code. Find two line numbers in your C code. First is right after you assign the initial value to the variable x, the second is the equivalent in C after computing the value in $t1 in the MIPS assembly program. This second line number should be somewhere before the end of your main function. Say you determine that these two lines are 15 and 30, assuming you have included some comments in the program. We can then define the breakpoint on these lines with:

1

2

(gdb) break 15

(gdb) break 30

After the breakpoints are set, you can run the program with:

1

(gdb) run

Once the program stops at a breakpoint, you can print the contents of the variable x with:

1

(gdb) print/x x

The /x part tells print to format the output as hexadecimal (you should do this to match the output from MARS). Don’t be confused by the second x in that line! It is just coincidence that the variable of interest in this example is also named x. If everything is correct, the value printed at the first break point should be the same as the one before executing but after assembling the MIPS program, the value printed at the second break point should be the same value after the executing the MIPS program, in the address 0x10010004 in MARS.

Exercise 3: Cross your fingers

If you are asking yourself why not just use gcc to translate our C program into assembly to verify the program is correct, good point: you should try to do that. The command line option to stop gcc after translating C to assembly is -S. The complete command is:

1

$ gcc -S c_mystery.c -o c_mystery.asm

This translates c_mystery.c to assembly placing the output in file c_mystery.asm. Open up c_mystery.asm in your text editor. You should see some unfamiliar commands: pushq, movq, movl, addl. These aren’t the MIPS assembly commands we’re looking for. Because this lab machine uses an Intel x86 processor, gcc outputs Intel x86 ISA assembly language which is completely different from MIPS.

In order to get what we want to see, we need to ask gcc to output MIPS assembly even though we are running on an Intel processor. It might seem a bit odd to use one architecture to generate code for another, but it is actually a fairly common practice. The process is called cross-compiling. You can find a MIPS cross-compiler at
/usr/remote/mipsel/bin/mipsel-linux-gcc .

To access the MIPS cross compiler easily, you can add /usr/remote/mipsel/bin to your path by adding a line to the end of your ~/.bashrc file with the command below:

1

$ echo 'export PATH=$PATH:/usr/remote/mipsel/bin/' >> ~/.bashrc

Note you must use single quotes or escape the $PATH or else bash will expand your current path string and you will miss out on future system-wide path changes. See this link for the bash quoting rules.

Since this modifies the state of your running shell, you need to source your ~/.bashrc file again with:

1

$ source ~/.bashrc

Once this is done, you can invoke the cross compiler with the command below:

1

$ mipsel-linux-gcc -S c_mystery.c -o c_mystery.asm

Open up the generated c_mystery.asm file in your text editor. You will now see the more familiar MIPS instructions like: lw, sw, addu, etc. In addition, you will see several lines that begin with a period (.). These are assembler directives. Don’t worry about them for now; we have only talked about the .data and .textdirectives (which are in there too).

Although this algorithm should be the same one in mystery.asm, it is very likely that mipsel-linux-gcc generated different assembly code. If you study this file carefully, you should see that it does the same thing, although in a slightly different way. The generated version is most likely a longer program than your mystery.asm. Even though mipsel-linux-gcc is a very good compiler, it is possible for a human coder to take shortcuts that gcc cannot. That is why assembly programming is still relevant today. In cases where every CPU cycle counts, assembly is the best way to go. Later this semester, we will learn how to mix C and assembly coding to get the best of both worlds.

When you are done with this assignment, you need to make sure all of your source files are added to your git repo (mystery.asm, c_mystery.c c_mystery.asm) and you push those files to the gitlab server.