1. ホーム
  2. assembly

[解決済み] アセンブリでの再帰的フィボナッチ

2022-03-15 10:57:13

質問

アセンブリで再帰的なフィボナッチ・プログラムを実装しようとしています。しかし、プログラムが処理されない例外でクラッシュしてしまい、問題を特定することができないようです。スタックの不適切な使用が原因であることは間違いないのですが、どこが原因なのかがわかりません。

.386
.model Flat
public Fibonacci
include iosmacros.inc ;includes macros for outputting to the screen

.code
Fibonacci proc

MOV EAX, [EBP+8]
CMP EAX, 1
    JA Recurse
    MOV ECX, 1
    JMP exit

Recurse:
    DEC EAX
    MOV EDX, EAX
    PUSH EAX
    CALL Fibonacci
    ADD ESP, 4
    MOV EBX, ECX
    DEC EDX
    PUSH EDX
    CALL Fibonacci
    ADD ECX, EBX
exit:
ret
Fibonacci endp


.data


end

また、フィボナッチ値を取得するために使用している数値を外部プロシージャでスタックにプッシュしています。どこに問題があるのか、何か思い当たることはありませんか?

解決方法は?

を実行すると call の場合、次の操作のアドレスが戻り値としてスタックにプッシュされます。関数を作成する際には、スタックフレームを作成することが一般的です。このフレームは、ローカル変数や引数のオフセットだけでなく、コールスタックを表示するために使用することができます。このフレームは、関数の最初にある2つの操作によって作成されます。

push ebp
mov ebp, esp

関数の終了時に、コールスタックを削除するには leave という2つの操作の逆を行うことに相当します。スタックフレームを使用する場合 esp に格納されます。 ebp フレームベースと呼ばれるスタック上の位置を指すようにします。このアドレスの上には、古い値である ebp とリターン・アドレスを指定すると、通常、最初の引数を取得するために [ebp+8] . しかし、スタックフレームを設定しませんでした。つまり、古い値である ebp はスタックにプッシュされず、現在の値である ebp がどこにあるか分からないので、引数の取得に使用することはできません。したがって、引数を取得するには [esp+4] .

また、返り値は慣習的に eaxebx は呼び出し側で保存されます。あなたのコードは、このどちらの規則にも従っていません。また、技術的には、関数は必ずしも ecx または edx そのため、通常、それらを保存したい場合は、他の関数を呼び出す前にスタックにプッシュしておく必要があります。このコードでは edxebx は、2以上の値で呼び出されると上書きされ、不正な結果を引き起こします。

ここに、私が言及したすべての修正を含む完全なリストを示します。スタックフレームを作成する必要はなく、あなたのコードもそうなっていないためです。

.386
.model Flat
public Fibonacci
include iosmacros.inc ;includes macros for outputting to the screen

.code
Fibonacci proc

    MOV EAX, [ESP+4]
    CMP EAX, 1
    JA Recurse
    MOV EAX, 1 ; return value in eax
    JMP exit

Recurse:
    PUSH EBX ; preserve value of ebx
    DEC EAX
    PUSH EAX
    CALL Fibonacci
    MOV EBX, EAX ; ebx is preserved by callee, so it is safe to use
    DEC [ESP] ; decrement the value already on the stack
    CALL Fibonacci
    ADD EAX, EBX ; return value in eax
    ADD ESP, 4 ; remove value from stack
    POP EBX ; restore old value of ebx
exit:
ret
Fibonacci endp