Использование ассемблера в Дельфи


         

Где размещать ассемблерный код


Ассемблерные инструкции размещаются внутри блока asm...end. Эти блоки могут появляться внутри процедур и функций обычного кода, но я настоятельно не рекомендую поступать таким образом. Гораздо лучше изолировать их в отдельной функции или процедуре. Вставка asm блока внутри обычной процедуры создает сложности для компилятора Паскаля и код становится не эффективным с точки зрения производительности. Переменные, которые обычно передаются через регистры, в этом случае будут передаваться через стек или перезагрузку. Также, это заставляет компилятор адаптировать собственный код к вашему вставленному коду, что делает механизм оптимизации менее эффективным. Так, что это становится правилом помещать ассемблерный код в отдельную процедуру или функцию. Кроме того - это вопрос проектирования. Читабельность и управляемость вашего кода становится выше, если он помещен в свой собственный блок.

Часто, ассемблерный код ассоциируется со скоростью. Поэтому циклы вы также должны по возможности организовывать внутри ассемблерного кода. Это не сложно, а иначе вы просто потеряете множество времени за счет постоянного вызова. Вместо того, чтобы делать так (см. примечание 1):

function CriticalCode(...): ...; register;

asm

...

{Here comes your assembler code}

...

end;

procedure Example;

var

I: Integer;

begin

I:=0;



...

while I < NumberOfTimes do begin

CriticalCode(...);

Inc(I);

end;

...

end;

Вы должны сделать так:

function CriticalCode(...): ...; register;

Asm

...

mov ECX,{NumberOfTimes}

@@loop:

...

{Остальной код}

...

dec ECX

jnz @@loop

...

end;

procedure Example;

begin

...

CriticalCode(...);

...

end;

Использование цикла в обратном направлении позволяет просто проверять флаг установки нуля после команды dec. Если же цикл начинать с нуля, то потребуется больше на одну команду сравнения с конечным значением, каждый раз при проходе цикла.

mov ECX,0

@@loop:

...

inc ECX

cmp ECX,{NumberOfTimes}

jne @@loop

Другая возможность – это вычесть значение NumberOfTimes из 0 и затем увеличивать переменную цикла, пока она не станет равной нулю. Этот метод обычно используется, когда переменная цикла также является индексом в таблице или массиве в памяти, поскольку механизм кэширования работает лучше, чем при доступе в прямом направлении. Это можно сделать так:

xor ECX,ECX

sub ECX,{NumberOfTimes}

@@loop:

...

inc ECX

jnz @@loop

Помните, что в этом случае базовый регистр или адрес, должен указывать на конец массива, вместо его начала.

 (1) В данных главах мы специально указываем соглашение о вызове. На самом деле указание register избыточно, так как соглашением по умолчанию является передача параметров через регистры, это сделано исключительно для читабельности (или как дополнительный комментарий) и как напоминание читателю, что параметры передаются через регистры. Этот совет поступил от Christen Fihl, http://HSPascal.Fihl.net



Содержание раздела