рекурсивный процесс с локальными переменными в сборке - PullRequest
0 голосов
/ 20 января 2012

Я хочу сделать рекурсивный процесс с локальными переменными в сборке (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 ]

2 голосов
/ 20 января 2012

Домашнее задание

Основная проблема с вашим кодом заключается в том, что объявленные переменные (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 голос
/ 20 января 2012

Вы можете явно выделить место в стеке, или с помощью MASM или совместимого ассемблера вы можете использовать директиву LOCAL для создания ваших локальных переменных.

...