2

私は現在、アセンブリのコースを受講していますが、次の割り当てに問題があります。

(適切なプロンプトで) 20 個の整数のシーケンスを読み取って配列に格納し、次の 3 つの関数を呼び出して結果を読み取り可能な形式で出力するプログラムを作成します。

3 つの関数は次のとおりです。 leastLargest:配列内の最小値と最大値を計算します。 divisible: 4 で割り切れる配列内の整数の数を計算し ます。 SumProduct:整数の和と積を計算します。

この問題を解決するためにアセンブリ コード (以下) を書きましたが、配列内の最大数を除いて正しい出力を取得できません。他のすべてが私に問題を与えています。何が悪いのかわからず、過去 1 週間半にわたってこれに取り組んできたので、どんな助けも大歓迎です。

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

1 に答える 1

3

コードを修正しました。可能な限り忠実であり続けようとしましたが、かなりのリストラをしなければなりませんでした。

注意すべき点がいくつかあります。

関数を作成しました(問題の説明に従って)。つまり、1 つの longmainに相当する代わりに、C で行うのと同じように分割します。各関数にブロック コメントを追加しました。

ではmain、配列カウントを にロードし$s0、配列ベース アドレスをにロードしました$s1。計算関数は、コードを複製するのではなく、これらから値を設定します。(つまり) 必要に応じて、アレイのアドレスとカウントを1か所で設定/変更できます。

サイドバーのコメントの一部を変更して、単に説明の仕組みを再度説明するのではなく、意図をより説明するようにしました。

また、ラベルを変更して、それらが含まれていた関数に一致させやすくしました(たとえば、関数内のすべてのラベルは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

これは、ある種のトリックを使用した、よりコンパクトなバリエーションです。各計算関数で印刷コードを複製する代わりに、「末尾呼び出しの最適化」に相当するものを使用して、共通の印刷ルーチンを作成します。

つまり、「トリック」は、計算関数が印刷の引数を設定し、[第2レベルを実行する代わりに]経由でそれにジャンプし、印刷関数が計算関数によって通常行われることを実行することです。jjaljr $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を好みます。

于 2016-07-19T19:44:30.577 に答える