Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Руководство по языку Паскаль 1.doc
Скачиваний:
12
Добавлен:
22.04.2019
Размер:
2.48 Mб
Скачать

Глава 25. Компоновка с программами на языке ассемблера

─────────────────────────────────────────────────────────────────

С помощью директивы компилятора $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. Все инициализированные частные пере-

B.Pascal 7 & Objects/LR - 438 -

менные должны помещаться в сегмент с именем 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 сообщает Турбо

Ассемблеру, что параметры были занесены в стек слева-направо - в

том порядке, в котором они обнаружены в исходном операторе, вызы-

вающем процедуру.

B.Pascal 7 & Objects/LR - 439 -

Директива 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;

B.Pascal 7 & Objects/LR - 440 -

Примеры программ на языке ассемблера

─────────────────────────────────────────────────────────────────

Следующая программа является примером модуля и представляет

собой две программы на ассемблере, предназначенные для обработки

строк. Функция 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 ; сохранить результат

B.Pascal 7 & Objects/LR - 441 -

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.

B.Pascal 7 & Objects/LR - 442 -

Включаемый машинный код

─────────────────────────────────────────────────────────────────

Для небольших подпрограмм на языке ассемблера очень удобно

использовать внутренние директивы и операторы Borland Pascal

(операторы inline). Они позволяют вставлять инструкции машинного

кода непосредственно в программу или текст блока, вместо того,

чтобы использовать объектный файл.

Операторы Inline

─────────────────────────────────────────────────────────────────

Оператор inline состоит из зарезервированного слова Inline,

за которым следует одна или более встроенных записей (записей ма-

шинного кода), разделенных косой чертой и заключенных в круглые

скобки:

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

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

┌────────┐ ┌───┐ ┌──────────┐ ┌───┐

подставляемый ──>│ inline ├─>│ ( ├────>│ запись в ├─┬─>│ ) ├─>

оператор └────────┘ └───┘ ^ │ машинном │ │ └───┘

│ │ коде │ │

│ └──────────┘ │

│ ┌───┐ │

└──────┤ / │<────┘

└───┘

Каждый оператор inline состоит из необязательного специфика-

тора размера, < или >, и константы или идентификатора переменой,

за которой следуют ноль или более спецификаторов смещения (см.

описанный далее синтаксис). Спецификатор смещения состоит из +

или -, за которым следует константа.

┌───────────┐

запись во ──┬────────────────────>│ константа ├───────────────>

встроенном │ ┌───┐ ^ └───────────┘ ^

машинном ├──>│ < ├──────┤ │

коде │ └───┘ │ │

│ ┌───┐ │ │

├──>│ > ├──────┘ │

│ └───┘ │

│ ┌───────────────┐ │

└─>│ идентификатор ├─┬────────────────────┘

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

└───────────────┘ │ │

┌────┘ └─────────┐

│ ┌────┐ ┌─────────┐ │

└─────>│знак├──>│константа│──┬────┘

^ └────┘ └─────────┘ │

B.Pascal 7 & Objects/LR - 443 -

└─────────────────────────┘

Каждая запись 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 }

B.Pascal 7 & Objects/LR - 444 -

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

чередоваться с другими операторами.

Директивы 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 }

Обратите внимание на отсутствие инструкций входа и выхода и

инструкции возврата управления. Их присутствия не требуется, пос-

кольку при вызове этой функции содержащиеся в ней четыре байта

просто включаются в текст программы.

B.Pascal 7 & Objects/LR - 445 -

Директивы inline предназначены только для очень коротких

(менее 10 байт) процедур и функций.

Из-за того, что процедуры и функции типа inline имеют харак-

тер макроопределений, они не могут использоваться в качестве ар-

гумента операции @ или в функциях Addr, Offs и Seg.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]