- •Введение
- •1.1. Основные направления эволюции микрокомпьютеров
- •1.2. Основные сведения о компьютерах
- •1.3. Представление чисел
- •Заключение
- •Лекция 2 машинная организация процессора 80286
- •2.1. Введение
- •2.2. Структура памяти
- •2.3. Сегментация памяти
- •2.4. Структура ввода-вывода
- •2.5. Регистры
- •2.6. Операнды и режимы адресации операндов
- •2.7. Замечания о режимах адресации
- •Глава 3 базовая система команд процессора 80286
- •3.1. Нотация языка ассемблера
- •3.2. Команды передач данных
- •3.3. Арифметические команды
- •Лекция 6. Цепочечные команды
- •Лекция 7. Команды безусловной передачи управления
- •Лекция 8. Команды условной передачи управления
- •Лекция 9. Прерывания
- •Int n
- •Лекция 10. Флажковые команды
- •3.10. Команды синхронизации
- •3.11. Поддержка языков высокого уровня
- •3.12. Замечания о префиксах
- •3.13. Воздействие на флажки
- •Заключение
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.

