如何在MIPS中查找数组的最小值

时间:2016-07-19 02:45:15

标签: arrays loops for-loop mips cpu-registers

这是我的代码,我无法获得正确的输出。我哪里错了?我将min设置为零,然后检查数组是否小于或等于此值,然后如果是,我跳转到标签并将min的值设为数组的值,然后跳回到迭代数组。

$_SERVER

1 个答案:

答案 0 :(得分:4)

好的,我发现了几个漏洞。我创建了三个版本,并添加了输出系统调用,以便您可以看到结果[请原谅无偿的样式清理]:

以下是您的原始代码,其中包含错误注释:

    .data
xyz:        .word       -8,16,-32,64,-128,256

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    # NOTE/BUG: to find minimum, you want to init this to the first array
    # element
    # also, initializing with a minimum value (e.g. 0), or more correctly, the
    # largest possible negative number (e.g. 0x80000000) implies a search for
    # maximum
    add     $s2,$zero,$zero         # min = 0

L1:
    beq     $s0,$s1,L2              # if (p == end) goto L2
    lw      $t0,($s0)               # $t0 = *p

    # NOTE/BUG: s2 is a register variable that contains "min" (e.g. int min)
    # and is _not_ a pointer to a "min" variable in memory (e.g. int *min)
    lw      $t1,($s2)               # $t1 = min

    # NOTE/BUG: the the check should be reversed:
    slt     $t2,$t1,$t0             # check if min is less than p
    add     $s3,$s3,$t0             # total += $t0

    bne     $t2,$zero,L3            # if min is less than p, go to L3

    # NOTE/BUG: this pointer increment is out of place (i.e. it does not
    # get incremented if there is a jump to L3)
    # this won't affect the min value, but it will double count the value in
    # the total
    addi    $s0,$s0,4               # p++
    j       L1

L2:
    add     $v0,$zero,$zero         # return value from main = 0
    jr      $ra

L3:
    move    $s2,$t0
    j       L1

这是一个固定版本:

    .data
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    lw      $s2,0($s0)              # min = xyz[0]

L1:
    beq     $s0,$s1,L2              # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    slt     $t2,$t0,$s2             # *p < min?
    bne     $t2,$zero,L3            # yes, fly

    j       L1

L2:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    move    $a0,$s2                 # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

L3:
    move    $s2,$t0                 # set new/better min value
    j       L1

这是一个稍微清理过的版本,我颠倒了分支的感觉,并且能够收紧循环。此外,我更改了标签以更具描述性/功能:

    .data
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int min     $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    lw      $s2,0($s0)              # min = xyz[0]

main_loop:
    beq     $s0,$s1,main_done       # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    slt     $t2,$s2,$t0             # *p < min?
    bne     $t2,$zero,main_loop     # no, loop

    move    $s2,$t0                 # set new/better min value
    j       main_loop

main_done:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    move    $a0,$s2                 # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall

<强>更新

  

感谢帮助很多,但lw $t1,($s2)不起作用,因为lw只能用于指针?

右。请注意您使用s3保留total的方式。这就是代码使用s2来保持min的方式。我[部分]这样做是因为最高评论:

#   int min     $s2

要使用lw,最高评论应为:

#   int *min    $s2

要以原始方式使用s2,您需要以下内容:

min:    .word    0

并且,您需要(在循环开始之前):

la    $s2,min

并且,您必须lwsw,这只会减慢速度。因此,除了已经存在的内容之外,您还需要额外添加lw和额外sw

mips有一个很多的寄存器[强项]。因此,它会加快速度,以便在其中保留自动的函数范围变量。

但是,为了完整起见,这是一个允许lw使用它的版本。注意额外的内存访问。它很像用-O0编译的C代码:

    .data
min:        .word       0
xyz:        .word       -8,16,-32,64,-128,256
msg_min:    .asciiz     "min: "
msg_tot:    .asciiz     " total: "
msg_nl:     .asciiz     "\n"

# int main(void)
#
# local variable    register
#   int *p      $s0
#   int *end    $s1
#   int *min    $s2
#   int total   $s3
#
    .text
    .globl  main

main:
    la      $s0,xyz                 # p = foo
    addi    $s1,$s0,24              # end = p + 6
    add     $s3,$zero,$zero         # total = 0

    la      $s2,min                 # point to min
    lw      $t4,0($s0)              # fetch xyz[0]
    sw      $t4,0($s2)              # store in min

main_loop:
    beq     $s0,$s1,main_done       # if (p == end) goto L2

    lw      $t0,0($s0)              # $t0 = *p
    addi    $s0,$s0,4               # p++

    add     $s3,$s3,$t0             # total += $t0

    lw      $t4,0($s2)              # fetch min
    slt     $t2,$t4,$t0             # *p < min?
    bne     $t2,$zero,main_loop     # no, loop

    sw      $t0,0($s2)              # store new/better min value
    j       main_loop

main_done:
    li      $v0,4
    la      $a0,msg_min
    syscall

    li      $v0,1
    lw      $a0,0($s2)              # get min value
    syscall

    li      $v0,4
    la      $a0,msg_tot
    syscall

    li      $v0,1
    move    $a0,$s3                 # get total value
    syscall

    li      $v0,4
    la      $a0,msg_nl
    syscall

    # exit program
    li      $v0,10
    syscall