Синтаксис ассемблерных команд

Здесь и далее ассемблерными командами называются команды на языке встроенного ассемблера, вставляемые в тело ассемблерного оператора asm... end. Структура ассемблерной команды такова:

[Метка] [Префикс] [Код [Операнд [,Операнд]]] 

В квадратных скобках указываются необязательные элементы структуры.

Метки

Любой команде ассемблерного оператора может предшествовать одна или несколько меток. В ассемблере используется два типа меток: глобальные и локальные. Глобальные метки - это обычные метки Турбо Паскаля. Они объявляются в разделе описаний после зарезервированного слова Label. С помощью глобальной метки можно передать управление в тело ассемблерного оператора оператором GOTO. Например:

Label

AltEnt; 

begin 

Goto AltEnd; {Передаем управление внутрь ассемблерного опера тора}

.......

asm

.......

AltEnd: {Сюда можно передать управление извне}

.......

end;

Локальные метки объявляются непосредственно в теле ассемблерного оператора. Эти метки обязаны начинаться символом «@». Поскольку этот символ нельзя использовать в именах Турбо Паскаля, он позволяет отличить локальную метку от глобальной. Локальная метка не известна нигде вне ассемблерного оператора, поэтому на нее нельзя передать управление оператором GOTO. По этой же причине в разных ассемблерных операторах можно использовать одноименные локальные метки.

Префиксы 

Встроенный ассемблер поддерживает следующие префиксы команд:

LOCK             Захват шины

REP/REPE/REPNE    Повтор строковой команды

REPZ/REPNZ       Синоним REPE/REPNE

SEGCS            Перекрытие CS

SEGDS            Перекрытие DS

SEGSS            Перекрытие SS

SEGES            Перекрытие ES

Префиксы LOCK/REP/REPE/REPNE описаны в п. 12.1.3. Префиксы SEGxx определяют сегментный регистр, который должен использоваться вместо умалчиваемого, и распространяются только на следующие за ними ассемблерные команды.

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

Код инструкции очень редко имеет более одного префикса и никогда - более трех: допускается следующая последовательность

LOCK SEGxx REPxx

Замечу, что если при обработке строковой команды произошло аппаратное прерывание, МП 8086/8088 «забывает» префиксы LOCK и SEGxx, которые, возможно, определены в той же команде, так что использовать сложные префиксные конструкции не рекомендуется.

Коды инструкций

Встроенный ассемблер поддерживает мнемонику всех команд, перечисленных в п.12.1.3. Кроме того, в ассемблерных командах может использоваться мнемоника инструкций процессора 8087, а также команды процессоров 80286/80287. Замечу, что инструкции 8087 допустимы только при активном состоянии {SN+}, 80286 - при {$G+}, a 80287 - в случае {$G+,N+}.

Операнды

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

Регистры

Во встроенном ассемблере используется мнемоника регистров, указанная в п. 12.1.1, а также имя ST для ссылки на регистры арифметического сопроцессора.

Константы

Ассемблер поддерживает строковые и числовые константы.

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

'Строковая константа'

"Это - тоже строковая константа"

'Символ '' не считается ограничителем'

'внутри строки, обрамленной кавычками "..."'

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

- 2 147 483 648...+ 4 294 967 295.

По умолчанию при записи числовых констант используется десятичная нотация, но ассемблер поддерживает также двоичные, восьмеричные и шестнадцатеричные константы. Двоичная константа составляется как комбинация единиц и нулей, заканчивающаяся символом В (от Binary - двоичный); при записи восьмеричной константы используются символы 0...7, а в ее конце ставится символ О (Octal - восьмеричный); шестнадцатеричная константа записывается по правилам Турбо Паскаля (начинается с символа #) либо по правилам Турбо Ассемблера: начинается с цифры, в конце ставится символ H (от Hexadecimal - шестнадцатеричный).

Имена

Локальные метки - это единственные имена, которые разрешается определять внутри ассемблерного оператора. Имена остальных объектов программы - констант, переменных, подпрограмм - должны определяться только с помощью средств Турбо Паскаля.

Область определения имен подчиняется тем же правилам, что и в Турбо Паскале -имена должны быть «видны» в том месте, где они используются, и они локализуются в пределах блока, в котором описаны.

Во встроенном ассемблере могут использоваться три предопределенных имени:

@@Code - текущий сегмент кода

@Data - начальный сегмент данных

@Result - ссылка внутри функции на ее результат

Имена @Code и @Data могут использоваться только в сочетании с директивой SEG для ссылки на нужный сегмент. Например:

asm

mov ax, SEG ©Data

mov ds,ax 

end;

Имя @Result используется для присвоения результата функции. Например:

Function Min(X,Y: Integer): Integer; 

{Эта функция сравнивает два целых числа и возвращает наименьшее из них) 

begin 

asm

mov ax,X {Помещаем Х в АХ} 

cmp ax,Y {X<Y ?}

jl @ {Да - на выход}

mov ax,Y {Нет - помещаем Y в АХ} 

@: mov ©Result,ax {АХ содержит результат}

end 

end;

Для доступа к полям записей разрешается использование составных имен. Например:

type

Point = record X,Y: Integer 

end; 

Rect = record

A,B: Point 

end; 

var

P: Point; 

R: Rect; 

begin 

asm

mov ax,P.X 

add ax,P.Y

mov R.A.X,ax 

end 

end.

Идентификаторы типов можно применять к операндам для уточнения данных, о которых идет речь. Каждая из следующих команд реализует одно и то же действие: загружает в регистр АХ слово по адресу ES: [DI+4 ]:

mov ax,(Rect PTR es:[di]).В.Х

mov ax,Rect(es:[di]).В.Х

mov ax,esrRect[di].B.X

mov ax,Rect[es:di].B.X

mov ax,es:[di].Rect.B.X

Следующие имена не могут использоваться в операндах встроенного ассемблера:

Выражения

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

Регистровое выражение - это выражение, состоящее из имени регистра. Все следующие команды содержат только регистровые выражения:

push ds 

pop es 

mov ah,bl 

add ex,ax

Непосредственные выражения - это нетипизированные константы и имена типов. Примеры непосредственных выражений:

const

dec =10;

.....

asm

mov ax, dec 

mov bx,0 

add cx,2*dec+l 

sub dh,- 5 

end;

Все остальные выражения относятся к ссылкам на память. Например:

const

dec: Word = 10; 

Step =12;

var

Х,Y: Byte;

asm

mov ax, dec

mov ex, [Step]

add ah,X mov Y,bl

mov ax,[bx] 

end;

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

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

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

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

х+10

в Паскале означает: «к содержимому переменной X прибавить 10». В ассемблерной команде это означает: .«к адресу (смещению) переменной X прибавить 10». Однако команда

mov ах,X

означает: «поместить в регистр АХ первые два байта переменной X». Если бы нам понадобилось загрузить в АХ адрес переменной X, мы должны были бы написать

mov ax,OFFSET X  

Замечу, что попытка «перехитрить» ассемблер командами типа

mov ax,X+0 mov ax,X+1-1

и т.п. не дает желаемого результата: ассемблер просто загружает в АХ содержимое переменной X.

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

Встроенный ассемблер имеет следующие предопределенные типы:

Тип

Длина в памяти

BYTE

1

WORD

2

DWORD

4

QWORD

8

TBYTE

10

NEAR

-

FAR

-

Имена предопределенных типов можно использовать для приведения типов выражений. Например, если определены переменные

var

Flag: Boolean; 

X : Word;

то такие ассемблерные выражения недопустимы:

mov Flag,bx 

mov ah,X

Для корректного задания последней команды можно использовать следующие варианты:

mov ah,BYTE PTR X 

mov ah,Byte(X) 

mov ah,X.Byte

Во всех случаях в АН будет загружен первый (младший) байт переменной X. Встроенный ассемблер поддерживает операции, перечисленные в следующей таблице (в порядке убывания приоритета).

Операции встроенного ассемблера

Операция

Комментарий

&

Перекрытие идентификатора

0

Подвыражение

[]

Ссылка на память

. (точка)

Селектор структуры

HIGH LOW

Доступ к байту в слове

+ -

Унарные операции задания знака

 

Перекрытие сегмента

OFFSET SEG TYPE PTR *

 

/ MOD SHL SHR

 

+ -

Бинарные операции

NOT AND OR XOR

Операции над битами

Операция &

Осуществляет перекрытие идентификатора: следующий за знаком & идентификатор считается определенным в программе, даже если он совпадает с зарезервированным словом. Например:

var

Ch: Byte;

.......

mov ch,0 {Посылаем 0 в регистр СН} 

mov &Ch,0 {Посылаем 0 в переменную Ch}

Операция 0

Круглые скобки используются обычным для Паскаля образом - для изменения порядка исчисления выражения (подвыражение, ограниченное скобками, вычисляется в первую очередь). Если перед скобками стоит имя типа, все выражение приобретает указанный тип. Например:

mov ах,((1+2)*3+4)*5 {АХ = 65} 

mov bх,1+2*3+4*5 {ВХ = 27}

Операция[]

Определяет ссылку на память. Выражение внутри скобок вычисляется в первую очередь. Обычно оно связывается с регистрами BX, BP ,SI, DI и может использовать операции + и - для указания индексации. Например:

mov ah,100 mov ah,[100]

{АН = 100} {Загружаем в АН содержимое байта по адресу DS-.100}

Операция . (точка)

Селектор элемента структуры. Результат - сумма выражений до и после точки с типом второго выражения. Например: 

var

R: record

X: Word; У: Byte 

end;

.......

mov ax, R. X 

mov R.Y,al

Операции HIGH и LOW

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

mov al,High $1000 {AL = $10}

Операция : (двоеточие)

Указывает ассемблеру, что выражение после операции должно относиться к сегменту, указанному до операции. Результат - ссылка на память со значением второго выражения. Например:

mov ax, [10] {AX = слово по адресу DS:10} 

mov ax,BS:[10] {АХ = слово по адресу BS:10}

Операция OFFSET

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

mov ах,Х {АХ = слово по адресу переменной X}

mov ax,offset X {АХ = смещение адреса X}

Операция SEG

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

Операция PTR

Осуществляет приведение типа. Результат - ссылка на память со значением выражения после операции и типом выражения до операции. Например:

Function Swap(X: Integer): Integer;

{Меняет местами байты в слове X}

begin 

asm

mov ax,X

mov BYTE PTR @Result,ah 

mov BYTE PTR @Result+l,al 

end; 

end;

Операции * и /

* - умножение, / - целочисленное деление. Оба выражения должны иметь непосредственные абсолютные значения, такое же значение имеет и результат операции. Например:

mov ax,2*2 {АХ = 4}

mov ах,17/3. {АХ = 5}

Операция MOD

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

mov ах,17 mod 3 {АХ =2}

Операции SHL и SHR

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

mov ah,1 shl 7 {Ah = $80 = 128}

Бинарная операция +

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

Бинарная операция

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

Побитовые операции NOT, AND, OR, XOR

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