Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Паскаль / tp3 / tp3 / 22

.doc
Скачиваний:
17
Добавлен:
10.12.2013
Размер:
126.46 Кб
Скачать

Выражения встроенного Ассемблера подразделяются на три класса: регистровые значения, ссылки на память и непосредственные значения.

Выражение, состоящее только из имени регистра, является регистровым значением. Примерами регистровых значений являются AX, CL, DI и ES. Используемые в качестве операндов, регистровые выражения указывают Ассемблеру на необходимость генерировать инструкции, которые работают с регистрами ЦП.

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

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

Непосредственные значения и ссылки на память при использовании их в качестве операндов приводят к генерации различного кода. Например:

const

Start = 10;

var

Count: Integer;

...

asm

mov ax,Start { MOV AX,xxxx }

mov bx,Count { MOV BX,[xxxx] }

mov cx,[Start] { MOV CX,[xxxx] }

mov dx,OFFSET Count { MOV DX,xxxx }

end;

Поскольку Start - это непосредственное значение, первая инструкция MOV ассемблируется в непосредственную инструкцию. Однако вторая инструкция MOV транслируется в инструкцию, ссылающуюся на память, так как Count - это ссылка на память. В третьей инструкции MOV для преобразования Start в ссылку на память (в данном случае слово со смещением 10 в сегменте данных) используется операция квадратных скобок. В четвертой инструкции MOV для преобразования Count в непосредственное значение (смещение Count в сегменте данных) используется операция OFFSET.

Как вы можете видеть, квадратные скобки и операция OFFSET дополняют друг друга. В терминах результирующего машинного кода следующий оператор asm идентичен первым двум строкам предыдущего оператора asm:

asm

mov ax,OFFSET [Start]

mov bx,[OFFSET Count]

end;

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

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

Встроенный Ассемлер позволяет вам выполнять любую операцию с абсолютным значением, но операции с перемещаемыми значениями ограничиваются сложением и вычитанием констант.

Типы выражений

Каждое выражение встроенного Ассемлера имеет соответствующий тип, или, если говорить точнее, размер, поскольку встроенный Ассемлер рассматривает тип выражения просто как его размер в памяти. Например, тип (размер) переменной Integer равен 2, так как она занимает два байта.

Там, где это возможно, встроенный Ассемлер выполняет проверку типов, поэтому в инструкциях:

var

QuitFlag: Boolean;

OutBufPtr: Word;

...

asm

mov al,QuitFlag

mov bx,OutBufPtr

end;

встроенный Ассемблер проверяет, что размер QuitFlag равен 1 (байт), а размер OutBufPtr - двум (слово). Если проверка типа обнаруживает несоответствие, возникает ошибка. Например, следующее недопустимо:

asm

mov dl,OutBufPtr

end;

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

asm

mov dl,BYTE PTR OutBufPtr

mov dl,Byte(OutBufPtr)

mov dl,OutBufPtr.Byte

end;

Все эти инструкции ссылаются на первый (менее значащий) байт переменной OutBufPtr.

В некоторых случаях ссылка на память является нетипизованной, то есть не имеет соответствующего типа. Приведем пример с непосредственным значением, заключенным в квадратные скобки:

asm

mov al,[100H]

mov bx,[100H]

end;

Встроенный Ассемблер допускает обе этих функции, поскольку выражение [100H] не имеет соответствующего типа, оно просто означает "содержимое по адресу 100H в сегменте данных", а тип можно определить из первого операнда (байт для AL, слово для BX). В том случае, когда тип нельзя определить из другого операнда, встроенный Ассемблер требует явного назначения типа:

asm

mov BYTE PTR [100H]

mov WORD PTR [100H]

end;

В Таблице 22.2 приведены предопределенные идентификаторы типа, которые предусмотрены во встроенном Ассемблере дополнительно к типам, описанным в Паскале:

Таблица 22.2 Предопределенные идентификаторы типа

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

Идентификатор Тип

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

BYTE 1

WORD 2

DWORD 4

QWORD 8

TBYTE 10

NEAR 0FFFEH

FAR 0FFFFH

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

Заметим в частности, что NEAR и FAR - это псевдотипы, которые используются с идентификаторами процедур и функций для указания их модели вызова. Вы можете использовать назначения типа NEAR и FAR аналогично другим идентификаторам. Например, если FarProc - процедура с дальним типом вызова (FAR):

procedure FarProc; far;

и если вы записываете код встроенного Ассемблера в том же модуле, где находится FarProc, то вы можете использовать для ее вызова более эффективную инструкцию NEAR:

asm

push cs

call NEAR PTR FarProc

end

Операции в выражениях

Встроенный Ассемблер предусматривает множество операций, подразделяемых по старшинству на 12 классов. В Таблице 22.3 перечислены операции, использующиеся в выражениях встроенного Ассемблера в порядке убывания их старшинства:

Старшинство операций встроенного Ассемблера Таблица 22.3

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

Операция Комментарий

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

& Операция переопределения

идентификатора.

(), [], * Выбор элемента структуры.

HIGH, LOW

+, - Унарные операции.

: Операция переопределения

сегмента.

OFFSET, SEG, TYPE, PTR,

*, /, MOD, SHL, SHR

+, - Бинарные операции сложения/

вычитания.

NOT, AND, OR, XOR Поразрядные операции.

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

Операция &

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

Операция (...)

Подвыражение. Выражение в скобках полностью вычисляется, после чего интерпретируется, как один элемент. Выражению в скобках может предшествовать другое выражение. Результатом в этом случае будет сумма значений двух выражений с типом первого выражения.

Операция [...]

Ссылка на память. Выражение в квадратных скобках полностью вычисляется, после чего интерпретируется, как один элемент. Выражение в квадратных скобках может комбинироваться с регистрами BX, BP, SI, DI с помощью операции +, что указывает на индексирование регистра ЦП. Выражению в квадратных скобках может предшествовать другое выражение. Результатом в этом случае будет сумма значений двух выражений с типом первого выражения. Результатом всегда будет ссылка на память.

Операция .

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

Операция HIGH

Возвращает старшие 8 бит выражения размером в слово, следущего за операцией. Выражение должно представлять собой непосредственное абсолютное значение.

Операция LOW

Возвращает младшие 8 бит выражения размером в слово, следущего за операцией. Выражение должно представлять собой непосредственное абсолютное значение.

Операция +

Унарный плюс. Возвращает следующее за плюсом выражение без изменений. Выражение должно представлять собой непосредственное абсолютное значение.

Операция -

Унарный минус. Возвращает следующее за минусом выражение с обратным знаком. Выражение должно представлять собой непосредственное абсолютное значение.

Операция :

Переопределение сегмента. Указывает Ассемблеру, что выражение после двоеточия относится к сегменту, заданному именем сегментного регистра (CS, DS, SS или ES) перед двоеточием. Результатом является ссылка на память со значением выражения после двоеточия. Когда переопределение сегмента используется в операнде инструкции, инструкции предшествует соответствующий префикс переопределения сегмента, обеспечивающий выбор указанного сегмента.

Операция OFFSET

Возвращает смещение следующего за операцией выражения (младшее слово). Результатом будет непосредственное значение.

Операция SEG

Возвращает сегмент следующего за операцией выражения (старшее слово). Результатом будет непосредственное значение.

Операция TYPE

Возвращает тип (размер в байтах) следующего за операцией выражения. Типом непосредственного значения будет 0.

Операция PTR

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

Операция *

Умножение. Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция /

Целочисленное деление. Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция MOD

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

Операция SHL

Логический сдвиг влево. Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция SHR

Логический сдвиг вправо. Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция +

Сложение. Выражения могут представлять собой непосредственные абсолютные значения или ссылки на память, но перемещаемым значением может быть только одно выражение. Если одно из выражений - перемещаемое значение, то результатом также будет перемещаемое значение. Если одно из выражений - ссылка на память, то результатом также будет ссылка на память.

Операция -

Вычитание. Первое выражение может иметь любой класс, а второе выражение должно быть непосредственным абсолютным выражением. Результат имеет тот же тип, что и первое выражение.

Операция NOT

Порязрядное отрицание. Выражение должно представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция AND

Поразрядная операция AND (И). Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция OR

Поразрядная операция OR (ИЛИ). Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Операция XOR

Поразрядная операция XOR (исключающее ИЛИ). Оба выражения должны представлять собой непосредственные абсолютные значения. Результатом будет непосредственное абсолютное значение.

Процедуры и функции Ассемблера

До сих пор мы рассматривали конструкцию asm...end, как оператор с обычной частью begin...end. Директива assembler в Турбо Паскале позволяет вам писать на встроенном Ассемблере целиком процедуры и функции без необходимости begin...end. Приведем пример функции на Ассемблере:

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

asm

mov ax,X

imul Y

end;

Директива assembler приводит к тому, что Турбо Паскаль выполняет при генерации кода следующую оптимизацию:

- Компилятор не генерирует код для копирования параметров-значений в локальные переменные. Это влияет на все параметры-значения строкового типа и другие значения-параметры, размер которых не равен 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 (32-разрядные значения):

- результаты функции строкового типа возвращаются во временной ячейке, на которую указывает @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;

Соседние файлы в папке tp3