MIPS程序集中的简单递归问题

时间:2016-11-03 07:46:37

标签: recursion assembly mips

所以我试图用mips写一个程序只做一些简单的递归,我读了所有其他文章和关于我能找到的主题的问题,但无济于事。无论如何我试图向后打印我的名字。所以我一次读取一个字节,在我的字符串末尾寻找0,如果我发现它,我开始展开并打印我保存的字节。 提前感谢您的帮助。

代码:

.data
    # print name backwards
    Yn: .asciiz "frodobaggins"  #yn stands for your name
.text
main:
    li $v0, 4           #the v registers tell system what to do 4 ==
    print_string
    la $a1, Yn          #load Yn's address into a0  
    addi $sp, $sp, -4   #increment stack pointer, always initially points to garbage
    sw  $a1, 0($sp)     #store address of a1 (Yn) into stack pointer
    jal recurse         #recursive func call
    addi $sp, $sp, 4    #increment sp back to beggining
    addi $v0, $0, 10    #v0 = exit
    syscall             #exit

recurse:
    lw $a3, 0($sp)      #load my name into reg $a3 
    lw $t6, 0($a3)      #read byte
    addi $sp, $sp, -12  #increment stack ptr
    sw $ra, 8($sp)      #store address of $ra onto stack
    sw $t6, 4($sp)      #store the byte loaded into $t6 onto stack
    bne $t6, $0, otherwise #if t6 == 0 goto otherwise 
    add $a3, $a3, 1     #a1 = a1 + 1
    sw $a3, 0($sp)      #store the word, without the byte $t6 used, onto the stack
    jal recurse
    lw $t6, 4($sp)      #load the byte that was stored into $t6
    lw $ra, 8($sp)      #get $ra ready for returning
    addi $v0, $0, 4     #setup $v0 to tell pc to print to console
    lw $a0, 0($t6)      #console always reads from $a0
    syscall
    addi $sp, $sp, 12   #put stack pointer back
    jr $ra              #string is finished, return.

otherwise:
    #because this is just returning, i need to get the ra that was just stored on the stack
    lw $ra 8($sp)
    jr $ra              #go back to original call of jal

1 个答案:

答案 0 :(得分:0)

有一些问题。

加载$t6应该是一个字节操作。 otherwise的条件分支有不正确的意义。要打印的系统调用应该是11 [putc]而不是4 [puts]。在otherwise恢复堆栈帧是有问题的。

我创建了三个版本。带注释的版本,显示错误。清理和工作版本。而且,我是从头开始创建的一个版本。

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

    .data
Yn:         .asciiz     "frodobaggins"  # yn stands for your name
# print name backwards
    .text

main:
    li      $v0,4                   # syscall for print string
    la      $a1,Yn                  # load Yn's address into a0
    addi    $sp,$sp,-4              # increment stack pointer
    sw      $a1,0($sp)              # store address of a1 (Yn) into stack pointer
    jal     recurse                 # recursive func call
    addi    $sp,$sp,4               # increment sp back to beggining
    addi    $v0,$0,10               # v0 = exit
    syscall                         # exit

recurse:
    lw      $a3,0($sp)              # load my name into reg $a3

# NOTE/BUG: this should be lb
# NOTE/BUG: by loading $t6 _before_ we've saved it, we are destroying the
# value that _caller_ set
    ###lw       $t6,0($a3)              # read byte
    lb      $t6,0($a3)              # read byte

    addi    $sp,$sp,-12             # increment stack ptr
    sw      $ra,8($sp)              # store address of $ra onto stack
    sw      $t6,4($sp)              # store the byte loaded into $t6 onto stack

# NOTE/BUG: this should be beq and _not_ bne
    ###bne      $t6,$0,otherwise        # if t6 == 0 goto otherwise
    beq     $t6,$0,otherwise        # if t6 == 0 goto otherwise

    add     $a3,$a3,1               # a1 = a1 + 1
    sw      $a3,0($sp)              # push the word, without the byte $t6 used

    jal     recurse

    lw      $t6,4($sp)              # load the byte that was stored into $t6
    lw      $ra,8($sp)              # get $ra ready for returning

# NOTE/BUG: this is the puts syscall -- what we want is putc (i.e. 11)
    ###addi $v0,$0,4                # setup $v0 to tell pc to print to console
    addi    $v0,$0,11               # setup $v0 to tell pc to print to console

# NOTE/BUG: we want to load the byte into $a0
# NOTE/BUG: at this point $t6 has the byte value and _not_ a pointer
    ###lw       $a0,0($t6)              # console always reads from $a0
    move    $a0,$t6

    syscall
    addi    $sp,$sp,12              # put stack pointer back
    jr      $ra                     # string is finished, return.

otherwise:
    # because this is just returning, i need to get the ra that was just
    # stored on the stack
    lw      $ra,8($sp)
# NOTE/BUG: by _not_ restoring the $t6 value of _caller_ things are broken
    jr      $ra                     # go back to original call of jal

这是工作版本。请注意早期堆栈帧设置和完整堆栈帧弹出otherwise。这有点多见。也就是说,建立堆栈帧的唯一一个地方。并且,只有一个地方可以弹出框架并返回:

    .data
Yn:         .asciiz     "frodobaggins"  # yn stands for your name
# print name backwards
    .text

main:
    li      $v0,4                   # syscall for print string
    la      $a1,Yn                  # load Yn's address into a0

    addi    $sp,$sp,-4              # increment stack pointer
    sw      $a1,0($sp)              # store address of a1 (Yn) into stack

    jal     recurse                 # recursive func call

    addi    $sp,$sp,4               # increment sp back to beggining

    addi    $v0,$0,10               # v0 = exit
    syscall                         # exit

recurse:
    lw      $a3,0($sp)              # load my name into reg $a3
    lb      $t6,0($a3)              # read byte

    addi    $sp,$sp,-12             # establish stack frame
    sw      $ra,8($sp)              # store address of $ra onto stack
    sw      $t6,4($sp)              # store the byte loaded into $t6 onto stack

    beq     $t6,$0,otherwise        # if t6 == 0 goto otherwise

    add     $a3,$a3,1               # a1 = a1 + 1

    sw      $a3,0($sp)              # push the word, without the byte $t6 used
    jal     recurse

    lw      $t6,4($sp)              # load the byte that was stored into $t6
    lw      $ra,8($sp)              # get $ra ready for returning

    addi    $v0,$0,11               # setup $v0 to tell pc to print to console
    move    $a0,$t6
    syscall

otherwise:
    lw      $ra,8($sp)              # restore return address from stack
    lw      $t6,4($sp)              # store the byte loaded into $t6 onto stack
    addi    $sp,$sp,12              # put stack pointer back
    jr      $ra                     # string is finished, return.

这是我的重构版本。这是一个更符合ABI标准的mips。可能会有一些细微的变化:

    .data
name:   .asciiz     "frodobaggins"
nl:     .asciiz     "\n"

    .text
    .globl  main
main:
    li      $v0,4                   # print string syscall
    la      $a0,name                # string address
    syscall

    li      $v0,4                   # print string syscall
    la      $a0,nl                  # string address
    syscall

    la      $a0,name                # string address
    jal     recurse1

    li      $v0,4                   # print string syscall
    la      $a0,nl                  # string address
    syscall

    li      $v0,10
    syscall

# recurse1 -- reverse print string
#
# arguments:
#   a0 -- current string pointer
recurse1:
    sub     $sp,$sp,8               # create stack frame
    sw      $ra,4($sp)              # save return address
    sw      $a0,0($sp)              # save current string pointer

    lb      $t0,0($a0)              # get current byte
    beqz    $t0,recurse1_exit       # is it EOS? if yes, fly

    addu    $a0,$a0,1               # advance to next char
    jal     recurse1                # print other chars
    subu    $a0,$a0,1               # go back to our character

    lb      $a0,0($a0)              # get current byte
    li      $v0,11                  # putc syscall number
    syscall

recurse1_exit:
    lw      $ra,4($sp)              # restore return address
    lw      $a0,0($sp)              # restore current string pointer
    add     $sp,$sp,8               # pop stack frame
    jr      $ra                     # return

# recurse2 -- reverse print string
#
# arguments:
#   a0 -- current string pointer
recurse2:
    sub     $sp,$sp,8               # create stack frame
    sw      $ra,4($sp)              # save return address
    sw      $a0,0($sp)              # save current string pointer

    lb      $t0,0($a0)              # get current byte
    beqz    $t0,recurse2_exit       # is it EOS? if yes, fly

    addu    $a0,$a0,1               # advance to next char
    jal     recurse2                # print other chars
    lw      $a0,0($sp)              # restore our pointer

    lb      $a0,0($a0)              # get current byte
    li      $v0,11                  # putc syscall number
    syscall

recurse2_exit:
    lw      $ra,4($sp)              # restore return address
    lw      $a0,0($sp)              # restore current string pointer
    add     $sp,$sp,8               # pop stack frame
    jr      $ra                     # return

# recurse3 -- reverse print string
#
# arguments:
#   a0 -- current string pointer
recurse3:
    sub     $sp,$sp,8               # create stack frame
    sw      $ra,4($sp)              # save return address

    lb      $t0,0($a0)              # get current byte
    sw      $t0,0($sp)              # save current char value
    beqz    $t0,recurse3_exit       # is it EOS? if yes, fly

    addu    $a0,$a0,1               # advance to next char
    jal     recurse3                # print other chars

    lw      $a0,0($sp)              # get current byte
    li      $v0,11                  # putc syscall number
    syscall

recurse3_exit:
    lw      $ra,4($sp)              # restore return address
    add     $sp,$sp,8               # pop stack frame
    jr      $ra                     # return