汇编中的递归斐波那契

发布于 2024-10-31 09:12:41 字数 576 浏览 4 评论 0原文

我正在尝试在汇编中实现递归斐波那契程序。但是,我的程序崩溃了,出现未处理的异常,而且我似乎无法找出问题所在。我不怀疑这涉及我对堆栈的不当使用,但我似乎无法指出在哪里......

.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

此外,我已将用于获取斐波那契值的数字推送到堆栈中一个外部过程。有什么想法可能是问题出在哪里吗?

I'm attempting to implement a recursive Fibonacci program in Assembly. However, my program crashes, with an unhandled exception, and I can't seem to pick out the problem. I don't doubt that it involves my improper use of the stack, but I can't seem to point out where...

.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

Also, I've pushed the number that I'm using to get the Fibonacci value to the stack in an external procedure. Any ideas where the problem might lie?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

因为看清所以看轻 2024-11-07 09:12:41

当您执行调用时,下一个操作的地址将作为返回值推送到堆栈。创建函数时,通常习惯创建“堆栈框架”。该帧可用于打印调用堆栈以及局部变量和参数的偏移量。该帧是通过函数开头的两个操作创建的:

push ebp
mov ebp, esp

在函数结束时,使用 leave 删除调用堆栈,这相当于这两个操作的相反操作。使用堆栈帧时,esp 的值存储到 ebp 中,使其指向堆栈上称为帧基址的位置。由于在此地址之上有 ebp 的旧值和返回地址,因此您通常会使用 [ebp+8] 获取第一个参数。但是,您没有设置堆栈框架。这意味着ebp的旧值没有被压入堆栈,并且ebp的当前值不能用于获取参数,因为你不知道它在哪里。因此,您应该使用 [esp+4] 来获取参数。

此外,通常将返回值放置在 eaxebx 中为调用者保留。您的代码不遵循这些约定。此外,从技术上讲,函数不需要保留 ecxedx,因此,如果您希望保留它们,通常应该在调用另一个函数之前将它们推送到堆栈。使用此代码,如果使用大于 2 的值调用,edxebx 将被覆盖,从而导致无效结果。

这是一个完整的列表,其中包括我提到的所有修复。我没有创建堆栈框架,因为它不是必需的,并且您的代码也没有。

.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

When you perform a call, the address of the next operation is pushed to the stack as a return value. When creating a function, it is often customary to create a "stack frame". This frame can be used to print the call stack, as well as an offset for local variables and arguments. The frame is created through two operations at the beginning of the function:

push ebp
mov ebp, esp

At the end of the function, the call stack is removed using leave, which is equivalent to the reverse of those 2 operations. When using a stack frame, value of esp is stored into ebp, making it point to a location on the stack called the frame's base. Since, above this address, there are the old value of ebp and the return address, you would normally get the first argument using [ebp+8]. However, you did not set up a stack frame. This means that the old value of ebp was not pushed to the stack, and the current value of ebp cannot be used to get arguments because you don't know where it is. Therefore, you should get your argument using [esp+4].

Also, it is customary that return values are placed in eax and ebx be preserved for the caller. Your code does not follow either of these conventions. Also, technically functions aren't required to preserved ecx or edx, so normally you should push them to the stack before calling another function if you want them preserved. With this code, edx and ebx would be overwritten if called with a value greater than 2, causing an invalid result.

Here is a full listing which includes all of the fixes I have mentioned. I did not create a stack frame as it is not necessary and your code didn't.

.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
窝囊感情。 2024-11-07 09:12:41

几个问题:

  • 如果要在堆栈上传递参数,则没有正确的(标准)函数入口,因此 EBP 指向错误的位置,
  • 您没有在 edx 中正确保存参数值,

这就是我的想法想要,假设您在堆栈上传递参数(最好为每个指令添加注释
明确你的想法):

Fibonacci proc
  PUSH EBP          ; save previous frame pointer
  MOV  EBP, ESP     ; set current frame pointer

  MOV  EAX, [EBP+8] ; get argument N
  CMP  EAX, 1       ; N<=1?
  JA   Recurse      ; no, compute it recursively
  MOV  ECX, 1       ; yes, Fib(1)--> 1
  JMP  exit

Recurse:
  DEC  EAX          ; = N-1
  MOV  EDX, EAX     ; = N-1
  PUSH EDX          ; save N-1
  PUSH EAX          ; set argument = N-1
  CALL Fibonacci    ; compute Fib(N-1) to ECX
  POP  EAX          ; pop N-1
  DEC  EAX          ; = N-2
  PUSH ECX          ; save Fib(N-1)
  PUSH EAX          ; set argument = N-2
  CALL Fibonacci    ; compute Fib(N-2) to ECX
  POP  EAX          ; = Fib(N-1)
  ADD  ECX, EAX     ; = Fib(N-1)+FIB(N-2)
exit:
  MOV  ESP,EBP      ; reset stack to value at function entry 
  POP  EBP          ; restore caller's frame pointer
  RET               ; and return

但是你不必在堆栈上传递参数。使用寄存器更有效:

Fibonacci proc ; computes Fib(EAX) --> EAX; do not call with EAX==0
  CMP  EAX, 1      ; N<=1?
  JBE  exit        ; yes, we have the answer
  DEC  EAX         ; = N-1
  PUSH EAX         ; save N-1
  CALL Fibonacci   ; computing FIB(n-1)to EAX
  XCHG EAX,0[ESP]  ; swap FIB(n-1) for saved N-1 (You'll love this instruction, look it up in the Intel manual)
  DEC  EAX         ; = N-2
  CALL Fibonacci   ; computing FIB(N-2) to EAX
  POP  ECX         ; = FIB(N-1)
  ADD  EAX,ECX     ; = FIB(N-1)+FIB(N-2)
exit:
  RET

Several problems:

  • if you are going to pass parameters on the stack, you don't have the right (standard) function entry, so EBP points to the wrong place
  • you aren't saving the argument value properly in edx

Here's what I think you wanted, assuming you are passing parameters on the stack (best to add a comment to each instruction
making it clear what you think it does):

Fibonacci proc
  PUSH EBP          ; save previous frame pointer
  MOV  EBP, ESP     ; set current frame pointer

  MOV  EAX, [EBP+8] ; get argument N
  CMP  EAX, 1       ; N<=1?
  JA   Recurse      ; no, compute it recursively
  MOV  ECX, 1       ; yes, Fib(1)--> 1
  JMP  exit

Recurse:
  DEC  EAX          ; = N-1
  MOV  EDX, EAX     ; = N-1
  PUSH EDX          ; save N-1
  PUSH EAX          ; set argument = N-1
  CALL Fibonacci    ; compute Fib(N-1) to ECX
  POP  EAX          ; pop N-1
  DEC  EAX          ; = N-2
  PUSH ECX          ; save Fib(N-1)
  PUSH EAX          ; set argument = N-2
  CALL Fibonacci    ; compute Fib(N-2) to ECX
  POP  EAX          ; = Fib(N-1)
  ADD  ECX, EAX     ; = Fib(N-1)+FIB(N-2)
exit:
  MOV  ESP,EBP      ; reset stack to value at function entry 
  POP  EBP          ; restore caller's frame pointer
  RET               ; and return

But you don't have to pass the parameters on the stack. It is more efficient to use the registers:

Fibonacci proc ; computes Fib(EAX) --> EAX; do not call with EAX==0
  CMP  EAX, 1      ; N<=1?
  JBE  exit        ; yes, we have the answer
  DEC  EAX         ; = N-1
  PUSH EAX         ; save N-1
  CALL Fibonacci   ; computing FIB(n-1)to EAX
  XCHG EAX,0[ESP]  ; swap FIB(n-1) for saved N-1 (You'll love this instruction, look it up in the Intel manual)
  DEC  EAX         ; = N-2
  CALL Fibonacci   ; computing FIB(N-2) to EAX
  POP  ECX         ; = FIB(N-1)
  ADD  EAX,ECX     ; = FIB(N-1)+FIB(N-2)
exit:
  RET
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文