Введение.
Язык, на котором написана исходная программа, называется входным языком, а язык, на который она переводится для исполнения процессором, — выходным языком. Процесс преобразования входного языка в выходной язык называется трансляцией. Так как процессоры способны выполнять программы на машинном языке двоичных кодов, который не используется для программирования, то необходима трансляция всех исходных программ. Известны два способа трансляции: компиляция и интерпретация.
При компиляции исходная программа сначала полностью переводится в эквивалентную программу на выходном языке, называемую объектной программой, а затем выполняется. Этот процесс реализуется с помощью специальной программы, называемой компилятором. Компилятор, для которого входной язык является символической формой представления машинного (выходного) языка двоичных кодов, называется ассемблером.
При интерпретации каждая строка текста исходной программы анализируется (интерпретируется) и указанная в ней команда сразу выполняется. Реализация такого способа возлагается на программу–интерпретатор. Интерпретация занимает длительное время. Для повышения ее эффективности вместо обработки каждой строки интерпретатор предварительно осуществляет преобразование всех командных строк в символы (
token
). Сформированная последовательность символов используется для выполнения возложенных на исходную программу функций.
Рассматриваемый ниже язык ассемблера реализуется с помощью компиляции.
Особенности языка.
Основные особенности ассемблера:
? вместо двоичных кодов в языке используются символьные имена — мнемоника. Например, для команды сложения (
add
) используется мнемоника
ADD
, вычитания (
subtract
) —
SUB,
умножения (
multiply
) —
MUL
, деления (
divide
) —
DIV
и т. д. Символьные имена используются и для адресации ячеек памяти. Для программирования на языке ассемблера вместо двоичных кодов и адресов нужно знать только символические названия, которые ассемблер транслирует в двоичные коды;
? каждое высказывание соответствует одной машинной команде (коду), т. е. существует взаимно однозначное соответствие между машинными командами и операторами в программе на языке ассемблера;
? язык обеспечивает доступ ко всем объектам и командам. Языки высокого уровня такой способностью не обладают. Например, язык ассемблера позволяет выполнить проверку бита регистра флагов, а язык высокого уровня (например,
Java
) такой способностью не обладает. Отметим, что языки для системного программирования (например, С) часто занимают промежуточное положение. С точки зрения возможностей доступа они ближе к языку ассемблера, однако обладают синтаксисом языка высокого уровня;
? язык ассемблера не является универсальным языком. Для каждой определенной группы микропроцессоров имеется свой ассемблер. Языки высокого уровня лишены этого недостатка.
В отличие от языков высокого уровня написание и отладка программы на языке ассемблера занимает много времени. Несмотря на это, язык ассемблера получил широкое распространение в силу следующих обстоятельств:
? составленная на языке ассемблера программа имеет значительно меньшие размеры и работает гораздо быстрее, чем программа, написанная на языке высокого уровня. Для некоторых приложений эти показатели играют первостепенную роль, например, многие системные программы (в том числе компиляторы), программы в кредитных карточках, сотовых телефонах, драйверах устройств и др.;
? некоторым процедурам требуется полный доступ к аппаратному обеспечению, что обычно невозможно осуществить на языке высокого уровня. К этому случаю относятся прерывания и обработчики прерываний в операционных системах, а также контроллеры устройств во встроенных системах, работающих в режиме реального времени.
В большинстве программ лишь небольшой процент всего кода отвечает за большой процент времени выполнения программы. Обычно 1% программы отвечает за 50% времени выполнения, а 10% программы отвечает за 90% времени выполнения. Поэтому для написания конкретной программы в реальных условиях используется как ассемблер, так и один из языков высокого уровня.
Формат оператора в языке ассемблера.
Программа на языке ассемблера представляет собой список команд (высказываний, предложений), каждая из которых занимает отдельную строку и содержит четыре поля: поле метки, поле операции, поле операндов и поле комментариев. Для каждого поля отводится отдельная колонка.
Поле метки.
Для поля метки отводится колонка 1. Метка является символическим именем, или идентификатором, адреса памяти. Она необходима для того, чтобы можно было:
? совершить условный или безусловный переход к команде;
? получить доступ к месту, где хранятся данные.
Такие высказывания снабжаются меткой. Для обозначения имени используются (прописные) буквы английского алфавита и цифры. В начале имени должна стоять буква, в конце — разделитель в виде двоеточия. Метку с двоеточием можно писать на отдельной строке, а код операции — на следующей строке в колонке 2, что упрощает работу компилятора. Отсутствие двоеточия не позволяет отличить метку от кода операции, если они расположены на отдельных строках.
В некоторых версиях языка ассемблера двоеточия ставятся только после меток команд, но не после меток данных, а длина метки может быть ограничена 6 или 8 символами.
В поле метки не должно встречаться одинаковых имен, так как метка ассоциируется с адресами команд. Если в процессе выполнения программы отсутствует необходимость вызова команды или данных из памяти, то поле метки остается пустым.
Поле кода операции.
Это поле содержит мнемокод команды или псевдокоманды (см. ниже). Мнемокод команд выбирается разработчиками языка. В языке ассемблера
SPARC
для загрузки регистра из памяти выбрана мнемоника
LD
(
Load
), а для сохранения содержимого регистра в памяти — мнемоника
ST
(
Storage
). В языках ассемблера
Intel
и
Motorola
для обеих операций можно использовать одно имя, соответственно
MOV
,
MOVE
. Если выбор названий мнемоник может быть произвольным, то необходимость использования двух машинных команд обусловлено архитектурой процессоров
SPARC
.
Мнемоника регистров также зависит от версии ассемблера (табл. 5.2.1).
Поле операнда.
Здесь располагается дополнительная информация, необходимая для выполнения операции. В поле операндов для команд перехода указан адрес, куда нужно совершить переход, а также заданы адреса и регистры, которые являются операндами для машинной команды. В качестве примера приведем операнды, которые могут быть использованы для 8–разрядных процессоров
INTEL
:
? числовые данные,
представленные в различных системах счисления. Для обозначения используемой системы счисления за константой следует одна из латинских букв: В,
Q
, Н,
D
— соответственно двоичная, восьмеричная, шестнадцатеричная, десятичная системы счисления (
D
можно не записывать). Если первой цифрой шестнадцатеричного числа являются А, В, С,
D
, Е,
F
, то впереди добавляется незначащий 0 (нуль);
? коды внутренних регистров микропроцессора и ячейки памяти
М (источников или приемников информации) в виде букв А, В, С,
D
, Е, Н,
L
, М или их адреса в любой системе счисления (например, 10В — адрес регистра
D
в двоичной системе);
? идентификаторы,
для регистровых пар ВС,
DE
,
HL
— первые буквы В,
D
, Н; для пары аккумулятора и регистра признаков —
PSW
; для счетчика команд —
PC
;для указателя стека —
SP
;
? метки, указывающие адреса операндов или следующих команд в условных
(при выполнении условия) и безусловных переходах. Например, операнд М1 в команде
JMP
Ml
означает необходимость безусловного перехода к команде, адрес которой в поле метки отмечен идентификатором М1;
? выражения,
которые строятся путем связывания рассмотренных выше данных с помощью арифметических и логических операторов. Отметим, что способ резервирования пространства для данных зависит от версии языка. Разработчики языка ассемблера для
Intel
выбрали
DW
(
Define
Word
— определить слово), а позднее ввели альтернативный вариант .
WORD
,
который с самого начала был в языке для процессоров
SPARC
. В версии языка
Motorola
используется
DC
(
Define
Constant
— определить константу).
Процессоры обрабатывают операнды разной длины. Для ее определения разработчики ассемблера приняли разные решения, например:
? в
Pentium
II регистры разной длины имеют разные названия: ЕАХ — для размещения 32–битных операндов (тип
long
); АХ — для 16–битных (тип
word
), а
AL
и АН — для 8–битных (тип
byte);
? для процессоров
Motorola
к каждому коду операции прибавляются суффиксы: суффикс
«.L»
для типа
long;
суффикс
«.W»
— для типа
word
; суффикс «.В» для типа
byte;
? в
SPARC
для операндов разной длины используются разные коды операций, например, для загрузки байта, полуслова (
halfword
) и слова в 64–битный регистр используются коды операций
LDSB
,
LDSH
и
LDSW
соответственно.
Поле комментариев.
В этом поле приводятся пояснения о действиях программы. Комментарии не влияют на работу программы и предназначены человеку. Они могут понадобиться для модификации программы, которая без таких комментариев может быть совершенно непонятна даже опытным программистам. Комментарий начинается с символа и используется для пояснения и документирования программ. Начальным символом комментария могут служить:
? точка с запятой (;) в языках для процессоров фирмы
INTEL
;
? восклицательный знак (!) в языках для
SPARC
.
Каждая отдельная строка, отведенная под комментарий, предваряется начальным символом.
Псевдокоманды (директивы).
В языке ассемблера можно выделить два основных вида команд:
? базовые команды, являющиеся эквивалентом машинного кода процессора. Эти команды выполняют всю предусмотренную программой обработку;
? псевдокоманды, или директивы, предназначенные для обслуживания процесса трансляции программы на язык кодовых комбинаций. В качестве примера в табл. 5.2.2 приведены некоторые псевдокоманды из ассемблера
MASM
для семейства
Intel
.
Макросы.
При программировании встречаются ситуации, когда согласно алгоритму одну и ту же цепочку команд необходимо многократно повторить. Для выхода из этой ситуации можно:
? писать нужную последовательность команд всякий раз, когда она встречается. Такой подход приводит к увеличению объема программы;
? оформить эту последовательность в процедуру (подпрограмму) и вызывать ее при необходимости. Такой выход имеет свои недостатки: каждый раз придется выполнять специальную команду вызова процедуры и команду возврата, что при короткой и часто используемой последовательности может сильно снизить скорость работы программы.
Наиболее простой и эффективный способ многократного повторения цепочки команд состоит в использовании макроса, который можно представить как псевдокоманду, предназначенную для повторной трансляции часто встречающейся в программе группы команд.
Макрос, или макрокоманда, характеризуется тремя аспектами: макроопределением, макрообращением и макрорасширением.
Макроопределение
— это обозначение многократно повторяемой последовательности команд программы, используемое для ссылок в тексте программы.
Макроопределение имеет следующую структуру:
NAME
MACRO
Список выражений ; Макроопределение
… …
ENDM
В приведенной структуре макроопределения можно выделить три части:
? заголовок
макроса, включающий в себя имя
NAME
, псевдокоманду
MACRO
и набор параметров;
? отмеченное точками тело макроса;
? команда
ENDM
окончания
макроопределения.
Набор параметров макроопределения содержит перечень всех параметров, приведенных в поле операнда для выбранной группы команд. Если эти параметры приведены в программе ранее, то их в заголовке макроопределения можно не указывать.
Для повторного ассемблирования выбранной группы команд используется обращение, состоящее из имени
NAME
макрокоманды и перечня параметров с другими значениями.
Когда в процессе компиляции ассемблер встречает макроопределение, он сохраняет его в таблице макроопределений. При последующих появлениях в программе имени (
NAME
) макроса ассемблер замещает его телом макроса.
Использование имени макроса в качестве кода операции называется макро–обращением (макровызовом), а его замещение телом макроса — макрорасширением.
Если программу представить как последовательность символов (букв, цифр, пробелов, знаков пунктуации и возврата каретки для перехода на новую строку), то макрорасширение состоит в замене одних цепочек из этой последовательности другими цепочками.
Макрорасширение происходит во время процесса ассемблирования, а не во время выполнения программы. Способы манипулирования цепочками символов возлагается на макросредства.
Процесс ассемблирования осуществляется в два прохода:
? на первом проходе сохраняются все макроопределения, а макровызовы расширяются. При этом исходная программа считывается и преобразуется в программу, в которой удалены все макроопределения, а каждый макровызов замещен телом макроса;
? на втором проходе обрабатывается полученная программа без макросов.
Макросы с параметрами.
Для работы с повторяющимися последовательностями команд, параметры которых могут принимать различные значения, предусмотрены макроопределения:
? с фактическими параметрами, которые помещаются в поле операндов макро-обращения;
? с формальными параметрами. В процессе расширения макроса каждый формальный параметр, появляющийся в теле макроса, замещается соответствующим фактическим параметром.
Пример
использования макросов с параметрами.
В программе 1 приведено две похожих последовательности команд, отличающихся тем, что первая из них меняет местами Р и
Q
, а вторая
R
и
S
.
В программе 2 включен макрос с двумя формальными параметрами Р1 и Р2. Во время расширения макроса каждый символ Р1 внутри тела макроса замещается первым фактическим параметром (Р,
R
), а символ Р2 замещается вторым фактическим параметром (
Q
,
S
) из программы № 1. В макровызове
CHANGE
P
,
Q
и
CHANGE
R
,
S
программы 2 обозначено: Р,
R
— первый фактический параметр,
a
Q
,
S
— второй фактический параметр.
Программа 1
Программа 2
MOV
EAX,P
CHANGE
MACRO
P1,
P2
MOV EBX,Q MOV EAX,Pl
MOV Q,EAX MOV EBX,P2
MOV P,EBX MOV P2,EAX
MOV
P1,EBX
ENDM
MOV EAX,R
MOV
EBX,S
CHANGE
P,Q
MOV S,EAX
MOV R,EBX
CHANGE
R,S
Расширенные возможности.
Рассмотрим некоторые расширенные возможности языка
MASM
.
Если макрос, содержащий команду условного перехода и метку, к которой совершается переход, вызывается два и более раз, то метка будет дублироваться (проблема дублирование меток), что вызовет ошибку. Поэтому при каждом вызове в качестве параметра приписывается (программистом) отдельная метка. В языке
MASM
метка объявляется локальной (
LOCAL
) и благодаря расширенным возможностям ассемблер автоматически порождает другую метку при каждом расширении макроса.
Язык
MASM
позволяет определять макросы внутри других макросов. Такая расширенная возможность весьма полезна в сочетании с условной компоновкой программы. Рассмотрим
пример
:
Ml MACRO
IF WORDSIZE GT 16 M2 MACRO
…
ENDM
ELSE
M
2
MACRO
ENDM
END
IF
ENDM
Макрос М2 может быть определен в обеих частях оператора
IF
. Однако определение зависит от того, на каком процессоре ассемблируется программа: на 16–битном или на 32–битном. Если М1 не вызывается, то макрос М2 вообще не будет определен.
Еще одна расширенная возможность состоит в том, что макросы могут вызывать другие макросы, в том числе самих себя — рекурсивный вызов. В последнем случае, чтобы не получился бесконечный цикл, макрос должен передавать самому себе параметр, который изменяется при каждом расширении, а также проверять этот параметр и завершать рекурсию, когда параметр достигает определенного значения.
Об использовании макросредств в ассемблере.
При использовании макросов ассемблер должен уметь выполнять две функции: сохранять макроопределения и расширять макровызовы.
Сохранение макроопределений.
Все имена макросов хранятся в таблице. Каждое имя сопровождается указателем на соответствующий макрос, чтобы в случае необходимости его можно было вызвать. Одни ассемблеры имеют отдельную таблицу для имен макросов, другие — общую таблицу, в которой наряду с именами макросов находятся все машинные команды и директивы.
При встрече с макросом в процессе ассемблирования создается:
? новый элемент таблицы с именем макроса, числом параметров и указателем на другую таблицу макроопределений, где будет храниться тело макроса;
? список формальных параметров.
Затем считывается и сохраняется в таблице макроопределений тело макроса, представляющее собой просто цепочку символов. Формальные параметры, встречающиеся в теле цикла, помечаются специальным символом.
Внутреннее представление макроса
CHANGE
из приведенного выше примера для программы 2 (стр. 244) имеет вид:
MOV EAX,&P1; MOV EBX,&P2; MOV &P2EAX;MOV &
Р
1,
ЕВХ
;
где в качестве символа возврата каретки используется точка с запятой, а в качестве символа формального параметра — амперсант &.
Расширение макровызовов.
Всякий раз, когда при ассемблировании встречается макроопределение, оно сохраняется в таблице макросов. При вызове макроса ассемблер временно приостанавливает чтение входных данных из входного устройства и начинает считывать сохраненное тело макроса. Извлеченные из тела макроса формальные параметры замещаются фактическими параметрами и предоставляются вызовом. Амперсант & перед параметрами позволяет ассемблеру распознать их.
Несмотря на то, что существует много версий ассемблера, процессы ассемблирования имеют общие черты и во многом сходны. Ниже рассматривается работа двухпроходного ассемблера.
Двухпроходной ассемблер.
Программа состоит из ряда операторов. Поэтому, казалось бы, что при ассемблировании можно использовать следующую последовательность действий:
? считать оператор;
? транслировать его на машинный язык;
? перенести полученный машинный код в файл, а соответствующую часть листинга — в другой файл;
? повторять перечисленные процедуры до тех пор, пока вся программа не будет оттранслирована.
Однако такой подход не является эффективным. Примером может служить так называемая проблема опережающей ссылки. Если первым оператором является переход к оператору Р, расположенному в самом конце программы, то ассемблер не может транслировать его. Он сначала должен определить адрес оператора Р, а для этого необходимо прочитать всю программу. Каждое полное прочтение исходной программы называется проходом. Покажем, как можно решить проблему опережающей ссылки с использованием двух проходов:
? на первом проходе следует собрать и сохранить в таблице все определения символов (в том числе меток), а на втором проходе — выполнить чтение и ассемблирование каждого оператора. Такой способ относительно прост, однако второй проход по исходной программе требует дополнительных временных затрат на операции ввода–вывода;
? на первом проходе следует преобразовать программу в промежуточную форму и сохранить ее в таблице, а второй проход выполнить не по исходной программе, а по таблице. Такой способ ассемблирования позволяет сэкономить время, так как на втором проходе не выполняются операции ввода–вывода.
Для вывода листинга нужно сохранить полностью исходное выражение, включая комментарии. Если листинг не нужен, то промежуточную форму можно сократить, оставив только одни команды.
Ассемблер, способный читать исходную программу дважды, называется двух –проходным ассемблером.
Первый проход.
Цель первого прохода — построить таблицу символов. Как отмечалось выше, еще одной задачей первого прохода является сохранение всех макроопределений и расширение вызовов по мере их появления. Следовательно, в одном проходе происходит и определение символов, и расширение макросов. Символом может быть либо метка, либо значение, которому с помощью директивы приписывается определенное имя:
МЕТКА:
;Метка
BUFSIZE
EQU
8192
;Значение — размер буфера
Придавая значения символьным именам в поле метки команд, ассемблер по сути дела задает адреса, которые будет иметь каждая команда во время выполнения программы. Для этого ассемблер во время процесса ассемблирования сохраняет счетчик адреса команд (
Instruction
Location
Counter
—
ILC
) как специальную переменную. В начале первого прохода значение специальной переменной устанавливается на 0 и увеличивается после каждой обработанной команды на длину этой команды. В качестве примера в табл. 5.2.3 приведен фрагмент программы с указанием длины команд и значений счетчика. При первом проходе формируются таблицы символьных имен, директив и кодов операций, а при необходимости литеральная таблица. Литерал — это константа, для которой ассемблер автоматически резервирует память. Сразу же отметим, что современные процессоры содержат команды с непосредственными адресами, поэтому их ассемблеры не поддерживают литералы.
Таблица символьных имен
содержит один элемент для каждого имени (табл. 5.2.4). В каждом элементе таблицы символьных имен содержится само имя (или указатель на него), его численное значение и иногда некоторая дополнительная информация, которая может включать:
? длину поля данных, связанного с символом;
? биты перераспределения памяти (которые показывают, изменяется ли значение символа, если программа загружается не в том адресе, в котором предполагал ассемблер);
? сведения о том, можно ли получить доступ к символу извне процедуры.
Символьные имена являются метками. Они могут быть заданы с помощью операторов (например,
EQU
).
Таблица директив.
В этой таблице приводятся все директивы, или псевдокоманды, которые встречаются при ассемблировании программы.
Таблица кодов операций.
Для каждого кода операции в таблице предусмотрены отдельные графы: обозначение кода операции, операнд 1, операнд 2, 16–ричное значение кода операции, длина команды и тип команды (табл. 5.2.5). Коды операций делятся на группы в зависимости от числа и вида операндов. Тип команды определяет номер группы и задает процедуру, которая вызывается для обработки всех команд данной группы.
Второй проход.
Цель второго прохода — создание объектной программы и распечатка при необходимости протокола ассемблирования; вывод информации, необходимой компоновщику для связывания процедур, которые ассемблировались в разное время, в один выполняемый файл.
При втором проходе (как и при первом) строки, содержащие операторы, считываются и обрабатываются одна за другой. Исходный оператор и полученный из него в шестнадцатеричной системе выходной объектный код можно напечатать или поместить в буфер для последующей распечатки. После переустановки счетчика адреса команды вызывается следующий оператор.
Исходная программа может содержать ошибки, например:
? приведенный символ не определен или определен более одного раза;
? код операции представлен недопустимым именем (из–за опечатки), не снабжен достаточным количеством операндов или имеет слишком много операндов;
? отсутствует оператор
END
и др.
Некоторые ассемблеры могут выявить неопределенный символ и заменить его. Однако в большинстве случаев при обнаружении оператора с ошибкой ассемблер выводит сообщение об ошибке на экран и пытается продолжить процесс ассемблирования.
Вторая частьстатьи посвященной языку ассемблер.
Встретят у себя, приедут к тебе, феи проститутки в Красноярске, интим досуг на любой вкус. Все вопросы уйдут на второй план, если вы вместо поиска обычных девушек воспользуйтесь услугами проституток города. Изящные феи проститутки в Красноярске, желанные и восхитительные, они такие игривые и молоденькие, что мужчины тают под их чарами. Выбери девушку сейчас.
Изложение более- менее понятное, но написано в столбик, а говорится о строке и понимай как хочешь или вычисляй вдруг угадаешь.