CS 354 Computer Organization and Systems Fall 2019 

Project 4 - Computing Primes using Arrays in MIPS Assembly Language

Goals

In this project you will work with programming MIPS to use arrays to compute prime numbers. You will gain an understanding of how arrays are implemented in memory and how they can be accessed in MIPS.

Arrays in MIPS

Most assembly languages, like MIPS, do not have built-in capabilities for sophisticated data structures. Even the most commonly available structure, the array, is not available in MIPS. As we shall see shortly, we can set aside a block of memory in MIPS in assembly language and treat it similar to an array.

What is an array anyway? One way to think about an array is a set of values that can simultaneously be considered a single collective entity and many individual elements. In Java, C, and other high level languages, the array is a set of references using an identifier like any other variable. To access each individual element, one uses an integer to specify a particular element. In most high-level languages, arrays are stored as a contiguous block of n memory cells starting with a base address. In java, we might declare and use an array as follows.


int a[] = new int[10];
int sum = 0;
...
for (int i = 0;  i < 10;  i++)
   sum += a[i];
In this example, the elements in the array are added and the result is stored in the variable sum. Note a is the identifier that refers to the array and [i] refers to the ith element of the array. Here a is the base location of the array and i indicates the offset into memory from the base address.

In MIPS, there is no formal array construct. The first issue to resolve is the association of a block of memory with a particular identifier. This can be done using a label and a .space directive in the .data section. For example


.data
a:      .space  40
reserves 40 bytes (10 words) of memory of data associated with the label a. The memory location where this label is stored (by the assembler) becomes the base address of the array. To access the ith element from the array, we need to determine the memory offset from the beginning address and add the number of bytes per element. For simplicity, let's assume the array stores elements that each require on word of storage and $t0 is the register that represents i. Then

        la      $t1, a
        muli	$t2, $t0, 4
        add     $t2, $t2, $t1    
        lw      $t3, 0($t2)
will load the ith element from the array into register $t3. Here $t1 contains the base address of the array and $t2 is the address of the ith element of the array.

While this will work, it is not the only way to access the elements of the array. Consider storing the integers 0-9 in the first ten elements of an array then reading the elements and adding them together. The program sum10.s shown below illustrates one way to do this.

.text

main:

# Fill the array

	    la       $t4, n            # address of n
	    lw       $t4, 0($t4)       # t4 = n
	    and      $t0, $0, $0       # i = 0
	    la       $t1, a            # address of a
loop1:
	    sll      $t2, $t0, 2       # byte offset for ith element
	    add      $t2, $t2, $t1     # address of a[i]
	    sw       $t0, 0($t2)       # put i into a[i]
	    addi     $t0, $t0, 1       # increment i
	    slt      $t5, $t0, $t4     # is $t0 < $t4 ?
	    bne      $t5, $0, loop1    # branch if so


# Sum the array values

	    and      $s0, $0, $0       # sum = 0
	    and      $t0, $0, $0       # i = 0
	    add      $t2, $t1, $0      # address of a[i]
loop2:
	    lw       $t3, 0($t2)       # load a[i]
	    add      $s0, $s0, $t3     # increment sum
	    addi     $t0, $t0, 1       # increment i
	    addi     $t2, $t2, 4       # increment address of a[i]
	    slt      $t5, $t0, $t4     # is $t0 < $t4 ?
	    bne      $t5, $0, loop2    # branch if so

# Output Sum

            li       $v0, 1            # Load 1=print_int into $v0
            add      $a0, $s0, $zero   # Load first number into $a0
            syscall                    # Output the prompt via syscall

			li		$v0, 10
			syscall


.data

n:          .word  10			  # n = 10
			.align 4              # align a to start on an even word boundary
                                  #    this is important!
a:          .space 40			  # Allocate 10 words (40 bytes)

Here are some important things to note about the above program. The la instruction loads the address of the label in the specified register. The sll instruction can be used to multiply by a power of 2; in the example above it is used as a multiplication by 4 (the number of bytes per word). In the second loop, note the array address is incremented by 4 each time through the loop rather than adding 4 times i plus the base address.

Load and assemble this program using the load button. Step through the program and watch the memory and the register values change.

Prime Numbers and the Sieve of Eratosthenes

A classical application of computers is determining prime numbers. Recall a prime number is an integer that only has one and itself as positive factors. For example 2, 3, 5, 7, 11, 13, and 17 are the prime numbers less than 20 (1 is not prime). From the early days of computing, finding the largest prime number has been a quest for mathematicians and computer scientists. The Largest Known Prime by Year: A Brief History has some interesting graphs and tables showing how computing power has allowed the discovery of large prime numbers. This was thought to be a purely academic exercise without practical value. However, in recent years prime numbers have been used extensively in data security applications.

One of the simplest and most efficient ways to compute prime numbers is the Sieve of Eratosthenes. The algorithm is named for the Greek mathematician Eratosthenes, who also is the first person to accurately compute the circumference of the Earth, and dates to around 240 BC. To find the primes less than a given integer n:

  1. List the positive integers between 2 and n, inclusive
  2. 2 is the first number, so it is prime
  3. cross out the multiples of 2 greater than 2
  4. find the next active integer, it is prime, call it p (3 is the next)
  5. Cross out all multiples of p greater than p
  6. repeat until no numbers are left in the list

Task

Write a MIPS program that prompts the user for an input integer value of n and prints all the prime numbers less than or equal to n. Assume n will be less than or equal to 1,000,000. Store your list of n integers in memory, using the index to correspond to the integer and the value stored at i either 1 or 0 depending if the number is prime or composite.

Deliverables

Send me an email with the MIPS file containing your program. Comment each line and code segment explaining its function.

References

  1. MARS: MIPS Assembler and Runtime Simulator
  2. Eric W. Weisstein. "Prime Number". From MathWorld--A Wolfram Web Resource.
  3. The Largest Known Prime by Year: A Brief History from the Prime Pages
  4. Eric W. Weisstein. "Sieve of Eratosthenes". From MathWorld--A Wolfram Web Resource.
  5. Sieve of Eratosthenes from the Prime Pages.


Copyright © 2019, David A. Reimann. All rights reserved.