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.
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
.globl 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:
// These variables will go in the .data segment.
static int display = 0;
static char operation = 0;
- After the
.end_macro
line, you’re going to declare two variables. You have to switch to the.data
segment, then declare the variables, and then switch back to.text
.- Look at the lecture 3 slides (Memory and Addresses) for the syntax for declaring global variables.
- Although the Java uses
char
for operation, you can declare bothdisplay
andoperation
as.word
variables in your MIPS code.
-
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, you should see:
main
at0x00400000
display
at0x10010000
operation
at0x10010004
display
andoperation
could be swapped if you declareoperation
beforedisplay
. That’s okay.
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:# while(true) { _loop: # } 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, remove any test prints, and now you’re going to do the equivalent of this:
System.out.print(display);
System.out.print("\nOperation (=,+,-,*,/,c,q): ");
operation = read_char();
System.out.print("\n"); // <<<<<< this is not in Lab2.java but you should do this!
- Use syscall number 1 to print out
display
’s contents.- Since
display
is a variable, you must uselw
to get its value intoa0
.
- Since
- Print the prompt string by using
print_str
:"\nOperation (=,+,-,*,/,c,q): "
- Use syscall number 12 to read a single character, then store it into
operation
.- after the
syscall
instruction, the character typed will be inv0
(it replaces whatever you put into it beforesyscall
). - so now, you can store
v0
into theoperation
variable, withsw
.- Remember that
sw
is the only instruction that is “backwards,” i.e. the variable that you are storing into is the second argument to the instruction!
- Remember that
- after the
- Finally, just 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 it’s 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, when you did operation = read_char();
, that turned into multiple lines of assembly. To make it easier for you to read, put the Java code as a comment before the assembly that implements it.
# operation = read_char();
...the instructions that do syscall 12 and then store into operation...
# and keep commenting as you write
Typically, each line of high-level language code will turn into multiple lines of assembly. Make sure to use high-level language comments like this so you don’t have to “decode” the assembly in your head to know what it does.
4. Checking what command it is
Okay. Now that we’ve stored the user’s input into operation
, we have to look at it and decide what to do.
After the previous step (and still inside the loop!), do this:
# switch(operation) {
lw t0, operation
beq t0, 'q', _quit
beq t0, 'c', _clear
j _default
# indentation is not *required* in asm, but it can be helpful.
# case 'q':
_quit:
print_str "quit\n"
j _break
# case 'c'
_clear:
print_str "clear\n"
j _break
# default:
_default:
print_str "Huh?\n"
# no j _break needed cause it's the next line.
_break:
# }
# this "j _loop" is already here! you just add the code above before it.
j _loop
This is the equivalent of this Java code. See the similarities?
switch(operation) {
case 'q':
System.out.println("quit");
break;
case 'c':
System.out.println("clear");
break;
default:
System.out.println("Huh?");
break;
}
Try it out. What happens when you hit c
? q
? Anything else? Does it make sense?
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, q
and c
just print a message. You’re gonna replace that code.
- For
_quit
, 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
_clear
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.
6. The commands with an operand
Now you’ll implement =
, +
, -
, *
, and /
. Notice in the Java code, this line:
case '+': case '-': case '*': case '/': case '=':
This means “for all of these characters, run the following code.”
- Add
beq
s for those commands in the switch you just made, and have them all go to a new case label, like_get_operand
.- Don’t just put that label anywhere. Think like you’re writing HLL code. Put it before the default case.
- After that label, translate the Java code for prompting the user and reading an int.
- Don’t forget to do the equivalent of the
System.out.print("Value: ");
line. Everyone forgets that! - Syscall 5 is like
scanner.nextInt()
in Java. - After doing syscall 5, the return value (what the user typed) will come out in
v0
, like usual.
When you run syscall 5, 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. - Don’t forget to do the equivalent of the
- Now, just like the Java code, you will do another
switch-case
onoperation
inside this case.- Look in
Lab2.java
! See, there are two switches, one inside the other. - Make cases for each of the five operations but don’t fill them in yet.
- You can jump to the same
_break
label to break at the end of each case. - Since you already know that the character is one of
+ - * / =
, you don’t have to check for an invalid character here.
- Look in
- For the
=
command, store what the user typed in (v0
) intodisplay
.
Now test it. You should be able to enter the =
command, then type a value, and the display
should change to that value. Then you should be able to clear it back to 0 with c
. For example:
Welcome to CALCY THE CALCULATOR!
0
Operation (=,+,-,*,/,c,q): =
Value: 50
50
Operation (=,+,-,*,/,c,q): c
0
Operation (=,+,-,*,/,c,q):
If it doesn’t work, DO NOT MOVE ONTO THE NEXT SECTION. Don’t “come back and fix it.” This is the easiest way to get completely overwhelmed and discouraged. Try single-stepping through your program, seeing where it goes, and comparing that to where you expect it to go. And if you can’t figure it out, ask for help.
7. Finishing it off
All that’s left are the arithmetic commands, + - * /
.
- Fill out the code for each of those operations after the labels you made.
- You will load
display
, modify that value, and store it back intodisplay
.
- You will load
- Try it out.
- If you divide by 0, what happens?
- To avoid that, let’s add an
if-else
inside the division case:- Look at
Lab2.java
in thecase '/'
. You want to do that! - See this part of the correspondences page or review the lecture 4 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.
- Look at
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 unknown 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, just click “New Attempt” on the assignment page and upload it again.
Try this: If you want to go further, here are some things to try:
- 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…- 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 value to 0.s
: copiesdisplay
into the memory value.r
: copies the memory value intodisplay
.+
:memory += display
-
:memory -= display