MARS MIPS超出范围错误

时间:2016-09-21 23:32:43

标签: assembly mips mars

我是MIPS的新手,我正在尝试一个程序来查找伪随机数组中的最大数值。但是,我在一条线上得到一个超出范围的错误,我相当确定已分配了适当的内存等等。大多数骨架代码,注释等都给出了,我只是想尝试编写find_max函数。我评论了这条线给我提出了问题。

    .data               #data segment
nl:     .asciiz "\n"        #ASCII for a new line
separator:  .asciiz ":"     #string that seprates index from number 

msg1:   .asciiz "The function returned "

msg2:   .asciiz "\nThe maximum value is "
li $v0, $t5
#.asciiz 
msg3:   .asciiz "\nThe address of the word is "
#.asciiz 
li $v0, $t6

.align 2        # important for words
buffer: .space 4096     # allocate space for 1K words

.text           # Code segment
.globl  main        # declare main to be global

main: 
addi    $sp, $sp, -4    # allocate space on the stack for $s0
sw  $s0, ($sp)  # save $s0

li  $s0, 16     # specify the size of the array, must be less than 1024

la  $a0,name    # load the address of "name" into $a0
li  $v0,4       # system call, type 4, print an string, *$a0
syscall         # call the "OS"

# call 
la  $a0, buffer 
move    $a1, $s0    
jal init_array  # initialize the array with random values

la  $a0, buffer
move    $a1, $s0
jal print_array     # call print. You can comment it out.

# call your find_max function

la  $a0, buffer
move    $a1, $s0
lw  $t0, 0($a0)
la  $t5, 0($a0)
jal find_max    # call find_max
lw  $v0, 0($t0)
lw  $v1, 0($t5)
# add code to print the results
# print the returned value
la  $a0, msg1   # print mssage 1
li  $v0, 4
syscall

# print the maximum value
la  $a0, msg2   # print mssage 2
li  $v0, 4
syscall

# print the address of the value (in hex).
la  $a0, msg3   # print mssage 3
li  $v0, 4
syscall

la  $a0, nl     # print end of line

syscall

# restore  $s0. You can check $sp here to make sure the stack is maintained     correctly. 
lw  $s0, ($sp)  # load $s0
addi    $sp, $sp, 4 # restore $sp
Exit:   lw  $v0, 0($t5)
lw  $v1, 0($t6)
#li $v0,10      # System call, type 10, standard exit
syscall         # ...and call the OS

find_max: # your implementation of find_max
addi    $sp, $sp, -16   # allocate space on the stack for four registers
sw  $s0, ($sp)  # save $s0
sw  $s1, 4($sp) # save $s1
sw  $s2, 8($sp) # save $s2
sw  $s3, 12($sp)    # save $s3
# put your code below. 
# You can use temporary registers and $s0 to $s3
add $t1,$t1,1 # increment index i by 1

beq $t1,$s0,Exit # if all elements examined, quit

sll $t2,$t2,2 # compute 4i in $t2

add $t2,$t2,$s1 # form address of A[i] in $t2
# THIS IS THE LINE GIVING PROBLEMS
lw $t3,0($t2) # load value of A[i] into $t3

slt $t4,$t0,$t3 # maximum < A[i]?

beq $t4,$zero,find_max # if not, repeat with no change

addi $t0,$t3,0 # if so, A[i] is the new maximum

addi $t5, $t2, 0
# restore saved registers. 
# make sure $sp is restored if you changed it.
lw  $s0, ($sp)  
lw  $s1, 4($sp)  
lw  $s2, 8($sp)  
lw  $s3, 12($sp)     
addi    $sp, $sp, 16    # restore $sp
    jr  $ra                 # return to calling routine

1 个答案:

答案 0 :(得分:1)

好的,你必须编写的函数是在计算数组索引时使用了错误的寄存器:

变化:

sll $t2,$t2,2 # compute 4i in $t2

分为:

sll $t2,$t1,2 # compute 4i in $t2

但是,您的功能还有更多严重的错误。您正在循环回到函数开始并在此过程中创建多个堆栈帧。我想你可能已经在某种程度上意识到了这一点,这就是为什么你试图通过跳转到Exit: [这是main]的一部分来补偿和退出函数的原因。

您的计算功能也会在找到第一个最大值后提前终止。 继续遍历数组的其余部分以找到更好(即更大)的值。

还有许多其他错误。

我创建了两个版本的程序。一个有关于错误的注释。而且,第二个版本,清理和工作。

这是带注释的版本[请原谅无偿的风格清理]:

    .data                           # data segment
nl:         .asciiz     "\n"        # ASCII for a new line
separator:  .asciiz     ":"         # string that seprates index from number

msg1:       .asciiz     "The function returned "

# NOTE/BUG: these "li" are misplaced and have no meaning
msg2:       .asciiz     "\nThe maximum value is "
    ###li       $v0,$t5
msg3:       .asciiz     "\nThe address of the word is "
    # .asciiz
    # .asciiz
    ###li       $v0,$t6

    .align  2                       # important for words
###buffer:      .space      4096        # allocate space for 1K words
buffer:     .space      128     # allocate space for 1K words
bufe:

    .text                           # Code segment
    .globl  main                    # declare main to be global

main:
    addi    $sp,$sp,-4              # allocate space on the stack for $s0
    sw      $s0,($sp)               # save $s0

    # specify the size of the array, must be less than 1024
    la      $s1,bufe                # get address of array end
    la      $a0,buffer              # get address of array
    subu    $s1,$s1,$a0             # get byte length of array
    srl     $s0,$s1,2               # get word count of array

    # NOTE/BUG: "name" is undefined
    ###la       $a0,name                # load the address of "name" into $a0
    ###li       $v0,4                   # system call, type 4, print an string, *$a0
    ###syscall                          # call the "OS"

    # call
    la      $a0,buffer
    move    $a1,$s0
    jal     init_array              # initialize the array with random values

    la      $a0,buffer
    move    $a1,$s0
    jal     print_array             # call print. You can comment it out.

    # call your find_max function

    la      $a0,buffer
    move    $a1,$s0

    # NOTE/BUG: these serve no purpose
    lw      $t0,0($a0)
    la      $t5,0($a0)

    jal     find_max                # call find_max

    # NOTE/BUG: these serve no purpose
    lw      $v0,0($t0)
    lw      $v1,0($t5)

    # NOTE/BUG: the messages are printed below, but not the actual values

    # add code to print the results
    # print the returned value
    la      $a0,msg1                # print mssage 1
    li      $v0,4
    syscall

    # print the maximum value
    la      $a0,msg2                # print mssage 2
    li      $v0,4
    syscall

    # print the address of the value (in hex).
    la      $a0,msg3                # print mssage 3
    li      $v0,4
    syscall

    la      $a0,nl                  # print end of line

    syscall

    # restore  $s0. You can check $sp here to make sure the stack is maintained     correctly.
    lw      $s0,($sp)               # load $s0
    addi    $sp,$sp,4               # restore $sp

Exit:
    # NOTE/BUG: these serve no purpose
    lw      $v0,0($t5)
    lw      $v1,0($t6)

    # NOTE/BUG: the "li" should _not_ be commented out
    # li $v0,10      # System call, type 10, standard exit
    syscall                         # ...and call the OS

# your implementation of find_max
#
# arguments:
#   a0 -- buffer address
#   a1 -- buffer count
#
# registers:
#
find_max:
    addi    $sp,$sp,-16             # allocate stack space for four registers
    sw      $s0,($sp)               # save $s0
    sw      $s1,4($sp)              # save $s1
    sw      $s2,8($sp)              # save $s2
    sw      $s3,12($sp)             # save $s3

    # NOTE/BUG: the "max" value is never initialized to anything

    # put your code below.
    # You can use temporary registers and $s0 to $s3

    # NOTE/BUG: $t1 has _not_ been initialized
    add     $t1,$t1,1               # increment index i by 1

    # NOTE/BUG: we just want to exit the function and _not_ the program -- this
    # bug is linked to the one below
    beq     $t1,$s0,Exit            # if all elements examined, quit

    # NOTE/BUG: this should be:
    #   sll $t2,$t1,2
    ###sll      $t2,$t2,2               # compute 4i in $t2
    sll     $t2,$t1,2               # compute 4i in $t2

    add     $t2,$t2,$s1             # form address of A[i] in $t2
    # THIS IS THE LINE GIVING PROBLEMS
    # NOTE/FIX: with the bugfix it won't
    lw      $t3,0($t2)              # load value of A[i] into $t3

    slt     $t4,$t0,$t3             # maximum < A[i]?

    # NOTE/BUG: this should _not_ loop back to find_max as that will create
    # _multiple_ stack frames -- it is because of _this_ bug that you
    # tried to compensate and leave this function by jumping to Exit
    beq     $t4,$zero,find_max      # if not, repeat with no change

    # NOTE/BUG: if you set a new maximum, you still have to loop back and _not_
    # just exit this function. you may have found the _first_ maximum but there
    # could be others (i.e. larger values)
    addi    $t0,$t3,0               # if so, A[i] is the new maximum

    addi    $t5,$t2,0

    # restore saved registers.
    # make sure $sp is restored if you changed it.
    lw      $s0,($sp)
    lw      $s1,4($sp)
    lw      $s2,8($sp)
    lw      $s3,12($sp)
    addi    $sp,$sp,16              # restore $sp
    jr      $ra                     # return to calling routine

# init_array -- initialize array with random values
#
# arguments:
#   a0 -- array address
#   a1 -- array count
#
# registers:
#   t0 -- array address
#   t1 -- array count
init_array:
    move    $t0,$a0                 # prevent value from being clobbered
    move    $t1,$a1                 # prevent value from being clobbered

init_loop:
    li      $v0,41                  # syscall for random int
    li      $a0,0                   # specify which generator to use
    syscall

    sw      $a0,0($t0)              # store into array
    addiu   $t0,$t0,4               # advance to next array element
    subi    $t1,$t1,1               # decrement count
    bgtz    $t1,init_loop           # more to do? if yes, loop

    jr      $ra                     # return

# print_array -- initialize array with random values
#
# arguments:
#   a0 -- array address
#   a1 -- array count
#
# registers:
#   t0 -- array address
#   t1 -- array index
print_array:
    move    $t0,$a0                 # prevent value from being clobbered
    li      $t1,0                   # array index

print_array_loop:
    move    $a0,$t1                 # index value
    li      $v0,1                   # syscall for print int
    syscall

    la      $a0,separator
    li      $v0,4
    syscall

    lw      $a0,0($t0)              # get array value
    li      $v0,1                   # syscall for print int
    syscall

    # output a newline
    la      $a0,nl
    li      $v0,4
    syscall

    addiu   $t0,$t0,4               # advance to next array element
    addi    $t1,$t1,1               # increment index
    blt     $t1,$a1,print_array_loop    # more to do? if yes, loop

    jr      $ra                     # return

这是修复和运行的版本。我添加了缺少的init_arrayprint_array函数以及结果打印。您给出的样板代码也有一些我修复的[次要]错误。我试图保持对find_max代码的忠诚,但不幸的是,我需要对其进行重构。

另外,请注意在mip ABI下,被调用函数可以使用$v*$a*$t* any 目的。因此,修改$s*寄存器从来不是find_max内部的问题。

    .data                           # data segment
    .align  2                       # important for words
###buffer:      .space      4096        # allocate space for 1K words
buffer:     .space      128     # allocate space for 1K words
bufe:

nl:         .asciiz     "\n"        # ASCII for a new line
separator:  .asciiz     ":"         # string that seprates index from number

msg1:       .asciiz     "The function returned "

msg2:       .asciiz     "\nThe maximum value is "
msg3:       .asciiz     "\nThe address of the word is "

    .text                           # Code segment
    .globl  main                    # declare main to be global

main:
    addiu   $sp,$sp,-4              # allocate space on the stack for $s0
    sw      $s0,($sp)               # save $s0

    # specify the size of the array, must be less than 1024
    la      $s0,bufe                # get address of array end
    la      $a0,buffer              # get address of array
    subu    $s0,$s0,$a0             # get byte length of array
    srl     $s0,$s0,2               # get word count of array

    la      $a0,buffer
    move    $a1,$s0
    jal     init_array              # initialize the array with random values

    la      $a0,buffer
    move    $a1,$s0
    jal     print_array             # call print. You can comment it out.

    # call your find_max function
    la      $a0,buffer
    move    $a1,$s0
    jal     find_max                # call find_max
    move    $t0,$v0                 # save address
    move    $t1,$v1                 # save value

    # add code to print the results

    # print the maximum value
    la      $a0,msg2                # print mssage 2
    li      $v0,4
    syscall
    move    $a0,$t1                 # get max value
    li      $v0,1
    syscall
    la      $a0,nl
    li      $v0,4
    syscall

    # print the address of the value (in hex).
    la      $a0,msg3                # print mssage 3
    li      $v0,4
    syscall
    move    $a0,$t0                 # get address of max value
    li      $v0,34
    syscall
    la      $a0,nl
    li      $v0,4
    syscall

    # restore  $s0. You can check $sp here to make sure the stack is maintained     correctly.
    lw      $s0,($sp)               # load $s0
    addiu   $sp,$sp,4               # restore $sp

    li      $v0,10                  # exit program
    syscall                         # ...and call the OS

# your implementation of find_max
#
# RETURNS:
#   v0 -- address of maximum value
#   v1 -- maximum value
#
# arguments:
#   a0 -- buffer address
#   a1 -- buffer count
#
# registers:
#   t0 -- current value
#   t1 -- test/temporary
find_max:
    addiu   $sp,$sp,-16             # allocate stack space for four registers
    sw      $s0,($sp)               # save $s0
    sw      $s1,4($sp)              # save $s1
    sw      $s2,8($sp)              # save $s2
    sw      $s3,12($sp)             # save $s3

    # put your code below.
    # You can use temporary registers and $s0 to $s3

    # initialize the maximum value from the first element
    move    $v0,$a0                 # get the address
    lw      $v1,0($a0)              # get the value

find_max_loop:
    addiu   $a0,$a0,4               # advance buffer pointer
    subi    $a1,$a1,1               # decrement remaining count
    blez    $a1,find_max_done       # more to do? if no, return

    lw      $t0,0($a0)              # get current value
    slt     $t1,$v1,$t0             # got new maximum?
    beqz    $t1,find_max_loop       # no, loop

    move    $v0,$a0                 # set new maximum pointer
    move    $v1,$t0                 # set new maximum value
    j       find_max_loop           # try next value

    # restore saved registers.
    # make sure $sp is restored if you changed it.
find_max_done:
    lw      $s0,($sp)
    lw      $s1,4($sp)
    lw      $s2,8($sp)
    lw      $s3,12($sp)
    addiu   $sp,$sp,16              # restore $sp
    jr      $ra                     # return to calling routine

# init_array -- initialize array with random values
#
# arguments:
#   a0 -- array address
#   a1 -- array count
#
# registers:
#   t0 -- array address
#   t1 -- array count
init_array:
    move    $t0,$a0                 # prevent value from being clobbered
    move    $t1,$a1                 # prevent value from being clobbered

    # seed the generator
    li      $v0,40
    li      $a0,0
    li      $a1,0x12345678
    syscall

init_loop:
    li      $v0,41                  # syscall for random int
    li      $a0,0                   # specify which generator to use
    syscall

    sw      $a0,0($t0)              # store into array
    addiu   $t0,$t0,4               # advance to next array element
    subi    $t1,$t1,1               # decrement count
    bgtz    $t1,init_loop           # more to do? if yes, loop

    jr      $ra                     # return

# print_array -- initialize array with random values
#
# arguments:
#   a0 -- array address
#   a1 -- array count
#
# registers:
#   t0 -- array address
#   t1 -- array index
print_array:
    move    $t0,$a0                 # prevent value from being clobbered
    li      $t1,0                   # array index

print_array_loop:
    move    $a0,$t1                 # index value
    li      $v0,1                   # syscall for print int
    syscall

    la      $a0,separator
    li      $v0,4
    syscall

    lw      $a0,0($t0)              # get array value
    li      $v0,1                   # syscall for print int
    syscall

    # output a newline
    la      $a0,nl
    li      $v0,4
    syscall

    addiu   $t0,$t0,4               # advance to next array element
    addi    $t1,$t1,1               # increment index
    blt     $t1,$a1,print_array_loop    # more to do? if yes, loop

    jr      $ra                     # return