在程序集中使用局部变量的递归proc

时间:2012-01-19 21:36:01

标签: assembly x86-16

我想在程序集中使用局部变量进行递归过程(intel 8086)。所以我按照以下代码执行:

MOEIN PROC NEAR
        IPTEMP DW ?
        NUM DW ?
        POP IPTEMP
        POP NUM
        DEC NUM
        CMP NUM,0H
        JZ EXIT

        PUSH NUM

        CALL MOEIN

        EXIT:
        PUSH IPTEMP
        RET
        MOEIN ENDP

它应该执行以下代码:

void moein(int x)
{
x--;
if (x != 0)
   moein(x);
}

但它不能这样做。它会失去回来的方式。 我怎么能像我在C中写的那样通过汇编来做呢?

2 个答案:

答案 0 :(得分:2)

作业?

代码的主要问题是声明的变量(IPTEMP和NUM)由函数的所有调用共享,而不是每次调用函数时都重新创建。因此,每次递归调用时,变量都会被覆盖。您可以想到它的方式是DW是编译时指令,因此它在编译期间保留了一些内存。编译器不知道调用函数的次数,因此它不知道它必须创建多少变量。

另一个问题是它以错误的方式混乱堆栈。想象一下,你把这个函数调用了10次。但是POP NUM之后使用的堆栈数量正好为零:您为此实例和之前的每个实例弹出其所有内容。这意味着有关最后9个实例的上下文的所有信息都将丢失。

解决这个问题的最简单方法是使用所谓的“堆栈框架” - 这就是高级语言在大多数情况下所做的事情。每个函数的堆栈帧看起来像这样,从顶部(较高地址)到底部(较低地址):

  • (可选)函数的参数,由调用者推送
  • 返回地址,由“CALL”指令推送
  • (可选)局部变量

假设我们处于16位模式(因为有8086标记),以下所有内容将使用16位整数和16位地址算法。

因此,[SP]指向返回地址,因此[SP + 2]指向第一个(最接近,仅在您的情况下)参数。你需要做的是阅读参数,减量,比较和调用自我:

MOEIN PROC NEAR
    MOV AX, [SP+2]   ; read the argument from the stack
    DEC AX
    ; you don't need CMP AX,0H here, since DEC sets the same flags
    JZ EXIT

    PUSH AX     ; place the argument on the stack for the next call
    CALL MOEIN
    ADD SP, 2   ; don't forget to clean the passed argument from the stack

EXIT:
    RET
MOEIN ENDP

其他变体可能包括:

  • 使用RET 2指令清除堆栈:您不需要ADD SP, 2然后
  • 通过其中一个寄存器传递值
  • 使用BP寄存器定义堆栈帧

所有这些都可以在多个来源中获得 - 或者只是阅读已编译的代码并尝试使用编译器选项:您将找到许多方法。祝你好运:这很有趣!

答案 1 :(得分:1)

您可以在堆栈上显式分配空间,或者使用MASM或兼容的汇编程序,您可以使用LOCAL指令创建本地变量。