Пильщиков
.pdf30 Программирование на языке ассемблера IBM PC Глава 1
М+1 не следует понимать как сложение содержимого ячейки с именем М ( т. е. числа 2) с числом 1. В Я А запись вида <имя>±к означает, что к адресу указанного имени надо прибавить (или отнять) число к, в результате чего получится некоторый новый адрес, и вот уже по этому адресу и осуществляется доступ к памяти. Таким образом, данная запись означает сложение/вычитание адресов.
Операнд - строка
Возможно еще одно сокращение в директиве DB: если в ней несколько соседних операндов - символы, то их можно объединить в одну строку. Например, следующие две директивы эквивалентны:
S DB ' а ' , ' Ь ' , ' С S DB ' а Ь с '
Отметим, что и в этом случае тип имени равен 1 (TYPE S = BYTE), т. к. любая из этих директив является сокращением следующих трех директив:
sDB ' а ' DB ' Ь '
DB ' с '
а здесь ясно видно, что имя S обозначает только первый байт.
Вопрос о том, объединять соседние символы в одну строку или нет, а если объединять, то какие именно, решает сам автор программы. Например, нашу директиву можно записать и так:
S DB ' a b ' , ' с ' или S DB ' а ' , ' b e '
Операнд - конструкция повторения DUP
Рассмотрим еще одно возможное сокращение в записи директивы DB. Довольно часто в директиве приходится указывать одинаковые операнды. Например, если мы хотим описать байтовый массив R из 8 элементов с начальным значением О для каждого из них, то это можно сделать так:
R DB 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Так вот, эту директиву можно записать и короче:
R DB 8 DOP(O)
Здесь в качестве операнда использована так называемая конструкция повторения, в которой сначата указывается коэффициент повторения, затем - служебное слово DUP (duplicate, копировать), а за ним в круглых скобках - повторяемая величина.
В общем случае эта конструкция имеет следующий вид:
k DUP ( р 1 # р 2 , . . . , р п )
где к - константное выражение с положительным значением, п>=1, pj - любой допустимый операнд директивы DB (в частности, это может быть снова кон-
Язык ассемблера. Начальные сведения 32 |
30 |
|
струкция повторения). Данная запись является сокращением для к раз повторенной последовательности указанных в скобках операндов:
Например, директивы слева эквивалентны директивам справа:
(Тип имен X и Y - BYTE.)
Отметим, что вложенность конструкций DUP можно использоваться для наглядного описания многомерных массивов. Например, директиву
A DB 20 DUP(30 DUP( 7 ) )
можно рассматривать как описание байтовой матрицы А размера 20x30, в которой элементы расположены в памяти следующим образом: первые 30 байтов - это элементы первой строки матрицы, следующие 30 бантов - это элементы второй строки и т. д.
2 3 2 . Директива DW
Директивой DW (define word, определить слово) описываются переменные размером в слово. Она аналогична директиве DB, поэтому лишь вкратце рассмотрим допустимые виды ее операндов.
Операнд ?
Возможный пример:
По этой директиве ассемблер отводит под переменную А слово памяти, в которое ничего не записывает, т. е. эта переменная не получает начального значения. Тип переменной равен 2, т. к. она занимает два байта. В Я А есть стандартная константа с именем WORD и значением 2, поэтому данный факт можно записать так:
ТУРЕ А - WORD = 2. |
i.:- |
Константное выражение со значением от -3276S до 65535
Возможные примеры:
В DW 1234h
С DW - 2
По этим директивам под переменные В и С отводится по слову памяти и в эти ячейки записываются указанные числа, которые становятся начальными значениями этих переменных.
"ДИАЛОГ-МИФИ"
32 Программирование на языке ассемблера IBM PC Глава 1
Как и в случае директивы DB, неотрицательные числа записываются в память как числа без знака, а отрицательные числа - в дополнительном коде. Поэтому числа, которые могут быть заданы как операнды директивы DW, должны принадлежать отрезку [-215, 216-1].
Но здесь имеется и отличие от директивы DB. Напомним, что в ПК числа размером в слово хранятся в памяти в "перевернутом" виде. Так вот, па ЯА такие числа записываются в нормачьном, неперевернутом виде, а "переворачиванием" их занимается сам ассемблер, поэтому по нашим двум директивам память заполнится следующим образом:
С учетом этого при программировании на ЯА можно в общем-то забыть о "перевернутом" представлении чисел в памяти ПК.
Частным случаем рассматриваемого вида операнда директивы DW может быть строка из одного или двух символов, например:
s i |
DW |
' 0 1 ' |
S2 |
DW |
' 1 ' |
Если указана строка из двух символов, тогда ассемблер берет коды указанных символов (в нашем случае - 30h (код '0') и 31h (код '1')) и образует из них числослово (303 lh), которое и считается начальным значением описываемой переменной (S1). Но как и любое число размером в слово, данное значение будет записано в память в "перевернутом" виде. Если же в правой части директивы DW указан один символ, тогда к нему слева приписывается символ с кодом 0 и дальнейшие действия ассемблера будут такими же, как и в случае двухсимвольной строки. Поэтому по нашим двум директивам память будет заполнена следующим образом:
В связи с тем, что операнды-строки записываются в память в "перевернутом" виде, что в общем-то не характерно для строк, то подобные операнды редко указываются в директиве DW.
Адресное выражение
Вкачестве операнда директивы DW может быть указано адресное выражение,
т.е. выражение, значением которого является адрес. Как записываются такие выражения, мы еще рассмотрим, а пока лишь отметим, что основной случай адресного выражения - это имя переменной или метка. Поэтому допустим такой пример:
С DB ? D DW С
Язык ассемблера. Начальные сведения |
33 |
В этом случае ассемблер записывает в слово, выделенное под переменную D, адрес переменной С, который становится начальным значением переменной D.
Несколько операндов, конструкция повторения
В правой части директивы DW можно указать любое число операндов, а также конструкцию повторения. Возможный пример:
Е DW 4 0 0 0 0 , 3 D U P ( ? )
2.3.3.Директива DD
По директиве DD (define double word, определить двойное слово) описываются переменные, под которые отводятся двойные слова. Поэтому имена этих переменных имеют тип 4 или DWORD (значением этой стандартной константы как раз является число 4). В остальном эта директива похожа на две предыдущие.
Допустимые типы операндов этой директивы таковы.
Операнд ?
Пример:
A DD ?
Под переменную А выделяется двойное слово, в которое ассемблер ничего не записывает, т. е. переменная А не получает начального значения.
Целое число со значение от -2п до 232-1
Пример:
ВDD 123456h
Вданном случае переменная В получает начальное значение, причем это значение ассемблер записывает в память в "перевернутом" виде:
Константное выражение (со значением от -2'5 до 2,б-1)
Обратите внимание на диапазон возможных значений выражения - он в два ра-
за меньше диапазона |
чисел, которые можно |
записать |
в двойном слове. Дело |
в том, что в ЯА все |
выражения вычисляются |
в области |
16-битовых чисел, т. е. |
результаты всех операций берутся по модулю 216 (lOOOOh). Поэтому построить выражение, значением которого являлось бы 32-битовое или даже 17-битовое число, не удастся. Единственное исключение - это явно задать в директиве DD "большое" число. Если же мы укажем хотя бы одну операцию, то ответ тут же будет взят по модулю 216. Например, по директиве
X DD 8000h+8002h
начальным значением переменной X станет число 2, а не число 10002h.
"ДИАЛОГ-МИФИ"
34 Программирование на языке ассемблера IBM PC Глава 1
Конечно, такая особенность задания начальных значений для переменных размером в двойное слово не очень-то приятна, но так уж устроен ЯА, и это надо учитывать.
Адресное выражение
Такой операнд задает абсолютный адрес. Как это делается, будет рассмотрено позже, в гл. 7.
Несколько операндов, конструкция повторения
Возможный пример:
DW 33 DUP(?), 12345h •
2.4.Директивы эквивалентности и присваивания
Мы рассмотрели, как в ЯА описываются переменные. Теперь рассмотрим, как в этом языке описываются константы. Это делается с помощью директивы эквивалентности - директивы EQU (equal, равно), имеющей следующий синтаксис:
<имя> EQU <операнд>
Здесь обязательно должно быть указано имя, должен быть и операнд, причем только один.
Эта директива аналогична описанию константы в языке Паскаль:
c o n s t <имя> " <операнд>;
Директивой EQU автор программы заявляет, что указанному операнду он дает указанное имя, и требует, чтобы все вхождения этого имени в текст программы ассемблер заменял на этот операнд. Например, если есть директива
STAR EQU ' * '
то ассемблер будет рассматривать предложение
т DB STAR
как предложение
т DB ' * '
Другими словами, указать имя STAR и указать '*' - это одно и то же.
Отметим, что директива EQU носит чисто информационный характер, по ней ассемблер ничего не записывает в машинную программу. Поэтому директиву EQU можно ставить в любое место программы - и между командами, и между описаниями переменных, и в других местах.
Теперь рассмотрим, каким может быть операнд директивы EQU и в каких случаях полезна эта директива.
Язык ассемблера. Начальные сведения |
35 |
Операнд - имя
Если в правой части директивы указано имя регистра, переменной, константы и т. п., тогда имя слева объявляется синонимом данного имени и все последующие вхождения в текст программы этого имени-синонима ассемблер
. будет заменять на имя, указанное справа. Например:
. A DW ? |
|
В EQU А |
|
С DW В |
; э к в и в а л е н т н о : С DW А |
Имена-синонимы обычно используются для введения более удобных, наглядных обозначений. Например, само по себе имя регистра АХ ни о чем не говорит, но если мы используем этот регистр для вычисления какой-то суммы, то его можно обозначить SUM:
SUM EQU АХ
и далее использовать это более наглядное имя SUM.
Отметим, что имя, указанное в правой части директивы EQU, может быть описано в программе как до этой директивы, так и после нее.
Операнд - константное выражение
Примеры:
N EQU 100
К EQU 2*N-1 STAR EQU ' * '
Если в правой части директивы EQU стоит константное выражение, тогда указанное слева имя принято называть именем константы. Значением этой константы объяачястся значение выражения. Например, N - это константа со значением 100, К - со значением 199, a STAR - со значением 2Ah (это код звездочки в системе ASCII). Все последующие вхождения в текст программы имени константы ассемблер будет заменять на значение этой константы. Например, директива
X DB N DUP(?)
эквивалентна директиве
X DB 100 DUP(?)
Случаи, когда полезно применение констант, - такие же, как и в языках высокого уровня. Например, в качестве констант рекомендуется описывать размеры массивов, поскольку в таком случае легко настроить программу на работу с массивом другого размера - для этого достаточно внести изменение лишь в директиву EQU, описывающую константу.
Отметим, что если в константном выражении используются имена других констант, то они должны быть описаны раньше данной директивы EQU, иначе
"ДИАЛОГ-МИФИ"
36 Программирование на языке ассемблера IBM PC Глава 1
ассемблер, просматривающий текст программы сверху вниз, не сможет вычислить значение этого выражения.
Операнд - любой другой текст
Примеры:
S EQD 'Вы ошиблись'
LX EQD X+(N-1)
WP EQD WORD PTR
В данном случае считается, что указанное имя обозначает операнд в том виде, как он записан (операнд не вычисляется). Именно на этот текст и будет заменяться каждое вхождение данного имени в программу. Например, следующие предложения слева эквивалентны предложениям справа
ANS DB S, |
' 1 ' |
ANS DB |
'Вы о ш и б л и с ь ' , ' 1 ' |
NEG LX |
|
NEG |
X+(N-1) |
INC WP |
[ВХ] |
INC |
WORD PTR [BX] |
Такой вариант директивы EQU обычно используется для того, чтобы ввести более короткие обозначения для часто встречающихся длинных текстов. Введя короткое имя, мы далее в программе можем им пользоваться, а уж ассемблер сам будет его заменять на соответствующий текст.
Отметим, что текст, указанный в правой части директивы EQU, должен быть сбалансирован по скобкам и кавычкам и не должен содержать вне скобок и кавычек символа Кроме того, поскольку текст не вычисляется, то в нем можно использовать как имена, описанные до этой директивы EQU, так и имена, описанные после нее.
Теперь рассмотрим еще одну директиву ЯА, похожую на директиву EQU, - директиву присваивания:
<имя> = <константное выражение>
Эта директива определяет константу с именем, указанным в левой части, и с числовым значением, равным значению выражения справа. Но в отличие от констант, определенных по директиве EQU, данная константа может менять свое значение, обозначая в разных частях текста программы разные числа. Например:
К=10
A DW К ; э к в и в а л е н т н о : A DW 10 К=К+4
В DB К ; э к в и в а л е н т н о : В DB 14
Подобного рода константы можно использовать, например, ради "экономии имен": если в разных частях текста программы используются разные константы и области использования этих констант не пересекаются, тогда, чтобы не придумывать новые имена, этим константам можно дать одно и то же имя
Язык ассемблера. Начальные сведения |
37 |
(другими словами, поменять значение константы с таким именем). Однако главное применение таких констант - в макросредствах (см. гл. 11).
Теперь кое-что уточним.
Если с помощью директивы EQU можно определить имя, обозначающее не только число, но и другие конструкции, то по директиве присваивания можно определить только числовую константу. Кроме того, если имя указано в левой части директивы EQU, то оно не может появляться в левой части других директив (его нельзя переопределять). А вот имя, появившееся в левой части директивы присваивания, может снова появиться в начале другой такой директивы (но только такой!). Поэтому ошибочными являются все следующие три фрагмента программы:
К |
EQD |
1 |
К EQO 1 |
К>1 |
К |
EQO |
2 |
К=2 |
К EQD 2 |
Появление в языке констант, которые могут менять свои значения, вносит некоторую неопределенность. Рассмотрим, к примеру, фрагмент слева:
к-1 |
|
к-1 |
|
|
N EQU К |
|
N |
EQO К+10 |
|
A DW N |
;А=1 |
С |
DW N |
; С - 1 1 |
К=2 |
|
К - 2 |
|
|
В DW N |
;B=2 |
D |
DW N |
; D - 1 1 |
Какие начальные значения получат переменные А и В? Относительно значения переменной А сомнений нет - это 1. Но вот значением переменной В, оказывается, будет число 2, а не 1. Почему? Дело в том, что имя N объявлено синонимом имени К, поэтому все вхождения имени N ассемблер будет заменять на имя К. Значит, директива В DW N понимается как директива В DW К. Но в этом месте текста программы константа К имеет значение 2, поэтому данная директива воспринимается как В DW 2.
В то же время в примере справа переменные С и D получат одно и то же значение 11. Дело в том, что в директиве EQU указано, так сказать, настоящее константное выражение, и потому ассемблер вычисляет его сразу. Значение 11 этого выражения объявляется значением константы N, которая далее и будет обозначать это число.
Таким образом, необходимо внести следующее уточнение в действие директивы EQU: если в правой части директивы указано имя константы, то имя слева надо понимать не как имя константы (не как имя числа), а как синоним имени справа; если же в правой части указано любое другое константное выражение, тогда имя слева действительно становится именем константы (обозначением числа). Что же касается директивы присваивания, то ее правая часть всегда вычисляется сразу и полученное число тут же становится новым значением константы.
"ДИАЛОГ-МИФИ"
38 Программирование на языке ассемблера IBM PC Глава 1
2.5.Выражения
Операнды директив, как правило, описываются в виде выраженийВыражения используются и для описания операндов команд.
В целом выражения ЯА похожи на арифметические выражения языка высокого уровня, однако между ними есть и отличия. Наиболее важное отличие заключается в том, что выражения ЯА вычисляются не во время выполйения программы, а во время ее трансляции: встретив в тексте программы выражение, ассемблер вычисляет его и полученное значение (например, число) записывает в машинную программу. Поэтому, когда программа начнет выполняться, от выражений не останется никаких следов. В связи с этим в выражениях ЯА можно использовать только такие величины, которые известны на этапе трансляции (например, адреса и типы имен), и ни в кое случае нельзя использовать величины (например, содержимое регистров или ячеек памяти), которые станут известными лишь во время счета программы.
Вообще говоря, в ЯА для записи операндов директив и команд достаточно только чисел и имен. Более сложные выражения являются лишь удобной формой записи этих чисел и имен. Например, если имеется переменная X размером в слово и нужно описать переменную ТХ, начальным значением которой является тип (размер) этой переменной, то вместо директивы
ТХ DB 2
лучше использовать эквивалентную ей директиву
ТХ DB TYPE Х
поскольку она более универсальна и ее не надо менять, если изменится тип переменной X.
В ЯА выражения делятся на два класса - на константные и адресные, в зависимости от типа их значений. Если значением выражения является целое число, то оно называется консгантным выражением, а если значением является адрес, то это адресное выражение. Конечно, адрес - это тоже целое число (порядковый номер ячейки в памяти), но по смыслу адреса отличаются от просто чисел, и в ЯА они рассматриваются как самостоятельный тип данных.
Отметим, что выражений иных типов в ЯА нет, однако имеются объекты (например, строки и имена регистров), которые не относятся ни к константным, ни к адресным выражениям, но которые также используются для записи операндов команд и директив.
По структуре и назначению выражения, константные и адресные, можно разделить на простейшие выражения и операторы. Из простейших выражений строятся любые другие выражения. К простейшим выражениям относятся числа, имена констант, имена переменных и т. п. Термином "оператор" в ЯА принято обозначать то, что мы обычно называем функциями и операциями. Операторы ЯА делятся на одноместные (это аналог функций одного аргумента) и двухместные (это аналог бинарных операций); примером может служить TYPE X или А+1. С помощью операторов из простейших выражений строятся более сложные выражения.
Язык ассемблера. Начальные сведения |
39 |
С простейшими выражениями и операторами мы будем знакомиться по ходу дела. Однако уже сейчас укажем старшинство всех операторов ЯА (в порядке убывания; в каждой строке указаны операторы одного старшинства):
1.О, [], LENGTH, SIZE, WIDTH, MASK
3.:
4.PTR, OFFSET, SEG, TYPE, THIS
5.HIGH, LOW
6.одноместные + и -
7.*, /, MOD, SHL, SHR
8.двухместные + и -
9.EQ, NE, LT, LE, GT, GE
10.NOT
11.AND
12.OR, XOR
13.SHORT, .TYPE
Операторы одного старшинства вычисляются слева направо. Например, А+В-С - это (А+В)-С.
2.5.1.Константные выражения
Значениями константных выражений всегда являются 16-битовые целые числа (единственным исключением является директива DD, в которой можно явно указывать 32-битовые числа). При этом, если значением (в математическом смысле) константного выражения является отрицательное число, то в формируемую машинную программу оно записывается в дополнительном коде.
Кпростейшим константным выражениям относятся:
•число от -215 до 216-1 (числа вне этого диапазона рассматриваются как ошибка);
•символ (значением такого выражения является код символа; например, значением выражения '0' будет число 30h);
•строка из двух символов (значением является число-слово, составленное из
кодов этих символов; например, значением выражения '01' будет число
303 lh);
•имя константы (значением такого выражения яатяется значение константы; например, если была директива К EQU 10, то значение К равно 10).
Другие простейшие константные выражения будут рассмотрены позже.
Среди константных операторов, т. е. операторов с числовым значением, пока отметим уже известный нам оператор TYPE и следующие арифметические операторы (k, kl и к2 означают любые константные выражения):
•одноместные плюс и минус: +к, -к
•операторы сложения и вычитания: kl+k2, kl-k2
•операторы умножения и деления: kl*k2, kl/k2, kl MOD k2
(* - умножение, / - деление нацело, MOD - взятие остатка от деления)
"ДИАЛОГ-МИФИ"