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

Танненбаум Е. Архітектура компютера [pdf]

.pdf
Скачиваний:
114
Добавлен:
02.05.2014
Размер:
5.59 Mб
Скачать

Введение в язык ассемблера

523

Для компьютеров семейства Intel существует несколько ассемблеров, которые отличаются друг от друга по синтаксису. В этой книге мы будем использовать язык ассемблера Microsoft MASM. Мы будем говорить о процессоре Pentium II, но все, что мы будем обсуждать, применимо и к процессорам 386,486, Pentium и Pentium Pro. Для процессора SPARC мы будем использовать ассемблер Sun. Все это также применимо к более ранним 32-битным версиям. В книге коды операций и регистры всегда обозначаются прописными буквами, причем не только в ассемблере для Pentium II, как это обычно принято, но и в ассемблере Sun, где по соглашению используются строчные буквы.

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

Вкаждом из трех примеров есть 4 метки: FORMULA, I, J и N. Отметим, что

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

адля Motorola — нет. В компьютерах Intel двоеточия ставятся только после меток команд, но не после меток данных. Данное различие вовсе не является фундаментальным. Разработчики разных ассемблеров имеют разные вкусы. Архитектура машины никак не определяет тот или иной выбор. Единственное преимущество двоеточия состоит в том, что метку можно писать на отдельной строке, а код операции — на следующей строке в колонке 1. Это упрощает работу компилятора: без двоеточия нельзя было бы отличить метку на отдельной строке от кода операции на отдельной строке.

Внекоторых ассемблерах длина метки ограничена до 6 или 8 символов. А в большинстве языков высокого уровня длина имен произвольна. Длинные и хорошо подобранные имена упрощают чтение и понимание программы другими людьми.

Вкаждой машине содержится несколько регистров, но всем им даны совершенно разные названия. Регистры в Pentium II называются ЕАХ, ЕВХ, ЕСХ и т. д. Регистры в Motorola называются DO, Dl, D2. Регистры в машине SPARC имеют несколько названий. Здесь для их обозначения мы будем использовать %R1 и %R2.

Вполе кода операции содержится либо символическая аббревиатура этого кода (если высказывание является символической репрезентацией машинной команды), либо команда для самого ассемблера. Выбор имени — дело вкуса, и поэтому разные разработчики языков ассемблера называют их по-разному. Разработчики ассемблера Intel решили использовать обозначение MOV и для загрузки регистра из памяти, и для сохранения регистра в память. Разработчики ассемблера Motorola выбрали обозначение MOVE для обеих операций. Аразработчики ассемблера SPARC

5 2 4 Глава 7. Уровень языка ассемблера

Version 9), а команды могут содержать максимум 22 бита данных. Следов чтобы передать все биты полного виртуального адреса, всегда требуется манды. Команда

SETHI янкп.та

обнуляет старшие 32 бита и младшие 10 битов 64-битного регистра R1, помещает старшие 22 бита 32-битного адреса переменной I в регистр R1 в позиции с 10 по 31. Следующая команда

юсш+шхш.да

складывает R1 и младшие 10 битов адреса I (в результате чего получается адрес I), вызывает данное слово из памяти и помещает его в регистр R1.

Процессоры семейства Pentium, 680x0 и SPARC — все допускают оп разной длины (типа byte (байт), word (слово) и long). Каким образом ас определит, какую длину использовать? И опять разработчики ассемблера ли разные решения. В Pentium II регистры разной длины имеют разны ния. Так, для перемещения 32-битных элементов используется название Е 16-битных — АХ, а для 8-битных — AL и АН. Разработчики ассемблера M решили прибавлять к каждому коду операции суффикс .L для типа long, . типа word и .В для типа byte. В SPARC для операндов разной длины ис ются разные коды операций (например, для загрузки байта, полуслова (h и слова в 64-битный регистр используются коды операций LDSB, LDSH и LD ветственно). Как видите, разработка языка произвольна.

Три ассемблера, которые мы рассматриваем, различаются по способу р рования пространства для данных. Разработчики языка ассемблера для I брали DW (Define Word — определить слово). Позднее был введен альтерна вариант .WORD. В Motorola используется DC (Define Constant — определи станту). Разработчики SPARC с самого начала предпочли .WORD. И слова р произвольны.

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

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

Директивы

Введение в язык ассемблера

блера. Мы уже видели одну типичную псевдокоманду DW (см. табл. 7.2). В та приведены некоторые другие псевдокоманды (директивы). Они взяты из ас лера MASM для семейства Intel.

Таблица 7.5. Некоторые директивы ассемблера MASM

Директива Значение

SEGMENT

Начинает новый сегмент (текста, данных и т.п.) с определенными атрибута

ENDS

Завершает текущий сегмент

ALIGN

Контролирует выравнивание следующей команды или данных

EQU

Определяет новый символ, равный данному выражению

DB

Выделяет память для одного или нескольких байтов

DD

Выделяет память для одного или нескольких 16-битных полуслов

DW

Выделяет память для одного или нескольких 32-битных слов

DQ

Выделяет память для одного или нескольких 64-битных двойных слов

PROC

Начинает процедуру

ENDP

Завершает процедуру

MACRO

Начинает макроопределение

ENDM

Завершает макроопределение

PUBLIC

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

EXTERN

Импортирует имя из другого модуля

INCLUDE

Вызывает другой файл и включает его в текущий файл

IF

Начинает условную компоновку программы на основе данного выражения

ELSE

Начинает условную компоновку программы, если условие IF над директиво

 

не выполнено

ENDIF

Завершает условную компоновку программы

COMMENT Определяет новый отделитель комментариев

PAGE

Совершает принудительный обрыв страницы в листинге

END

Завершает программу ассемблирования

Директива SEGMENT начинает новый сегмент, а директива ENDS завершае Разрешается начинать текстовый сегмент, затем начинать сегмент данных, переходить обратно к текстовому сегменту и т. д.

Директива ALIGN переводит следующую строку (обычно данные) в адрес, рый делим на аргумент данной директивы. Например, если текущий сегмен содержит 61 байт данных, тогда следующим адресом после ALIGN 4 будет адр Директива EQU дает символическое название некоторому выражению. Н

мер, после записи

BASE EQU 1000

символ BASE можно использовать вместо 1000. Выражение, которое следует

5 2 6 Глава 7. Уровень языка ассемблера

В,Ш v^ ръетсределэдэт тосштсъ для одшлт скольких переменных размером 1, 2,4 и 8 байтов соответственно. Наприме

TABLE D8 11 . 23 . 49

выделяет пространство для 3 байтов и присваивает им начальные значения 49 соответственно. Эта директива, кроме того, определяет символ TABLE, тому адресу, где хранится число 11.

Директивы PROC и ENDP определяют начало и конец процедур языка асс ра. Процедуры в языке ассемблера выполняют ту же функцию, что и в программирования высокого уровня. Директивы MACRO и ENDM определяют и конец макроса. О макросах мы будем говорить ниже.

Далее идут директивы PUBLIC и EXTERN. Программы часто пишут в виде со ности файлов. Часто процедуре, находящейся в одном файле, нужно вызва цедуру или получить доступ к данным, определенным в другом файле. Чт кие отсылки между файлами стали возможными, обозначение (имя), которое сделать доступным для других файлов, экспортируется с помощью дир PUBLIC. Чтобы ассемблер не ругался по поводу использования символа, кото определен в данном файле, этот символ может быть объявлен внешним (E это сообщит ассемблеру, что символ определен в каком-то другом файле. Си которые не определены ни в одной из этих директив, используются только делах одного файла. Поэтому даже если символ F00 используется в неск файлах, это не вызовет никакого конфликта, поскольку этот символ лока отношению к каждому файлу.

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

Многие языки ассемблера, в том числе MASM, поддерживают условну поновку программы. Например, программа

WORDSIZE EQU 16

IF WORDSIZE GT 16 WSIZE: DW32 ELSE

WSIZE: DW 16 ENDIF

выделяет в памяти одно 32-битное слово и вызывает его адрес WSIZE. Этом придается одно из значений: либо 32, либо 16 в зависимости от значения W (в данном случае 16). Такая конструкция может использоваться в прог для 16-битных машин (как 8088) или для 32-битных машин (как Pentium II в начале и в конце машинозависимого кода поставить IF и ENDIF, а затем из одно определение, WORDSIZE, программу можно автоматически установить н из двух размеров. Применяя такой подход, можно сохранять одну такую ис программу для нескольких разных машин. В большинстве случаев все маш

Макросы

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

Макросы

Программистам на языке ассемблера часто приходится повторять одни цепочки команд по несколько раз. Проще всего писать нужные команды раз, когда они требуются. Но если последовательность достаточно длинн если ее нужно повторять очень много раз, то это становится утомительны Альтернативный подход — оформить эту последовательность в процедур зывать ее в случае необходимости. У такой стратегии тоже есть свои недо поскольку в этом случае каждый раз придется выполнять специальную к вызова процедуры и команду возврата. Если последовательности команд кие (например, всего две команды), но используются часто, то вызов про может сильно снизить скорость работы программы. Макросы являются п

и эффективным решением этой проблемы.

Макроопределение, макровызов и макрорасширение

Макроопределение — это способ дать имя куску текста. После того как был определен, программист может вместо куска программы писать имя м В сущности, макрос — это обозначение куска текста. В листинге 7.1 приведе грамма на языке ассемблера для Pentium II, которая дважды меняет мест держимое переменных р и q. Эти последовательности команд можно опр как макросы (листинг 7.2). После определения макроса каждое имя SWAP в п ме замещается следующими четырьмя строками:

MOV EAX.P

MOV EBX.Q

MOV Q.EAX

MOV P.EBX

Программист определил SWAP как обозначение для этих четырех операт Хотя разные языки ассемблера используют немного разные записи для

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

1.Заголовок макроса, в котором дается имя определяемого макроса.

2.Текст, в котором приводится тело макроса.

3.Директива, которая завершает определение (например, ENDM).

528

Глава7. Уровень языка ассемблера

Листинг7 . 1 . Коднаязыкеассемблера,вкоторомпеременныериqдважды меняются местами (без использования макроса)

MOV EAX.P

MOV EBX.Q

MOV Q.EAX

MOV Р.ЕВХ

MOV EAX.P

MOV EBX.Q

MOV Q.EAX

MOV P.EBX

Листинг 7.2. Тот же код с использованием макроса

SWAP MACRO

MOV EAX.P

MOV EBX.Q

MOV Q.EAX

MOV P.EBX ENDM

SWAP

SWAP

Макрорасширение происходит во время процесса ассемблирования, а не мя выполнения программы. Этот момент очень важен. Программы, приве в листингах 7.1 и 7.2, произведут один и тот же машинный код. По програ машинном языке невозможно определить, использовались ли макросы пр рождении. После завершения макрорасширения ассемблер отбрасывает ма ширения. В полученной программе никаких признаков макросов не остает

Макровызовы не следует путать с вызовами процедур. Основное ра состоит в том, что макровызов — это команда ассемблеру заменить имя м телом макроса. Вызов процедуры — это машинная команда, которая вст в объектную программу и которая позднее будет выполнена, чтобы вызва цедуру. В табл. 7.6 сравниваются макровызовы и вызовы процедур.

Таблица 7.6. Сравнение макровызовов и вызовов процедур

 

Макровызов

Вызов проце

Когда совершается вызов

Во время ассемблирования Во время выпо

программы?

 

 

Вставляетсялителомакроса

Да

Нет

илипроцедурывобъектнуюпрограмму

 

 

каждыйраз,когдасовершаетсявызов?

 

 

Команда вызова процедуры

Нет

Да

вставляется в объектную программу,

 

 

Макросы 52

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

Важно иметь в виду, что программа представляет собой цепочку символов. Это могут быть буквы, цифры, пробелы, знаки пунктуации и «возврат каретки» (пере ход на новую строку). Макрорасширение состоит в замене определенных подцепочек из этой цепочки другими цепочками. Макросредства — это способ манипу лирования цепочками символов безотносительно их значений.

Макросы с параметрами

Макросредства, описанные ранее, можно использоватьдля сокращения программ в которых часто повторяется точно одна и та же последовательность команд. Одна ко очень часто программа содержит несколько похожих, но не идентичных после довательностей команд (листинг 7.3). Здесь первая последовательность меняет местами Р и Q, а вторая последовательность меняет местами R и S.

Листинг 7.3. Почти идентичные последовательности команд без использования макроса

MOV EAX.P

MOV EBX.Q

MOV Q.EAX

MOV P.EBX

MOV EAX.R

MOV EBX.S

MOV S.EAX

MOV R.EBX

Листинг 7.4. Те же последовательности с использованием макроса

CHANGE

MACRO

P1

.P2

 

MOV

EAX.P1

 

MOV

EBX.P2

 

MOV

P2.EAX

 

MOV

Pl.EBX

 

ENDM

 

 

CHANGE

P.Q

 

CHANGE

R.S

Для работы с такими почти идентичными последовательностями предусмот-

5 3 0 Глава 7. Уровень языка ассемблера

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

CHANGE P.Q

Р — это первый фактический параметр, a Q — это второй фактический п Таким образом, программы в листингах 7.3 и 7.4 идентичны.

Расширенные возможности

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

MASM и большинство других ассемблеров позволяют определять внутри других макросов. Эта особенность очень полезна в сочетании с у компоновкой программы. Обычно один и тот же макрос определятся в о тях оператора IF:

Ml

MACRO

 

IF

WORDSIZE GT 16 M2

MACRO

ENDM

ELSE

M2 MACRO

ENDM

ENDIF tNDM

В любом случае макрос М2 б>дет определен, но определение зависит о какой машине ассемблируется программа: на 16-битной или на 32-битн Ml не вызывается, макрос М2 вообще не будет определен.

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

Процесс ассемблирования

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

Когда встречается макроопределение, создается новый элемент таблицы с нем макроса, числом параметров и указателем на другую таблицу — таблицу ма определений, где будет храниться тело макроса. Список формальных параме тоже создается в это время. Затем считывается тело макроса и сохраняется в лице макроопределений. Формальные параметры, которые встречаются в цикла, указываются специальным символом.

Ниже приведен пример внутреннего представления макроса CHANGE. В к стве символа возврата каретки используется точка с запятой, а в качестве сим формального параметра — амперсант.

MOV EAX.&P1;MOV EBX.&P2:M0V &P2EAX;M0V &P1.EBX:

В таблице макроопределений тело макроса представляет собой просто цеп символов.

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

Процесс ассемблирования

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

Двухпроходной ассемблер

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

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

5 3 2 Глава 7. Уровень языка ассемблера

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

ной программе, но зато такая стратегия относительно проста.

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

Еще одна задача первого прохода — сохранить все макроопределения рить вызовы по мере их появления. Следовательно, в одном проходе прои определение символов, и расширение макросов.

Первый проход

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

BUFSIZE EQU 8192

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

адресакоманд (ILC —InstructionLocationCounter) (специальнуюперем Эта переменная устанавливается на 0 в начале первого прохода и увели после каждой обработанной команды на длину этой команды (табл. 7.7.). написан для Pentium П. Мы не будем давать примеры для SPARC и Moto скольку различия между языками ассемблера не очень важны и одного будет достаточно. Кроме того, язык ассемблера для SPARC неудобочитае

Соседние файлы в предмете Аппаратное обеспечение ЭВМ, средств телекоммуникаций и сетей