From now on, code style will be graded.

Your programs are getting bigger and the grader(s) needs to be able to read them without clawing their eyeballs out. See the style guide for examples of good code style.

You are not required to use “Java-style” indentation, but your code should at least be indented as shown in the “Indentation” section. Remember, tab key, not space bar.

Failure to use proper style can cause you to lose points from now on.

In this lab, you’ll be using arrays, functions, and for loops. You’ll also get a taste of that “top-down design” I talked about in the lecture on functions.

When complete, your lab will ask for 5 numbers, putting them into an array; then it will print that array out; and finally, print out the characters of a couple strings:

enter value: 10
enter value: 30
enter value: 20
enter value: 40
enter value: 60
arr[0] = 10
arr[1] = 30
arr[2] = 20
arr[3] = 40
arr[4] = 60
t
e
s
t
i
n
g
!

-- program is finished running --

0. Getting started

Here’s the starting code, including the print_str macro you got in lab 2.

# YOUR NAME HERE
# YOUR USERNAME HERE

# preserves a0, v0
.macro print_str %str
	.data
	print_str_message: .asciiz %str
	.text
	push a0
	push v0
	la a0, print_str_message
	li v0, 4
	syscall
	pop v0
	pop a0
.end_macro

# -------------------------------------------
.eqv ARR_LENGTH 5
.data
	arr: .word 100, 200, 300, 400, 500
	message: .asciiz "testing!"
.text
# -------------------------------------------
.globl main
main:

	# exit()
	li v0, 10
	syscall
# -------------------------------------------

Small tangent: named constants

Look above the .data line in the starting code. You see:

.eqv ARR_LENGTH 5

The Java equivalent would be something like:

public static final int ARR_LENGTH = 5;

This is a named constant. Named constants are wonderful. Names are wonderful. Whenever you have some “magic value” that has specific importance, don’t write it inside your code. Make a named constant for it.

These are not variables. You do not use lw with them. Instead, you can use them anywhere a constant is normally written (like in li, or as the last operand to an add, or as the second operand in a beq, etc.).

Also, don’t just write the value of the named constant when you need it. Use the name!:

MysteriousObvious
# 5? why 5? what is 5?
blt t0, 5, _top
# ohh, we're looping over an array
blt t0, ARR_LENGTH, _top

Why do we do this? Not only is it more readable, it means we can change the length of the array in the future, and none of the code that depends on the length has to be changed.


1. Stubbing out the program

Look at the expected output at the top of the page. We have 3 broad steps in this program:

  1. ask the user for ARR_LENGTH numbers, putting them into an array
  2. print out the contents of the array
  3. print out the characters of a string, one on each line

So, let’s make three stub functions (empty functions) for these three steps.

  1. Make three empty functions named input_arr, print_arr, and print_chars.
    • You start a function with a label and push ra. It ends with pop ra and jr ra, like:
        # ---------------------------------------------------
        input_arr:
        push ra
            # all the code will go here
      
        pop ra
        jr ra
      
    • You should use comments to visually separate those functions, like the --- above.
    • Remember that the push ra and pop ra are like the { } around a function’s code. You never write anything before push ra or after pop ra. All the code goes between them.
  2. Call them all from main using jal.
    • So, you are making main do this:
        void main() {
            input_arr();
            print_arr();
            print_chars();
            exit(); // already there for you
        }
      
  3. Assemble and run.
    • If it does nothing, and exits, that’s good! We’re just testing that we wrote things correctly.
    • If it doesn’t exit, something is wrong…
    • If you’re not convinced it’s working, try putting some test prints in the three stub functions and see if they print anything.

2. Printing the array

Remember the slide on for loops from the Arrays lecture? Go find that…

First let’s just try printing the array. The array has some values in it by default, so when we get this working, our program will output:

arr[0] = 100
arr[1] = 200
arr[2] = 300
arr[3] = 400
arr[4] = 500
  1. Inside print_arr (where else?), make a for loop using t0 as the index variable.
    • It should do the equivalent of for(t0 = 0; t0 < ARR_LENGTH; t0++)
      • The arrays lecture showed for loops…
    • Remember, you have to name your control flow labels with an underscore _ at the beginning.
    • Now test it!
      • You could step through it…
      • You could put print_str "test\n" inside it, run it, and make sure it prints 5 times…
      • Just make sure it loops correctly.
  2. Inside that loop, you need to do the equivalent of this code:
     print_str("arr[");
     print_int(t0);
     print_str("] = ");
     print_int(arr[t0]); // !!
     print_str("\n");
    
    • The print_int lines above are syscall 1 which you’ve been using.
      • Remember that you can copy registers with move.
    • One line indexes arr using t0 as the index and loads that value into the argument register a0
      • Have a look at the slides for a refresher.
      • Remember I showed two ways of indexing an array, a longer way (that uses la) and a shorter way (that uses a special form of lw/sw)? Use the shorter form here.
        • It will be just two instructions.

Get the above working! Don’t move on until you do!


3. Filling the array with user input

Now we can work on input_arr. You’re on your own for this one, but you know everything you need now. Note that this function will be shaped very similarly to print_arr, but instead of loading values out of the array, it will be storing values into the array.

This is what it should do:

void input_arr() {
    for(int i = 0; i < ARR_LENGTH; i++) {
        print_str("enter value: ");
        // anything on the right side of an = happens *before*
        // the stuff on the left side. hint hint.
        arr[i] = syscall_5();
    }
}

Tip: you are allowed to reuse the names of local labels in multiple functions! So you can have _loop once in print_arr and once in input_arr. They are different labels, and the assembler will not confuse them. Don’t name them _loop1, _loop2 etc. it’s unnecessary.

Done correctly, your program should ask for 5 numbers and then print them out.

enter value: 10
enter value: 30
enter value: 20
enter value: 40
enter value: 60
arr[0] = 10
arr[1] = 30
arr[2] = 20
arr[3] = 40
arr[4] = 60

4. Printing the characters of a string

Strings are arrays of characters. A character is just an integer with a special meaning.

The character '0' is represented by the integer 48. Do not get '0' confused with 0!

ASCII is a way of encoding strings where each character is one byte. The .asciiz directive makes a zero-terminated ASCII string. That means that the end of the string is marked with a byte with value 0. Not the character '0', an actual 0 value.

If I wrote .asciiz "ABC012", this string is 6 characters long, but it would take up 7 bytes in memory:

ASCII 'A' 'B' 'C' '0' '1' '2' '\0'
Decimal 65 66 67 48 49 50 0

To print a zero-terminated ASCII string character-by-character, it works something like this:

// notice the loop condition!
int i = 0;
while(str[i] != 0) {
    print_char(str[i]); // syscall 11
    i++;
}

// do not use syscall 10 down here. idk why but a ton of people try to do that.

So. Here’s what I want you to do:

  1. Comment out the jal input_arr and jal print_arr while you’re working on print_chars.
    • See, isn’t that nice? Two #s and your program’s behavior changes a lot.
  2. Inside print_chars, implement the pseudocode above, keeping these things in mind:
    • message is the name of the array to index.
    • Instead of lw, since it is an array of .byte:
      • You do not need to multiply the index because it’s pointless to multiply by 1.
      • You do not use lw to load a byte; instead you use lb (“load byte”).
    • The loop condition here is NOT checking if the index (i) is != 0. It’s checking if the character at that index is != 0. So that check needs to be done after loading a byte message.
  3. Test. Step through it if it doesn’t work.
    • If you get □ printed in the output after the !, you are trying to print the 0 character. Don’t do that. The loop should exit before printing the character, not after.
    • You can check the “ASCII” box in the bottom of the “Data Segment” window to see the ASCII strings in memory. It’s not the best view, but hey.
    • Remember this is the goal:
        t
        e
        s
        t
        i
        n
        g
        !
      
        -- program is finished running --
      
  4. Don’t forget to uncomment those other jals in main once you get this part working!

Submitting

First, be sure you uncomment things if you had them commented for testing. Your program’s output should be as shown at the top of this page.

Then, you can submit:

  1. On Canvas, go to “Assignments” and click on this lab.
  2. Click “Start Assignment.”
  3. Under “File Upload,” click the “Browse” button and choose your .asm file.
  4. Click “Submit Assignment.”

If you need to resubmit, that’s fine, just click “New Attempt” on the assignment page and upload it again.