|
Раздел 6. Уровень языка ассемблераЯзык ассемблера – это язык, в котором каждое высказывание соответствует ровно одной машинной команде. Иными словами, существует взаимно однозначное соответствие между машинными командами и операторами в программе на языке ассемблера. Если каждая строка в программе на языке ассемблера содержит ровно один оператор и каждое машинное слово содержит ровно одну команду, то программа на языке ассемблера в n строк произведет программу на машинном языке из n слов. Программировать на языке ассемблера гораздо проще, т.к. это позволяет избежать работы с кодами команд. Многие могут запомнить, что обозначениями для сложения (add), вычитания (subtract), умножения (multiply) и деления (divide) служат команды ADD, SUB, MUL и DIV, но мало кто может запомнить соответствующие числа, которые использует машина. Программисту на языке ассемблера нужно знать только символические названия, поскольку ассемблер компилирует их в машинные команды. Все выше сказанное касается и адресов. Программист на языке ассемблера может дать имена ячейкам памяти, и уже ассемблер должен будет выдавать правильные числа. При работе с машинным языком программист всегда работает с числовыми номерами адресов, что гораздо сложнее. Сейчас уже нет программистов, которые пишут программы на машинном языке, хотя несколько десятилетий назад до изобретения ассемблеров программы именно так и писались. Язык ассемблера имеет несколько особенностей, отличающих его от языков высокого уровня. Во-первых, это взаимно однозначное соответствие между высказываниями языка ассемблера и машинными командами (об этом мы уже говорили). Во-вторых, программист на языке ассемблера имеет доступ ко всем объектам и командам, присутствующим на целевой машине. У программистов на языках высокого уровня такого доступа нет. Наконец, программа на языке ассемблера может работать только на компьютерах одного семейства, а программа, написанная на языке высокого уровня, потенциально может работать на разных машинах. Возможность переносить программное обеспечение с одной машины на другую очень важна для многих прикладных программ.
Язык ассемблера довольно труден. Написание программы на языке ассемблера занимает гораздо больше времени, чем написание той же программы на языке высокого уровня. Кроме того, очень много времени занимает отладка готовой программы. Но зачем же тогда вообще писать программы на языке ассемблера? Есть две причины, перевешивающие все трудности: производительность и доступ к машине. Во-первых, профессиональный программист языка ассемблера может составить гораздо меньшую по размеру программу, которая будет работать гораздо быстрее, чем программа, написанная на языке высокого уровня. Для некоторых программ скорость и размер весьма важны, к примеру, таких как встроенные прикладные программы в кредитных карточках, сотовых телефонах, драйверах устройств. Во-вторых, в некоторых случаях требуется полный доступ к аппаратному обеспечению, что обычно невозможно сделать на языке высокого уровня. В эту категорию попадают прерывания и обработчики прерываний в операционных системах, а также контроллеры устройств во встроенных системах, работающих в режиме реального времени. Тема 6.1. Формат оператора в языке ассемблера В результате изучения данной темы Вы будете:
Типичная программа, написанная на языке ассемблера, представляет собой набор строк, в каждой из которых имеется ровно одна команда (которая, в свою очередь, соответствует ровно одной машинной команде). Строку, написанную на языке ассемблера, называют оператором, или высказыванием. Хотя структура оператора в языке ассемблера отражает структуру соответствующей машинной команды, языки ассемблера для разных машин и разных уровней во многом сходны друг с другом, что позволяет говорить о языке ассемблера вообще. Для компьютеров семейства Intel существует несколько ассемблеров, которые отличаются друг от друга по синтаксису. Мы будем использовать язык ассемблера Microsoft MASM. Высказывания языка ассемблера состоят из четырех полей: поля метки, поля операции, поля операндов и поля комментариев (Таблица 6.1).
Таблица 6.1. Вычисление выражения N=I+J в Pentium II Метки Метки используются для того, чтобы обеспечить символические имена для адресов памяти. Они нужны для того, чтобы можно было совершить переход к командам, а также для слов с данными, чтобы по символическому имени можно было получить доступ к тому месту, где они хранятся. Если высказывание снабжено меткой, то эта метка обычно располагается в колонке 1 (Таблица 6.1). Метка в языке Ассемблер может содержать следующие символы:
точка (.) (только первый символ) знак-"коммерческое эт" (@) подчеркивание(_) доллар ($) Первым символом в метке должна быть буква или специальный символ. Ассемблер не делает различия между заглавными и строчными буквами. Для примера можно привести метки: COUNT, PAGE25, $Е10. Рекомендуется использовать описательные и смысловые метки. Все имена регистров, например, АХ, DI или AL, являются зарезервированными и используются только для указания соответствующих регистров. Команды В поле команды содержится либо символическая аббревиатура кода операции (если высказывание является символической репрезентацией машинной команды), либо команда для самого ассемблера. Операнды В поле операндов определяются адреса и регистры, которые являются операндами для машинной команды. В поле операндов команды целочисленного сложения сообщается, что и к чему нужно прибавить, поле операндов команд перехода определяет, куда нужно совершить переход. Операндами могут быть регистры, константы, ячейки памяти и т. д. Комментарии В поле комментариев приводятся пояснения о действиях программы. Они могут понадобиться программистам, которые будут использовать и переделывать чужую программу, или программисту, который изначально писал программу и возвратился к работе над ней через некоторый промежуток времени. Программа на ассемблере без таких комментариев совершенно непонятна программистам (даже автору этой программы). Комментарии нужны только человеку, они никак не влияют на работу программы. Подведем итоги
Вопросы для самоконтроля 1. Можете ли вы представить себе обстоятельства, при которых метка совпадет с кодом операции (например, может ли быть MOV меткой)? Аргументируйте свой ответ. 2. Чем отличается счетчик адреса команд от счетчика команд? А существует ли вообще между ними различие? Ведь и тот и другой следят за следующей командой в программе. 3. Какие из следующих имен неправильны: а) PC_AT, б) $50, в) @$_z, г) 34b7, д) EAX? Тема 6.2. Директивы В результате изучения данной темы Вы будете:
Программа на языке ассемблера должна не только определять, какие машинные команды нужно выполнить, но и содержать команды, которые должен выполнять сам ассемблер (например, потребовать от него определить местонахождение какой-либо сохраненной информации или выдать новую страницу листинга). Команды для ассемблера называютсяпсевдокомандами илидирективамиассемблера. Мы уже видели одну типичную псевдокоманду DW (Таблица 6.1), ниже приведены некоторые другие директивы. Они взяты из ассемблера MASM для семейства Intel (Таблица 6.2).
Таблица 6.2. Некоторые директивы ассемблера MASM Директива SEGMENT начинает новый сегмент, а директива ENDS завершает его. Разрешается начинать текстовый сегмент, затем начинать сегмент данных, затем переходить обратно к текстовому сегменту и т. д. Директива ALIGN переводит следующую строку (обычно данные) в адрес, который делим на аргумент данной директивы. Например, если текущий сегмент уже содержит 61 байт данных, тогда следующим адресом после ALIGN 4 будет адрес 64. Директива EQU дает символическое название некоторому выражению. Например, после записи: BASE EQU 1000 символ BASE можно использовать вместо 1000. Выражение, которое следует за EQU, может содержать несколько символов, соединенных арифметическими и другими операторами, например: LIMIT EQU 4 * BASE + 2000 Большинство ассемблеров, в том числе MASM, требуют, чтобы символ был определен в программе до появления в некотором выражении. Следующие 4 директивы DB, DD, DW и DQ распределяют память для одной или нескольких переменных размером 1, 2, 4 и 8 байтов соответственно. Например: TABLE DB 11, 23, 49 выделяет пространство для 3 байтов и присваивает им начальные значения 11, 23 и 49 соответственно. Эта директива, кроме того, определяет символ TABLE, равный тому адресу, где хранится число 11. Директивы PROC и ENDP определяют начало и конец процедур языка ассемблера. Процедуры в языке ассемблера выполняют ту же функцию, что и в языках программирования высокого уровня. Директивы MACRO и ENDM определяют начало и конец макроса. Далее идут директивы PUBLIC и EXTERN. Программы часто пишут в виде совокупности файлов (модулей). Часто процедуре, находящейся в одном файле, нужно вызвать процедуру или получить доступ к данным, определенным в другом файле. Чтобы такие отсылки между файлами стали возможными, обозначение (имя), которое нужно сделать доступным для других файлов, экспортируется с помощью директивы PUBLIC. Чтобы ассемблер не «ругался» по поводу использования символа, который не определен в данном файле, этот символ может быть объявлен внешним (EXTERN), это сообщит ассемблеру, что символ определен в каком-то другом файле. Символы, которые не определены ни в одной из этих директив, используются только в пределах одного файла. Директива INCLUDE приказывает ассемблеру вызвать другой файл и включить его в текущий файл. Такие включенные файлы часто содержат определения, макросы и другие элементы, необходимые для разных файлов. Директива COMMENT позволяет пользователю изменять символ комментария на что-либо отличное от точки с запятой. Директива PAGE используется для управления распечаткой листинга (текста) программы. Наконец, директива END отмечает конец программы. Подведем итоги
Вопросы для самоконтроля 1. Чем отличается команда от директивы? 2. Какова длина в байтах для элементов данных, определенных директивами: а) DW, б) DD, в) DB, г) DQ? 3. Укажите различия в значении RET и END. 4. Какая директива заставляет делать прогон листа? Тема 6.3. Макросы В результате изучения данной темы Вы будете:
Программистам на языке ассемблера часто приходится повторять одни и те же цепочки команд по несколько раз. Проще всего писать нужные команды всякий раз, когда они требуются, но если последовательность достаточно длинная или если ее нужно повторять очень много раз, то это становится утомительным. Альтернативный подход – оформить эту последовательность в процедуру и вызывать ее в случае необходимости. У такой стратегии тоже есть свои недостатки, поскольку в этом случае каждый раз придется выполнять специальную команду вызова процедуры и команду возврата. Если последовательности команд короткие (например, всего две команды), но используются часто, то вызов процедуры может сильно снизить скорость работы программы. Макросы являются простым и эффективным решением этой проблемы. Макроопределение – это способ дать имя куску текста. После того как макрос был определен, программист может вместо куска программы писать имя макроса. В сущности, макрос – это обозначение куска текста. Ниже приведена программа, которая дважды меняет местами содержимое переменных P и Q (Листинг 6.1). Эти последовательности команд можно определить как макросы (Листинг 6.2).
MOV EAX, P MOV EBX, Q MOV Q, EAX MOV P, EBX
MOV EAX, P MOV EBX, Q MOV Q, EAX MOV P, EBX Листинг 6.1. Код на языке ассемблера, в котором содержимое переменных P и Q дважды меняются местами (без использования макроса)
SWAP MACRO MOV EAX, P MOV EBX, Q MOV Q, EAX MOV P, EBX ENDM
SWAP SWAP Листинг 6.2. Тот же код, но с использованием макроса После определения макроса каждое имя SWAP в программе замещается следующими четырьмя строками: MOV EAX, P MOV EBX, Q MOV Q, EAX MOV P, EBX Программист определил SWAP как обозначение для этих четырех операторов. Макросы состоят из базовых частей:
Когда ассемблер наталкивается на макроопределение в программе, он сохраняет его в таблице макроопределений для последующего использования. Всякий раз, когда в программе в качестве кода операции появляется макрос (в нашем примере SWAP), ассемблер замещает его телом макроса. Использование имени макроса в качестве кода операции называетсямакровызовом, а его замещение телом макроса называетсямакрорасширением. Макросы могут содержать параметры. Например, макрос SWAP можно переписать как: SWAP MACRO P1, P2 MOV EAX, P1 MOV EBX, P2 MOV P2, EAX MOV P1, EBX ENDM Таким образом, программа примет вид: SWAP P, Q SWAP P, Q А макрос SWAP можно будет применять не только для переменных P и Q, но и любых других. Подведем итоги
Вопросы для самоконтроля 1. Что такое макрос, макровызов, макрорасширение? 2. Может ли регистр использоваться в качестве фактического параметра в макровызове? А константа? Объясните свой ответ. 3. Чем отличается макрос от вызова процедуры? 4.
Индивидуальные задания Напишите макрос SWAP для обмена содержимым двух регистров. _________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________ ![]() ![]() ЧТО ПРОИСХОДИТ ВО ВЗРОСЛОЙ ЖИЗНИ? Если вы все еще «неправильно» связаны с матерью, вы избегаете отделения и независимого взрослого существования... ![]() Что делает отдел по эксплуатации и сопровождению ИС? Отвечает за сохранность данных (расписания копирования, копирование и пр.)... ![]() Что вызывает тренды на фондовых и товарных рынках Объяснение теории грузового поезда Первые 17 лет моих рыночных исследований сводились к попыткам вычислить, когда этот... ![]() ЧТО ПРОИСХОДИТ, КОГДА МЫ ССОРИМСЯ Не понимая различий, существующих между мужчинами и женщинами, очень легко довести дело до ссоры... Не нашли то, что искали? Воспользуйтесь поиском гугл на сайте:
|