Понятие вложенной процедуры включает в себя возможность
описания процедур внутри друг друга, при этом каждая из процедур может
иметь локальные данные, видимые для вложенных в нее процедур, но не
видимые для процедур, находящихся на одном уровне вложенности с данной
процедурой. Для организации такой вложенности существуют две возможности
— организация средствами ассемблера и определенная самим пользователем.
Рассмотрим первую из них. Для нее требуются команды ассемблера ENTER и
LEAVE. Их формат приведен ниже.
Команда enter loc_size.lexjev — ENTER (setup parameter block for
ENTERing procedure) — реализует установку кадра стека для параметров
процедуры. Работа команды заключается в следующем.
- 1. Размещение текущего значения регистра ЕВР/ВР в стеке.
- 2. Сохранение текущего значения ESP/SP в промежуточной переменной FP (имя переменной выбрано случайно).
- 3. Если лексический уровень вложенности (операнд lexlev) не равен нулю, то (1ex_1ev-l) сделать следующее:
- в зависимости от установленного режима адресации usel6 или use32
- выполнить вычитание (ВР-2) или (ЕВР-4) и записать результат обратно в ЕВР/ВР;
- сохранить значение ЕВР/ВР в стеке;
- сохранить в стеке значение промежуточной переменной fp.
- 4. Запись значения промежуточной переменной fp в регистр ЕВР/ВР.
5. Уменьшение значения регистра ESP/SP на величину, заданную первым операндом, минус размер области локальных переменных locsize: ESP/SP= (ESP/SP)-loc size.
Команда LEAVE (LEAVE from procedure — выход из процедуры) не имеет операндов и выполняет удаление из стека области локальных (динамических) переменных, выделенной командой ENTER. Команда выполняет обратные команде ENTER действия.
- 1. Содержимое ebp/bp копируется в ESP/SP, тем самым восстанавливается значение ESP/SP, которое было до вызова данной процедуры. С другой стороны, восстановление старого значения ESP/SP означает освобождение пространства в стеке, отведенного для завершающейся процедуры (локальные переменные процедуры уничтожаются).
- 2. Из стека восстанавливается содержимое ЕВР/ВР, которое было до входа в процедуру. После этого действия значение ESP/SP также становится таким, каким оно было до входа в процедуру.
В результате этих двух действий также восстанавливается кадр стека, если он был, вызывающей программы.
Команды ENTER и LEAVE специально введены в систему команд
микропроцессора для поддержки вложенных процедур, как это делают для
блочно-структури-рованных языков высокого уровня типа Паскаль или С. В
этих языках программа разбивается на блоки. В блоках можно описать свои
собственные (локальные) идентификаторы, которые не могут быть
использованы вне этого блока. К примеру, на рис. 3.1 в виде блоков
изображена структура некоторой программы.
Рис. 3.1. Изображение структуры некоторой программы в виде блоков
В правом верхнем углу каждого блока (процедуры) стоит номер
лексического уровня вложенности этого блока относительно других блоков
программы. Большинство блочно-структурированных языков в качестве
основного метода распределения памяти для переменных в блоках используют
автоматическое распределение памяти. Это означает, что при входе в блок
(вызове процедуры и т. п.) в некотором месте оперативной памяти (или в
стеке) выделяется область памяти для переменных этого блока (ее можно
назвать областью инициализации). После выхода из этого блока связь
программы с этой областью теряется, то есть эти переменные становятся
недоступными. Но если, как в нашем примере, в этой Процедуре есть
вложенные блоки (процедуры), то для некоторого внутреннего
блока (например, С) могут быть доступны области инициализации
(переменные) блоков, объемлющих данный блок. В нашем примере для блока С
доступны также переменные блоков В и А, но не D. Возникает вопрос: как
же программа, находясь в конкретной точке своего выполнения, может
отслеживать то, какие области инициализации ей доступны? Это делается с
помощью структуры данных называемой дисплеем. Дисплей содержит указатели
на самую последнюю область текущего блока и на области инициализации
всех блоков, объемлющих данный блок в программе. Например, если в
программе А была вызвана сначала процедура В, а затем С, то дисплей
содержит указатели на области инициализации А В и С (рис. 3.2).
Рис. 3.2. Соответствие содержимого дисплея области инициализации после вызова процедур В и С
Если после этого вызвать процедуру D (в то время как В и С еще не завершены), то картина изменится (рис. 3.3).
Рис. 3.3. Соответствие содержимого дисплея области инициализации после вызова процедуры D
После того как некоторый блок (процедура) завершает свою
работу, его область инициализации удаляется из памяти (стека) и
одновременно соответствующим образом корректируется дисплей.
Большинство языков высокого уровня хранит локальные данные блоков в
стеке. Эти переменные называют еще автоматическими, или динамическими.
Память для них резервируется путем уменьшения значения
регистра-указателя стека ESP/SP на величину, равную длине области,
занимаемой этими динамическими переменными. Доступ к этим переменным
осуществляется посредством регистра ЕВР/ВР. Если один блок вложен в
другой, то для его динамических (локальных) переменных также выделяется
место (кадр) в стеке, но в этот кадр помещается указатель на кадр стека
для включающего его блока. Команды ENTER И LEAVE как раз и позволяют
поддержать в языке ассемблера принципы работы с переменными блоков, как в
блочно-структурированных языках. Дисплей организуется с помощью второго
операнда команды ENTER и стека.
Например, в начале работы главной процедуры А и после вызова процедуры
В кадр стека будет выглядеть так, как показано на рис. 3.4.
Рис. 3.4. Кадр стека после вызова процедур А и В
Из рисунков видно, что, используя дисплей, мы фактически имеем адреса областей инициализации, доступных по признаку вложенности объемлющих блоков. Обратный процесс завершения работы с блоками и удаления соответствующих областей инициализации поддерживается командой LEAVE.