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:
q
: exit the programc
: clear the display (setdisplay
to 0)=
: asks for a number and setsdisplay
to that number+
,-
,*
,/
: asks for an operand and performs that operation ondisplay
/
also checks for division by 0 and shows an error message
- anything else gives an error message
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;
- 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
.
-
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
at0x00400000
display
at0x10010000
2. main
and the main loop
-
Inside
main
(anything aftermain:
is “inside” it), print out a welcome message usingprint_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. -
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. -
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:
- Use syscall number 1 to print out
display
’s contents.- Since
display
is a variable, you must uselw
to get its value intoa0
. - By the way, this is the only time you need to print
display
. Don’t print it again anywhere else in this lab!
- Since
- Print the prompt string (the
"\nOperation
etc) withprint_str
. - Use syscall number 12 to read a single character for the operation.
- after the
syscall
instruction, the character typed will be inv0
(it replaces whatever you put into it beforesyscall
). - 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 whatoperation
does in the original Java.
- after the
- 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:
- some
beq
s at the top to check the value that we are switching on - a
j _default
after thebeq
s to go to the “default” case if none of the values match - the cases themselves, labeled, with
j _break
at the end of each - the
_break:
label at the very end
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:
- You will not be doing
lw
like on the correspondences page. - The value you are switching on is in
v0
, because you just did syscall 12 to get a character. Thisv0
corresponds to theoperation
variable in the original Java. - You can put single-quoted characters as the second value in a
beq
, likebeq v0, 'q', ...
- (This is because a single character is really just an integer.)
- For the code inside the three cases, you know how to print out strings by now.
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:
- It prints 0 (the contents of
display
) after every command. That’s correct and normal. - Entering the
c
command printsclear
. - Entering the
q
command printsquit
. - Entering
x
(or any other letter) printsHuh?
. This is the default case.
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.
- Remove the prints from the
q
andc
cases. - 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.”
- Make sure it works! When you use the
c
will set thedisplay
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, usingc
, and it should change to 0. But change the declaration back to 0 when you’re done testing it.
- What instruction stores into a
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.
- At the top of the
switch
, you will add anotherbeq
after the two that are already there, to check for'='
. Put it right before the jump to the default case. - Right before the label for the default case you’ll add your new case for
=
. Don’t forget thej _break
! - Inside the
=
case (between the label and thej _break
):- Print the prompt asking for the value.
- Use syscall 5.
- Store the return value from syscall 5 into the
display
variable (the equivalent ofdisplay = 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.)
- Start with the case for
'+'
. Remember you need to add both abeq
at the top of the switch, and a new case (withj _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 ofdisplay
, then addv0
onto that value, then finally SET that new sum back intodisplay
.- (
li
andmove
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.
- The contents of the case will be similar to the
- 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.
- 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.
- does it loop infinitely until you exit with
q
? - can you set the displayed number with
=
? - can you clear the displayed number back to 0 with
c
? - can you do all the mathematical operations?
- does dividing by 0 show an error message, but the program keeps running?
- does dividing by something other than 0 work?
- does using an invalid command give an error message?
Then, you can submit:
- On Canvas, go to “Assignments” and click on this lab.
- Click “Start Assignment.”
- Under “File Upload,” click the “Browse” button and choose your
.asm
file. - 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:
- Make it a proper floating-point calculator. We aren’t learning about the floating-point instructions/registers in class, but it’s not really that much different from using integers. Things to note:
- There is an alternate set of float registers visible in the “Coproc 1” tab on the right side of MARS.
- They are named
f0
throughf31
, but if you use doubles, you only use the even numbered ones:f0
,f2
,f4
, etc.
- They are named
- All the floating-point instructions have a
.
in their names, and the double-precision ones end in.d
.- You can see these in the Help > Help window.
- Declare
display
as a.double 0.0
instead of.word 0
- Use
l.d
ands.d
instead oflw
andsw
when accessingdisplay
- And you’ll have to load into/store from float registers.
- Use syscall 3 to print
display
instead of syscall 1- It expects its argument in
f12
, nota0
- It expects its argument in
- Use syscall 7 to get values for
=
,+
,-
,*
,/
instead of syscall 5- It returns its value in
f0
, notv0
- It returns its value in
- Use
add.d
,sub.d
,mul.d
, anddiv.d
for the arithmetic operations
- There is an alternate set of float registers visible in the “Coproc 1” tab on the right side of MARS.
- Allow “command repetition.” On a real calculator, if you type in
3 * 2 =
, it shows 6. If you hit=
again, it will repeat the* 2
and show 12, and then 24, 48, 96…- To make this work, you would have to store the previous operation and operand into new variables.
- You can either add a new command like
r
to mean repeat, or you can check if the user entered the character'\n'
- a newline. Then they can just hit enter at the command prompt to repeat the last command.
- Four-function calculators usually have a memory feature to store an extra number.
- Add a new variable to hold that extra number.
- You can make
m
do memory commands. It would prompt the user for one of the following:c
: clears the memory variable to 0.s
: copiesdisplay
into the memory variable.r
: copies the memory variable intodisplay
.+
:memory += display
-
:memory -= display