In this lab, you’ll start using the Keypad and LED Display Simulator plugin to draw some sweet low-res graphics!

Make a file named abc123_lab5.asm in a new directory.

Opening the plugin

In MARS, go to to Tools > Keypad and LED Display Simulator. Not Keyboard and Display MMIO Simulator.

This will pop up a window. Click the “Connect to MIPS” button in the bottom left.

Once it’s connected, you don’t have to close the window or reconnect it.

You can re-assemble and re-run your program as many times as you want while the display is open.

Using the plugin from your code

.include "led_keypad.asm"

Now you can use the constants and call the functions from led_keypad.asm!


Okay the lab for real now

For your first project, you’ll make an interactive video game. Today’s lab has all the same parts as a game, but very simplified. It will look something like the thing on the right when you’re done.

The way any interactive program works is like this:

  1. wait for a little while
  2. check for user inputs
  3. respond to those inputs by updating your program state
    • “state” means “your variables, data structures, etc.”
  4. change the output (screen) to reflect the new state
  5. loop back to step 1

You’ve probably done simple games in programming classes before: user’s turn, get inputs, opponent’s turn, update variables, loop again. The difference now is… it’s faster. The player and opponent(s) get 60 turns every second! AAAAAH!


1. First steps

  1. Make your state variables.
    • Make two global variables (in .data) to hold the dot’s X and Y coordinates.
    • Name them appropriately.
    • They should be words.
    • Initialize them both to 32.
  2. Make the main loop.
    • Make a main function like you have before.
      • Don’t forget .globl main.
    • Inside main, make an infinite loop.
      • Do not jump to main. Make a new label for the loop.
      • You could name it… main_loop: or something.
  3. Wait a little while.
    • I don’t mean sit there! ;)
    • I mean you have to make your program wait for a bit.
    • This is important. Without it, your program will run WAY too fast and you might not be able to stop it without force-closing MARS.

Pausing execution

Syscall 32 lets you pause your program for a bit. It takes the number of milliseconds to wait in a0. Do this syscall as the first thing inside your main loop. But what number do you pass?

There are 1000 milliseconds in one second. You want this loop to run 60 times per second. So how many milliseconds do you have to wait? (cmon. very basic algebra/dimensional analysis. please figure this out.)


2. Drawing the dot on the screen

The screen is a 64 x 64 grid of pixels.

This is usually how displays are represented.

If you look inside led_keypad.asm you will see some constants at the top. (.eqv is like MIPS’s #define.) There are 8 constants for the colors that can be displayed:

There are also a few functions in there. You will be calling display_set_pixel.

  1. Make a new function called draw_dot. It will have no arguments and return nothing.
  2. Call it from your main loop after the waiting code (after the syscall 32).
  3. Inside draw_dot, write the equivalent of this code:
     display_set_pixel(dot_x, dot_y, COLOR_WHATEVER);
    
    • display_set_pixel is a function I gave you in led_keypad.asm. You do not have to write it!
    • dot_x and dot_y are your state variables. You have to get their values out of memory and into the argument registers.
    • COLOR_WHATEVER is whatever color you want to use.
      • Since it’s a constant, you’re gonna use li.
      • Use the named constant, not a number! That’s why we name constants!

Now if you run it, you should see….. nothing!!! What???


3. Making your drawings appear

Like this.

In your main loop, after calling draw_dot, call display_update_and_clear. NOW your dot will appear.

What’s going on?

The display plugin is double-buffered. When you draw to the screen, you are really drawing to a hidden area in memory first. Then you must call a function to actually show the graphics on the screen. This technique avoids problems when the screen is redrawn while you’re in the middle of drawing things, causing weird graphical artifacts.

You should only call display_update_and_clear ONCE per main loop iteration, after drawing everything.


4. Moving it around

First, try this. This is just to test, don’t leave this code in: In the main loop, before drawing, try decrementing your dot’s Y variable. Remember, load-modify-store. The dot should move, but it might crash once it hits the top of the screen. That’s fine. Just make sure it’s moving, and then take that code out.

The way input works in this plugin is that you use the arrow keys and B key on your keyboard. Then, your program can detect that by using input_get_keys, another function from led_keypad.asm. It returns which keys are being held down, but it does so by returning bitflags.

Bitflags

A special case of bitfields is when all the fields are 1 bit long. In that way, we can think of an integer as a small array of boolean values. We call this bitflags.

input_get_keys returns an integer where the lower 5 bits represent the four arrow keys and the 🅱️ key.

OK, tangent over

  1. Make a new function called check_for_input. It will have no arguments and return nothing.
  2. Call it from your main loop, after waiting but before drawing.
  3. Have it do… this. This is not an if-else if-else if… This is a sequence of 4 separate ifs.
    • So if the user is not pressing KEY_L, it should then check if they’re pressing KEY_R.
v0 = input_get_keys()

if((v0 & KEY_L) != 0) // bitwise AND!
    dot_x--;
if((v0 & KEY_R) != 0) // KEY_L, KEY_R etc. are constants.
    dot_x++;
if((v0 & KEY_U) != 0) // you can use e.g. `and t0, v0, KEY_L`.
    dot_y--;
if((v0 & KEY_D) != 0) // don't hardcode the key values. use the constants.
    dot_y++;

Now run your program, click the display, and use the arrow keys on your keyboard. It should work, but… try going off the top of the screen. What happens?


5. Moving it around, without crashing

What’s happening is your x and y coordinates are going negative or off the sides of the screen. On the top side, you’re going to start writing into a part of memory you’re not allowed to, so it crashes.

To prevent this, you have to limit the x and y coordinates.

At the end of your check_for_input function, before it returns, do the equivalent of the following:

dot_x = dot_x & 63; // bitwise AND!
dot_y = dot_y & 63;

Remember what this does? We learned about this as a shortcut for another mathematical operation…

Now your dot should wrap around to the other side like in the gif at the top of this page.


Fun things to try


Submitting

Do not submit led_keypad.asm. Just your lab file, thanks.

Make sure your file is named username_lab5.asm, like jfb42_lab5.asm.

Submit here.

Drag your asm file into your browser to upload. If you can see your file, you uploaded it correctly!

You can also re-upload if you made a mistake and need to fix it.