This was a project that started pretty much by accident. I say accident but what I really mean is that it started because of a dumb joke.
As part of my university studies I am currently taking a course called Concepts of Computer Systems which explores the more behind-the-scenes aspects of how computers take software instructions and translate that into actions performed by the hardware. As you can probably imagine, this involves some fairly low-level programming in MIPS assembly, which seems to be the most common instruction set used for education.
In this course, when assignments are submitted in assembly, they are run using a custom MIPS simulator called RSIM that was developed by the CS department. In class we have also been using a tool called MARS, which includes a handy debugger for stepping-through the code and creating breakpoints.
A solution looking for a problem
As someone who enjoys many of the movies that marvel makes, I couldnt help but notice that the original developers had made a critical oversight in the naming of the menu options at the top of the program.
After fixing this issue, the menus are now much more enjoyable to use.
Making this change required me to learn how to extract and run the source code that was shipped with the MARS jarfile and was an interesting experience in making surgical changes to code I had never seen before. However, the joke quickly became less funny and left me with the ability to generate new builds of an app and very few ideas for what to do with it. Talking with some classmates revealed the possibly of adding a dark mode to the app, but that still has not happened yet.
Compatibility is never gauranteed
Fast forward a few weeks. The course was going well and we had been tasked with implementing the math portion of a provided MIPS assembly program for computing square roots using Newton’s method. I had created a working solution in MARS, but uploading it to the CS department servers and running it using their RSIM tool caused my square-root calculator to output a very large value in the neighbourhood of 1600-3200 when tasked with square-rooting the number 25.
After a classmate experienced the same issue and reached out to the professor about it, we were told that the issue was due to the way that MARS and RSIM handled the
xori instruction. Using the recommended fix solved the issue with my code, but by this point I had become curious about what that inconsistency was in the first place and whether i could fix it with my new MARS fork.
As it turns out, MARS implements many shortcuts (known aspseudo-instructions) that expand out to additional lines of code automatically. One example of a useful shortcut implemented by MARS this way is the instruction
addi $t0, 1. Normally the
addi instruction takes three arguments: a register to store the resulting output, a register for the initial value, and a 16 bit immediate value. As you might expect, this instruction will add the value stored in the given input register to the immediate value provided and store the result in the output register.
If you wanted to increment a value in-place, a fairly common programming task, you could just use the same register for the input and output values by writing
addi $t0, $t0, 1. However, programmers will indeed go to any lengths possible to save typing a few characters, so MARS has added an
addi $t0, 1 pseudo instruction allowing you to add values in-place with less typing.
Remember how I said earlier that the immediate value is only be 16 bits? This is because MIPS itself is a 32-bit instruction set, meaning that each register and each instruction is represented by 32 bits. However, because each instruction needs to contain more information than just a single number, there isnt space inside a 32-bit instruction to both tell the computer what to do (i.e. add some numbers) and also provide the 32-bit value that it should add.
Normally, if you wanted to add the hexadecimal value 0xdeadbeef to a register using the operation
addi $t0, $t0, 0xdeadbeef, it would likely raise an error or end up only adding 0xbeef because the immediate value can only hold 16 bits. However, using pseudo-instructions, MARS creates a shortcut that inserts additional instructions to handle 32-bit values, meaning the
addi $t0, $t0, 0xdeadbeef instruction from earlier would work as you would expect.
So how does RSIM handle 32 bit immediate values? RSIM’s implementation of MIPS assembly takes the approach that a 32-bit immediate value is not legal, meaning that if a 32-bit value is provided, such as in the 0xdeadbeef example, it is treated as an error. However, if the value happens to be something like -1 which can be represented in both 16 and 32 bits, RSIM will only use the lower 16 bits, resulting in a value of 0x0000FFFF instead of 0xFFFFFFFF.
This was the problem that was causing my code to break in RSIM but run perfectly fine in MARS.
A new teaching tool
Since I now had the ability to make changes to MARS and I now knew exactly what the problem was, I dove into the MARS code to see if I could fix the problem.
Being brand new to the codebase initially led me down a few false paths, but eventually I discovered that there was a
PseudoOps.txt file within the MARS codebase. I had initially assumed that this file was some kind of documentation until I discovered that I was able to comment out entire pseudo operatons and create brand new ones just by changing lines in that text file.
Talking with my professor revealed that changing this single text file could also give MARS a wide range of additional classroom capabilities. Some of these additional uses include:
- overriding the default pseudo operations so that MARS operates in the same way as RSIM, eliminating shortcuts that are not allowed to be used for assignments
- progressively expanding the set of pseudo operations as students learn them by simply distrubuting this pseudoops file
- allowing students to write their own pseudo operations to create their own flavors of MIPS assembly
Allowing different versions of this file to be passed in by the user, rather than just being a hardcoded path to an embedded resource in the .jar file, would also allow this file to be changed without needing to rebuild MARS each time.
The final product
After a couple days spent implementing this new feature in the MARS source code I had a working version that adds a new Settings > Pseudo Operations… menu, allowing end users to select the path to a modified version of the
The jarfile for this version has been released to github as an unofficial MARS version 4.5.2
While using the MARS debugger to set breakpoints and step through code makes learning assembly so much easier for students, I hope that this additional flexibility for custom pseudo operations helps to give teachers more control ofer the available shortcuts when using MARS in the classroom.