Полезное:
Как сделать разговор полезным и приятным
Как сделать объемную звезду своими руками
Как сделать то, что делать не хочется?
Как сделать погремушку
Как сделать так чтобы женщины сами знакомились с вами
Как сделать идею коммерческой
Как сделать хорошую растяжку ног?
Как сделать наш разум здоровым?
Как сделать, чтобы люди обманывали меньше
Вопрос 4. Как сделать так, чтобы вас уважали и ценили?
Как сделать лучше себе и другим людям
Как сделать свидание интересным?
Категории:
АрхитектураАстрономияБиологияГеографияГеологияИнформатикаИскусствоИсторияКулинарияКультураМаркетингМатематикаМедицинаМенеджментОхрана трудаПравоПроизводствоПсихологияРелигияСоциологияСпортТехникаФизикаФилософияХимияЭкологияЭкономикаЭлектроника
|
Двухпроходный Ассемблер — второй проход
Обычно 2-й проход Ассемблера читает исходный модуль с самого начала и отчасти повторяет действия 1-го прохода (лексический разбор, распознавание команд и директив, подсчет адресов). Это, однако, скорее дань традиции — с тех времен, когда в вычислительных системах ощущалась нехватка (или даже полное отсутствие) внешней памяти. В те далекие времена колода перфокарт или рулон перфоленты, содержащие текст модуля, вставлялись в устройство ввода повторно. В системах с эволюционным развитием сохраняются перфокарты и перфоленты (виртуальные), так что схема работы Ассемблера — та же. В новых системах Ассемблер может создавать промежуточный файл — результат 1-го прохода, который является входом для 2-го прохода. Каждому оператору исходного модуля соответствует одна запись промежуточного файла, и формат этой записи приблизительно такой:
Текст исходного оператора нужен только для печати листинга, Ассемблер на 2-м проходе использует только первые 4 поля записи. Первое поле позволяет исключить строки, целиком состоящие из комментария. Второе поле позволяет избежать подсчета адресов, третье — поиска мнемоники в таблицах. Основная работа 2-го прохода состоит в разборе поля операндов и генерации объектного кода. Некоторые общие соображения, касающиеся этой работы. Объектный код команды состоит из поля кода операции и одного или нескольких полей операндов. Код операции, как правило, имеет размер 1 байт, количество, формат и семантика полей операндом определяется для каждого типа команд данной аппаратной платформы. В общем случае операндом команды может быть: u регистр; u непосредственный операнд; u адресное выражение. Виды адресных выражений зависят от способов адресации вычислительной системы, некоторые (возможно, наиболее типовые) способы адресации: u абсолютный адрес; u [базовый регистр]+смещение (здесь и далее квадратные скобки означают «содержимое того, что взято в скобки»); u [базовый регистр]+[индексный регистр]+смещение; u имя+смещение; u литерал. Адресное выражение моет содержать арифметические операции, соображения, касающиеся арифметики в этом случае — те же, что и в адресной арифметике языка C. Имена в адресных выражениях должны заменяться на значения. Замена абсолютных имен (определенных в директиве EQU) очень проста — значение имени из таблицы символов просто подставляется вместо имени. Перемещаемые имена (метки и имена переменных) превращаются Ассемблером в адресное выражение вида [базовый регистр]+смещение. В таблице символов значения этих имен определены как смещение соответствующих ячеек памяти относительно начала программы. При трансляции имен необходимо, чтобы: u Ассемблер «знал», какой регистр он должен использовать в качестве базового; u Ассемблер «знал», какое значение содержится в базовом регистре; u в базовом регистре действительно содержалось это значение. Первые два требования обеспечиваются директивами, третье — машинными командами. Ответственность за то, чтобы при обеспечении этих требований директивы согласовывались с командами, лежит на программисте. Эти требования по-разному реализуются в разных вычислительных системах. Приведем два примера. В Intel Ассемблер использует в качестве базовых сегментные регистры (DS при трансляции имен переменных, CS при трансляции меток). Для простой программы, состоящей из одной секции,
Загрузчик перед выполнением заносит во все сегментные регистры сегментный адрес начала программы и Ассемблер считает все смещения относительно него. Сложная программа может состоять из нескольких секций, и в сегментном регистре может содержаться адрес той или иной секции, причем содержимое сегментного регистра может меняться в ходе выполнения программы. Загрузка в сегментный регистр адреса секции выполняется машинными командами: MOV AX,секция MOV сегментный_регистр,AX Для того, чтобы Ассемблер знал, что адрес секции находится в сегментном_регистре, применяется директива: ASSUME сегментный_регистр:секция Далее при трансляции имен Ассемблер превращает имена в адресные выражения вида [сегментный_регистр]+смещение в секции Отмена использования сегментного регистра задается директивой: ASSUME сегментный_регистр:NOTHING Обратим внимание на то, что при трансляции команды MOV AX,секция в поле операнда заносится относительный адрес секции, при выполнении же здесь должен быть ее абсолютный адрес. Поэтому поля операндов такого типа должны быть модифицированы Загрузчиком после размещения программы в оперативной памяти. Более гибкая система базовой адресации применяется в S/360, S/370, S/390. В качестве базового может быть использован любой регистр общего назначения. Директива: USING относительный_адрес,регистр сообщает Ассемблеру, что он может использовать регистр в качестве базового, и в регистре содержится адрес — 1-й операнд. Чаще всего относительный_адрес кодируется как * (обозначение текущего значения счетчика адреса), это означает, что в регистре содержится адрес первой команды, следующей за директивой USING. Занесение адреса в базовый регистр выполняется машинной командой BALR. Обычный контекст определения базового регистра: BALR регистр,0 USING *,регистр
С такими операндами команда BALR заносит в регистр адрес следующей за собой команды. Зная смещение именованной ячейки относительно начала программы и смещение относительно начала же программы содержимого базового регистра, Ассемблер легко может вычислить смещение именованной ячейки относительно содержимого базового регистра. В отличие от предыдущего примера, в этом случае не требуется модификации при загрузке, так как команда BALR заносит в регистр абсолютный адрес. Директива DROP регистр отменяет использование регистра в качестве базового. В качестве базовых могут быть назначены несколько регистров, Ассемблер сам выбирает, какой из них использовать в каждом случае. Выше мы говорили, что Ассемблер «знает» базовый регистр и его содержимое. Это «знание» хранится в таблице базовых регистров. Обычно таблица содержит строки для всех регистров, которые могут быть базовыми и признак, используется ли регистр в таком качестве. Формат строки таблицы:
Алгоритм выполнения 2-го прохода представлен на рисунке. Мы исходили из того, что 2-й проход использует промежуточный файл, сформированный 1-м проходом. Если же 2-й проход использует исходный модуль, то алгоритм должен быть расширен лексическим разбором оператора, распознаванием мнемоники и подсчетом адресов — так же, как и в 1-м проходе.
F Блок1: Начало 2-го прохода ассемблирования. F Блок2: Начальные установки: u создание пустой таблицы базовых регистров; u открытие промежуточного файла исходного модуля; u установка в FASLE признака окончания F Блок3: Признак окончания TRUE? F Блок4: Считывание следующей записи промежуточного файла. F Блок5: Если запись промежуточного файла описывает комментарий, переход на печать строки листинга. F Блок6: Выясняется, содержит оператор команду или директиву F Блок7: Если оператор содержит команду, формируется байт кода операции (код выбирается из таблицы команд) в объектном коде. F Блок8: Выделение следующего элемента из списка операндов с удалением его из списка и с проверкой, не обнаружен ли при выделении конец списка операндов? F Блок9: Если конец не обнаружен, обрабатывается выделенный операнд. Проверяется, не превысило ли число операндов требуемого для данного типа команды (выбирается из таблицы команд) F Блок10: Если число операндов превышает требуемое — формирование сообщения об ошибке F Блок11: Если число операндов правильное, распознается и проверяется тип операнда. F Блок12: Если тип операнда не распознан или недопустим для данной команды — формирование сообщения об ошибке. F Блок13: Есть ли в команде имя? F Блок14: Если в команде есть имя, оно ищется в таблице символов. F Блок15: Если имя в таблице символов не найдено — формирование сообщения об ошибке. F Блок16: Если найдено имя в таблице символов, оно переводится в «база-смещение» F Блок17: Если имени в команде нет, выполняется разбор и интерпретация операнда с проверкой правильности его кодирования. F Блок18: Если обнаружены ошибки в кодировании операнда — формирование сообщения об ошибке. F Блок19: Формируется код поля операнда и заносится в объектный код команды и обрабатывается следующий элемент списка операндов. F Блок20: Если обнаружен конец списка операндов, проверяется, не меньше ли число операндов требуемого для данного типа команды. Если число операндов соответствует требуемого, управление переходит на вывод объектного кода. F Блок21: Если число операндов меньше требуемого — формирование сообщения об ошибке F Блок22: Если обрабатываемый оператор является директивой, алгоритм разветвляется, в зависимости от того, какая это директива. При обработке любой директивы производится разбор и анализ ее операндов и (не показано на схеме алгоритма) возможно формирование сообщения об ошибке. F Блок23: Обработка директивы типа DD включает в себя: u выделение элементов списка операндов; u для каждого элемента — распознавание типа и значения константы; u генерация объектного кода константы; u обработка возможных коэффициентов повторения. F Блок24: Обработка директивы типа BSS может вестись точно так же, как и DD за исключением того, что вместо кода константы генерируются некоторые «пустые» коды. Однако, эти коды не нужны в объектном модуле, они могут не генерироваться, в этом случае должны предприниматься некоторые действия, формирующие «разрыв» в объектных кодах. F Блок25: Обработка директивы типа USING (ASSUME) включает в себя занесение в соответствующую строку таблицы базовых регистров значения операнда-адреса и установку для данного регистра признака использования. F Блок26: Обработка директивы типа USING (ASSUME) включает в себя занесение в соответствующую строку таблицы базовых регистров значения операнда-адреса и установку для данного регистра признака использования. F Блок27: Обработка директивы END устанавливает признак окончания в TRUE. При обработке этой директивы в объектный модуль также может заносится стартовый адрес программы — параметр директивы. F Блок28: Обработка прочих директив ведется по своим алгоритмам. F Блок29: После окончания обработки команды или директивы сформированный объектный код выводится в файл объектного модуля. F Блок30: Печать строки листинга. На эту точку также управление передается при выявлении ошибок. При наличии ошибки сообщение об ошибке печатается после строки листинга. Управление затем передается на считывание следующей записи промежуточного файла. F Блок31: После того, как установлен признак окончания работы формируются и выводятся в объектный модуль коды литерального пула, таблицы связываний и перемещений. F Блок32: Закрываются файлы, освобождается выделенная память. F Блок33: Работа Ассемблера завершается. При рассмотрении алгоритма мы принимали во внимание только генерацию объектных кодов, соответствующих командам и константам. Мы не рассматривали то, какова общая структура объектного модуля. Date: 2015-05-22; view: 535; Нарушение авторских прав |