MIPS questions about writing assembly to call functions on an array

亡梦爱人 提交于 2019-12-13 16:26:37

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!