Встретив такой оператор, компилятор прекращает на время генерировать код и вставляет Код1, Код2 и другие из оператора inline без каких-либо особых преобразований, а затем снова начинает преобразовывать предложения Турбо Паскаля в выполнимый код. Это более гибкий способ, чем компоновка объектного файла директивой {$L ИмяФайла}, поскольку позволяет вставлять в программу «чисто ассемблерные» отдельные операторы, в то время как {$L...} позволяет лишь использовать внешние процедуры и функции. Программирование в чистых машинных кодах, как это требует формат inline, — занятие не для начинающих. Не имея большого опыта работы с ассемблерами для процессоров семейства 8086/286, лучше избегать применения оператора inline, тем более, что необходимость в нем возникает не так уж часто. В книге Дж.Дунтемана [5], посвященной подобным вопросам, приводится такой список задач, требующих прямого использования машинных кодов:
1. Процедуры обработки прерываний.
2. Резидентные программы.
3. Организация многозадачных режимов.
Дж. Дунтеман характеризует эти задачи как задачи «не для малодушных», особенно третью. Код в операторе inline — это либо константа, либо имя переменной. Константа должна быть целым положительным числом типа Byte или Word и может быть записана как в десятичном, так и в шестнадцатеричном формате. Пример: оператор
inline( $CD / 05 ); { Мнемоника -> INT 05H }
это то же самое, что вызов прерывания 05 (печать с экрана). Если значение константы попадает в диапазон 0...255, то она хранится
- 306 -
как однобайтовый код. Если же оно превосходит 255, то хранится как слово (два байта). В последнем случае код хранится по машинному правилу: младший байт (Lo( Word )) предшествует старшему (Hi( Word )). Можно явно заказывать формат хранения кода, используя перед константой знаки «<» и «>». Если константа записана с предшествующим знаком «<», то от нее будет взят только младший байт. Но если ей предшествует знак «>», то она будет записана в двух байтах, например:
inline( >1 ) даст два байта кода: $01 и $00.
Размер хранения кодов важен для организации переходов внутри оператора inline.
Если вместо константы стоит имя переменной, то оно трактуется как слово (два байта) — смещение в сегменте хранения этой переменной. Таким образом, переменная в inline задает адрес в сегменте DS (DSeg) или SS (SSeg) первого байта своего значения. Если же нужен не первый байт значения, а, например, третий, то можно указать это, дописав к переменной константу — смещение от ее первого байта, например:
inline(.../ XVar+2 /...);
Адрес первого байта XVar может быть записан как XVar+0. Смещение от начала переменной может быть и отрицательным, достаточно заменить знак «+» на «-».
Внутри кодов inline могут быть доступны значения глобальных переменных и типизированных констант (они хранятся в сегменте DS), а также, если inline стоит внутри тела процедуры или функции, становятся доступными локальные переменные этих процедур и их переменные параметры. Подробное изложение техники обращения к ним, соглашений о размещении и размерах локальных переменных, способах передачи значений в процедуры, функции и обратно заняло бы слишком много места (не считая таблицы машинных кодов для 8088/86/286). Подробно вопросы интерфейса с ассемблерными программами и работа с inline рассматривается в гл. 15 справочного руководства по Турбо Паскалю 5.0 [2] и в уже упоминавшейся книге [5] (с оговорками, ибо она написана для версии 3.0). Здесь же мы дадим один последний совет начинающим любителям машинных кодов: во избежание фатальных последствий запрещено модифицировать кодами оператора inline регистры процессора ВР, SP, SS и DS. Остальные — можно.
- 307 -
14.7.2. Процедуры с директивой inline
Вставки машинных кодов в программу могут производиться и другим способом: посредством описания процедур или функций директивой inline, содержащей машинные коды, как и оператор inline, рассмотренный в предыдущем разделе. Такие процедуры, собственно, уже не столько процедуры, сколько ассемблерные макросы. Механизм их работы существенно изменяется.
Когда вызывается обычная процедура или функция, не имеющая директивы inline, происходят предварительные действия по размещению локальных переменных в стеке, затем выполняется тело процедуры, после чего освобождается стек и управление передается вызывающей части программы. Но при вызове inline-процедуры или функции просто будут выполнены коды, указанные в директиве inline, без каких-либо предварительных или последующих манипуляций. Пример объявления такой процедуры:
PROCEDURE PrintScreen; inline( $CD / 05 );
Вызов такой процедуры PrintScreen эквивалентен выполнению ассемблерной команды INT 05.
Inline-процедуры и функции не могут иметь тела. Все их содержимое описывается кодами в директиве inline. Но они могут иметь параметры, которые, впрочем, нельзя указывать по имени совместно с кодами (это не относится к глобальным идентификаторам).