PCSPIM: MIPS Control Instructions and working with Arrays

In this article, we will understand MIPS control instructions by examples. MIPS control instructions are a fundamental aspect of programming in MIPS assembly language. They allow you to control the flow of your program, make decisions, and loop through code based on conditions.

MIPS Control Instructions

Here are some common MIPS control instructions:

Conditional Branching Instructions

  • beq (Branch on Equal): Branches if two registers are equal.
  • bne (Branch on Not Equal): Branches if two registers are not equal.
  • bgtz (Branch on Greater Than Zero): Branches if a register is greater than zero.
  • blez (Branch on Less Than or Equal to Zero): Branches if a register is less than or equal to zero.
  • bgez (Branch on Greater Than or Equal to Zero): Branches if a register is greater than or equal to zero.
  • bltz (Branch on Less Than Zero): Branches if a register is less than zero.

Unconditional Jump Instructions

j (Jump): Jumps to a specified address.
jal (Jump and Link): Jumps to a specified address and saves the return address in $ra (return address register).

Comparison Instructions

  • slt (Set Less Than): Sets a register to 1 if one register is less than another; otherwise, sets it to 0.

Function Calls

  • jr (Jump Register): Jumps to an address stored in a register (typically used for returning from functions).
  • jal (Jump and Link): Jumps to a function and stores the return address in $ra.

Working with arrays in MIPS involves using these control instructions along with load and store instructions to access and manipulate array elements. Here’s a high-level overview of working with arrays in MIPS:

MIPS Control Instructions Examples

In this example, we will use both branch and jump instructions to sum the first ten positive integers (10+9+8+7+…). Follow along with the code provided below and fill in the missing parts:

.text
.globl main

main:
addi $s0, $zero, 10           # counter initialized to 10
addi $t0, $zero, 0            # sum/accumulator initialized to 0

loop1:
add $t0, $t0, $s0             # Performing accumulation in $t0
addi $s0, $s0, -1             # counter−−
bne $s0, $zero, loop1         # Exit the loop after 10th iteration
nop                           # Few no operations to indicate end of interest
nop

After running the program, the value of $t0 will be 0x00000037 in hexadecimal or 55 in decimal when we reach the end of the program (after the first nop).

Exercise 2

Now let’s modify the program from Exercise 1 to calculate the sum of powers of 2 (1+2+4+8+16+32+…) for the first 20 numbers. Use the registers mentioned in Exercise 1, i.e., $s0, $t0, and $zero. You can also use $t1. Two possible implementations are shown below:

Implementation 1:

.text
.globl main

main:
addi $s0, $zero, 20           # counter initialized to 20
addi $t0, $zero, 0            # sum/accumulator initialized to 0
addi $t1, $zero, 1            # $t1 initialized with “1”

loop1:
add $t0, $t0, $t1             # Performing accumulation in $t0
add $t1, $t1, $t1             # Calculating new number to be added
addi $s0, $s0, -1             # counter−−
bne $s0, $zero, loop1         # Exit the loop after 20th iteration
nop

After running this program, the final sum will be 0x000fffff in hexadecimal or 1048575 in decimal.

Implementation 2:

.text
.globl main

main:
addi $s0, $zero, 20           # counter initialized to 20
addi $t0, $zero, 0            # sum/accumulator initialized to 0
addi $t1, $zero, 1            # $t1 initialized with “1”

loop1:
add $t0, $t0, $t1             # Performing accumulation in $t0
sll $t1, $t1, 1               # Calculating new number to be added
addi $s0, $s0, -1             # counter−−
bne $s0, $zero, loop1         # Exit the loop after 20th iteration
nop
nop

After running this modified program, the final sum will still be 0x000fffff in hexadecimal or 1048575 in decimal.

Solution for Implementation 1:

Let’s modify the program from Implementation 1 to calculate the sum of more numbers. We will increase the number of loops to 50 and observe the intermediate sum contained in $t0 when an overflow occurs in any of the registers involved:

After running the modified program for 50 numbers, when an overflow occurs, the intermediate sum contained in $t0 will be 0x7fffffff in hexadecimal. We can identify the overflow by the exception caused due to an arithmetic overflow. However, note that the overflow was not caused by the intermediate sum but by the new number obtained using the instruction (add $t1, $t1, $t1). This instruction performs a signed addition. When the value in $t1 reaches 0x40000000, an overflow occurs and the program jumps to the kernel part of the code due to the exception caused by an arithmetic overflow into the signed bit.

Now, let’s repeat the above program for 50 numbers but this time use the addu instruction instead of add while calculating the sum. We will observe the first value of the intermediate sum for which an overflow occurs:

After running the modified program with the addu instruction, the first value of the intermediate sum for which an overflow occurs is 0xffffffff in hexadecimal. This overflow is caused by the new number obtained by the instruction (addu $t1, $t1, $t1). The overflow is caused because the value of $t1 becomes 0x100000000, which cannot be accommodated in the 32-bit space. At this point, the value in $t1 becomes zero, and the register $t0 contains 0xffffffff for the rest of the program. It is important to note that this overflow occurs at a different point compared to the signed addition case, because the unsigned addition does not cause an exception but overflows in $t1 due to running out of space.

Solution for Implementation 2:

Similarly, let’s modify Implementation 2 to calculate the sum of more numbers (50 numbers) and observe the intermediate sum contained in $t0 when an overflow occurs:

After running the modified program for 50 numbers, when an overflow occurs, the intermediate sum contained in $t0 will be 0xffffffff in hexadecimal. We can identify the overflow by observing that the instruction add $t0, $t0, $t1 performs the signed accumulation, while the instruction sll $t1, $t1, 1 creates the new number to be added on each iteration. When the value in $t1 reaches 0x40000000, an overflow occurs and the value of $t0 becomes 0xffffffff.

On the next left shift operation, the number 0x80000000 becomes 0x100000000, which cannot be accommodated in a 32-bit space. As a result, $t1 becomes 0x00000000. This can be classified as a logical overflow beyond the available 32-bit space. The contents of $t0 at this point are 0xffffffff, and this value remains in $t0 until the end of the program.

To observe the same behavior with the addu instruction, repeat the above program (of the same 50 numbers) using the addu instruction instead of add. The value and overflow occur at the same point in this case.

Using Array in MIPS Assembly Language

Let’s see example to use arrays in MIPS assembly language with or without control and branch instructions.

Summing the Integers in an Array

Now, let’s write a program without using branches to sum the integers in an array with 5 elements. We will save the result in the variable “sum”. The starting code is provided below:

.data
myData:   .word  2, 12, -5, 7, 4    # array initialization
sum:      .word  0    # this will contain the sum

.text
.globl   main
main:
la   $s0, myData      # load the address of myData into $s0
li   $t2, 0           # initialize $t2 to save the sum

lw $t0, 0($s0)
add $t2, $t2, $t0

lw $t0, 4($s0)
add $t2, $t2, $t0

lw $t0, 8($s0)
add $t2, $t2, $t0

lw $t0, 12($s0)
add $t2, $t2, $t0

lw $t0, 16($s0)
add $t2, $t2, $t0

la   $s0, sum      # Load the address of "sum" into $s0
sw   $t2, 0($s0)   # Store the value of the final sum from $t2 to the memory location at "sum"

After running this program, the value of sum will be 0x00000014 in hexadecimal or 20 in decimal.

Below are the missing values filled in the table:

Address (Hex)Value (Hex)Value (Decimal)
0x100100000x000000022
0x100100040x0000000c12
0x100100080xfffffffb-5
0x1001000c0x000000077
0x100100100x000000044

Summing the Integers in an Array using Branch Instructions

Let’s repeat Exercise 3 but this time use branch instructions and the following 15-element array:

.data
myData:   .word  200, -1299, -5000, 7123, 4, -2, 3, -7, 89, 4, -1000, 11, 0, 14, -1
sum:      .word  0    # this location will contain the sum

.text
.globl main
main:
la   $s0, myData        # load the address of myData into $s0
li   $t1, 15            # initialize the index
add  $t2, $zero, $zero  # initialize to save the sum in $t2

loop:
addi $t1, $t1, -1
sll  $t3, $t1, 2        # Multiply the index by 4 to get the offset
add  $t3, $s0, $t3      # Calculate the memory address of the current element
lw   $t0, 0($t3)        # Load the value from memory
add  $t2, $t2, $t0      # Add the value to the sum in $t2
bne  $t1, $zero, loop   # Continue until the index becomes zero

exit:
la   $s0, sum           # Load the address of "sum" into $s0
sw   $t2, 0($s0)        # Store the value of the final sum from $t2 to memory

After running this program, the value of sum will be 0x0000008b in hexadecimal or 139 in decimal. The branch instruction produced by the assembler is bne $9, $0, −20.

How Does this Code Work?

This MIPS assembly code calculates the sum of the elements stored in an array named myData and stores the result in a memory location named sum. Here’s a step-by-step explanation of what the code does:

  1. In the .data section, an array myData is defined with 15 integer values. Another memory location sum is also reserved to store the sum of the array elements.
  2. In the .text section, the program starts with the main function:
    • The base address of the myData array is loaded into register $s0 using the la (load address) instruction.
    • Register $t1 is initialized to 15, representing the index of the last element in the array.
    • Register $t2 is initialized to zero to store the sum of the elements.
  3. The program enters a loop labeled as loop:
    • It decrements the index in $t1 by 1.
    • It calculates the memory address of the current element using the index by multiplying it by 4 (since each element is 4 bytes) and adding it to the base address in $s0. The result is stored in $t3.
    • It loads the value from memory at the calculated address into $t0.
    • It adds the loaded value ($t0) to the running sum ($t2).
  4. The loop continues to the next element until the index ($t1) becomes zero. At this point, the sum of all elements in the array is stored in $t2.
  5. The program exits the loop and proceeds to the exit section:
    • The address of the sum memory location is loaded into $s0.
    • The final sum stored in $t2 is stored in the memory location pointed to by $s0, which is sum.

In summary, this code calculates the sum of the elements in the myData array and stores the result in the sum memory location. It demonstrates the use of load and store instructions, indexing, and looping in MIPS assembly to perform a common array operation.

Video Demonstration

Conclusion

In conclusion, this article provided an in-depth understanding of MIPS control instructions through examples. We explored conditional branching instructions, unconditional jump instructions, and comparison instructions. We also learned about function calls and how to work with arrays in MIPS assembly language. By going through the provided exercises and modified implementations, we were able to gain hands-on experience with these control instructions and observe their effects on program execution. Overall, this article serves as a comprehensive guide for anyone looking to enhance their proficiency in programming with MIPS control instructions.

Related content:

Leave a Comment