MIPS关于编写汇编以调用数组

时间:2016-07-19 13:52:34

标签: arrays assembly mips spim qtspim

我目前正在参加大会的课程,但我在完成以下任务时遇到了麻烦。

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

这三个功能是: smallestLargest:计算数组中的最小值和最大值。 divisible:计算数组中可被4整除的整数数 SumProduct:计算整数的和和乘积。

我编写汇编代码(下面)试图解决这个问题,但除了数组中最大的数字外,我无法得到正确的输出。其他一切都给了我一个问题。我不知道出了什么问题,过去一周半我一直在研究这个问题,所以任何帮助都会非常感激。

.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后得到的输出

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 个答案:

答案 0 :(得分:3)

我修复了你的代码。我尽可能地保持忠诚,但是,我必须做大量的重组。

有几点需要注意。

我创建了函数(根据问题陈述)。也就是说,而不是相当于一个长main,我将你分开,就像你在C中做的那样。我为每个函数添加了块注释。

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

我改变了一些侧边栏评论,以更加描述意图,而不仅仅是重述他们所处理的指令的机制。

我还更改了标签,因此更容易将它们与其所处的功能相匹配(例如,功能foo中的所有标签都是foo_blah

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

以下是更正后的代码:

    .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

这是一个使用各种技巧的更紧凑的变体。它使用等效的“尾调用优化”来创建一个通用的打印例程,而不是在每个计算函数中复制打印代码。

也就是说,“技巧”是计算函数为打印设置参数,然后通过j 跳转 [而不是做第二级jal ]和print函数执行通常由计算函数完成的jr $ra

无论如何,这是代码:

    .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程序:

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

在大多数情况下,我使用了两者并且更喜欢mars - YMMV