【问题标题】:Recursive function using MIPS assembly使用 MIPS 程序集的递归函数
【发布时间】:2016-01-27 17:48:22
【问题描述】:

我在作业中遇到了一些问题,希望能得到一些帮助。我不是在问答案,我更喜欢把两个和两个放在一起来自己弄清楚,但我对 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 中的值是否正确?

【问题讨论】:

    标签: recursion mips fibonacci


    【解决方案1】:

    这里有一些提示:

    你必须写一个递归函数,但你根本不是在写一个函数。 要在 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。

    【讨论】:

      【解决方案2】:

      这是您的函数的代码。我知道您不是在寻找答案,而是有时寻找示例并查看其工作原理可以让您更轻松地了解它的实际工作原理。

      .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,所以将来如果你想回到开头,你将能够使用@987654326 @。如果您不保存退货地址并尝试通过jr 访问它,您很可能会收到invalid program counter error。

      【讨论】:

      • 感谢您的信息。这确实有助于我理解。现在我只需要更改它以删除提示。我不相信我们应该包含任何用户输入,只使用 MIPS 来显示序列中的前 10 个数字,所以我相信或多或少从 1 开始。还对添加 .globl main 感到好奇。这是某种常见的做法吗?这不是我们在课堂上讨论过的。
      • main 将是一个全局名称,也可以在其他文件中引用。如果你正在学习 MIPS 和汇编,你很可能不需要这个。我可以重定向您阅读来自 wiki 的有关 Mips 文章:en.wikipedia.org/wiki/MIPS_instruction_set 看看。我记得当我学习计算机架构时,我有点困惑,但在线阅读一些文章帮助了我。这里还有一个很好的教程,可以更好地理解 mips 及其架构。祝你好运logos.cs.uic.edu/366/notes/mips%20quick%20tutorial.htm
      【解决方案3】:

      这解释了如何在 MIPS 中实现斐波那契函数以及如何将 MIPS 函数转换回等效的 C 代码,非常详细:Fibonacci function in MIPS by illinois.edu

      1. 基本案例的代码:
      fib:
      bgt $a0, 1, recurse
      move $v0, $a0
      jr $ra
      
      1. 转换基本情况的代码。
      fib:
      bgt $a0, 1, recurse
      move $v0, $a0
      jr $ra
      
      1. 将被调用者和调用者保存的寄存器保存在堆栈上。 递归:
      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
      
      1. 递归调用 fib:
      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
      
      1. 再次递归调用 fib:
        lw $a0, 4($sp) # retrieve original value of N  
        addi $a0, $a0, -2 #N-2  jal fib
      

      【讨论】:

      猜你喜欢
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 2021-05-31
      • 2014-03-19
      • 1970-01-01
      • 2021-02-13
      • 2010-12-23
      • 1970-01-01
      相关资源
      最近更新 更多