Домашнее задание
Основная проблема с вашим кодом заключается в том, что объявленные переменные (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
регистра для определения кадра стека
Все это доступно из нескольких источников - или просто прочитайте скомпилированный код и поэкспериментируйте с опциями компилятора: там вы найдете много подходов. Удачи: это очень весело!