MARS MIPS Out of Range Error

2019-07-29 14:25发布

问题:

I'm a newbie at MIPS and am attempting a program to find the value of the max number in a pseudo random array. However, I get an out of range error on a line I was fairly sure was allocated proper memory and such. Most of the skeleton code, comments, etc were given, I just am attempting to write the find_max function. I commented at the line giving me issues.

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

Okay, the function you had to write was using the wrong register when calculating the array index:

Change:

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

Into:

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

But, your function also had a much more severe bug. You were looping back to the function beginning and creating multiple stack frames in the process. I think you may have realized this on some level which is why you tried to compensate and exit the function with a jump to Exit: [which is part of main].

Your calculation function would also terminate prematurely after finding the first maximum. It would not continue to loop through the remainder of the array to find better (i.e. larger) values.

There were a number of other bugs as well.

I've created two versions of your program. One with annotations regarding the bugs. And, a second version, cleaned up, and working.


Here is the annotated version [please pardon the gratuitous style cleanup]:

    .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

Here is the fixed and running version. I added the missing init_array and print_array functions as well as the results printing. The boilerplate code you were given also had some [minor] bugs that I fixed. I tried to remain faithful to your find_max code, but, unfortunately, I needed to refactor it.

Also, make note of the fact that under the mips ABI, the $v*, $a*, and $t* can be used by the called function for any purpose. So, modifying the $s* registers was never an issue internally for 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