5
\$\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\$
0
4
\$\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\$
1
  • \$\begingroup\$ I didn't understand the "arrend" part. how does it know that $t2 = 10? \$\endgroup\$ – ovedpo ovedpo Aug 13 '16 at 10:34
0
\$\begingroup\$

I wrote code for a similar problem that is quite precise and can be implemented with ease. Here I take an input n which prints the sum of product for consecutive integers starting from 1, i.e. array size n.
In your case, what can be done is that you take the length and array both as the input and run the loop array length times and the loop instead of running for n should read the elements of the array, store them then store their square, increment the address by 4 to read each index and keep adding the squares to the previously updated sum. Your code however is pretty much accurate and seems to give an accurate result.

# SUM OF SQUARES
    .data
#initialisation prompt to ask the user for their input
n: .asciiz "enter n: "

.text
.globl main
main:
#declaring i and initiating its value to 0
li $t0, 0  

#printing prompt to ask from user
li $v0,4
la $a0, n
syscall

#inputting value from console
li $v0,5
syscall 
#moving to temporary variable t1
move $t1, $v0

#initialising sum as 0, stored as t3
li $t3 0
#Function acting as a loop
function:
#condition to end the loop and jump to print function
#while(t0!=t1)
    beq $t0 $t1 print
#incrementation of loop variable (t0)
    addi $t0 $t0 1
#calculating square of t0 and storing it in t4
    mul $t4 $t0 $t0
#adding the current squared to previous sum and again storing it in sum(t3)
    add $t3 $t3 $t4 
#loop to this function again
    j function

#procedure to print the sum(t3)
print:
move $a0 $t3
li $v0 1
syscall

#exit procedure
end:
li $v0 10
syscall
\$\endgroup\$
1
  • 1
    \$\begingroup\$ (You don't need to multiply to generate squares of consecutive integers.) Accuracy is a non-issue with integral arithmetic, and I find it difficult to apply to program source code. That one sentence was all referring to the code presented for review that I could discern: You seem to be asking a new question?! Then don't use "the Your Answer editor", but "the Ask Question button". \$\endgroup\$ – greybeard Mar 27 at 16:25

Your Answer

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