All Source Code: Github

Instructions

To run:

  1. Load the "super-mario.asm" file into MARS (this may take 15-20 seconds for it to load)
  2. Then hit "assemble" (again, will take 15-20 seconds)
  3. Open the Bitmap Display with the following settings:
    1. unit pixel width = 2
    2. unit pixel height = 2
    3. width = 1024
    4. height = 512
    5. source = $gp (global pointer
  4. Open the keyboard simulator and full-screen the Bitmap Display
  5. Hit "Run" and then "connect to mips" on both the bitmap and keyboard display
  6. Use the WASD keys to move the player around the screen. Walking off the screen wraps you around the start.

Written Overview

I was very interested in creating animations in assembly, so I chose to create an animated mario character for my MIPS project. Due to time constraints, I stopped there and didn't create an actual playable game, but this code could easily be refactored and continued to represent a super mario level.

Since I knew before starting that the codebase for this project would become progressively messier and more difficult to debug, I decided to create macros for most small-but-annoying tasks in order to make later development more painless. This included single line drop-in replacements for

  1. looping over a range and invoking another macro for each iteration step
    1. variants for reverse for loops and with step-sizes
  2. loading in immediate values for registers and floats
  3. converting between and doing arithmetic with int and float values
  4. printing any int, float, string
  5. bitshifting
  6. sleep (delay)

I also had compound macros that invoked other macros, such as:

  1. logging all player data
  2. computing player distance from ground
  3. drawing a single pixel at a certain x, y coordinate
    1. variants with different argument types (immediate vs register)
  4. get memory address for a certain x, y location for a pixel in the bitmap
  5. overlay black pixels on the old mario character for frame updates
  6. increment/decrement player velocity x/y
  7. increment/decrement player position x/y
  8. processing input character
  9. drawing mario run/idle frames 0-7

You can find a flowchart of the most high-level macros and how they're used in the flowchart diagram below.

Using macros was a critical part of development since it made debugging code significantly easier.

Now let's do a high-level walkthrough of the codebase and talk about some important design decisions.

Register and Memory Usage

Graphics Macros

I wrote python code (which is also supplied in this submission) that was used to convert the following 2 gifs of mario running and idling (standing) into individual frames, then process those into lines of code in MIPS that use my draw_pixel macro.

mario-idle.gif mario-run.gif

This gave thousands of commands using the macro draw_pixel_with_color_and_offset_immediate(x, y, color)

ex.
draw_pixel_with_color_and_offset_immediate(24, 0, 0x007D2F3A)

Main Program Flow

Here's some of the higher-level labels

main:

main_loop:

update_player:

update_physics:

clear_and_redraw_player:

Screenshots and Sample run video

Sample run: https://youtu.be/LhNIEds_Tz0

Hints, Warnings, and Future Work

  1. The code for animations consists of over 25,000 lines of calls to the draw_pixel macro with different coordinate and color arguments. This is obviously not the best way to do animations - a far more efficient way that I tried was converting the frame data into a different file format, then loading that into MIPS memory, but I kept running into memory errors so I ditched the approach for the easier way of just writing out all the function calls for every pixel for each frame.

  2. Currently, when mario exits the screen, there is no code that puts his x position on the other side of the screen - instead what happens is that the x value is incremented beyond the max x value, so the y value effectively increases by 1 in the code that finds the memory address for the reference pixel.

  3. The x velocity is dampened by 1% each frame if the player is on the ground, simulating friction, but this restriction does not apply when the player is in the air. Thus, it is possible to achieve infinite speed by repeatedly jumping and moving in one direction, since the screen wraps the player around whenever mario exits leaves the viewable area. This could be prevented by enforcing a maximum X velocity for ther player.

Flowchart

You can click/drag/zoom on the below interactive flowchart, or open in another page using the link below
Flowchart External Link