好的,我发现了几个bug。我已经创建了三个版本,并添加了输出系统调用,以便您可以查看结果[请原谅过度的样式清理]:
下面是带有错误注释的原始代码:
.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
而且,你需要对其进行lw
和sw
,这会减慢速度。因此,除了已有的内容外,你需要添加额外的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
slt
不能很好地处理负数,因为它将数字视为无符号数。你需要处理负值吗? - Craig Esteyslt
是有符号版本[还有一个无符号的sltu
]。我只是还没有得到预期的结果,所以请祈祷吧... - Craig Estey