【问题标题】:Recursion in Assembly汇编中的递归
【发布时间】:2016-02-29 18:43:18
【问题描述】:

我需要关于我刚开始学习的汇编代码的帮助。

.intel_syntax noprefix;
.text;
.globl main;

main:
    mov eax, 3;
    mov ebx, 0;
    push eax;
    push ebx;
    call f;
    add esp, 8;
    push eax;
    mov eax, offset message;
    push eax;
    call printf
    add esp,8;
    mov eax,0;
    ret;

f:
    mov eax, [esp+8];
    mov ebx, [esp+4];
    cmp eax,3;
    jge ety2;
    cmp eax,2;
    je ety1;
    cmp eax,0;
    je ety1;
    cmp eax,1;
    je ety3;
    ety3:
    mov eax,0;
    ret;

ety1:
    mov eax,1;
    ret;

ety2:
    xor ebx,ebx;
    dec eax;
    push eax;
    push ebx;
    call f;
    add esp,8;
    add ebx,[esp+4];
    add ebx,eax;
    mov eax,[esp+8];
    dec eax;
    dec eax;
    push eax;
    push ebx;
    call f;
    add esp,8;
    add ebx,[esp+4];
    add ebx,eax;
    add ebx,eax;
    mov eax,[esp+8];
    dec eax;
    dec eax;
    dec eax;
    push eax;
    push ebx;
    call f;
    add esp,8;
    add ebx,[esp+4];
    sub ebx,eax;
    mov eax,[esp+8];
    mov eax,ebx;
    ret;

.data;
message:
.asciz "Result=%i\n";
.att_syntax prefix; 

在主函数中,“eax”寄存器用作函数的“n”参数:

for n=0 or n=2 returns 1;
for n=1 returns 0;
for n>=3 returns f(n-1)+(2*f(n-2))-f(n-3);

所以对于 n=3 函数返回 0,n=4 返回 2,n=5 返回 1,n=6 返回 5 e.t.c.

递归是非常有问题的,对于值

需要使用“PUSH”和“[esp+4]”,“add esp,4;”以及代码中已经存在的其他简单指令。 程序在-m32命令参数下编译(gcc -Wall funcas.s -m32 -o test)。

我在 C 中写下了相同的代码来展示我想要实现的目标

#include <stdio.h>
#include <stdlib.h>

int funkcja(int n)
{
    if(n>=3)
    {
        return (funkcja(n-1)+(2*funkcja(n-2))-funkcja(n-3));
    }
    else
    {   
        if(n==2)return 1;
        if(n==1)return 0;
        if(n==0)return 1;
    }
    return -1;
}

int main()
{
    int a=6;
    printf("%d\n", funkcja(a));
    return 0;
}

【问题讨论】:

  • 请修正缩进,以便您的代码可读。 (例如,说明比标签缩进一级)。那堵asm的墙让我想停止阅读。所以我只想说,要编写递归函数,你必须保存/恢复任何需要在函数调用中生存的状态的寄存器,如果有任何不在堆栈上的话。 (例如,当您计算涉及三个递归调用的表达式时的临时变量)。
  • 您也可以努力调试它。调试汇编代码非常简单。
  • f 中,您需要在进入时保留ebx 中的内容(至少)并在返回给调用者之前恢复它。您可以使用 push/pop ebx 做到这一点,但您需要调整偏移量以访问参数。

标签: gcc assembly 32-bit gnu-assembler


【解决方案1】:

问题在于代码不断累积所有结果。将 f 更改为仅使用一个参数。示例 Microsoft 类型的汇编代码。在 f() 和 main() 中,n 都存储在堆栈中。

        .model  flat,c
;       ...
        .data
fmt     db      '%d',00ah,000h

        .code
        extern  printf:proc
        public  main

f       proc                    ;int f(int n)
        mov     eax, [esp+4]
        cmp     eax,3
        jge     f2
        cmp     eax,2
        je      f1
        cmp     eax,1
        je      f0
        cmp     eax,0
        je      f1
        mov     eax,-1
        ret

f0:     mov     eax,0
        ret

f1:     mov     eax,1
        ret

f2:     push    ebx             ;save ebx
        dec     eax             ;eax = n-1
        push    eax             ;[esp] = n-1
        call    f               ;eax = f(n-1)
        mov     ebx,eax         ;ebx = f(n-1)
        dec     dword ptr [esp] ;[esp] = n-2
        call    f               ;eax = f(n-2)
        add     eax,eax         ;eax = 2*f(n-2)
        add     ebx,eax         ;ebx = f(n-1) + 2*f(n-2)
        dec     dword ptr [esp] ;[esp] = n-3
        call    f               ;eax = f(n-3)
        add     esp,4           ;restore esp
        sub     ebx,eax         ;ebx = f(n-1) + 2*f(n-2) - f(n-3)
        mov     eax,ebx         ;eax = f(n-1) + 2*f(n-2) - f(n-3)
        pop     ebx             ;restore ebx
        ret
f       endp

main    proc    near
        push    dword ptr 0     ;[esp] = n
main0:  call    f
        push    eax
        push    offset fmt
        call    printf
        add     esp,8
        inc     dword ptr [esp]
        cmp     dword ptr [esp],20
        jl      main0
        add     esp,4
        xor     eax,eax
        ret
main    endp

【讨论】:

    【解决方案2】:

    我不明白你对EBX 和堆栈上的第二个参数的操作。

    让我们从头开始。递归函数也是一个函数。当您调用它时,您必须保留可由函数更改的寄存器,并且在函数返回后您需要保持不变。该函数以不同的n 调用自身三次,并以不同的结果运行。虽然您在堆栈上有 n 以进行任意恢复,但您必须保留结果。将return (funkcja(n-1)+(2*funkcja(n-2))-funkcja(n-3)); 拆分为

    int result = 0;
    result += funkcja(n-1);
    result += ( 2 * funkcja(n-2) );
    result -= funkcja(n-3);
    return result;
    

    result 是所谓的局部变量。它仅在此函数运行时需要,并且会随着函数返回而丢失。局部变量通常存储在堆栈中。您无需使用 prolog 和 epilog 构建 stackframe,简单的 push/pop 组合也可以。

    # f(n) = f(n-1) + (2*f(n-2)) - f(n-3)
    # 0 1
    # 1 0
    # 2 1
    # 3 0   1 + 0 - 1
    # 4 2   0 + 2 - 0
    # 5 1   2 + 0 - 1
    # 6 5   1 + 4 - 0
    # 7 5   5 + 2 - 2
    # 8 14  5 + 10 - 1
    # 9 19  14 + 10 - 5
    
    .intel_syntax noprefix
    .text
    .globl main
    
    main:
    
        mov eax, 9
        push eax
        call funkcja
        add esp, 4
    
        push eax
        mov eax, offset message
        push eax
        call printf
    
        add esp,8
        mov eax,0
        ret
    
    funkcja:
        mov eax, [esp+4]
        cmp eax,3
        jge 3f
    
        2:
        cmp eax,2
        jne 0f
        mov eax, 1
        ret
    
        0:
        cmp eax,0
        jne 1f
        mov eax, 1
        ret
    
        1:
        xor eax, eax
        ret
    
        3:
        push 0                      # Result = 0
    
        # 1. Call
        mov eax, [esp+8]            # +8: retrieve n behind push and return address
        sub eax, 1
        push eax
        call funkcja
        add esp, 4
        add [esp], eax              # Result += EAX
    
        # 2. Call
        mov eax, [esp+8]            # +8: retrieve n behind push and return address
        sub eax, 2
        push eax
        call funkcja
        add esp, 4
        add eax, eax
        add [esp], eax              # Result += EAX
    
        # 3. Call
        mov eax, [esp+8]            # +8: retrieve n behind push and return address
        sub eax, 3
        push eax
        call funkcja
        add esp, 4
        sub [esp], eax              # Result -= EAX
    
        pop eax                     # Return EAX = Result
        ret
    
    .data;
        message: .asciz "Result=%i\n"
    
    .att_syntax prefix
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-08-02
      • 2019-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多