Moein Hosseini
Moein Hosseini

Reputation: 4383

recursive proc with local variables in assembly

I wanna make recursive proc with local variables in assembly ( intel 8086 ). so I do it like following code:

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

it should do following code:

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

but it can't do it.it will lost the back way. how could I do it by assembly like what I write in C?

Upvotes: 0

Views: 2727

Answers (2)

Jerry Coffin
Jerry Coffin

Reputation: 490038

You can explicitly allocate space on the stack, or with MASM or compatible assembler you can use the LOCAL directive to create your local variables.

Upvotes: 1

Rom
Rom

Reputation: 4199

Homework?

The main problem with your code is the fact that the declared variables (IPTEMP and NUM) are being shared by all invocations of the function, as opposed being created anew every time the function is called. Therefore, every time you recursively invoke yourself, the variables get overwritten. The way you can think of it is DW is compile-time instruction, therefore it reserves a pice of memory during compile-time. Compiler doesn't know how many times the function will be called, therefore it has no knowledge of how many variables it has to create.

The other problem is that it messes with the stack in a wrong way. Imagine you called the function 10 times. But the amount of stack used after POP NUM is exactly zero: you pop all its content for this instance and for every previous instance. Which means all the information about the context of last 9 instances is lost.

The simplest way to fix this is to use what's called the "stack frame" -- that's what higher-level languages do most of the time. The stack frame for each function looks like this, from top (higher addresses) to bottom (lower addresses):

  • (optional) arguments of the function, pushed by the caller
  • the return address, pushed by the "CALL" instruction
  • (optional) local variables

Assuming we're in 16-bit mode (since there is 8086 tag), all the following will use 16-bit integers and 16-bit address arithmetic.

So, the [SP] points to the return address, therefore [SP + 2] points to the first (closest, only in your case) argument. What you need to do is to read the argument, decrement, compare and call self:

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

Other variants may include:

  • using RET 2 instruction to clear the stack: you won't need ADD SP, 2 then
  • passing a value through one of the registers
  • using BP register for defining the stack frame

All of this is available in multiple sources -- or just read the compiled code and experiment with compiler options: you'll find many approaches there. Good luck: it's a lot of fun!

Upvotes: 2

Related Questions