До сих пор мы рассматривали конструкцию asm...end, как опе-
ратор с обычной частью begin...end. Директива assembler в Borland
Pascal позволяет вам писать на встроенном ассемблере целиком про-
цедуры и функции без необходимости begin...end. Приведем пример
функции на ассемблере:
function LongMul(X, Y: Integer) : Longint; assembler;
asm
mov ax,X
imul Y
end;
Директива assembler приводит к тому, что Borland Pascal вы-
полняет при генерации кода следующую оптимизацию:
- Компилятор не генерирует код для копирования парамет-
ров-значений в локальные переменные. Это влияет на все па-
раметры-значения строкового типа и другие значения-пара-
метры, размер которых не равен 1, 2 или 4 байтам. Внутри
процедуры или функции такие параметры должны интерпретиро-
ваться, как если бы они были параметрами-переменными.
- Компилятор не выделяет память для результата функции, и
ссылка на идентификатор @Result будет ошибкой. Однако
строковые функции являются исключением из этого правила -
они всегда имеют указатель @Result, который распределяется
пользователем.
- Для процедур и функций, не имеющих параметров и локальных
переменных, компилятор не генерирует кадров стека.
- Для процедуры и функции на ассемблере автоматически гене-
рируется код выхода:
push bp ; присутствует, если Locals <> 0 или
; Params <> 0
mov bp,sp ; присутствует, если Locals <> 0 или
; Params <> 0
sub sp,Locals ; присутствует, если Locals <> 0
...
mov sp,bp ; присутствует, если Locals <> 0
pop bp ; присутствует, если Locals <> 0 или
; Params <> 0
ret Params ; всегда присутствует
где Locals - размер локальных переменных, а Params - раз-
мер параметров. Если и Locals и Params = 0, то кода входа
не будет, и код выхода состоит просто из инструкции RET.
Функции, использующие директиву assembler, должны возвращать
результат следующим образом:
- результаты функции порядкового типа (Integer, Char,
Boolean, перечислимые типы) возвращаются в AL (8-разрядное
значение), AX (16-разрядное значение) или DX:AX (32-раз-
рядное значение);
- результаты функции вещественного типа (Real) возвращаются
в DX:BX:AX;
- результаты функции типов 8087 (Single, Double, Extended,
Comp) возвращаются в ST(0) (регистр стека сопроцессора
8087);
- результаты функции типа указатель возвращаются в DX:AX;
- результаты функции строкового типа возвращаются во времен-
ной ячейке, на которую указывает @Result.
Директива assembler во многом похожа на директиву external.
Процедуры и функции на ассемблере должны должны подчиняться тем
же правилам, что и процедуры и функции типа external. Следующие
примеры показывают некоторые отличия операторов asm в обычных
процедурах и функциях от процедур и функций ассемблера. В первом
примере оператор asm используется в обычной функции для преобра-
зования строки в верхний регистр. Заметим, что значение параметра
Str в этом случае ссылается на локальную переменную, поскольку
компилятор автоматически генерирует код входа, копирующий факти-
ческий параметр в локальную память.
function UpperCase(Str: String): String;
begin
asm
cld
lea si,Str
les di,@Result
SEGSS lodsb
stosb
xor ah,ah
xchg ax,cx
jcxz @3
@1:
SEGSS lodsb
cmp al,'a'
ja @2
cmp al,'a'
ja @2
cmp al,'z'
jb @2
sub al,20H
@2:
stosb
loop @1
@3:
end;
end;
Второй пример на ассемблере представляет собой версию функ-
ции UpperCase. В этом случае Str не копируется в локальную па-
мять, и функция должна интерпретировать Str, как параметр-пере-
менную.
function UpperCase(S: String): String; assembler;
asm
push ds
cld
lds si,Str
les di@Result
lodsb
stosb
xor ah,ah
xchg ax,cx
jcxz @3
@1:
lodsb
cmp al,'a'
ja @2
cmp al,'z'
jb @2
sub al,20H
@2:
stosb
loop @1
@3:
pop ds
end;