С помощью директивы компилятора $L можно выполнить компоновку программ или модулей на языке Паскаль и процедур и  функций на  языке ассемблера.  Из исходного файла на языке ассемблера можно с  помощью ассемблера получить объектный файл (с  расширением .OBJ).  Используя компоновщик, несколько объектных файлов можно скомпоновать с программой или модулем.  При этом используется  директива  компилятора $L.

В программе  или модуле на языке Паскаль процедуры или функции, написанные  на языке  ассемблера,  должны быть описаны как  внешние. Например:

function LoCase(Ch : Char): Char; external;

В соответствующем  файле на  языке ассемблера все процедуры  или функции должны находиться в сегменте с именем CОDЕ  или CSEG,  или в  сегменте,  имя которого заканчивается на _TEXT, а имена  внешних процедур и  функций должны  быть  указаны в  директивах  PUВLIC.

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

В исходном  файле на языке ассемблера могут описываться инициализированные переменные,  содержащиеся  в  сегменте  с именем  CONST или в сегменте,  оканчивающемся на _DAТA, и неинициализированные переменные в сегменте с именем DATA или DSEG,  или в  сегменте,  имя  которого  оканчивается на _BSS.  В исходном файле на  языке ассемблера эти переменные являются частными, и на них нельзя ссылаться из модуля или программы на Паскале. Они, однако, находятся в том же сегменте, что и глобальные переменные Паскаля, и  доступны через регистр сегмента DS.

На все процедуры,  функции и переменные,  описанные в модуле  или программе на Паскале и на те из них, которые описаны в интерфейсной секции используемых модулей, можно ссылаться из исходного  файла на языке ассемблера с помощью  директивы  EXTRN. При  этом  обязанность  обеспечить  корректный тип в определении EXTRN также  возлагается на вас.

Когда объектный  файл указывается  в директиве $L,  Borland  Pascal преобразует файл из формата перемещаемых объектных модулей  (.OBJ) фирмы Intel в свой собственный внутренний формат перемещаемых модулей. Это преобразование возможно лишь при соблюдении некоторых правил:

1.  Все процедуры и функции должны быть помещены в сегмент с  именем CODЕ или CSEG,  или в сегмент, имя которого оканчивается на _TEXT.  Все инициализированные частные переменные  должны помещаться в сегмент с именем Const или в  сегмент,  имя которого оканчивается на _DATA. Все неинициализированные частные переменные должны быть помещены  в сегмент, имя которого оканчивается на _DAТA. Неинициализированные локальные переменные  должны  помещаться в  сегмент с именем DATA или DSEG, или в сегмент, имя которого оканчивается на _BSS. Все другие сегменты игнорируются,  поэтому имеется директива GRОUР.  В определениях  сегмента может задаваться выравнивание на границу  слова  или байта (WORD или ВYTE). При компоновке они всегда выравниваются на границу слова.  В определениях  сегментов  могут указываться директивы PUВLIС и имя класса (они игнорируются).

2.  Borland Pascal игнорирует все данные для  сегментов, отличных  от сегмента  кода (CODE,  CSEG или xxxx_TEXT) и  инициализированного   сегмента  данных (CONST или  xxxx_DATA).  Поэтому при описании переменных в сегменте  неинициализированных данных (DAТA,  DSEG  или  xxxx_BSS)  для определения значения всегда используйте вопросительный знак (?). Например:

   Count   DW ?

  Buffer  DB  128 DUP(?)

3. Байтовые  ссылки на идентификаторы типа EXTRN недопустимы.  Это означает, например,  что операторы НIGНТ и LОW  нельзя использовать с идентификаторами типа EXTRN.

Турбо Ассемблер  (TASM) значительно  облегчает   разработку  программ  на  языке  ассемблера и организации в них интерфейса с  программами Borland Pascal. Турбо Ассемблер поддерживает специфическое использование сегментов, схему памяти и языковую поддержку  для программистов, работающих на Borland Pascal.

Используя ключевое  слово PASCAL и директиву .MODEL,  можно  обеспечить соблюдение соглашений о вызовах с Borland Pascal,  определить имена сегментов,  выполнить инструкции  PUSH BP  и  MOV  PB,SP, а также обеспечить возврат управления с помощью операторов  POP BP и RET N (где N - это число байт параметра). Директива  .MODEL имеет следующий синтаксис:

.MODEL xxxx, PASCAL

  где xxxx - это модель памяти (обычно LARGE).

Задание в  директиве .MODEL  языка  PASCAL сообщает  Турбо  Ассемблеру, что параметры были занесены в стек слева-направо -  в  том порядке, в котором они обнаружены в исходном операторе, вызывающем процедуру.

Директива PROC позволяет вам задать параметры в том  же  порядке, как они определены в программе Borland Pascal. Если вы определяете функцию,  которая возвращает строку,  обратите внимание  на то,  что директива PROC имеет опцию RETURNS,  позволяющую вам  получить доступ к временному указателю строки в стеке и не оказывающую  влияния на число байт параметра, добавляемых в операторе  RET.

Приведем примеры  кода, в  которых  используются  директивы  .MODEL и PROC:

.MODEL LARGE, PASCAL

.CODE

MyProc PROC  FAR 1:BYTE, j : BYTE RETURNS result : DWORD

  PUBLIC MyProc

  les di,result   ; получить адрес временной строки

  mov al,i  ; получить первый параметр i

  mov bl,j  ; получить второй параметр j

.

.

.

  ret

Определение функции в Borland Pascal будет выглядеть следующим образом:

function MyProc(i,j : char) : string; external;

Следующая программа  является примером модуля и представляет  собой две программы на ассемблере,  предназначенные для обработки  строк.  Функция  UppеrCаsе преобразует символы строки в прописные  буквы,  а функция StringOf возвращает строку  символов  заданной  длины.

unit Strings;

interface

function UpperCase(S: string): string;

function StringOf(Ch: char; Count: byte): string;

inplementation

{$L STRS}

function UpperCase; external;

function StringOf; external;

end.

Далее приведен файл на языке ассемблера,  в котором реализованы программы StringOf и  UppеrCаsе. Перед  компиляцией  модуля  Strings этот файл должен  быть ассемблирован  в  файл с  именем  STRS.OBJ. Обратите внимание на то,  что в программах используется  дальний тип вызова,  так как они описаны  в интерфейсной  секции  блока.

 CODE SEGMENT BYTE PUBLIC

ASSUME CS:CODE

PUBLIC UpperCase, StringOf ; объявить имена 

  function Uppercase(S: String): String

  UpperRes  EQU DWORD PTR [BP+10]

  UpperStr  EQU DWORD PTR [BP+6]

  Uppercase PROC FAR

 PUSH BP   ; сохранить регистр BP

 MOV  BP,SP  ; установить стек

  PUSH DS   ; сохранить регистр DS

  LDS  SI,UpperStr   ; загрузить адрес строки

  LES  DI,UpperRes   ; загрузить адрес результата

  CLD ; переместить строку

  LODSB  ; загрузить длину строки

  STOSB  ; скопировать результат

  MOV CL,AL ; поместить длину строки в СХ

  XOR CH,CH

  JCXZ U3  ; пропустить в случае пустой

   ; строки

  U1:   LODSB  ; пропустить, если символ отличен

   ; от 'а'...'z'

  CPM AL,'a'

  JB  U2

 CPM AL,'z'

 JA  U2 ; переместить строку

  SUB AL,'a'-'A'  ; преобразовать в прописные буквы

  U2:   STOBS  ; сохранить результат

 LOOP U1   ; цикл по всем символам

  U3:   POP  DS   ; восстановить регистр DS

  POP  BP   ; восстановить регистр ВР

     RET  4 ; удалить параметры и возвратить

   ;управление 

 UpperCase   ENDP

  ; function StringOf(Ch: Char; Count: Byte): String

  StrOfRes  EQU DWORD PTR [BP + 10]

  StrOfChar EQU BYTE  PTR [BP + 8]

  StrOfCOunt   EQU BYTE  PTR [BP + 6]

   StringOf PROC FAR

  PUSH BP   ; сохранить регистр ВР

  MOV  BP,SP   ; установить границы стека

  LES  DI,StrOfRes   ; загрузить адрес результата

  MOV  AL,StrOfCount ; загрузить счетчик

  CLD ; продвинуться на строку

  STOSB  ; сохранить длину

  MOV  CL,AL   ; поместить значение счетчика в CX

  XOR  CH,CH

 MOV  AL,StrOfChar  ; загрузить символ 

 REP  STOSB   ; сохранить строку символов

  POP; восстановить ВР

  RET ; извлечь параметры и выйти

  SrtingOf ENDP

 CODE ENDS

END

Чтобы ассемблировать  этот пример  и скомпилировать модуль,  можно использовать следующие команды:

TASM STR5

BPC stringer

Методы, реализованные на языке ассемблера,  можно скомпоновать с программами Borland Pascal с помощью директивы компилятора  $L и зарезервированного ключевого слова external.  Описание внешнего метода  в  объектном  типе не отличается от обычного метода;  однако в реализации метода перечисляется только заголовок метода,  за которым  следует зарезервированной слово external.  В исходном  тексте на ассемблере вместо точки (.) для записи уточненных идентификаторов следует использовать  операцию @ (точка в ассемблере  уже имеет другой смысл и не может  быть частью  идентификатора).  Например, идентификатор Паскаля Rect.Init записывается на ассемблере как Rest@Init.  Синтаксис @ можно использовать как в идентификаторах PUBLIC, так и EXTRN.

Для небольших подпрограмм на языке ассемблера  очень  удобно  использовать внутренние  директивы и  операторы  Borland Pascal  (операторы inline).  Они позволяют вставлять инструкции машинного  кода непосредственно в программу или текст  блока,  вместо  того,  чтобы использовать объектный файл.

Оператор inline состоит из зарезервированного слова  Inline,  за которым следует одна или более встроенных записей (записей машинного кода),  разделенных косой чертой и заключенных в  круглые  скобки:

inline(10/$2345/Count+1/Data-Offset);

    Оператор inline имеет следующий синтаксис:

  ЪДДДДДДДДї  ЪДДДї ЪДДДДДДДДДДї ЪДДДї

   подставляемый ДД>і inline ГД>і ( ГДДДД>і запись в ГДВД>і ) ГД>

   оператор   АДДДДДДДДЩ  АДДДЩ ^   і машинном і і  АДДДЩ

 і   і   коде   і і

 і   АДДДДДДДДДДЩ і

 і   ЪДДДї  і

 АДДДДДДґ / і<ДДДДЩ

   АДДДЩ

Каждый оператор inline состоит из необязательного спецификатора размера,  < или >, и константы или идентификатора переменой,  за которой  следуют  ноль или более спецификаторов смещения (см.  описанный далее синтаксис).  Спецификатор смещения состоит  из  +  или -, за которым следует константа.

ЪДДДДДДДДДДДї

   запись во ДДВДДДДДДДДДДДДДДДДДДДД>і константа ГДДДДДДДДДДДДДДД>

   встроенном  і   ЪДДДї   ^  АДДДДДДДДДДДЩ ^

   машинном ГДД>і < ГДДДДДДґ  і

   коде  і   АДДДЩ   і  і

   і   ЪДДДї  і  і

   ГДД>і > ГДДДДДДЩ  і

   і   АДДДЩ   і

   і  ЪДДДДДДДДДДДДДДДї і

   АД>і идентификатор ГДВДДДДДДДДДДДДДДДДДДДДЩ

   і  переменной  і і   ^

   АДДДДДДДДДДДДДДДЩ і   і

ЪДДДДЩ  АДДДДДДДДДї

    і  ЪДДДДї   ЪДДДДДДДДДї і

АДДДДД>ізнакГДД>іконстантаіДДВДДДДЩ

^   АДДДДЩ  АДДДДДДДДДЩ  і

    АДДДДДДДДДДДДДДДДДДДДДДДДДЩ

Каждая запись  inline порождает 1 байт или одно слово кода.  Значения вычисляется,  исходя из значения  первой  константы  или  смещения идентификатора переменной, к которому добавляется или из  которого вычитается значение каждой из последующих констант.

Если запись  в машинном  коде состоит только из констант и,  если ее значение лежит в 8-битовом диапазоне (0..255), то она порождает один байт кода.  Если значение выходит за границу 8-битового диапазона или если запись inline ссылается на переменную, то  генерируется одно слово кода (младший байт следует первым).

Операции < и > могут использоваться для отмены  автоматического выбора  размера,  который был описан ранее.  Если оператор  inline начинается с операции <,  то в код включается только младший значащий  байт значения,  даже если это 16-битовое значение.  Если оператор inline начинается с операции >, то в код включается  всегда слово,  даже если старший значащий байт равен 0. Например,  оператор:

inline(<$1234/>$44);

  гененирует код длиной три байта: $34,$44,$00.

Значение идентификатора переменной в записи inline представляет собой адрес смещения переменной внутри ее базового сегмента.  Базовый сегмент глобальных переменных (переменных,  описанных  на  самом внешнем уровне в модуле или программе) и типизованные константы, доступ к которым организован через регистр DS, представляют собой  сегмент  данных. Базовый сегмент локальных переменных  (переменных, описанных  внутри подпрограммы)  является сегментом стека. В этом случае смещение переменной относится к регистру ВР,  что автоматически влечет за собой выбор сегмента стека.

Примечание: Регистры BP, SP, SS и DS должны сохраняться с помощью операторов inline.  Значение всех  других регистров можно изменять.

В следующем примере оператора inline  генерируется  машинный  код для записи заданного числа слов или данных в указанную переменную.  При вызове процедуры FillWord Count  слов  со  значением  Data записывается в памяти, начиная с первого байта, обозначенного как Dest.

procedure FillWord(var Dest, Count, Data: word);

begin

   inline(

  $C4/$BE/Dest/ { LES DI,Dest[BP]  }

  $8B/$8e/Count/   { MOV CX,Xount[BP] }

  $8B/$86/Data/ { MOV AX,Data[BP]  }

  $FC/ { CLD  }

  $F3/$AB);  { REP STOSW  }

В операторной  части блока  операторы inline могут свободно  чередоваться с другими операторами.

Директивы inline позволяют писать процедуры и функции, которые преобразуются при каждом вызове в заданную последовательность  инструкций, представляющих собой машинный код. Синтаксис у директивы inline такой же, как у оператора inline:

ЪДДДДДДДДДДДДї

директива ДДДДДДДДДДДДДДДДДДДДДД>і оператор  ГДДДДДДДДДДДД>

  inline  і inline і

АДДДДДДДДДДДДЩ

При вызове обычной процедуры или функции (включая те,  которые содержат в себе операторы inline)  компилятором  генерируется  такой код,  в  котором параметры (если они имеются) помещаются в  стек, а затем уже для обращения к процедуре или функции генерируется инструкция CALL.  Однако,  когда вы обращаетесь к процедуре  или функции типа inline,  компилятор вместо инструкции CALL генерирует код из директивы inline. Вот короткий пример двух директив  inline:

procedure DisableInterrupts; inline($FA); { CLI }

procedure EnableInterrupts; inline($FB); { STI }

Когда вызывается  процедура DisableInterrupt то генерируется  один байт кода - инструкция CLI.

Процедуры или функции,  описанные с помощью директив inline,  могут иметь параметры,  однако на параметры нельзя ссылаться символически (хотя для других переменных это  допускается).  К тому  же, поскольку  такие  процедуры или функции фактически являются  макрокомандами, у них отсутствуют автоматический код с инструкциями входа или выхода и никаких инструкций возврата управления не  требуется.

Следующая функция выполняет умножение двух целых значений, в  результате чего получается число длинного целого типа:

function LongMul(X,Y : Integer): Longint;

   inline(

  $58/  { POP DS ; извлечь из стека Y }

  $5A/  { POP AX ; извлечь из стека X }

  $F7/$EA);   { IMUL DX ; DX:AX = X*Y }

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

Директивы inline предназначены  только  для очень  коротких  (менее 10 байт) процедур и функций.

Из-за того, что процедуры и функции типа inline имеют характер макроопределений,  они не могут использоваться в качестве аргумента операции @ или в функциях Addr, Offs и Seg.