Иллюстрированный самоучитель по задачам и примерам Assembler

Реализация рекурсивных процедур

В общем-то ничего необычного в этом коде нет. Передача параметров в рекурсивную процедуру производится через стек. При этом необязательно все данные помещать в стек. Возможен вариант, когда локальные данные определяют в сегменте данных или в выделенной динамической области памяти, а в стек помещается только указатель на них. В любом случае, начало рекурсивной процедуры будет содержать код пролога, подобный приведенному в программе выше:

fact ргос
push bp mov bp.sp mov cx.[bp+4]

Смысл этого фрагмента легче понять, наблюдая поведение программы вычисления факториала в отладчике. Как сказано выше, перед вызовом процедуры в стек помещаются данные (или указатель на них), информация о местонахождении которых должна быть сохранена в интересах как вызывающей, так и вызываемой процедуры. В нашем случае в процедуру fact передается переменная факториала. После этого производится вызов процедуры, в результате чего в стек помещается адрес возврата. В вызванной процедуре к данным переменным необходимо получить доступ. Для этого предназначен регистр ВР. Перед использованием его содержимое должно быть также сохранено в стеке. Для первого вызова его значение несущественно. В этот момент весь предыдущий контекст работы программы сохранен. Команда mov bp.sp загружает в регистр ВР указатель на текущую вершину стека, после чего можно обращаться к данным, переданным в процедуру.

По сути, сейчас мы с вами сформировали кадр стека. Следующий рекурсивный вызов этой функции придает действию сохранения регистра ВР особый смысл. Команда push bp сохраняет в стеке адрес кадра стека для предыдущего вызова рекурсивной процедуры. Теперь для выхода из процедуры достаточно выполнить приведенные ниже команды эпилога, позволяющие корректно отработать обратную цепочку возврата в основную программу: будет рассмотрена в этой главе ниже. Далее сравним работу функций DrawPattern i и DrawPattern 1.

Вызов функции DrawPattern_1 из основной программы осуществляется следующим фрагментом кода.

:prg3_1.asm – фрагмент оконного приложения, вызывающего рекурсивную процедуру
:DrawPattern_l объявление пользовательских процедур (из maket_dll.DLL)
extrn DrawPattern_l:PROC extrn DrawPattem_2:PR0C
.data
определение констант для фигуры "Узор из окружностей"
р dd 5;порядок узора
г dd 60:радиус окружности
y_Pdd 140 начальная у-координата центра окружности
х_Р dd 200 начальная х-координата центра окружности
.code
обработка сообщений от меню
MenuProc proc
arg (a@hwnd: DWORD. №wparam: DWORD, @(ahdc: DWORD.@@hbrush: DWORD
uses eax.ebx
mov ebx.@@wparam:в bх идентификатор меню
onpbx.IDMJ)LL_LACESJ je @@idmdlllaces_l cmpbx.IDM_DLLJ_ACES_2 je @@idmdlllaces_2 jmp@@exit
e@1 chndl 11 aces_l:
;рисуем узор из окружностей, рекурсивная функция для рисования находится
;в DLL-библиотеке:
;DrawPattern_l(hwnd.hdc,x.y.r.p) – функция не работает с локальными переменными:
push p:порядок узора
push r:радиус окружности
push y_P:у-координата центра окружности
push x_P;х-координата центра окружности
push memdc:контекст устройства
push @@hwnd
call DrawPattern_1
jmp@@exit:………
Фрагмент файла maket_dll.DLL, содержащий процедуру DrawPattern_1, приведен ниже:
iinaket_dn.DLL – фрагмент DLL-библиотеки, содержащей рекурсивную процедуру DrawPatternJ
объявление процедур DLL-библиотеки общедоступными
publicdll WriteCon publicdll DrawPatternJ publicdll DrawPattern_2
.code DrawPatternJ proc
:DrawPattern_l – рекурсивная процедура рисования узора:(без использования локальных переменных)
arg @@hwnd:dword.@@hdc:dword.@@x:dword.@@y:dword.@@r:dword.@@p:dword
:рисуем окружность
:рекурсивно вызываем DrawPattern_l(hwnd.hdc,x.y.r,p)
:BOOL Ellipse(HDC hdc .int nLeftRect .int nTopRect .int nRightRect.int nBottomRect):
:готовим параметры в стеке для вызова Ellipse
call Ellipse:рисуем окружность:и еще четыре меньшего порядка
dec @@p
стр @@р, 0
je @@End_Draw
shr@@r,l – . делим на 2
:готовим параметры в стеке для вызова DrawPatternJ
call DrawPattern_l:готовим параметры в стеке для вызова DrawPattern_l
call DrawPatternJ:готовим параметры в стеке для вызова DrawPatternJ
call DrawPattern_l:готовим параметры в стеке для вызова DrawPatternJ.
call DrawPatternJ
@@End_Draw: генерация сообщения WM_PAINT для вывода изображения на экран
call InvalidateRect endp DrawPatternJ
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.