问题
I currently am taking a course in assembly and am having trouble with the following assignment.
Write a program that reads (with an appropriate prompt) a sequence of 20 integers and stores them in an array, and then calls the following three functions and prints the results in a readable format.
The three functions are: smallestLargest: computes the smallest and the largest values in the array. divisible: computes the number of integers in the array which are divisible by 4 SumProduct: computes the sum and the product of the integers.
I wrote assembly code (below) to try to solve this problem but I can't get the right output except for the largest number in the array. Everything else is giving me a problem. I have no clue what's wrong, and I've been working on this for the past week and a half, so any help would be greatly appreciated.
.data
array: .space 80
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: "
Result1:.asciiz "The smallest value in the array is "
Result2:.asciiz "The largest value in the array is "
Result3:.asciiz "The number of integers divisible by 4 is "
Result4:.asciiz "The sum of the integers is "
Result5:.asciiz "The product of the integers is "
.globl main
.text
main:
li $t0,20 # $t0 keeps track of the number of integers to be read
la $t1,array # loading the starting address of an array
loopQ:
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,loopQ # branch to read and store the next integer
li $t0,20
la $t1,array
smallestLargest:
lw $v0,0($t1) # $v0 = Mem($t1)
move $v1,$v0 # $v1 = $v0
addi $t0,$t0,-1 # decrement $t0
blez $t0,ret1 # while ($t0 > 0)
loopR:
addi $t1,$t1,4
lw $t2,0($t1) # load $t1 to $t2
bge $t2,$v0,next# if ($t2 < $v0)
move $v0,$t2 # $v0 = $t2
b chk
next:
ble $t2,$v1,chk # if ($t2 > $v1)
move $v1,$t2 # $v1 = $t2
chk:
addi $t0,$t0,-1 # decrement t0
bgtz $t0,loopR
ret1:
li $v0,4 # system call code for print_str
la $a0,Result1 # load address of Result1 into $a0
syscall
move $a0,$v0 # 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,Result2 # load address of Result2 into $a0
syscall
move $a0,$v1 # 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 $t0,20 # initialize length parameter
la $t1,array # initialize address parameter
Div4:
li $v0,0 # $v0 = 0
li $t3,3 # $t3 = 3
b skip
loopS:
lw $t2,0($t1) # $t2 = Mem($t1)
addi $t1,$t1,4 # $t1 = $t1 + 4
and $t4,$t2,$t3 # $t4 = $t2 & $t3
bnez $t4,skip # if ($t4 == 0)
addi $v0,$v0,1 # $v0 = $v0 + 1
skip:
addi $t0,$t0,-1 # $t0 = $t0 - 1
bgez $t0,loopS # if $t0 > 0
ret2:
li $v0,4 # system call code for print_str
la $a0,Result3 # load address of Result3 into $a0
syscall
move $a0,$v0 # move value to be printed to $a0
li $v0,1 # system call code for print_int
syscall
SumAMult:
la $a0,newLine # start a new line
li $v0,4
syscall
li $v0,4 # system call code for print_str
la $a0,Result4 # load address of Result4 into $a0
syscall
li $t0,20 # initialize length parameter
la $t1,array # initialize address parameter
jal sum # call sum
move $t1,$v0 # move value to be printed to $t1
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,Result5 # load address of Result5 into $a0
syscall
li $v0,1 # system call code for print_int
move $t1,$v1 # move value to be printed from $v1
syscall
li $v0,10
syscall
sum:
li $v0,0 # $v0 will hold the sum
li $v1,0 # $v1 will hold the product
loopT:
blez $t0, retzz # If t0 <= branch
addi $t0,$t0,-1 # decrement loop count
lw $t5,0($t1) # get a value from the array
addi $t1,$t1,4 # increment array pointer
word:
add $v0,$v0,$t5 # add to sum
# mult $v1,$t5 # multiply to product
b loopT # branch to loopT
retzz:
jr $ra
Below is the output I am getting after entering integers 1-20
The smallest value in the array is 4
The largest value in the array is 20
The number of integers divisible by 4 is 4
The sum of the integers is 268501210
The product of the integers is 268501238
回答1:
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
来源:https://stackoverflow.com/questions/38460566/mips-questions-about-writing-assembly-to-call-functions-on-an-array