MIPS Programming


To easily simulate MIPS code in software, separately from the PIC32 hardware, we will use the MIPS Assembly and Runtime Simulator MARS. We won’t make it beep and blink, but it will be good enough for us to verify that you can think structurally about MIPS operations.


To launch MARS on the MathLAN, you can type

java -jar /home/weinman/shared/Mars4_5.jar

In addition, you may also copy the JAR (Java ARchive) file from that location or directly download it to your own computer and run it there, using a similar invocation of Java.

Basic Operation

  1. Download the first starter file mips-basics.asm

  2. Open the file in MARS.

  3. To assemble your code, click “Assemble” under the Run menu (or the Screwdriver and Wrench icon in the toolbar).

  4. You can step through your code line by line with the F7 key (or the green and white arrow button with a “1”), but this is tedious. A better way is to switch to the “Execute” pane, which shows your assembled code (along with the actual instructions, the machine version, and its address in the virtual machine). The left-most column allows you to set any breakpoints at which you may want to investigate behavior. If you just want to run the whole test program, the syscall at the end of main will terminate the program and allow you to inspect the final state of the registers. Other desirable breakpoints may arise as you wish to diagnose certain (mis)behaviors.

Broader MIPS Background: Directives

Before we proceed, we will unpack a few new bits of syntax and semantics it is helpful to understand.

In addition to the sequence of instructions you have seen (e.g., addi, lw), assembly files usually contain other important information. Like many assembly languages, directives to the MIPS assembler begin with a period and typically mark up regions and provide data. You will see the following directives:

Precedes a list of symbols exported in the symbol table (with the symbol’s address in the final object) to be available for linking with other objects or use by the debugger.
Marks the beginning of a data section of the assembly language file, for things like global variables.
Marks the beginning of a text section of the assembly language file, where instructions are found.
Precedes a single label that marks the entry/starting point of the program for the loader. MARS ignores this directive and instead starts executing at the beginning of the text section, but it is important for reading general MIPS programs.

Within the data section, followed by a label, reserves and initializes word-aligned, comma-separated blocks of data. For example, if you declared the following global variable in C,

int meaning = 42;

then you might end up with the following MIPS assembly line

meaning: .word 42

To place the address of a label into a register (i.e., for issuing a load or store command), you must use the la (load address) instruction. For example,

la $t0, meaning  # Set $t0 to &meaning (address of the value)
lw $t1, 0($t0)   # Dereference the pointer, i.e., put meaning into $t1

There are other important directives, but these constitute most of the important ones we will use this term.

Grading Guidelines

  1. Note that the MARS simulator does not enable simulation of the branch/jump delay by default. Do not enable it; branch delays will not be activated during grading for correctness, so your program would likely fail many tests, leading to a poor grade.

  2. The only pseudoinstructions you may use for completing the problems in this assignment are:

    In particular, you may NOT use ble, blt, etc.

  3. Your grade on each problem will depend on its correctness. A test suite will be used to validate your problem’s correctness; the grade for each problem will be determined by how many tests each problem passes.

  4. In addition to being graded on correctness, your code will be evaluated on its clarity, organization, and overall efficiency with respect to the number of instructions required to complete any given task. In part that means

    1. providing documentation for each procedure that indicates its purpose, parameters (with any preconditions), and return values or postconditions,
    2. ensuring comments clearly indicate the purpose of each instruction (or short set of instructions), rather than the literal interpretation, AND
    3. nicely aligning the various labels, fields, and comments in your code.
  5. Do not modify the existing .globl directives; the labels for your procedures are required to be global for them to be discoverable by the autograder. Moreover, do not add your main to the list of global labels; it will conflict with the autograder’s main.


Problem 1: Memory Swap

Complete a procedure called swap that transposes the values at the memory addresses stored in its two argument registers $a0 and $a1. The following C program illustrates how it might be used.

int a = 5;
int b = 7;
int * p = &a;
int * q = &b;

printf("%d %d\n",a,b); // prints "5 7"
printf("%d %d\n",a,b); // prints "7 5"

Problem 2: Blefuscu to Lilliput

Occasions sometime arise when one needs to reverse the order of bytes presented to you by the host platform. This often happens when reading data produced by a platform with a different “endian-ness”.

Complete a procedure byteflip that transposes the single 32-bit integer in register $a0 and returns the value in register $v0 with the bytes reversed. For example, if given the value 0xBA5EBA77, a byte flip would yield 0x77BA5EBA.

(Sadly, given 0xB7EF65C6 your procedure would not produce “Lilliput”.)

Problem 3: Going to Extremes

Complete a procedure extremes that takes four 32-bit signed integers in registers $a0, $a1, $a2, and $a3 and returns the smallest of the four values in register $v0 and the largest of the four values in $v1.

Problem 4: Product

Branching gets more interesting when loops are involved.

Suppose your microchip’s ISA did not have an MDU (multiply and division unit) that supported multiplication with a mult instruction. This can happen with extremely low cost devices that not only “make the common case fast” but pay very close attention to their minimal energy “budget”.

Complete a procedure product that multiplies the multiplicand value in register $a0 by the multiplier value in register $a1, using the elementary strategy of repeated addition. The result should be returned in register $v0.

You should assume $a1 is non-negative.

Copyright © 2019, 2020, 2022 Jerod Weinman

CC-BY-NC-SA This work is licensed under a Creative Commons Attribution NonCommercial ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/4.0/ or send a letter to Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.