Рекурсивная сборка Фибоначчи

Сегодня я написал рекурсивные фибоначчи в сборке, и это не работает. Я скомпилировал его в объектный файл с помощью NASM, а затем сделал его с помощью gcc.
Когда я ввожу 1 или 2, функция работает отлично, но когда я ввожу 3, 4, 5, 6 или более, функция не работает. Я думаю, что есть проблема, когда функция вызывает себя.

Это код:

SECTION .data ;init data




str: db "This equal: %d",10,0

SECTION .text   ;asm code


extern printf
global main

main:
push ebp
mov ebp,esp
;--------------------


push 03  ; the index 
call _fibonacci
add esp,4

push DWORD eax
push str
call printf


;---------------------

mov esp,ebp
pop ebp
ret

Это функция:

_fibonacci:

push ebp
mov ebp,esp


mov ebx, [ebp+8] ;; param n 
cmp ebx,0
jne CHECK2

mov eax,0
jmp _endFIbofunc        

CHECK2: 
    cmp ebx,0x1
    jne ELSE3
    mov eax,1
jmp _endFIbofunc

ELSE3:

mov ebx,[ebp+8] 
dec ebx  ;; n-1


;;  FIRST call
push ebx
call _fibonacci
add esp,4
mov edx,eax

;;  SEC CALL
dec ebx
push ebx
call _fibonacci
add esp,4 
add eax,edx


mov eax,[ebp-4]

_endFIbofunc:

mov esp,ebp
pop ebp
ret

После того, как я запустил его в Ubuntu 16.04, он отправляет ошибку:

Ошибка сегментации (ядро сброшено)

В чем может быть проблема?

3 ответа

mov eax,[ebp-4]

Вы используете память в [ebp-4] не вложив в это что-то полезное! Вам необходимо зарезервировать это место в прологе вашей функции:

_fibonacci:
    push ebp
    mov  ebp, esp
    sub  esp, 4

Возвращаясь с 1-го рекурсивного вызова, вы положили результат из EAX в этой памяти мечта.
Возврат со второго рекурсивного вызова, к которому вы добавляете EAX содержание этой памяти мечта.
Делая так, EDX Регистрация больше не будет засорена.


Почему вы используете EBX зарегистрироваться вообще? Если вы используете его, вы должны сохранить его, как объяснил в ответе Al Kepp.
Если вы начнете, положив аргумент в EAXВы знаете, что для обоих значений ниже 2 (т. е. 0 и 1) результат просто равен аргументу. Просто.

    mov  eax, [ebp+8] ;; param n 
    cmp  eax, 2
    jb   _endFIbofunc        

Если вы не уравновешиваете стек непосредственно после 1-го рекурсивного вызова, вы можете просто уменьшить значение уже существующего меча и сделать свой второй рекурсивный вызов.

    dec  eax              ; n-1
    push eax              ;(*)
    call _fibonacci
    mov  [ebp-4], eax
    dec  dword ptr [esp]  ; n-2
    call _fibonacci
    add  esp,4            ;(*)
    add  eax, [ebp-4]

Весь процесс:

_fibonacci:
    push ebp
    mov  ebp, esp
    sub  esp, 4           ;(*)
    mov  eax, [ebp+8] ;; param n 
    cmp  eax, 2
    jb   _endFIbofunc        
    dec  eax              ; n-1
    push eax              ;(*)
    call _fibonacci
    mov  [ebp-4], eax
    dec  dword ptr [esp]  ;(*) n-2
    call _fibonacci
    add  esp,4            ;(*)
    add  eax, [ebp-4]
_endFIbofunc:
    mov  esp, ebp
    pop  ebp
    ret

В дополнение к другим предоставленным ответам, вот альтернативное решение:

_fibonacci:
        mov     eax,[esp+4]             ;eax = n
        cmp     eax,2                   ;br if n < 2
        jb      _endFIbofunc
        dec     eax                     ;push n-1
        push    eax
        call    _fibonacci              ;returns eax = fib(n-1)
        xchg    eax,[esp]               ;eax = n-1, [esp] = fib(n-1)
        dec     eax                     ;push n-2
        push    eax
        call    _fibonacci              ;returns eax = fib(n-2)
        add     eax,[esp+4]             ;eax = fib(n-1)+fib(n-2)
        add     esp,8
_endFIbofunc:
        ret

Мелочи - фиб (47) самый большой < 2^32. Количество рекурсивных вызовов = 2*fib(n+1)-1.

 n     fib(n)      # calls

 0          0            1
 1          1            1
 2          1            3
 3          2            5
 4          3            9
 5          5           15
 6          8           25
 7         13           41
 8         21           67
 9         34          109
10         55          177
11         89          287
12        144          465
13        233          753
14        377         1219
15        610         1973
16        987         3193
17       1597         5167
18       2584         8361
19       4181        13529
20       6765        21891
21      10946        35421
22      17711        57313
23      28657        92735
24      46368       150049
25      75025       242785
26     121393       392835
27     196418       635621
28     317811      1028457
29     514229      1664079
30     832040      2692537
31    1346269      4356617
32    2178309      7049155
33    3524578     11405773
34    5702887     18454929
35    9227465     29860703
36   14930352     48315633
37   24157817     78176337
38   39088169    126491971
39   63245986    204668309
40  102334155    331160281
41  165580141    535828591
42  267914296    866988873
43  433494437   1402817465
44  701408733   2269806339
45 1134903170   3672623805
46 1836311903   5942430145
47 2971215073   9615053951
48 4807526976   ...

Вы должны сохранить (протолкнуть) регистры, которые вы собираетесь изменить в рекурсивном вызове. А затем восстановить их первоначальные значения (поп). Это должно решить проблему.

Что-то вроде этого:

  • Нажмите на все регистры, которые вы собираетесь использовать в своей функции (кроме eax, где вы получите возвращаемое значение)
  • Нажмите ebx, поскольку это ваш параметр
  • Вызвать функцию
  • Добавить esp,4
  • Вставьте все регистры, которые вы нажали на первом шаге, теперь в обратном порядке
Другие вопросы по тегам