ГЛАВА 18. ВОПРОСЫ КОНТРОЛЯ. Эта глава детально описывает различные способы, которыми Turbo Pascal реализует управление программой. Она включает соглашения о вызовах процедуры выхода, обработку прерываний и обработку ошибок. Соглашения о вызовах. Параметры передаются в процедуры и функции через стек. До вызова процедуры или функции параметры помещаются в стек в порядке их объявления. Перед возвратом процедура или функция удаляет все параметры из стека. Этот код для процедуры или функции будет выглядеть: Push Param1 Push Param2 . . . Push ParamX Call ProcOrFunc Параметры передаются по ссылке или по значению. Когда параметр передается по ссылке, в стек помещается указатель на действительное положение параметра. Когда параметр передается по значению, в стек помещается действительное значение. Изменяемые параметры. Изменяемый параметр (var параметр) всегда передается по ссылке - указатель указывает на действительное положение параметра (адрес). Неизменяемые параметры. Неизменяемый параметр передается по ссылке или по значению в зависимости от типа и размера параметра. Если значение параметра занимает 1, 2 или 4 байта, то значение помещается в стек. Иначе в стек помещается указатель на значение, и процедура или функция затем копирует это значение в локальную область памяти. Примечание: 8086 не поддерживает однобайтовые инструкции Push и Pop, поэтому однобайтовые параметры всегда помещаются в стек, как слова. Младший байт слова содержит значение, а старший байт не используется (не определен). Целый тип или параметр, передаваемый как байт, слово или двойное слово всегда использует такой же формат, как переменная целого типа. (Для двойного слова старшее слово помещается в стек до младшего слова так, что младшее слово имеет младший адрес). Параметр символьного типа передается как беззнаковый байт. Параметр логического типа передается как байт со значением 0 или 1. Параметр перечислимого типа передается как беззнаковый байт, если перечисление имеет 256 или меньше значений, иначе он передается как беззнаковое слово. Параметр типа Real передается как 6 байт в стек и является исключением из правил, что только 1, 2 или 4-x байтовые значения передаются прямо через стек. Параметр вещественного типа (Real, Single, Double, Extended и Comp) передается как 4, 6, 8 или 10 байт в стек и является исключением из правил, что только 1, 2 или 4 байтовые значения передаются прямо в стек. Примечание: Turbo Pascal версии 4.0 передавал параметры типа 8087 (Ringle, Double, Extended и Comp) во внутренний стек математического сопроцессора 8087. Для совместимости с другими языками и предотвращения переполнения стека 8087, эта версия использует стек 8086. Параметр типа Рointer передается как двойное слово (сегментная часть передается до части смещения так, что часть смещения занимает младшие адреса). Параметр типа String передается, как указатель на значение. Параметр типа множество передается, как указатель на "неупакованное" множество, которое занимает 32 байта. Массивы и записи длиной 1, 2 или 4 байта передаются прямо в стек. Другие массивы и записи передаются как указатель на значение. Результаты функции. Результаты функции порядкового типа (Integer, Char, Boolean и перечислимый) передаются в регистрах процессора: байты передаются в AL, слова передаются в AX и двойные слова передаются в DX:AX (старшее слово в DX, младшее слово в AX). Результат функции типа Real возвращается в регистрах DX:BX:AX. (Старшее слово в DX, среднее слово в BX, младшее слово в AX). Результаты функций типа 8087 (Single, Double, Extended и Comp) передаются в регистр вершины стека 8087 (ST(0)). Результат функции типа Pointer передается в DX:AX (сегментная часть в DX, часть смещения в AX). Для результатов функций типа String, вызывающая программа помещает указатель на временную область памяти до помещения любых параметров, и функция возвращает строковое значение в эту временную память. Функция не должна удалять указатель. Ближние и дальние вызовы (NEAR и FAR). Процессор 8086 поддерживает два типа инструкций вызова и возврата: NEAR и FAR. NEAR (ближняя) инструкция передает управление в другую точку внутри того же кодового сегмента, а FAR (дальняя) инструкция позволяет изменить кодовый сегмент. Инструкция NEAR CALL помещает 16-ти битовый адрес возврата (только смещение) в стек, а инструкция FAR CALL помещает 32-х битовый адрес возврата (и сегмент и смещение). Соответствующие инструкции RET выталкивают из стека только смещение или и смещение и сегмент. Turbo Pascal на основе объявления процедуры будет автоматически выбирать правильную модель вызова. Процедуры, объявленные в секции interfaсe модуля, будут FAR, так как они вызываются из других модулей. Процедуры, объявленные в программе или в секции implementation модуля, будут NEAR, так как они могут быть вызваны только внутри программы или модуля. Для некоторых специальных целей может требоваться процедура типа FAR. Например, в программах с оверлеями все процедуры и функции должны быть FAR; кроме того, если процедура или функция присваивается процедурной переменной, она также должна быть FAR. Директива компилятора $F используется для отмены автоматического выбора модели вызова компилятором. Процедуры и функции откомпилированные в состоянии {$F+} всегда FAR; в состоянии {$F-} - Turbo Pascal автоматически выбирает правильную модель. Состояние по умолчанию {$F-}. Вложенные процедуры и функции. Процедура или функция называется вложенной, когда она объявлена внутри другой процедуры или функции. По умолчанию вложенные процедуры и функции всегда используют модель вызова NEAR, поскольку они "видимы" только внутри определенной процедуры или функции, находящейся в том же кодовом сегменте. Однако в оверлейных программах директива {$F+} используется для установления всех процедур и функций в FAR, включая и вложенные. Когда вызывается вложенная процедура или функция, компилятор генерирует инструкцию PUSH BP перед CALL, в результате передавая BP вызывающей процедуре, как дополнительный параметр. Поскольку вызванная процедура устанавливает свой собственный BP, к BP вызывающей процедуры можно обратиться как к слову, хранящемуся в [BP + 4] или [BP + 6], если процедура FAR. Используя связь через [BP + 4] или [BP + 6], вызываемая процедура может обращаться к локальным переменным из стека вызывающей. Если вызывающая процедура в свою очередь является вложенной, она также имеет связь через [BP + 4] или [BP + 6]. Следующий пример демонстрирует, как обращаться к локальным переменным из оператора inline во вложенной процедуре: procedure A; near; var IntA: Integer; procedure B; far; var IntB: Integer; procedure C; near; var IntC: Integer; begin inline( $8B/$46/