Использование встроенных машин кодов
В Турбо Паскале имеется возможность непосредственного включения в программу небольших фрагментов, написанных в машинных кодах. Для этого используется стандартная директива INLINE, за которой в круглых скобках следует один или несколько элементов машинного кода, разделяемых косыми чертами. Элемент кода, в свою очередь, строится из одного или более элементов данных, разделенных знаками «+» или «-»
В качестве элемента данных может использоваться целая константа, идентификатор (переменной, константы или функции) или ссылка на счетчик адреса («*»). Каждый элемент данных вызывает генерацию 1 или 2 байт кода программы. Значение этого кода получается сложением или вычитанием элементов данных в соответствии с разделяющим их знаком. Значением идентификатора переменной, константы, функции служит адрес соответствующего объекта, значением ссылки на счетчик адреса является тот адрес, по которому будет размещаться следующий байт кода.
Элемент кода будет генерировать 1 байт кода, если этот элемент состоит только из целых констант и значение результата не превышает мощности одного байта, т.е. находится в диапазоне от 0 до 255. Если значение превышает 255 или элемент кода содержит ссылку на счетчик адреса, генерируются 2 байта. Знаки «<» и «>» могут использоваться -для отмены автоматического выбора размера генерируемого кода. Если элемент кода начинается со знака «<», в код заносится только 1 байт (с младшей значимостью), даже если само значение занимает 2 байта. Наоборот, если элемент начинается со знака «>», в код заносятся 2 байта (старший байт может оказаться нулевым).
Значением идентификатора является смещение соответствующего объекта. Если переменная - глобальная, смещение задается относительно сегмента данных, хранящееся в регистре DS, если это локальная переменная, - относительно сегмента стека регистр SP). Базовым сегментом типизированной константы является сегмент кода регистр CS).
В следующем примере приводятся две короткие процедуры, с помощью которых можно ввести или вывести данные через любой порт ПК.
Function InPort(Port: Word): Word;
var
pp: Word;
cc:Char;
egin
pp:=port;
inline (
$8b/$96/pp/ { mov DX,pp[bp] }
$EC/ { IN AX,DX }
$88/$86/cc); {mov cc[bp],AX}
InPort:=ord(cc);
end;
Procedure OutPort(Port,Bt: Word);
var
pp: Word;
cc:Char;
begin
pp:=port;
cc:=chr(Bt);
inline (
$8a/$86/cc/ { mov AX,cc[bp] }
$8b/$96/pp/ { mov DX,pp[bp] }
$EE) { OUT DX,AX }
end;
Операторы INLINE могут произвольным образом смешиваться с другими операторами Турбо Паскаля, однако при выходе из процедуры (функции) содержимое регистров ВР, SP, DS и SS должно быть таким же, как и при входе в нее.
С помощью директивы INLINE можно также задавать последовательность машинных кодов, которую необходимо несколько раз вставить в программу. Для этого используется описание INLINE-процедуры, например:
Procedure DisableInterrupts;
inline ($FA); {CLI}
INLINE-процедура имеет обычный для Турбо Паскаля заголовок, в то время как тело процедуры пишется целиком с помощью оператора INLINE. Всякий раз, когда в программе будет встречаться оператор вызова INLINE-процедуры, компилятор Турбо Паскаля будет вставлять на это место не код вызова процедуры, а нужные машинные коды. Например, вместо вызова процедуры в операторе
DisableInterrupt;
компилятор вставит команду запрета прерываний CLI. Таким образом, INLINE-процедуры служат своеобразным средством расширения возможностей стандартного компилятора Турбо Паскаля и подобны макросам ассемблера. Использование INLINE-процедур увеличивает скорость исполнения программы, так как не осуществляется генерация (и исполнение) команд передачи управления в процедуру. По этой причине в INLINE-процедурах не следует использовать команды выхода из подпрограммы. INLINЕ-процедура может иметь параметры, однако на них нельзя ссылаться в INLINE-директивах (на другие символы Турбо Паскаля ссьшаться можно). В следующем примере перемножаются два числа типа INTEGER, результат имеет тип LONGINT:
FUNCTION LongMul(X,YInteger) : Longint;
inline (
$5 А/ {POP AX; получить в АХ число Х }
$58/ { POP DX; получить в DX число Y }
$F7/$EA); { IMUL DX; DX:AX ;= X * Y }
Отметим, что в силу упоминавшегося сходства с макросами ассемблера, имена INLINE-подпрограмм не могут использоваться в качестве аргументов в операторах @ или служить параметрами функций ADDR, OFS и SEG.