Recursion 使用MIPS汇编的递归函数

Recursion 使用MIPS汇编的递归函数,recursion,mips,fibonacci,Recursion,Mips,Fibonacci,我在一项任务上遇到了一些麻烦,希望能得到一些帮助。我并不是在问答案,我更喜欢把两个和两个放在一起自己去弄清楚,但我对MIPS知之甚少,我很难知道从哪里开始 这是我开始的 .data .text main: addi $sp, $sp, -16 #prepare stack for 4 items sw $s0, 0($sp) sw $s1, 4($sp) sw $s2, 8($sp) sw $ra, 12($sp) move $s0, $a0 move $s1, $a1 add $s2

我在一项任务上遇到了一些麻烦,希望能得到一些帮助。我并不是在问答案,我更喜欢把两个和两个放在一起自己去弄清楚,但我对MIPS知之甚少,我很难知道从哪里开始

这是我开始的

.data


.text
main:

addi $sp, $sp, -16  #prepare stack for 4 items
sw $s0, 0($sp)
sw $s1, 4($sp)
sw $s2, 8($sp)
sw $ra, 12($sp)
move $s0, $a0
move $s1, $a1

add $s2, $s0, $s1   #add two previous numbers and store result in $s2

move $v0, $s2   #put answer into $v0

lw $s0, 0($sp)
lw $s1, 4($sp)
lw $s2, 8($sp)
lw $ra, 12($sp)
addi $sp, $sp, 16
jr$ra
本质上,我们要使用递归函数来计算斐波那契数,并使用循环来打印斐波那契序列的前10个数

我查阅了很多例子,但它们都使用了我们尚未学习过的说明,因此我无法理解,我只能假设我们不需要使用它们。在上面的代码中,我基本上创建了一个堆栈来存储$ra以及三个值,两个要添加的数字和总和。我的部分问题是理解函数从哪里开始和结束,以及所做的全部工作是什么

我们还被赋予了打印的权利,您可以使用以下内容

li $v0, 1
move $a0, $s0
syscall
我认为这是在打印存储在
$v0
中的值,对吗?

这里有一些提示:

你必须写一个递归函数,但你根本没有写函数。 要在MIPS汇编程序中编写此函数,我建议您首先用高级语言(C)编写它。 所以会是这样的:

int fib(int n)
{
  if(n == 0 or n == 1)
    return n;
  else return fib(n-1) + fib(n-2);
}
第一行检查您是否处于递归的基本情况(n=0或n=1)。如果是这样,fib(n)返回n。 否则,递归步骤将返回fib(n-1)和fib(n-2)之和

因此,您必须编写一个函数,定义输入/输出参数(哪个寄存器将保持n,哪个将返回fib(n))。 用户可以手动编译C代码。 要启动函数,只需添加一个标签

fib:
  • 然后将堆栈管理代码放在那里
  • 然后将IF-then-ELSE转换为MIPS汇编
  • 要发出递归调用,请使用
    jal
    指令
  • 使用
    jr$ra
    从函数返回之前,请从堆栈还原保存的值
  • 主程序将加载输入参数(用于输入参数的寄存器
    n
    ),然后加载
    jal fib

    • 这是你函数的代码。我知道你不是在寻找答案,而是有时在寻找一个例子,看看它是如何工作的。当你了解它的真正工作原理时,你会更加容易

      .data
      msg1: .asciiz "Give a number: "
      
      .text
      .globl main
      main:
          li $v0,  4
          la $a0,  msg1
          syscall             # print msg
          li $v0,  5
          syscall             # read an int
          add $a0, $v0, $zero # move to $a0
      
          jal fib             # call fib
      
          add $a0, $v0, $zero
          li  $v0, 1
          syscall
      
          li $v0, 10
          syscall
      
      fib:
          # $a0 = y
          # if (y == 0) return 0;
          # if (y == 1) return 1;
          # return fib(y - 1) + fib(y - 2);
      
          #save in stack
          addi $sp, $sp, -12 
          sw   $ra, 0($sp)
          sw   $s0, 4($sp)
          sw   $s1, 8($sp)
      
          add $s0, $a0, $zero
      
          addi $t1, $zero, 1
          beq  $s0, $zero, return0
          beq  $s0, $t1,   return1
      
          addi $a0, $s0, -1
      
          jal fib
      
          add $s1, $zero, $v0         # $s1 = fib(y - 1)
      
          addi $a0, $s0, -2
      
          jal fib                     # $v0 = fib(n - 2)
      
          add $v0, $v0, $s1           # $v0 = fib(n - 2) + $s1
      
          exitfib:
      
              lw   $ra, 0($sp)        # read registers from stack
              lw   $s0, 4($sp)
              lw   $s1, 8($sp)
              addi $sp, $sp, 12       # bring back stack pointer
              jr $ra
      
          return1:
              li $v0,1
              j exitfib
      
          return0:     
              li $v0,0
              j exitfib
      

      正如Gusbro所说,为了在mips中使用递归,您必须做两件事。
      jal
      (跳转和链接)返回函数名,但首先始终将返回地址存储到堆栈中:
      $ra
      ,因此,将来如果要返回到开头,可以使用
      jr$ra
      。如果不保存返回地址并尝试通过
      jr
      访问它,则很可能会出现
      无效的程序计数器错误r
      。希望我能帮助您,祝您好运,更好地掌握MIPS编程!

      这说明了如何在MIPS中实现斐波那契函数,以及如何将MIPS函数转换回等效的C代码,详细内容如下:

    • 基本情况的代码:
    • 转换基本案例的代码
    • 在堆栈上保存被调用方和调用方保存的寄存器。 递归:
    • 递归调用fib:
    • 再次递归调用fib:

    • 谢谢你提供的信息。这确实帮助我理解。现在我只需要更改它来删除提示。我不认为我们应该包括任何用户输入,只使用MIPS来显示序列中的前10个数字,所以我或多或少从1开始。我相信。我还对添加.globl main感到好奇。这是一种常见现象吗练习?这不是我们在课堂上讨论过的内容。
      main
      将是一个全局名称,也可以在其他文件中引用。如果你正在学习MIPS和汇编,你很可能不需要它。我可以重定向你阅读wiki上的MIPS文章吗:看一看。我记得当我学习计算机体系结构时,我有点像c在线阅读一些文章帮了我的忙。这里还有一个很好的教程,可以更好地了解mips及其体系结构。祝你好运,欢迎链接到解决方案,但请确保你的答案在没有它的情况下是有用的:这样你的其他用户就会知道它是什么以及它为什么存在,然后引用mos如果目标页面不可用或发生更改,请不要链接到页面的相关部分。谢谢您的评论,先生
      fib:
      bgt $a0, 1, recurse
      move $v0, $a0
      jr $ra
      
      fib:
      bgt $a0, 1, recurse
      move $v0, $a0
      jr $ra
      
      sub $sp, $sp, 12 # We need to store 3 registers to stack 
      sw $ra, 0($sp) # $ra is the first register 
      sw $a0, 4($sp) # $a0 is the second register, we cannot assume $a registers will not be overwritten by callee
      
      addi $a0, $a0, -1 # N-1 
      jal fib 
      sw $v0, 8($sp) # store $v0, the third register to be stored on the stack so it doesn’t get overwritten by callee
      
        lw $a0, 4($sp) # retrieve original value of N  
        addi $a0, $a0, -2 #N-2  jal fib