【问题标题】:NASM Assembly recursive fibonacciNASM 汇编递归斐波那契
【发布时间】:2013-10-13 18:15:42
【问题描述】:

在 32 位 Ubuntu 上学习 NASM 组装。

我一直在学习递归函数。在您的帮助下,我刚刚做了阶乘:Understanding recursive factorial function in NASM Assembly

看着代码,我想也许我也可以使用几乎相同的代码快速实现斐波那契。下面是代码,假设参数总是大于0

SECTION .text
global main
main:
; -----------------------------------------------------------
; Main
; -----------------------------------------------------------
push    6
call    fibonacci
mov     [ECX],EAX
add     byte [ECX],'0'
mov     EDX,1
call    print

; -----------------------------------------------------------
; Exit
; -----------------------------------------------------------
mov EAX,1
int 0x80

; -----------------------------------------------------------
; Fibonacci recursivo: f(n) = f(n - 1) + f(n - 2)
; -----------------------------------------------------------
fibonacci:

push    EBP         ; Retrieve parameter and put it
push    EBX         ; Save previous parameter
mov     EBP,ESP     ; into EBX register
add     EBP,12      ;
mov     EBX,[EBP]   ; EBX = Param

cmp     EBX,1       ; Check for base case
jle     base        ; It is base if (n <= 1)

dec     EBX     ; Decrement EBX to put it in the stack
push    EBX     ; Put (EBX - 1) in stack
inc     EBX     ; Restore EBX
call    fibonacci   ; Calculate fibonacci for (EBX - 1)
mov ESI,EAX     ; EAX = (EAX + EBX)
pop EBX         ; Retrieve EBX from the stack

sub EBX,2       ; Decrement EBX to put it in the stack
push    EBX     ; Put (EBX - 2) in stack
add EBX,2       ; Restore EBX
call    fibonacci   ; Calculate fibonacci for (EBX - 2)
mov EDX,EAX     ; EAX = (EAX + EBX)
pop EBX         ; Retrieve EBX from the stack

add ESI,EDX
mov EAX,ESI

jmp end
base:               ; Base case
mov EAX,1           ; The result would be 1

end:

pop     EBX         ; Restore previous parameter
pop     EBP         ; Release EBP
ret

有点粗糙。我为(parameter - 1) 计算斐波那契,然后为(parameter - 2) 再次计算,然后将它们相加并放入EAX

它不起作用:

2 => 2
3 => 3
4 => 4
5 => 4

幸运的是,我修复了分段错误,但我可能破坏了其他东西。现在我看不出有什么问题。你能告诉我为什么我得到了错误的值吗?

一个特别的观察是,出于某种原因,执行mov ECX,EAX 给了我一个分段错误错误。这就是我改用ESI 的原因。我不知道为什么,但我想它是相关的。

【问题讨论】:

    标签: linux assembly recursion nasm 32-bit


    【解决方案1】:

    每当您处理递归时,您必须非常小心递归链中的下一层将对当前层的状态(例如寄存器值)做些什么。我建议重写函数如下:

    fibonacci:
    push    EBP         ; Retrieve parameter and put it
    push    EBX         ; Save previous parameter
    mov     EBP,ESP     ; into EBX register
    add     EBP,12      ;
    mov     EBX,[EBP]   ; EBX = Param
    
    cmp     EBX,1       ; Check for base case
    jle     base        ; It is base if (n <= 1)
    
    lea ecx,[ebx-1]
    push ecx            ; push N-1
    call    fibonacci   ; Calculate fibonacci for (EBX - 1)
    pop ecx             ; remove N-1 off the stack
    
    push eax            ; save the result of fibonacci(N-1)
    lea ecx,[ebx-2]
    push ecx            ; push N-2
    call    fibonacci   ; Calculate fibonacci for (EBX - 2)
    pop ecx             ; remove N-2 off the stack
    pop ecx             ; ecx = fibonacci(N-1)
    add eax,ecx         ; eax = fibonacci(N-2) + fibonacci(N-1)
    
    jmp end
    base:               ; Base case
    mov EAX,1           ; The result would be 1
    
    end:
    pop     EBX         ; Restore previous parameter
    pop     EBP         ; Release EBP
    ret
    

    【讨论】:

    • 嗯,我遇到了分段错误错误。出于好奇,我用 EDX 替换了每个 ECX 实例,它开始工作了。但后来我用 4 测试,它返回 5(不应该是 3 吗?)
    • 该代码按照我发布的方式为我工作。关于fibonacci(4) 返回 5,那是因为如果 N
    • 啊,你是对的。我将调查我的 ECX 有什么问题。 LEA 仍然让我有点困惑(从未使用过它) - 但据我所知,它只是计算 [] 中的地址并存储它?谢谢。
    • LEA原本的目的是做地址计算,但你也可以用它来做简单的算术运算。在这种情况下,我使用lea ecx,[ebx-1] 来执行ECX = EBX-1。您也可以使用它,例如乘以非二次幂常数:lea eax,[ecx+ecx*4] ; EAX = ECX*5.
    猜你喜欢
    • 2012-11-06
    • 2019-04-23
    • 2011-08-02
    • 2021-07-18
    • 2014-06-24
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    相关资源
    最近更新 更多