2
\$\begingroup\$

I wrote a program (hard-code) in MIPS that gets an array of 10 integers and calculates the sum and the square sum of them. The array is {23,-2,45,67,89,12,-100,0,120,6}

.data
array: .word 23,-2,45,67,89,12,-100,0,120,6 # array = {23,-2,45,67,89,12,-100,0,120,6}
length: .word 10 # the length of the array is 10
sum: .word 0 # the sum of the integers (in array) is 0
squareSum: .word 0 # the square sum of the integers (in array) is 0
sumMessage: .asciiz "The sum of the array(sign) is: "
squareMessage: .asciiz "The sum of the squares(sign) is: "
newLine: .asciiz "\n"

# Algorithm being implemented to sum an array
# sum = 0 (use $t0 for sum)
# squarSum = 0 (use %t5 for squarSum)
# for i = 0 to length-1 do (use $t1 for i)
# sum = sum + array[i] (use $t2 for length-1)
# squareSum = squareSum + array[i]*array[i]
# end for (use $t3 for base addr. of array)

.text
main:

li $t0, 0 # load immediate 0 in register $t0 (sum)
li $t5, 0 # load immediate 0 in register $t0 (squarSum)
la $t3, array # load base addr. of array into $t3
lw $t2, length # load length in register $t2
addi $t2, $t2, -1 # $t2 = length - 1
li $t1, 0 # initialize i in $t1 to 0

loop:

bgt $t1, $t2, exit # exit loop when i > (length-1)
mul $t4, $t1, 4 # multiple i by 4 to get offset within array
add $t4, $t3, $t4 # add base addr. of array to $t4 to get addr. of array[i]
lw $t4, 0($t4) # load value of array[i] from memory into $t4
add $t0, $t0, $t4 # update sum
mul $t6, $t4, $t4 # temp register %t6 
add $t5,$t5,$t6 # update squareSum
addi $t1, $t1, 1 # increment i
j loop

exit:

# print sum message
li $v0, 4
la $a0, sumMessage
syscall 
# print value of sum
li $v0, 1
addi $a0,$t0,0
syscall
# print new line
li $v0, 4
la $a0, newLine
syscall 
# print square sum message 
li $v0, 4
la $a0, squareMessage
syscall 
# print value of squareSum
li $v0, 1
addi $a0,$t5,0
syscall

Is this a good implementation? How can I improve it?

\$\endgroup\$

migrated from stackoverflow.com Aug 16 '16 at 17:33

This question came from our site for professional and enthusiast programmers.

3
\$\begingroup\$

Your program is correct. I ran it and verified the results against a C program. So, it works and the code is pretty good, too.

You did a good job of commenting. Especially, the top block where you detail the algorithm and the intended register usage. And, putting a sidebar comment on each asm line is also very good.

But, you also wanted to know how to improve it. So, a few things ...

I realize you're just starting out, but sidebar comments should talk about intent. That is, what are we doing with the algorithm, instead of just rehashing the mechanics of the asm instruction.

I tightened things up a bit. Instead of what you did (in C/pseudo code):

int i;
int val;

for (i = 0;  i <= (length - 1);  ++i) {
    val = array[i];
    ...
}

I changed this into:

int *arrptr;
int *arrend;
int val;

arrptr = array;
arrend = &array[length];
for (;  arrptr < arrend;  ++arrptr) {
    val = *arrptr;
    ...
}

I was also able to eliminate a few extra instructions by changing some of the registers being used and eliminate a extra branch instruction in the loop


Anyway, here's the cleaned up/improved code [please pardon the gratuitous style cleanup]:

    .data
array:      .word       23,-2,45,67,89,12,-100,0,120,6
arrend:

sumMessage: .asciiz     "The sum of the array(sign) is: "
squareMessage:  .asciiz "The sum of the squares(sign) is: "
newLine:    .asciiz     "\n"

    # array = {23,-2,45,67,89,12,-100,0,120,6}
    # the sum of the integers (in array) is 0
    # the square sum of the integers (in array) is 0

    # Algorithm being implemented to sum an array
    # sum = 0 (use $t0 for sum)
    # squarSum = 0 (use %t5 for squarSum)
    # for i = 0 to length-1 do (use $t1 for i)
    # sum = sum + array[i] (use $t2 for length-1)
    # squareSum = squareSum + array[i]*array[i]
    # end for (use $t3 for base addr. of array)

    # registers:
    #   t0 -- sum
    #   t5 -- squarSum
    #
    #   t3 -- pointer to current array element (e.g. arrptr)
    #   t2 -- pointer to end of array
    #
    #   t4 -- current value fetched from array
    #   t6 -- temp to hold squared value

    .text

main:
    li      $t0,0                   # sum = 0
    li      $t5,0                   # squarSum = 0

    la      $t3,array               # load base addr. of array
    la      $t2,arrend              # load address of array end
    j       test

loop:
    lw      $t4,0($t3)              # load array[i]
    addi    $t3,$t3,4               # increment array pointer

    add     $t0,$t0,$t4             # update sum
    mul     $t6,$t4,$t4             # get val * val
    add     $t5,$t5,$t6             # update squareSum

test:
    blt     $t3,$t2,loop            # more to do? if yes, loop

    # print sum message
    li      $v0,4
    la      $a0,sumMessage
    syscall

    # print value of sum
    li      $v0,1
    addi    $a0,$t0,0
    syscall

    # print new line
    li      $v0,4
    la      $a0,newLine
    syscall

    # print square sum message
    li      $v0,4
    la      $a0,squareMessage
    syscall

    # print value of squareSum
    li      $v0,1
    addi    $a0,$t5,0
    syscall

    li      $v0,10
    syscall

If you'd like to see some further tips on writing good/clean asm, based on my own personal experience, see my answer here: https://stackoverflow.com/questions/36538325/mips-linked-list/36560575#36560575


UPDATE:

I didn't understand the "arrend" part. how does it know that $t2 = 10?

$t2 no longer has a count. It was repurposed to hold the address of the array's end address. The two loops are fundamentally different in how they iterate and terminate. This was evidenced in the C samples given above.

Your code used $t2 to hold the length/count of array, which was 10. Your loop was using an index variable [contained in $t1] to iterate through the array and stopping when the index value hit the count. It was incrementing the index value by 1 on each loop iteration.

In the modified code, $t2 holds the address of arrend. This is "one beyond" the last element of array. The loop stops when the pointer to the current array value [in $t3] hits/equals the array end. It was incrementing the pointer by 4 on each iteration.

In strictest terms, the loop doesn't use or care about the count. What it cares about is: "If I know the address of the array end, has my current address/pointer gone past it?"

Just for completeness, let's go the other way. In mars, array has address 0x10010000 and arrend has address 0x10010028. If we subtract the addresses, arrend is offset from array by 0x28 bytes, which is 40 (decimal). If we divide this by 4 [the size of a word] to get the count, we have 10

\$\endgroup\$
  • \$\begingroup\$ I didn't understand the "arrend" part. how does it know that $t2 = 10? \$\endgroup\$ – ovedpo ovedpo Aug 13 '16 at 10:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy