This time, you’ll use arrays, for loops, and functions to draw some simple things to the screen. Yes, the screen! This is also a little preview of the MARS plugin you’ll be using for labs 5/6 and the project.

Be sure you are using Mars_2201_1025. Also be sure to read all this stuff. Yes, all of it. If you don’t understand it, do the lab and it will help you understand!


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.


Memory-mapped input and output

Syscalls are one way of making our programs interact with the real world. Another way is to use memory-mapped input and output, or MMIO for short.

With MMIO, you can interact with hardware devices other than the CPU. You use loads and stores at special memory addresses. When you access memory at these “magical” addresses, the data is transferred between the CPU and the hardware device, instead of between the CPU and RAM.

The LED Display plugin has a few “magical” addresses:


How the display works

This is a diagram of the way the display and the pixel data are arranged:

a box with the top-left labeled DISPLAY_BASE. the top-left is (0, 0); the top-right is (63, 0); the bottom-left is (0, 63); the bottom-right is (63, 63). The address of the bottom-right is DISPLAY_BASE + 1023.

And this is how the data copying happens:

the RAM pixel data array is on the left. the visible display is on the right. sw zero, DISPLAY_CTRL will copy from the RAM to the display.

To sum up, if you want to draw things to this display, it’s pretty straightforward:

  1. Store some bytes into the array that starts at DISPLAY_BASE.
  2. Store zero into DISPLAY_CTRL.

That’s it! And that’s the basis of this lab.


0. Getting started

Copy and paste this stuff into a new abc123_lab3.asm file.

# YOUR FULL NAME HERE
# YOUR USERNAME HERE

.eqv DISPLAY_CTRL  0xFFFF0000
.eqv DISPLAY_KEYS  0xFFFF0004
.eqv DISPLAY_BASE  0xFFFF0008
.eqv COLOR_BLACK   0
.eqv COLOR_RED     1
.eqv COLOR_ORANGE  2
.eqv COLOR_YELLOW  3
.eqv COLOR_GREEN   4
.eqv COLOR_BLUE    5
.eqv COLOR_MAGENTA 6
.eqv COLOR_WHITE   7

.globl main
main:

	# exit
	li v0, 10
	syscall

1. Clearing the display and putting something on it

This works because when your program starts, the RAM is filled with 0s (black). So telling the display to update at the beginning of the program will clear it.

The display will always show what it was last told to, even if your program ends and you reassemble. To solve this, let’s clear the display whenever we run our program. All we have to do is put this at the beginning of main:

	sw zero, DISPLAY_CTRL

Now, let’s prove that we can draw something on the display. Put this after that:

	li t0, 0x06050401
	sw t0, DISPLAY_BASE
	sw zero, DISPLAY_CTRL

When you assemble and run, you should see this at the top-left:

If you don’t: make sure you clicked “Connect to MIPS”, then reassemble and run.

Look closely: Look at that number you put in t0, 0x06050401. Match each byte to the COLOR_ constants I gave you. Notice the order of the colors in the number versus the order the colors appear on the display. What endianness is being used here?


2. Making a function and drawing a horizontal line

Now you’ll make your first function. It will be the equivalent of this:

void draw_horiz_line() {
    for(int i = 0; i < 10; i++)
        DISPLAY_BASE[i + 10] = COLOR_BLUE;
}
  1. In main, after clearing the screen, put jal draw_horiz_line, and then sw zero, DISPLAY_CTRL.
    • jal is how you call a function.
  2. After the end of main (after the exit syscall), put this.
    • This is how you declare a function.
    • jr ra is how every function except main ends. Get in the habit of putting it there!
    • It’s a good idea to visually separate your functions using comments.
     # -----------------------------------------
     draw_horiz_line:
    
         jr ra
     # -----------------------------------------
    
  3. Now translate the for loop control flow. Don’t do the code inside yet!
    • A for loop is just a fancy while loop, like:
     int i = 0;
     while(i < 10) {
         // code inside the for loop
         i++;
     }
    
    • t0 will be fine to use as the loop counter (i).
    • Test that the loop works. Step through your program one instruction at a time.
  4. Finally, translate the array indexing.
    • Use li to put DISPLAY_BASE into another temp register.
    • add the loop counter and the constant 10 onto it.
      • What you want is DISPLAY_BASE + i + 10. How many + are in that expression?
    • And again use li to put COLOR_BLUE into another temp register.
    • Use sb since each pixel is a byte. it will look like sb t_, (t_), where:
      • the first register holds the color
      • the (t_) register holds the calculated address

Done right, it will look like this!

Try this: Once you get the line drawn correctly, play around with it!


3. The “walking pointer” technique and drawing a vertical line

Iterating over an array in assembly can often be done in a more natural way with something I like to call the “walking pointer” technique.

Instead of translating A[i] inside the loop, we calculate the address of the first item to access before the loop, and then increment that address each iteration of the loop.

  1. Make a new function draw_vert_line, and call it from main.
    • Don’t forget sw zero, DISPLAY_CTRL after the jal.
  2. To start, we’ll make it draw a horizontal line. Translate this code into draw_vert_line (read the comments!):
     p = DISPLAY_BASE + 20; // use t1
    
     for(int i = 0; i < 15; i++) // use t0
         *p = COLOR_ORANGE;      // store byte into address in p
         p += 1;                 // go to next address
     }
    
    • It should draw something like this:
  3. Now we’re gonna get crazy.
    • The p += 1 line? Change that to p += 2.
      • Well that’s neat.
    • Change it to p += 10.
      • Uh…?
    • What’s going on here?
      • Remember, in memory, each row is immediately after the previous row.
    • Figure out what value you have to add to make it draw a vertical line, like this.

Checkpoint: do you know the answers to these questions?

If you don’t, ask someone who does! Like me! Or the TAs! Whoever!

  1. To move one pixel to the right, you add __ to the address.
  2. To move one pixel down, you add __ to the address.
  3. If you draw off the right side of the screen, what happens?

4. Taking arguments and drawing a rectangle

You will write a function draw_rectangle which takes four arguments, and roughly does this:

void draw_rectangle(int x1, int y1, int x2, int y2) {
    for(x = x1; x < x2; x++) {
        for(y = y1; y < y2; y++) {
            ...something that sets pixel x,y to COLOR_WHITE...
        }
    }
}
  1. Call that function from main and update the display after.
    • To call it, you put the arguments in a0..a3, and then jal to it.
       li	a0, 30 # x1
       li	a1, 15 # y1
       li	a2, 50 # x2
       li	a3, 25 # y2
       jal	draw_rectangle
      
  2. Implement the nested for loops. Don’t be intimidated!
    • Tip: move t0, a0 copies the value from a0 into t0.
    • Do the outer for loop first, and test it (step through).
    • Then put the entire inner for loop inside it.
    • COMMON MISTAKE: make sure the inner for loop’s y = y1 step goes inside the outer for loop.
    • In other words, this is bad:
       x = x1;
       y = y1; // OOPS!!!!!!!!!!!!!!!!!!!!!!
       while(x < x2) {
        // y = y1 should be HERE<<<<<<<<<<<<<<<<<
        while(y < y2) {
            // ...code...
            y++;
        }
        x++;
       }
      
  3. Finally, implement the code inside the inner loop.
    • You know what to add to get from DISPLAY_BASE to the correct X coordinate.
    • You know what to add to move down one line.
    • You know basic arithmetic.
    • You can do this. ;)

When everything is done, the display should look like this!


Submitting

Make sure your file is named correctly. My username is jfb42, so:

Submit here.

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

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