Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции препода / Лекции для 3 курса.doc
Скачиваний:
86
Добавлен:
30.04.2013
Размер:
1.51 Mб
Скачать

3.11. Поддержка языков высокого уровня

Процессор 80286 имеет три команды (BOUND, ENTER и LEAVE), кото­рые упрощают программирование на языках высокого уровня. Например, команда BOUND осуществляет контроль массивов, а команды ENTER и LEAVE реализуют служебные функции, связанные с процедурами. Про­граммист, работающий на языке высокого уровня, совершенно не касается этих функций, так как все необходимые команды генерирует компилятор. В микропроцессоре 8086 указанных команд нет.

Команда BOUND проверяет, находится ли знаковое значение из 16-бит­ного регистра в заданных пределах. Пределы находятся в двух смежных словах памяти. Значение в 16-битном регистре должно быть больше или равно значению в первом слове и меньше или равно значению во втором слове; в противном случае генерируется прерывание типа 5. Пример команды BOUND имеет вид:

Эта команда удобна для проверки нахождения индекса массива в допустимых пределах; если этого нет, очевидно, в программе имеется ошибка.

Для понимания служебных функций, относящихся к процедурам, необходимо разобраться в обращениях к переменным на языке высокого уровня с блоковой структурой. Рассмотрим следующий фрагмент про­граммы на языке Паскаль (мы не предполагаем знание читателем языка Паскаль):

Процедура состоит из раздела объявлений и тела. Тело процедуры образуют те действия (исполняемые операторы), которые должна выпол­нять процедура. Операторы в теле процедуры могут обращаться к любым переменным, описанным в разделе объявлений.

Кроме переменных, в разделе объявлений можно указать и другие процедуры, например

Отметим, что к переменным и процедурам, находящимся в раздел объявлений процедуры А, могут обращаться операторы из процедур В и С, а также процедуры А. Однако к элементам, указанным в разделе объявле­ний процедуры В, могут обращаться только операторы из тела процедуры В; аналогичное ограничение касается и процедуры С.

Компилятору было бы просто генерировать код, который размещает все переменные в памяти при загрузке программы и никогда не изменяет местоположения ни одной переменной. Тогда к каждой переменной можно было бы обратиться в режиме прямой адресации. Однако в языке Паскаль процедура может вызывать саму себя. Такая возможность удобна (хотя и не столь важна) для вычисления, например, факториала (напомним, что х! = х*(х - 1)!). Каждый вызов такой процедуры должен иметь свою уникальную память для переменных, объявленных в процедуре. Следова­тельно, переменным нужно распределять память при каждом вызове процедуры и освобождать ее при каждом возврате. Удобным хранилищем для подобных переменных оказывается стек.

Для рекурсивных процедур компиляторы языка Паскаль обычно генерируют код, который при вызове процедуры выполняет следующие действия: распределяет пространство в стеке (называемое стековым кадром) для переменных, объявленных в процедуре, и устанавливает регистр ВР на начало (т.е. высший стековый адрес) стекового кадра. Так как фактическая ячейка памяти переменной будет другой при каждом вызове процедуры (стековый кадр не обязательно всякий раз начинается в одном и том же месте памяти), прямую адресацию памяти использовать нельзя. Константой остается только расстояние переменной от начала стекового кадра. Следовательно, генерируемый компилятором код для обращений к таким переменным должен быть представлен в терминах этого расстояния. В результате получается адресация, называемая адреса­цией относительно стекового кадра. Если, например, переменная а2 нахо­дится на расстоянии 8 байт от начала стекового кадра, а переменная a1 - на расстоянии 6 байт, компилятор в теле процедуры А может образо­вать следующие команды:

Теперь возникает проблема использования адресации относительно стекового кадра для доступа к переменным, объявленным в одной проце­дуре, из тела другой процедуры. Например, переменная а2 используется в процедуре В, но объявлена в процедуре А. Когда выполняется процедура В, последним распределенным стековым кадром является стековый кадр для процедуры В, а он не содержит переменной а2. Картина стекового кадра в этот момент времени показана на рис. 3.21,а. Каждая переменная, к которой обращается данная процедура, находится либо в стековом кадре данной процедуры, либо в стековом кадре той процедуры, в которую вложена данная. Следовательно, стековый кадр для данной процедуры должен содержать указатели на каждый из ранее созданных стековых кадров. Получающийся список указателей называетсяиндикатором. Стековый кадр с индикатором показан на рис. 3.21,6. Теперь компилятор может образовать код, который использует индикатор для локализации соответствующего стекового кадра, а затем прибавляет расстояние от начала стекового кадра для локализации переменной.

Когда происходит выход из данной процедуры, необходимо выполнить следующие действия: освободить стековый кадр и скорректировать ре­гистр ВР так, чтобы он указывал на стековый кадр, бывший текущим стековым кадром

во время вызова процедуры (динамическая связь). Отметим, что им не обязательно будет стековый кадр той процедуры, в которую вложена данная процедура (статическая связь). Например, процедура В вложена в процедуру А, но может вызываться из процедуры А или процедуры С. Освобождение стекового кадра реализуется передачей текущего значения регистра ВР в регистр SP (т.е. превращением регистра SP в указатель начала текущего стекового кадра). Коррекция содержимого регистра ВР невозможна, если не сохранить значение регистра ВР (динами­ческой связи) до вызова данной процедуры. Динамическую связь можно поместить в стековый кадр перед индикатором. Стековый кадр, содержащий динамическую связь для случая, когда процедура В вызвана из процедуры С, показан на рис. 3.21,в.

Таким образом, в начале каждой процедуры компилятор должен генерировать код для распределения стекового кадра, введения динами­ческой связи в стековый кадр и образования индикатора. Далее, перед каждой командой возврата компилятор должен генерировать код для освобождения стекового кадра и восстановления содержимого регистра ВР. Эти действия обычно требуют много команд. Но точно такие же задачи решают команды ENTER (войти в процедуру) и LEAVE (выйти из процеду­ры). Команда LEAVE не имеет операндов, а в команде ENTER указываются два операнда. Вторым операндом является уровень вложения процедуры (самый внешний уровень соответствует 1). Он сообщает процессору, сколь­ко элементов находится в индикаторе. Первый операнд показывает, сколько байт памяти требуется для переменных, объявленных в процеду­ре. Он сообщает процессору, сколько байт распределить для стекового кадра, исключая динамическую связь и индикатор. Например, команда:

создает стековый кадр, содержащий динамическую связь, индикатор из трех указателей стековых кадров (один из них показывает на создаваемый стековый кадр) и 48 байт для переменных, объявленных в процедуре.

Если второй операнд команды ENTER равен нулю, она не включает индикатор в стековый кадр. Это удобно для тех языков высокого уровня, которые не допускают вложенных процедур и не требуют индикатора. Примером такого языка служит язык Си.

Типичные действия команды ENTER показаны на рис. 3.22.

Соседние файлы в папке Лекции препода