MIPS VM

An (almost) purely functional virtual machine for emulating the MIPS Architecture. I wrote this as a way to review the code I had written for a university class. Since I couldn’t use the provided VM after the semester finished, I decided to make my own VM.

However in addition to writing the VM, my other goals were to use this as an opportunity to learn the Racket programming language better, and to think about programming in as purse of a functional way as possible.

What it looks like

$ racket main.rkt --twoints examples/collatz.mips
Enter value for register 1: 0
Enter value for register 2: 121
Running MIPS program.
MIPS program completed normally.
$01 = 0x00000000   $02 = 0x00000001   $03 = 0x0000005f   $04 = 0x00000000   
$05 = 0x00000000   $06 = 0x00000000   $07 = 0x00000000   $08 = 0x00000000   
$09 = 0x00000000   $10 = 0x00000000   $11 = 0x00000001   $12 = 0x00000002   
$13 = 0x00000003   $14 = 0x00000000   $15 = 0x00000000   $16 = 0x00000000   
$17 = 0x00000000   $18 = 0x00000000   $19 = 0x00000000   $20 = 0x00000000   
$21 = 0x00000000   $22 = 0x00000000   $23 = 0x00000000   $24 = 0x00000000   
$25 = 0x00000000   $26 = 0x00000000   $27 = 0x00000000   $28 = 0x00000000   
$29 = 0x00000000   $30 = 0x01000000   $31 = 0x8123456c

How I built it

I say this is “almost purely functional” because in the end I cheated a little: On command line initiation, a few global flags are set to enable or disable various pieces of functionality (for example, more verbose logging). While this is not necessarily the best design for long-term sustainability, it was a compromise I made in order to write the feature at all.

Challenges I ran into

Structuring a somewhat-complex project

Unlike creating a site in Django, or working on a school project, there’s no predefined structure that I could follow when programming it. This wasn’t a large barrier to starting the project, however after completing it, I came back a few times to see if I could better structure the project.

Testing my code

I wanted to test my code, however by leaving that to an afterthought, it became quite challenging. Following the above structural issues, in restructuring my code into testable pieces I found it broke down nicely along divisions that I had ignored before.

Accomplishments that I’m proud of

It’s significantly faster than I expected. Benchmarking on my laptop, the first iteration ran at around 200kHz, which seems pretty fast for an interpreted language.

I’m also pretty happy with how the code turned out. It’s fairly tight and functional. It could definitely be improved in a few ways, but for my first non-school functional programming project, I think it’s pretty darn good.

What I learned

Contracts are cool

In Racket, you can define contracts for your code, creating a programmatic guarantee that your inputs and outputs will conform to some shape. However, they’re just another tool. When I first discovered them, I went overboard and gave everything a contract. Of course, that ended up being a huge pain to fight with any time I refactored anything, and so learned to use them sparingly.

Functional programming is a dangerous game of code golf

Every time you think you’ve finished writing the program, you think of another way to make things even simpler. At some point however you have to say enough is enough, before you throw away a lot of time for little in the way of benefits.

What’s next?

While the project works, there’s all sorts of small pieces that I could add to it. Some that I’ve thought of are:

  • Writing more tests so I can make sure it works
  • Creating a simple UI to look into the VM as it runs
  • Adding a debugger, to allow stepping through machine code one cycle at a time
  • Adding a disassembler

If you’re interested in looking at the source for UWME, you can find it on GitHub.

Written December 4th, 2018