This lab is about familiarizing you with the MARS MIPS simulator software, and getting you started on your FUN ASSEMBLY LANGUAGE JOURNEY. Well I think it’s fun.

PAY ATTENTION to these colored boxes. For some reason, lots of people skip them. I don’t know why! Don’t be like them! Read these!

Preface

I write my labs and projects like tutorials. There’s a lot of reading, but that’s because you’re new to all these things and I have to explain everything. Just follow the directions in order and you’ll be done in no time.

Try this: boxes labeled “try this” are things to play around with and learn a little more. They are not required and you don’t have to turn in the code you write for these, so you can erase that code when you’re done playing around.


1. Getting started

This lab assumes you’ve done all the stuff from Lab 0 and you can run MARS. If you still haven’t done that, or you still have issues with MARS, please get that fixed before doing this lab.

When you run MARS, you will see this:

a diagram of the mars interface.

Setting things up (important!)

In the Settings menu, make sure the following things are checked (enabled):

Leave the other settings unchanged.

Also, if the registers view on the right side is enormous and taking up half the window, you don’t have to live like that. Just resize it by dragging the vertical divider so it’s narrower. Similarly, if the “Mars Messages” at the bottom is huge, drag the horizontal divider down so it’s shorter.


2. Hello, nothing

Now you’ll learn how to make an empty program, assemble it, and run it.

  1. Make a new file (File > New, or use the button in the toolbar), and save it somewhere sensible with the name username_lab1.asm, where username is your Pitt username. For example, I would put jfb42_lab1.asm. (You will put something different. Lol.)

    You have to save the file in order to assemble it!

  2. In MIPS, comments start with a # sign. At the top of the file, put your name and username in a comment.

  3. Just like in Java or C, our assembly programs start at a main function. Type these lines into your file.

    The .global directive is only needed for main. Most of your functions won’t need it.

     .global main
     main:
    

    main: is a label. Labels name parts of your code; not just functions, but also loops, if-elses, variables, etc.

  4. Instead of “compiling,” we “assemble” an asm program. When you assemble the program with the assemble button, it’ll switch to the Execute tab.

    a diagram of MARS's execution view.

If you cannot see these panels and just see a blue void, do this: close MARS and start it again. Before you open any files, drag the horizontal divider above “Mars Messages” down so it’s a reasonable size. Then do “File > Recent Files…” and reopen your whatever_lab1.asm file. Now when you assemble, you should see those three panels.

  1. Now run with the run button. In the “Run I/O” at the bottom of the screen, it’ll say

     -- program is finished running (dropped off bottom) --
    

    It’s a completely empty program that does nothing. Yay!

Try this: what error do you get if you remove the .global main line? Well now you know what to do if you see that error. :)


3. Playing with registers

Now you’ll learn how to put integers into registers and copy them around, and also to step through your program one instruction at a time.

Look at the register pane on the right side of the window. There are 35 registers listed, and all but gp, sp, and pc contain 0. These are the values they are set to when your program starts.

“Immediate” means “a constant that is embedded within the instruction.”

  1. Type these instructions in after your main: line. li stands for “load immediate”, and it puts a constant value into a register.

         li   t0, 1
         li   t1, 2
         li   t2, 3
    

    By analogy to Java, li t0, 1 is like doing t0 = 1.

  2. Assemble and run. Those t0, t1, t2 registers on the right side changed to 1, 2, and 3!

  3. But it was too fast to see what happened. Assemble again, but this time, step through instruction-by-instruction with this button: the step button. Watch the values of the registers and the highlighted instruction in the text window.

    • You’ll see the registers highlighted green when they change.
    • You’ll notice that the highlighted instruction is the one that is just about to run. This trips a lot of people up! The effects of the instruction will not take place until you execute that instruction and move onto the next one.
  4. But wait! You can go backwards too. Use this button: the step back button.

    • You’ll see the instructions happen in reverse, and the registers will get set back to 0.

    Stepping forward and backward one instruction at a time is extremely helpful when you first learn to program assembly. (It’s extremely helpful even when you’ve been using it for years, too.)

    Half a century ago, someone decided to use the word “move” instead of “copy” and now we’re stuck with it. Sorry!

  5. Last, we can copy values between registers with the move instruction. After your lis, write these lines:

         move a0, t0
         move v0, t1
         move t2, zero
    
    • Assemble and step through. The values are copied from the register on the right into the register on the left. This is important: data is never “moved” on a computer, always copied. (What would moving it even do? What would be left in the original register? I dunno.)

    A really, really common mistake is to read move a0, t0 as “move a0 into t0”. This is backwards. Try to imagine move a0, t0 as a0 = t0 instead.

Try this: Load a hexadecimal immediate into a register, like li t0, 0xF00D. Assemble, and look at the “Code” column in the text segment. Do you see it?

Now try putting a value into the zero register. That’s its name. li zero, 10. What happens when that runs? What value is still in it? Why do you think the register is named zero then? ;o

When you step through your program, one other register changes on every instruction… step back, too. What register is that? What do you notice about its value? With “Hexadecimal Values” on, do you see its values anywhere else in the execute tab?


Errors and line numbers

A lot of newer programmers seem to think that when they get errors (either compiler/assembler errors or runtime errors), they just have to guess and infer where the error happens. Not true! Pretty much every error message you ever get in Java or MIPS tells you exactly where in your code the error is.

  1. At the bottom left of the code editor in MARS, make sure this “Show Line Numbers” checkbox is checked (there is no reason to ever uncheck it):

  2. Let’s introduce an error into our program to test this out. In the code you just wrote, change one of the moves to li. (Like, change move a0, t0 to li a0, t0.) Now assemble.

  3. You’ll see an error at the bottom in the “Mars Messages” panel that looks something like this:

     Error in /path/to/your/abc123_lab1.asm line 16 column 9: "t0": operand is of incorrect type
     (expected INTEGER_16, got REGISTER_NAME)
    
    • See, there is no need to guess what part of your code is causing the problem. It says it’s on line 16 in your _lab1.asm file.
    • Then, if you double-click that error message, it takes you to that line of your code and highlights it!
  4. Change that code back so it works again.


Code formatting

This applies to any language, not just assembly: do not treat code formatting as a “final step” you do to “clean things up” before submitting. It is not. Code formatting is absolutely crucial for making your code readable and understandable. Keep your code formatted nicely while you write it, not at the end.

Go to the Materials page and look for the “MIPS style guide” in the References. Have a look at the code comparisons in particular. Here is an important rule about dealing with indentation, in any programming language:

Never use the spacebar at the beginning of a line of code to “line things up.” That is what the Tab key is for.

The Tab key is what you use to indent code. You can also deindent code with Shift+Tab. Finally, you can select several lines of code and use Tab and Shift+Tab to indent and deindent them in virtually any code editor, including MARS.

Knowing all this, format and comment the code you’ve written so far so that it looks like this:

code that is correctly formatted.

I have my editor settings (Settings > Editor) set a little different from the defaults. If your colors are different or your tabs are wider than the screenshot, that’s fine. Consistency and neatness are the most important parts of code formatting.


4. Printing things

Normally the first program you’d write in a language is “hello world” but that’s a bit too complicated for right now. Instead, let’s settle for printing some integers.

  1. After all the code from above, type this:

         # print 123
         li a0, 123
         li v0, 1
         syscall
    
  2. Assemble and run, and you’ll see the number 123 printed in the “Run I/O”.

What’s going on here? What is a0? What is v0? What is syscall?

As you’ll find out in 449, programs have to ask the operating system to get input or produce output, and we do that with system calls. MARS pretends like it’s a tiny operating system, so it has many built-in system calls to print things out, ask the user to type something, etc.

We choose which system call to run by putting a number into the v0 register before executing the syscall instruction.

From now on, I want you to treat the li v0, whatever and syscall as two halves of the same thing. You do not put any code between the li v0, whatever and the syscall. They go together. Never use syscall without first doing li v0, whatever.

In MARS, go to the “Help > Help” menu item. Then click “Syscalls” on the lower set of tabs. This tells you about what syscalls are, which ones are available, what their numbers are, and their arguments and return values.

Notice it says this at the top of the table:

Service Code
in $v0
Arguments Result
print integer 1 $a0 = integer to print  

There are lots of other possible values you can put into v0 to choose different syscalls, but syscall 1 means “print an integer.”

Then, the arguments to the syscalls go in the a registers. That’s why they are named a0, a1, etc. a is for argument.

So to sum up: the three lines above correspond roughly to this Java code:

System.out.print(123);

5. Printing more numbers and newlines

Don’t forget to keep commenting your code so you can tell what it does!

Right now, your program just prints out 123. Now:

  1. Add code to do the equivalent of System.out.print(456) after the first print. (It will be another 3 lines.)
  2. Run it and look at the output.

It comes out as 123456. Well, that’s confusing! But that’s because you printed both numbers on the same line. Unfortunately there is no equivalent of System.out.println. So, we have to print the newline ourselves.

  1. Between the two prints (right after the first syscall), we are going to do another syscall:
    • for the argument a0, use '\n' (that’s single quotes around a backslash and n)
    • for the syscall number v0, use 11 (look up what syscall 11 does in the MARS help)
  2. Run it again.

You should now have 123 and then 456 on another line after it. Nice!

Try this: see if you can use syscall 11 to print out a short message by using different characters as arguments (a0). It gets pretty repetitive, doesn’t it? Don’t worry, there are easier ways to print strings :)


6. Input and global variables

Note: if you are starting the lab early, this section uses stuff that we talk about in lecture 5.

Syscall 5 works a bit like Scanner.nextInt() in Java. It:

After all the code you’ve written so far, do this:

  1. print another newline with syscall 11 like you did before (you can copy and paste those lines)
  2. Use syscall number 5. It takes no arguments, so you don’t have to put anything in the a0 register before using it.

Run your program, and now instead of stopping, it shows:

123
456
|

with a flashing cursor on the line after 456, which is it waiting for you to type something.

If you click on the “Run I/O” box, type a number, and hit enter, the program will end.

If you type something other than a number and hit enter, or hit enter without typing anything, you’ll get something like Runtime exception at 0x00400034: invalid integer input (syscall 5). That’s okay, that’s not a bug or anything. It’s just that syscall 5 is picky and you have to type an integer.

Making global variables

Up at the top of your program before the .global main line, type the following. (Don’t copy-and-paste, type it so you get in the habit.)

.data
    x: .word 0
    y: .word 0
.text

Remember from lecture that this declares two global variables, x and y, both 32-bit ints, and both initialized to 0.

Setting the variables

  1. back at the end of main, right after doing syscall 5, you can set x to the return value from the syscall with:

         sw v0, x # x = v0
    

    Remember, sw is “backwards” in that the destination is on the right!

  2. Now, try stepping through your program. When you get to the syscall for syscall 5, it will expect you to type in a number, so do that. Then, if you step again, you can see the value that you typed in get stored into the variable x in the Data Segment view.
    • You may have to uncheck “Hexadecimal Values” at the bottom of the Data Segment view for it to be more readable.
  3. Okay, great! Now, at the end of the program, input a second number with syscall 5, but this time store it into y. Your program should now ask for two numbers in a row.
    • Remember, you HAVE to set v0 to 5 before doing syscall a second time.
    • You can run the program, type in the two numbers, and at the end, you should see them both in the Data Segment view next to each other.

Printing their sum

Last, we want to print the sum of x and y (i.e. print(x + y)). But the values of x and y are in the variables, not in the registers. So to get the variables’ values, we have to load them with lw.

I’m leaving this a little more open-ended, but remember:

When done correctly, your program should ask for two numbers, then print out their sum, like:

123
456
1000
533
1533
-- program is finished running (dropped off bottom) --

7. Exiting gracefully

Finally, you’ll learn how to exit your program more “properly”.

At the end of your main function (so, the end of your program), use the exit syscall (number 10). It takes no arguments, so you don’t have to put anything in the a0 register.

When you run now, your output should now look something like this (depending on what you type in for the numbers to add):

123
456
10
20
30
-- program is finished running --

So, the final message changed. It might not seem that important now, but it’ll save you a lot of trouble later if you end every main function with the exit syscall.


Submitting

To submit:

  1. On Canvas, go to “Assignments” and click Lab 1.
  2. Click “Start Assignment.”
  3. Under “File Upload,” click the “Browse” button and choose your .asm file.
  4. Click “Submit Assignment.”

If you need to resubmit, that’s fine, just click “New Attempt” on the assignment page and upload it again.