С помощью директивы компилятора $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.Турбо Ассемблер и Borland Pascal
Турбо Ассемблер (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, за которым следует одна или более встроенных записей (записей ма- шинного кода), разделенных косой чертой и заключенных в круглые скобки: inline(10/$2345/Count+1/Data-Offset); Оператор inline имеет следующий синтаксис: --------- ---- ----------- ---- подставляемый -->¦ inline +->¦ ( +---->¦ запись в +-T->¦ ) +-> оператор L--------- L---- ^ ¦ машинном ¦ ¦ L---- ¦ ¦ коде ¦ ¦ ¦ L----------- ¦ ¦ ---- ¦ L------+ / ¦<----- L---- Каждый оператор inline состоит из необязательного специфика- тора размера, < или >, и константы или идентификатора переменой, за которой следуют ноль или более спецификаторов смещения (см. описанный далее синтаксис). Спецификатор смещения состоит из + или -, за которым следует константа. ------------ запись во --T-------------------->¦ константа +---------------> встроенном ¦ ---- ^ L------------ ^ машинном +-->¦ < +------+ ¦ коде ¦ L---- ¦ ¦ ¦ ---- ¦ ¦ +-->¦ > +------- ¦ ¦ L---- ¦ ¦ ---------------- ¦ L->¦ идентификатор +-T--------------------- ¦ переменной ¦ ¦ ^ L---------------- ¦ ¦ ------ L--------- ¦ ----- ---------- ¦ L----->¦знак+-->¦константа¦--T----- ^ L----- L---------- ¦ L-------------------------- Каждая запись 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 ¦ L------------- При вызове обычной процедуры или функции (включая те, кото- рые содержат в себе операторы 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.
Назад | Содержание