我想在程序集中使用局部变量进行递归过程(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中写的那样通过汇编来做呢?
答案 0 :(得分:2)
作业?
代码的主要问题是声明的变量(IPTEMP和NUM)由函数的所有调用共享,而不是每次调用函数时都重新创建。因此,每次递归调用时,变量都会被覆盖。您可以想到它的方式是DW
是编译时指令,因此它在编译期间保留了一些内存。编译器不知道调用函数的次数,因此它不知道它必须创建多少变量。
另一个问题是它以错误的方式混乱堆栈。想象一下,你把这个函数调用了10次。但是POP NUM
之后使用的堆栈数量正好为零:您为此实例和之前的每个实例弹出其所有内容。这意味着有关最后9个实例的上下文的所有信息都将丢失。
解决这个问题的最简单方法是使用所谓的“堆栈框架” - 这就是高级语言在大多数情况下所做的事情。每个函数的堆栈帧看起来像这样,从顶部(较高地址)到底部(较低地址):
假设我们处于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
指令创建本地变量。