web-dev-qa-db-ja.com

MIPSアセンブリを使用した再帰関数

割り当てに問題があります。助けていただければ幸いです。私は答えを求めているのではなく、2つと2つを組み合わせて自分で理解することを好みますが、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を3つの値、加算する2つの数値、および合計とともに格納するスタックを作成しています。私の問題の一部は、関数がどこで開始および終了するか、そして実行されている作業の全体が何であるかを理解することです。

また、印刷するには次のものを使用するように指示されました

li $v0, 1
move $a0, $s0
syscall

これが$ v0に格納されている値を出力していると考えるのは正しいですか?

前もって感謝します

5

これが関数のコードです。私はあなたが答えを探していないことを知っていますが、時々例を探して、それがどのように機能するかは、それが実際にどのように機能したかを理解した時点でより簡単になります。

.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で再帰を使用するには、2つのことを行う必要があります。 jal(ジャンプしてリンク)を関数の名前に追加しますが、最初に常に戻りアドレスをスタックに格納します:$raしたがって、将来、最初に戻りたい場合は、次のことができるようになります。 jr $raを使用する。返信先アドレスを保存せずにjr経由でアクセスしようとすると、おそらくinvalid program counter errorが表示されます。私があなたを助けてくれて、MIPSプログラミングをよりよく理解できるように頑張ってください!

10
Korpel

ここにいくつかのヒントがあります:

再帰関数を作成する必要がありますが、関数はまったく作成していません。この関数を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
gusbro