If you’re confused, don’t start writing code to “have something to turn in.”

100% of the time, this will only make you more confused and frustrated, and the resulting code will probably be impossible to fix and difficult to grade. If you’re totally lost, get help before you start writing!

Never skip a confusing step to “come back to it later.”

You probably learned to do this on exams, but this strategy does not work when building something. For example, when you’re building a house, you don’t “come back” to the walls after building the roof. YOU HAVE TO BUILD THE WALLS FIRST. Programming is no different. Things build on top of each other.

In this lab, you’ll be making a very simple interactive program: a four-function calculator.

Since you’re very new to control flow in MIPS, I’m giving you a Java implementation of the lab so you can see how your program will work, and see the code that you will be translating to MIPS.

Before you start working…

Right click and save the Java solution from this link. Read the code, compile it, and run it. Basically, you’ll be translating the main function into MIPS. (If you’re already familiar with MIPS, just go for it, but maybe read the instructions below anyway. You might learn something new!)

How it works

Just like a real calculator, it remembers one value at a time, and everything you do operates on that value. In the Java program, it’s the static variable display.

The program accepts a command, and then optionally an operand. The commands are:

Here’s an example interaction with the Java program (and your program will work almost the same way):

Welcome to CALCY THE CALCULATOR!
0
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): =
Value: 55
55
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): *
Value: 123
6765
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): /
Value: 4
1691
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): /
Value: 0
Attempting to divide by 0!
1691
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): c
0
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): x
Huh?
0
Operation (=,+,-,*,/,c,q) (YOU HAVE TO PRESS ENTER HERE BUT YOU WON'T IN MARS): q

Like the message above says, when entering the operation in the Java version, you have to hit enter. There is no way to just “get a character” in Java, so that’s why. In MARS, there is a way to “get a character”.


0. Getting started

I’ve written a macro to make it easier to print messages to the output. Copy and paste this code into a new asm file, and save it as yourusername_lab2.asm like you did with lab 1. Then, assemble and run.

# YOUR NAME HERE
# YOUR USERNAME HERE

# preserves a0, v0
.macro print_str %str
	.data
	print_str_message: .asciiz %str
	.text
	push a0
	push v0
	la a0, print_str_message
	li v0, 4
	syscall
	pop v0
	pop a0
.end_macro

.global main
main:
	print_str "hello! remove this print_str line after you confirm it works.\n"

1. Variables

Never write a program all at once and then assemble it. Write a little at a time and let the assembler help you along the way. Then test it! Do this in other languages like C and Java, too!

In the Java file, you’ll see this:

// This variable will go in the .data segment.
static int display = 0;
  1. After the .end_macro line (right before the .global main line), you’re going to declare a variable. You have to switch to the .data segment, then declare the variable, and then switch back to .text.
    • Look at the lecture 5 slides (Memory and Addresses) for the syntax for declaring global variables.
    • A java int is the same as a MIPS .word.
  2. Now assemble your code. This program doesn’t do anything yet, but it’s good to check your syntax and see if you’re on the right track.

    In the Labels window, assuming both “Data” and “Text” are checked in that window, you should see:

    • main at 0x00400000
    • display at 0x10010000

2. main and the main loop

  1. Inside main (anything after main: is “inside” it), print out a welcome message using print_str. You use it like this:

     print_str "Hello! Welcome!\n"
    

    Don’t forget the \n at the end of the string if you want a newline.

  2. Now make an infinite loop after that. Name it _loop, like so:

         _loop:
             # code will go inside here. DON'T put this comment here!
             # I'm just putting the comment here so you can see where I mean!
         j _loop
    

    The code inside the loop will go between that label and the j instruction.

  3. Assemble and run. It should print your welcome message once, and then…

    Well, it’s in an infinite loop. It will run forever. Hit the stop button to make it stop. Otherwise your computer fans will start whirring. ;)

From here on, I’m leaving it up to you to assemble and test your program after each step. Get in the habit now while the programs are still simple. Use single-stepping and print_str to test that your control flow works before you try to fill it in. Take it slow!


3. Input and output

Inside the loop, you’re going to do the equivalent of this:

System.out.print(display);
System.out.print("\nOperation (=,+,-,*,/,c,q): ");
int operation = read_char(); // see step 3 below
System.out.print("\n"); // <<<<<< this is not in Lab2.java but you should do this!

So:

  1. Use syscall number 1 to print out display’s contents.
    • Since display is a variable, you must use lw to get its value into a0.
    • By the way, this is the only time you need to print display. Don’t print it again anywhere else in this lab!
  2. Print the prompt string (the "\nOperation etc) with print_str.
  3. Use syscall number 12 to read a single character for the operation.
    • after the syscall instruction, the character typed will be in v0 (it replaces whatever you put into it before syscall).
    • In the original Java code, we have the local variable operation to hold that value. But here, we don’t need a variable - v0 in your code represents what operation does in the original Java.
  4. Finally, use print_str to print the string "\n". This will make the output a little more readable.

If you assemble and run now, it should print 0 and the prompt, then if you hit a character, it loops around, printing 0 and the prompt again.

Try this: To check that you really are printing out the value of display, change your declaration of display so its value is something unique, like 17. When you run the program, now it should print 17 instead of 0. Be sure to change it back to 0 once you’ve confirmed it’s working!


Formatting check!

Don’t forget to keep your code looking nice! Make sure your instructions are properly indented.

Also, typically, each line of high-level language code will turn into multiple lines of assembly. Make sure to use high-level language comments before each group of assembly instructions so you don’t have to “decode” the assembly in your head to know what it does. For example, when printing the display variable, you can write:

    # print(display)
    ...the three instructions load display and do syscall 1...

4. Checking what command it is

Okay. Now that we’ve asked the user for the operation with syscall 12 and what they typed is in v0, we have to look at it and decide what to do. In the original Java, this is done with a big switch-case on the operation variable. We’ll do a switch-case too, on the contents of v0.

Let’s start simple. This is the next thing you will turn into asm:

switch(operation) {
    case 'q':
        System.out.println("quit\n");
        break;

    case 'c':
        System.out.println("clear\n");
        break;

    default:
        System.out.println("Huh?\n");
        break;
}

This is a simplified version of the switch-case in the original Java. We will add onto this in the next steps.

Next, have a look at this part of the correspondences page about switch-cases. Your switch-case will not look identical, so don’t copy and paste it! but it will have the same basic shape:

Now after the code from the previous step (and still inside the loop!), you are going to translate the simpler Java code above into asm following that pattern. Notes:

Now try it out. Here is an example interaction with my program at this point where I hit c, then q, then x (and then hit the stop button cause it loops forever):

Welcome to CALCY THE CALCULATOR!
0
Operation (=,+,-,*,/,c,q): c
clear
0
Operation (=,+,-,*,/,c,q): q
quit
0
Operation (=,+,-,*,/,c,q): x
Huh?
0
Operation (=,+,-,*,/,c,q):

Important things to note:

Try this: Comment out the j _break lines. What happens now? Does that make sense? Be sure to uncomment them after you test it.


5. Implementing the quit and clear commands

Right now, the cases for q and c just print a message for testing. You’re gonna make them do the equivalent of the code in the original Java.

  1. Remove the prints from the q and c cases.
  2. For q, make it use syscall 10 to exit the program like you learned in lab 1.
    • Make sure it works! When you use the q command, it should say “program is finished running.”
  3. c will set the display variable to 0, like the clear key on a calculator. You can do this with one instruction:
    • What instruction stores into a word variable?
    • What register holds 0, always? (It’s named after it.)
    • You can test this command by temporarily changing the display variable declaration so it holds some nonzero value, then running the program, using c, and it should change to 0. But change the declaration back to 0 when you’re done testing it.

Do not “move on” if you can’t get these working. Get help if you’re stuck.


6. The equals = command

The = command lets you type a number into the calculator. The number typed in replaces whatever is in the display variable.

First, see how the Java code does it:

    case '=': {
        System.out.print("Value: ");
        int value = read_int(); // syscall #5, and 'value' is gonna be in v0.
        display = value;
        break;
    }

Like before with operation, the local variable value here will be represented by v0 in your code.

To implement this command, you need to add a new case to your switch-case, which means adding code in two places.

  1. At the top of the switch, you will add another beq after the two that are already there, to check for '='. Put it right before the jump to the default case.
  2. Right before the label for the default case you’ll add your new case for =. Don’t forget the j _break!
  3. Inside the = case (between the label and the j _break):
    1. Print the prompt asking for the value.
    2. Use syscall 5.
    3. Store the return value from syscall 5 into the display variable (the equivalent of display = value in the Java code).

To test it: run the program. Hit = and it should ask for a value. Type in a new value and hit enter. Now it should print that same value! That means the display variable got updated. Try setting it to different values and also use c to set it back to 0. For example:

Welcome to CALCY THE CALCULATOR!
0
Operation (=,+,-,*,/,c,q): =
Value: 50
50
Operation (=,+,-,*,/,c,q): c
0
Operation (=,+,-,*,/,c,q):

When syscall 5 asks for an integer, if you type something that isn’t an integer, or you hit enter without typing anything, it will crash with an error like: Runtime exception at 0x00400080: invalid integer input (syscall 5). It’s okay! There’s nothing you can do about that. Just always type integers when it asks for them. That’s what we’ll do too.


7. The arithmetic operators

Time for the Four Functions that make this a… four-function calculator: add, subtract, multiply, and divide.

Note that this calculator only works on integers, so division will only give integer results. That’s fine.

The arithmetic operators work similarly to how the = command worked, except you’ll do different stuff after asking for the value. The Java does:

    case '+': {
        System.out.print("Value: ");
        int value = read_int(); // syscall #5, and 'value' is gonna be in v0.
        display += value;
        break;
    }

It looks pretty much identical to =, except it does display += value instead of display = value. (display += value is the same as writing display = display + value, if you’ve never seen that before.)

  1. Start with the case for '+'. Remember you need to add both a beq at the top of the switch, and a new case (with j _break) inside the switch, preferably right before the default case.
    • The contents of the case will be similar to the = case, but this time you have GET the current value of display, then add v0 onto that value, then finally SET that new sum back into display.
      • (li and move are the wrong instructions for getting and setting variables.)
    • Be sure to test it. You should be able to use + to add numbers onto the display. So starting at 0, using + to add 30 should give 30, then + to add 7 should give 37, etc.
  2. Once you have the case for addition working, repeat for subtraction, multiplication and division. Three more cases, only differing in the operation you perform to the value.
  3. Test all of them. Remember, this is just an integer calculator, so division will never give fractions.

Speaking of division, try dividing by 0:

Welcome to CALCY THE CALCULATOR!
0
Operation (=,+,-,*,/,c,q): /
Value: 0

It crashes with an error like this:

Error in /path/to/your/lab2.asm line 106: Runtime exception at 0x00400270: break instruction
executed; no code given.

It’s not the best error, but this is actually a division by zero crashing the program! So let’s fix that.


8. Fixing the division by zero crash

In the original Java code, the division case looks like this:

    case '/': {
        System.out.print("Value: ");
        int value = read_int(); // syscall #5, and 'value' is gonna be in v0.
        if(value == 0)
            System.out.print("Attempting to divide by 0!\n");
        else
            display /= value;
        break;
    }

So that’s what you need to do: use an if-else inside your division case to test if the value the user typed in was 0, and if so, print a message.

See this part of the correspondences page or review the lecture 6 slides for the “shape” of an if-else.

Do not exit the program if they try to divide by 0. Just print an error message like the Java program does. If you try to divide by 0 the program should keep running, like this:

Welcome to CALCY THE CALCULATOR!
0
Operation (=,+,-,*,/,c,q): =
Value: 10
10
Operation (=,+,-,*,/,c,q): /
Value: 0
Attempting to divide by 0!
10
Operation (=,+,-,*,/,c,q): /
Value: 5
2
Operation (=,+,-,*,/,c,q):

And that’s it! It should work just like the Java program now.


Submitting

First, be sure to test your program thoroughly.

Then, you can submit:

  1. On Canvas, go to “Assignments” and click on this lab.
  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! Click “New Attempt” on the assignment page and upload it again.

Try this: If you want to go further after submitting, here are some things you could try: