关于编写汇编以调用数组上的函数的MIPS问题 [英] MIPS questions about writing assembly to call functions on an array

查看:129
本文介绍了关于编写汇编以调用数组上的函数的MIPS问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在上汇编课程,并且在执行以下作业时遇到麻烦.

I currently am taking a course in assembly and am having trouble with the following assignment.

编写一个程序(在适当的提示下)读取20个整数的序列并将其存储在数组中,然后调用以下三个函数并以可读格式打印结果.

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.

这三个功能是: smallestLargest :计算数组中的最小和最大值. 可整除:计算数组中可被4整除的整数的数量 SumProduct::计算整数之和和.

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

下面是输入整数1-20后得到的输出

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

推荐答案

我已经修复了您的代码.我试图尽可能忠实于此,但是,我必须进行大量的重组.

I've fixed your code. I tried to remain faithful to it where possible, but, I had to do considerable restructuring.

一些注意事项.

我创建了函数(根据问题陈述).也就是说,就像您在C语言中一样,我将内容拆分成一个长main,而不是等效.我为每个函数添加了块注释.

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.

main中,我将数组计数加载到$s0中,并将数组基地址加载到$s1中.计算功能从这些功能中设置其值,而不是复制代码. (即)如果需要,可以在一个位置设置/更改阵列地址和计数.

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.

我还更改了标签,以便更轻松地将其与它们所在的功能匹配(例如,功能foo中的所有标签均为foo_blah)

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)

我创建了静态测试数据以加快测试速度.请注意,注释掉的jal dataread会实际提示用户.

I created static test data to speed up testing. Note the commented out jal dataread to actually prompt the user.

这是更正的代码:

    .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.

也就是说,技巧"是计算函数,它为打印设置参数,然后通过j跳转到 [而不是进行第二级jal]和打印函数执行通常由计算功能完成的jr $ra.

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.

无论如何,这是代码:

    .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


这是我用于比较测试和验证的C程序:


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;
}


旁注:

除了spim模拟器外,还有mars模拟器.可以在这里找到: http://courses.missouristate.edu/KenVollmar/MARS/

In addition to the spim simulator, there is the mars simulator. It can be found here: http://courses.missouristate.edu/KenVollmar/MARS/

在大多数情况下-YMMV我都使用过并且更喜欢mars

I've used both and prefer mars in most cases--YMMV

这篇关于关于编写汇编以调用数组上的函数的MIPS问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆