Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на языке ассемблера - общая ле...docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
115.95 Кб
Скачать

7.8. Локальные данные процедур

Процедуры часто нуждаются в локальных данных. Локальные переменные размещаются в стеке. Для того чтобы отвести место под локальные переменные в процедуре на языке ассемблера, достаточно просто вычесть из регистра ESP размер требуемой памяти. После этого все вызываемые процедуры будут «знать», что место в стеке занято, и размещать свои данные в незанятой части стека.

При вызове других процедур, а также в ходе выполнения текущей процедуры в стек могут быть положены другие данные. При этом значение регистра ESP изменится. Поэтому регистр ESP не является надёжной точкой отсчёта для адресов локальных переменных. Для того чтобы получить такую точку отсчёта, значение регистра ESP переписывают в регистр EBP, предварительно сохранив значение регистра EBP в стеке. В этом случае регистр EBP отмечает часть стека, занятую на момент начала работы процедуры (отсюда происходит название регистра EBP – указатель базы кадра стека). При таком подходе первый параметр процедуры всегда находится по адресу [EBP + 8]. Адреса локальных переменных отсчитываются от регистра EBP с отрицательным смещением. По окончании работы процедуры значение регистра ESP восстанавливается по регистру EBP, а значение регистра EBP – из стека.

Procedure proc var_104 = byte ptr -104h var_4 = dword ptr -4 arg_0 = dword ptr 8 arg_4 = dword ptr 0ch push ebp mov ebp, esp sub esp, 104h mov edx, [ebp + arg_0] mov eax, [ebp + arg_4] push ebx push esi push edi ... pop edi pop esi pop ebx mov esp, ebp pop ebp ret Procedure endp

Такой способ позволяет также отводить различное количество места под локальные данные, и при необходимости не заботится о парности команд PUSH и POP.

7.9. Рекурсивные процедуры

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

При реализации рекурсивных процедур становится особенно важным использование стека для передачи параметров и адреса возврата, что позволяет хранить данные, относящиеся к разным уровням рекурсивных вызовов, в разные областях памяти.

Для примера рассмотрим рекурсивную процедуру вычисления факториала целого беззнакового числа. Процедура получает параметр через стек и возвращает результат через регистр EAX.

factorial proc mov eax, [esp + 4] ; Заносим в регистр EAX параметр процедуры test eax, eax ; Проверяем значение в регистре EAX jz L1 ; Если EAX = 0, то обходим рекурсивную ветвь dec eax ; Уменьшаем значение в регистре EAX на 1 push eax ; Кладём в стек параметр для следующего рекурсивного вызова call factorial ; Вызываем процедуру add esp, 4 ; Очищаем стек, т.к. процедура использует RET без параметров mul dword ptr [esp + 4] ; Умножаем EAX, хранящий результат предыдущего вызова, на параметр текущего вызова процедуры jmp L2 ; Переходим к возврату из процедуры L1: inc eax ; Если EAX был равен 0, записываем в EAX единицу L2: ret ; Возврат из процедуры (без параметров) factorial endp