- •Глава 1
 - •1.2. Процедурные языки
 - •1.3. Языки, ориентированные на данные
 - •1.4. Объектно-ориентированные языки
 - •1.5. Непроцедурные языки
 - •1.6. Стандартизация
 - •1.7. Архитектура компьютера
 - •1.8. Вычислимость
 - •1.9. Упражнения
 - •Глава 2
 - •2.2. Семантика
 - •2.3. Данные
 - •2.4. Оператор присваивания
 - •2.5. Контроль соответствия типов
 - •2.7. Подпрограммы
 - •2.8. Модули
 - •2.9. Упражнения
 - •Глава 3
 - •3.1. Редактор
 - •3.2. Компилятор
 - •3.3. Библиотекарь
 - •3.4. Компоновщик
 - •3.5. Загрузчик
 - •3.6. Отладчик
 - •3.7. Профилировщик
 - •3.8. Средства тестирования
 - •3.9. Средства конфигурирования
 - •3.10. Интерпретаторы
 - •3.11. Упражнения
 - •Глава 4
 - •4.1. Целочисленные типы
 - •I: Integer; -- Целое со знаком в языке Ada
 - •4.2. Типы перечисления
 - •4.3. Символьный тип
 - •4.4. Булев тип
 - •4.5. Подтипы
 - •4.6. Производные типы
 - •4.7. Выражения
 - •4.8. Операторы присваивания
 - •4.9. Упражнения
 - •Глава 5
 - •5.1. Записи
 - •5.2. Массивы
 - •5.3. Массивы и контроль соответствия типов
 - •Подтипы массивов в языке Ada
 - •5.5. Строковый тип
 - •5.6. Многомерные массивы
 - •5.7. Реализация массивов
 - •5.8. Спецификация представления
 - •5.9. Упражнения
 - •Глава 6
 - •6.1. Операторы switch и case
 - •6.2. Условные операторы
 - •6.3. Операторы цикла
 - •6.4. Цикл for
 - •6.5. «Часовые»
 - •6.6. Инварианты
 - •6.7. Операторы goto
 - •6.8. Упражнения
 - •Глава 7
 - •7.1. Подпрограммы: процедуры и функции
 - •7.2. Параметры
 - •7.3. Передача параметров подпрограмме
 - •7.4. Блочная структура
 - •7.5. Рекурсия
 - •7.6. Стековая архитектура
 - •7.7. Еще о стековой архитектуре
 - •7.8. Реализация на процессоре Intel 8086
 - •7.9. Упражнения
 - •Глава 8
 - •8.1 . Указательные типы
 - •8.2. Структуры данных
 - •8.3. Распределение памяти
 - •8.4. Алгоритмы распределения динамической памяти
 - •8.5. Упражнения
 - •Глава 9
 - •9.1. Представление вещественных чисел
 - •9.2. Языковая поддержка вещественных чисел
 - •9.3. Три смертных греха
 - •Вещественные типы в языке Ada
 - •9.5. Упражнения
 - •Глава 10
 - •10.1. Преобразование типов
 - •10.2. Перегрузка
 - •10.3. Родовые (настраиваемые) сегменты
 - •10.4. Вариантные записи
 - •10.5. Динамическая диспетчеризация
 - •10.6. Упражнения
 - •Глава 11
 - •11.1. Требования обработки исключительных ситуаций
 - •11.2. Исключения в pl/I
 - •11.3. Исключения в Ada
 - •11.5. Обработка ошибок в языке Eiffei
 - •11.6. Упражнения
 - •Глава 12
 - •12.1. Что такое параллелизм?
 - •12.2. Общая память
 - •12.3. Проблема взаимных исключений
 - •12.4. Мониторы и защищенные переменные
 - •12.5. Передача сообщений
 - •12.6. Язык параллельного программирования оссаm
 - •12.7. Рандеву в языке Ada
 - •12.9. Упражнения
 - •Глава 13
 - •13.1. Раздельная компиляция
 - •13.2. Почему необходимы модули?
 - •13.3. Пакеты в языке Ada
 - •13.4. Абстрактные типы данных в языке Ada
 - •13.6. Упражнения
 - •Глава 14
 - •14.1. Объектно-ориентированное проектирование
 - •В каждом объекте должно скрываться одно важное проектное решение.
 - •14.3. Наследование
 - •14.5. Объектно-ориентированное программирование на языке Ada 95
 - •Динамический полиморфизм в языке Ada 95 имеет место, когда фактический параметр относится к cw-типу, а формальный параметр относится к конкретному типу.
 - •14.6. Упражнения
 - •Глава 15
 - •1. Структурированные классы.
 - •15.1. Структурированные классы
 - •5.2. Доступ к приватным компонентам
 - •15.3. Данные класса
 - •15.4. Язык программирования Eiffel
 - •Если свойство унаследовано от класса предка более чем одним путем, оно используется совместно; в противном случае свойства реплицируются.
 - •15.5. Проектные соображения
 - •15.6. Методы динамического полиморфизма
 - •15.7. Упражнения
 - •5Непроцедурные
 - •Глава 16
 - •16.1. Почему именно функциональное программирование?
 - •16.2. Функции
 - •16.3. Составные типы
 - •16.4. Функции более высокого порядка
 - •16.5. Ленивые и жадные вычисления
 - •16.6. Исключения
 - •16.7. Среда
 - •16.8. Упражнения
 - •Глава 17
 - •17.2. Унификация
 - •17.4. Более сложные понятия логического программирования
 - •17.5. Упражнения
 - •Глава 18
 - •18.1. Модель Java
 - •18.2. Язык Java
 - •18.3. Семантика ссылки
 - •18.4. Полиморфные структуры данных
 - •18.5. Инкапсуляция
 - •18.6. Параллелизм
 - •18.7. Библиотеки Java
 - •8.8. Упражнения
 
7.8. Реализация на процессоре Intel 8086
Чтобы дать более конкретное представление о реализации идей стековой архитектуры, рассмотрим вход в процедуру и выход из нее на уровне машинных команд для процессора серии Intel 8086. В качестве примера возьмем:
procedure Main is
Global: Integer;
procedure Proc(Parm: in Integer) is
Local'1, Local2: Integer;
begin
Ada  | 
	
end Proc;
begin
Proc(15);
end Main;
Процессор 8086 имеет встроенные команды push и pop, в которых подразумевается, что стек растет от старших адресов к младшим. Для стековых операций выделены два регистра: регистр sp, который указывает на «верхний» элемент в стеке, и регистр bр, который является указателем дна и идентифицирует местоположение начала записи активации.
При вызове процедуры в стек помещается параметр и выполняется команда вызова (call):
mov ax, #15 Загрузить значение параметра
push ax Сохранить параметр в стеке
call Proc Вызвать процедуру
На рисунке 7.11 показан стек после выполнения этих команд — параметр и адрес возврата помещены в стек.
Следующие команды являются частью кода процедуры и выполняются при входе в процедуру; они сохраняют старый указатель дна (динамическая связь), устанавливают новый указатель дна и выделяют память для локальных переменных, уменьшая указатель стека:
push bp Сохранить старый динамический указатель
mov bp, sp Установить новый динамический указатель
sub sp,#4 Выделить место для локальных переменных
Получившийся в результате стек показан на рис. 7.12.
Теперь можно выполнить тело процедуры:
mov ax,ds:[38] Загрузить переменную Global
add ax,[bp+06] Прибавить параметр Parm
add ax,[bp-02] Прибавить переменную Local 1
mov ax,[bp] Сохранить в переменной Local2
Обращение к глобальным переменным делается через смещения относительно специальной области памяти, на которою указывает регистр ds (сегмент данных). К параметру Parm, который располагается в стеке «ниже» начала записи активации, обращаются при положительном смещении относительно bp. К локальным переменным, которые в стеке располагаются «выше», обращаются при отрицательном смещении относительно bp. Важно обратить внимание, что поскольку процессор 8086 имеет регистры и способы адресации, разработанные для обычных вычислений с использованием стека, то ко всем этим переменным можно обращаться одной командой.
При выходе из процедуры должны быть ликвидированы все изменения, сделанные при входе в процедуру:
mov sp,bp Очистить все локальные переменные
pop bp Восстановить старый динамический указатель
ret 2 Вернуться и освободить память параметров
Указатель вершины стека принимает значение указателя дна и таким образом действительно освобождает память, выделенную для локальных переменных. Затем старый динамический указатель выталкивается (pop) из стека, и bр теперь указывает на предыдущую запись активации. Остается только выйти из процедуры, используя адрес возврата, и освободить память, выделенную для параметров. Команда ret выполняет обе эти задачи; операнд команды указывает, сколько байтов памяти, выделенных для параметра, необходимо вытолкнуть из стека.
Подведем итог: как для входа, так и для выхода из процедуры требуется только по три коротких команды, и доступ к локальным и глобальным переменным и к параметрам является эффективным.
