I've fixed your code. I tried to remain faithful to it where possible, but, I had to do considerable restructuring.
A few things to note.
I created functions (per the problem statement). That is, instead of the equivalent of one long main
, I split things up, just like you'd do in C. I added block comments for each function.
In main
, I loaded the array count into $s0
and the array base address into $s1
. The calculation functions set up their values from these rather than replicating the code. (i.e.) The array address and count can be set up/changed in one place, if desired.
I changed some of the sidebar comments to be more descriptive of intent instead of merely restating the mechanics of the instruction they're on.
I also changed the labels so it was easier to match them to the functions they were in (e.g. all labels in function foo
are foo_blah
)
I created static test data to speed up testing. Note the commented out jal dataread
to actually prompt the user.
Here's the corrected code:
.data
array: .space 80
array2: .word 3, 3, 3, 17, 3
.word 3, 24, 3, 3, 4
.word -4, -8, 97, 3, 2
.word 3, 3, 3, 3, 3
newLine: .asciiz "\n" # I will use it to start a new line
space: .asciiz " " # I will use it to have a space
Prompt: .asciiz "\n Enter an integer: "
msg_min: .asciiz "The smallest value in the array is "
msg_max: .asciiz "The largest value in the array is "
msg_div4: .asciiz "The number of integers divisible by 4 is "
msg_sum: .asciiz "The sum of the integers is "
msg_prod: .asciiz "The product of the integers is "
.globl main
.text
main:
li $s0,20 # set array count
la $s1,array2 # set array address
# NOTE: uncomment this to really prompt user (vs. testing)
###jal dataread # prompt user for data
jal minmax # compute minimum/maximum
jal div4 # count number divisible by 4
jal sumprod # compute sum and product
li $v0,10
syscall
# dataread -- prompt user for data
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
dataread:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
dataread_loop:
la $a0,Prompt
li $v0,4
syscall
li $v0,5 # reading an integer
syscall
sw $v0,0($t1) # storing the integer entered
add $t0,$t0,-1 # decrement the number of integers by one
add $t1,$t1,4 # load the address of the next integer
bgtz $t0,dataread_loop # branch to read and store the next integer
jr $ra # return
# minmax -- compute min/max
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
# t2 -- minimum value
# t3 -- maximum value
# t4 -- current array value
minmax:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
lw $t2,0($t1) # initialize smallest
move $t3,$t2 # initialize largest
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
minmax_loop:
blez $t0,minmax_done # at end of array? if yes, fly
lw $t4,0($t1) # fetch current array element
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
bge $t4,$t2,minmax_notlt # new minimum? if no, fly
move $t2,$t4 # yes, set it
minmax_notlt:
ble $t4,$t3,minmax_loop # new maximum? if no, loop
move $t3,$t4 # yes, set it
b minmax_loop
minmax_done:
li $v0,4 # system call code for print_str
la $a0,msg_min # message to print
syscall
move $a0,$t2 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
li $v0,4 # system call code for print_str
la $a0,msg_max # message to print
syscall
move $a0,$t3 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
jr $ra # return
# div4 -- get number of integers divisible by 4
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
# t2 -- count of array elements divisible by 4
# t4 -- current array value
div4:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
li $t2,0 # initialize count
div4_loop:
blez $t0,div4_done # at end of array? if yes, fly
lw $t4,0($t1) # fetch current array value
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
andi $t4,$t4,0x03 # divisible by 4?
bnez $t4,div4_loop # no, loop
addi $t2,$t2,1 # yes, increment count
b div4_loop # loop
div4_done:
li $v0,4 # system call code for print_str
la $a0,msg_div4 # message to print
syscall
move $a0,$t2 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
jr $ra
# sumprod -- compute sum and product
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
# t2 -- summation value
# t3 -- product value
# t4 -- current array value
sumprod:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
li $t2,0 # initialize sum
li $t3,1 # initialize product
sumprod_loop:
blez $t0,sumprod_done # at end of array? if yes, fly
lw $t4,0($t1) # fetch current array value
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
add $t2,$t2,$t4 # compute the sum
mul $t3,$t3,$t4 # compute the product
b sumprod_loop
sumprod_done:
li $v0,4 # system call code for print_str
la $a0,msg_sum # message to print
syscall
move $a0,$t2 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
li $v0,4 # system call code for print_str
la $a0,msg_prod # message to print
syscall
move $a0,$t3 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
jr $ra # return
Here's a more compact variation that uses a trick of sorts. It uses the equivalent of "tail call optimization" to have a common print routine instead of replicating the printing code in each calculation function.
That is, the "trick" is the calculation functions set up arguments for the print and then jump to it via j
[instead of doing a second level jal
] and the print function does the jr $ra
that would normally be done by the calculation functions.
Anyway, here's the code:
.data
array: .space 80
array2: .word 3, 3, 3, 17, 3
.word 3, 24, 3, 3, 4
.word -4, -8, 97, 3, 2
.word 3, 3, 3, 3, 3
newLine: .asciiz "\n" # I will use it to start a new line
space: .asciiz " " # I will use it to have a space
Prompt: .asciiz "\n Enter an integer: "
msg_min: .asciiz "The smallest value in the array is "
msg_max: .asciiz "The largest value in the array is "
msg_div4: .asciiz "The number of integers divisible by 4 is "
msg_sum: .asciiz "The sum of the integers is "
msg_prod: .asciiz "The product of the integers is "
.globl main
.text
main:
li $s0,20 # set array count
la $s1,array2 # set array address
# NOTE: uncomment this to really prompt user (vs. testing)
###jal dataread # prompt user for data
jal minmax # compute minimum/maximum
jal div4 # count number divisible by 4
jal sumprod # compute sum and product
li $v0,10
syscall
# dataread -- prompt user for data
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
dataread:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
dataread_loop:
la $a0,Prompt
li $v0,4
syscall
li $v0,5 # reading an integer
syscall
sw $v0,0($t1) # storing the integer entered
add $t0,$t0,-1 # decrement the number of integers by one
add $t1,$t1,4 # load the address of the next integer
bgtz $t0,dataread_loop # branch to read and store the next integer
jr $ra # return
# minmax -- compute min/max
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
# t2 -- minimum value
# t3 -- maximum value
# t4 -- current array value
minmax:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
lw $t2,0($t1) # initialize smallest
move $t3,$t2 # initialize largest
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
minmax_loop:
blez $t0,minmax_done # at end of array? if yes, fly
lw $t4,0($t1) # $v0 = Mem($t1)
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
bge $t4,$t2,minmax_notlt # new minimum? if no, fly
move $t2,$t4 # yes, set it
minmax_notlt:
ble $t4,$t3,minmax_loop # new maximum? if no, loop
move $t3,$t4 # yes, set it
b minmax_loop
minmax_done:
la $a2,msg_min # first message
la $a3,msg_max # second message
j print
# div4 -- get number of integers divisible by 4
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
# t2 -- count of array elements divisible by 4
# t4 -- current array value
div4:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
li $t2,0 # initialize count
div4_loop:
blez $t0,div4_done # at end of array? if yes, fly
lw $t4,0($t1) # fetch current array value
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
andi $t4,$t4,0x03 # divisible by 4?
bnez $t4,div4_loop # no, loop
addi $t2,$t2,1 # yes, increment count
b div4_loop # loop
div4_done:
la $a2,msg_div4 # first message
li $a3,0 # _no_ second message
j print
# sumprod -- compute sum and product
#
# registers:
# t0 -- remaining count
# t1 -- array address pointer
# t2 -- summation value
# t3 -- product value
# t4 -- current array value
sumprod:
move $t0,$s0 # initialize array count
move $t1,$s1 # initialize array pointer
li $t2,0 # initialize sum
li $t3,1 # initialize product
sumprod_loop:
blez $t0,sumprod_done # at end of array? if yes, fly
lw $t4,0($t1) # fetch current array value
add $t1,$t1,4 # load the address of the next integer
addi $t0,$t0,-1 # decrement remaining count
add $t2,$t2,$t4 # compute the sum
mul $t3,$t3,$t4 # compute the product
b sumprod_loop
sumprod_done:
la $a2,msg_sum # first message
la $a3,msg_prod # second message
j print
# print -- common print function
#
# arguments:
# a2 -- first message
# t2 -- first value
# a3 -- second message
# t3 -- second value
print:
beqz $a2,print_skip1 # skip if no first argument
li $v0,4 # system call code for print_str
move $a0,$a2 # get address of first message
syscall
move $a0,$t2 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
print_skip1:
beqz $a3,print_skip2 # skip if no second argument
li $v0,4 # system call code for print_str
move $a0,$a3 # get address of second message
syscall
move $a0,$t3 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
la $a0,newLine # start a new line
li $v0,4
syscall
print_skip2:
jr $ra # return
Here's a C program that I used for comparison testing and validation:
// mipsmmdsp/mipsmmdsp -- check validity of mask for divisibility
#include <stdio.h>
#include <stdlib.h>
int array2[20] = {
3,3,3,17,3,
3,24,3,3,4,
-4,-8,97,3,2,
3,3,3,3,3
};
// sumprod -- calculate sum and product
void
sumprod(void)
{
int idx;
int val;
int sum;
int prod;
int div4;
int min;
int max;
sum = 0;
prod = 1;
div4 = 0;
min = array2[0];
max = array2[0];
for (idx = 0; idx < 20; ++idx) {
val = array2[idx];
if (val < min)
min = val;
if (val > max)
max = val;
if ((val % 4) == 0)
++div4;
sum += val;
prod *= val;
}
printf("min=%d max=%d div4=%d sum=%d prod=%d\n",min,max,div4,sum,prod);
}
// modcheck -- check validity of mod mask
void
modcheck(void)
{
int lo;
int hi;
int val;
int mskflg;
int modflg;
long long cnt;
lo = -100;
hi = 100;
lo = -2000000000;
hi = 2000000000;
cnt = 0;
for (val = lo; val <= hi; ++val, ++cnt) {
mskflg = ((val & 0x03) == 0);
modflg = ((val % 4) == 0);
#if 0
printf("modcheck: %4d/%8.8X: mskflg=%d modflg=%d\n",
$val,$val,$mskflg,$modflg);
#endif
if (mskflg != modflg) {
printf("modcheck: FAIL %4d/%8.8X: mskflg=%d modflg=%d\n",
val,val,mskflg,modflg);
exit(1);
}
}
printf("modcheck: cnt=%lld\n",cnt);
}
// main -- main program
int
main(void)
{
modcheck();
sumprod();
return 0;
}
Side note:
In addition to the spim
simulator, there is the mars
simulator. It can be found here: http://courses.missouristate.edu/KenVollmar/MARS/
I've used both and prefer mars
in most cases--YMMV