Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: АрхеологияБиология Генетика География Информатика История Логика Маркетинг Математика Менеджмент Механика Педагогика Религия Социология Технологии Физика Философия Финансы Химия Экология ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Машинно-независимые характеристики ассемблераСодержание книги
Поиск на нашем сайте
В этом разделе мы обсудим некоторые общие характеристики ассемблера, не имеющие тесной связи со структурой машины. Конечно, более совершенные машины обычно имеют более сложное программное обеспечение, и поэтому средства ассемблера, которые мы здесь рассмотрим, вероятнее встретить на больших и сложных машинах. Однако наличие или отсутствие этих средств определяет скорее такие факторы, как удобство программирования или операционное окружение, нежели структура конкретной машины. В разд. 2.3.1 мы обсудим таблицы и алгоритмы, необходимые для реализации литералов. В разд.2.3.2 будут рассмотрены две директивы ассемблера (EQU и ORG), основной функцией которые является определение имен. В разд. 2.3.3 коротко рассматриваются выражения в языке ассемблера. Обсуждаются различные типы таких выражений, их вычисление и использование. В разд.2.3.4 и 2.3.5 мы познакомимся с такими важными понятиями, как программный блок и управляющая секция. Мы обсудим доводы в пользу включения этих средств в ассемблер и проиллюстрируем их использование с помощью примеров. Мы также познакомимся с набором директив ассемблера, которые служат для поддержания этих средств, и обсудим пути их реализации. Литералы Очень часто бывает удобно иметь возможность записывать значения константы, используемой в качестве операнда, непосредственно в команде, где она используется. Это позволяет отказаться от использования отдельного предложения для определения константы и соответствующей ей метки. Такой операнд называется литеральным (literal), поскольку значение константы задается в виде строки символов. На рис.2.9 приведен пример использования литералов. Объектный код, сгенерированный для предложений этой программы, показан на рис.2.10. (Эта программа является модификацией программы, представленной на рис.2.5; другие изменения мы обсудим в разд. 2.3.)
Строка Исходное предложение 5 COPY START 0 КОПИРОВАНИЕ ФАЙЛА 10 FIRST STL RETADR СОХРАНЕНИЕ АДРЕСА ВОЗВРАТА 13 LDB #LENGTH УСТАНОВКА БАЗОВОГО РЕГИСТРА 14 BASE LENGTH 15 CLOOP +JSUB RDREC ВВОД ВХОДНОЙ ЗАПИСИ 20 LDA LENGTH ПРОВЕРКА НА EOF (LENGTH = 0) 25 COMP #0 30 JEQ ENDFIL ВЫХОД, ЕСЛИ НАШЛИ EOF 35 +JSUB WRREC ВЫВОД ВЫХОДНОЙ ЗАПИСИ 40 J CLOOP ЦИКЛ 45 ENDFIL LDA =C"EOF" ЗАНЕСЕНИЕ МАРКЕРА КОНЦА ФАЙЛА 50 STA BUFFER 55 LDA #3 УСТАНОВИТЬ LENGTH = 3 60 STA LENGTH 65 +JSUB WRREC ЗАПИСЬ EOF 70 J @RETADR ВОЗВРАТ ИЗ ПРОГРАММЫ 93 LTORG 95 RETADR RESW 1 100 LENGTH RESW 1 ДЛИНА ЗАПИСИ 105 BUFFER RESB 4096 ДЛИНА БУФЕРА – 4096 БАЙТ 106 BUFEND EQU * 107 MAXLEN EQU BUFEND-BUFFER МАКСИМАЛЬНАЯ ДЛИНА ЗАПИСИ 110 * 115 * ПОДПРОГРАММА ВВОДА ЗАПИСИ НА БУФЕР 120 * 125 RDREC CLEAR X ОБНУЛЕНИЕ СЧЕТЧИКА ЦИКЛА 130 CLEAR A ОБНУЛЕНИЕ РЕГИСТРА А 132 CLEAR S ОБНУЛЕНИЕ РЕГИСТРА S 133 +LDT #MAXLEN 135 RLOOP TD INPUT ПРОВЕРКА УСТРОЙСТВА ВВОДА 140 JEQ RLOOP ЦИКЛ ДО ПОЛУЧЕНИЯ ГОТОВНОСТИ 145 RD INPUT ЧТЕНИЕ СИМВОЛА В РЕГИСТР А 150 COMP A,S ПРОВЕРКА НА КОНЕЦ ЗАПИСИ (Х"00") 155 JEQ EXIT ВЫХОД ИЗ ЦИКЛА ПО КОНЦУ ЗАПИСИ 160 STCH BUFFER,X ЗАПИСЬ СИМВОЛА В БУФЕР 165 TIXR T ЦИКЛ ДО ДОСТИЖЕНИЯ МАКСИМАЛЬНОЙ 170 JLT RLOOP ДЛИНЫ 175 EXIT STX LENGTH ЗАПОМИНАНИЕ ДЛИНЫ ЗАПИСИ 180 RSUB ВОЗВРАТ ИЗ ПОДПРОГРАММЫ 185 INPUT BYTE X"F1" КОД УСТРОЙСТВА ВВОДА 195 * 200 * ПОДПРОГРАММА ВЫВОДА ЗАПИСИ ИЗ БУФЕРА 205 * 210 WRREC CLEAR X ОБНУЛЕНИЕ СЧЕТЧИКА ЦИКЛА 212 LDT LENGTH 215 WLOOP TD =X"05" ПРОВЕРКА УСТРОЙСТВА ВЫВОДА 220 JEQ WLOOP ЦИКЛ ДО ПОЛУЧЕНИЯ ГОТОВНОСТИ 225 LDCH BUFFER,X ЧТЕНИЕ СИМВОЛА ИЗ БУФЕРА 230 WD =X"05" ВЫВОД СИМВОЛА 235 TIXR T ЦИКЛ, ПОКА НЕ БУДУТ ВЫВЕДЕНЫ 240 JLT WLOOP ВСЕ СИМВОЛЫ 245 RSUB ВОЗВРАТ ИЗ ПОДРОГРАММЫ 255 END FIRST
Рис. 2.9. Программа, демонстрирующая дополнительные возможности ассемблера.
Строка Адрес Исходное предложение Объектный код 5 0000 COPY START 0 10 0000 FIRST STL RETADR 17202D 13 0003 LDB #LENGTH 69202D 14 BASE LENGTH 15 0006 CLOOP +JSUB RDREC 4B101036 20 000A LDA LENGTH 032026 25 000D COMP #0 290000 30 0010 JEQ ENDFIL 332007 35 0013 +JSUB WRREC 4B10105D 40 0017 J CLOOP 3F2FEC 45 001A ENDFIL LDA =С"EOF" 032010 50 001D STA BUFFER 0F2016 55 0020 LDA #3 010003 60 0023 STA LENGTH 0F200D 65 0026 +JSUB WRREC 4B10105D 70 002A J @RETADR 3E2003 93 LTORG 002D * =C"EOF" 454F46 95 0030 RETADR RESW 1 100 0033 LENGTH RESW 1 105 0036 BUFFER RESB 4096 106 1036 BUFEND EQU * 107 1000 MAXLEN EQU BUFEND-BUFFER 110 * 115 * ПОДПРОГРАММА ВВОДА ЗАПИСИ НА БУФЕР 120 * 125 1036 RDREC CLEAR X B410 130 1038 CLEAR A B400 132 103A CLEAR S B440 133 103C +LDT #MAXLEN 75101000 135 1040 RLOOP TD INPUT E32019 140 1043 JEQ RLOOP 332FFA 145 1046 RD INPUT DB2013 150 1049 COMP A,S A004 155 104B JEQ EXIT 332008 160 104E STCH BUFFER,X 57C003 165 1051 TIXR T B850 170 1053 JLT RLOOP 3B2FEA 175 1056 EXIT STX LENGTH 134000 180 1059 RSUB 4F0000 185 105C INPUT BYTE X"F1" F1 195 * 200 * ПОДПРОГРАММА ВЫВОДА ЗАПИСИ ИЗ БУФЕРА 205 * 210 105D WRREC CLEAR X B410 212 105F LDT LENGTH 774000 215 1062 WLOOP TD =X"05" E32011 220 1065 JEQ WLOOP 332FFA 225 1068 LDCH BUFFER,X 53C003 230 106B WD =X"05" DF2008 235 106E TIXR T B850 240 1070 JLT WLOOP 3B2FEF 245 1073 RSUB 4F0000 255 END FIRST 1076 * =X"05" 05
Рис.2.10. Объектный код для программы на рис.2.9.
В нашем языке ассемблера литералы задаются с помощью префикса =, за которым следует константа, задаваемая точно так же, как в предложении BYTE. Таким образом, в предложении 45 001A ENDFIL LDA =С'ЕОF' 032010 литерал определяет 3-байтовый операнд, значением которого является строка символов EOF. Аналогично в предложении 215 1062 WLOOP ТD =X'05' ЕЗ2011 задается 1-байтовый литерал, имеющий шестнадцатеричное значение 05. Символьная нотация для задания литералов может быть различной, однако в большинстве ассемблеров для того, чтобы упростить идентификацию литералов, используется определенный символ (у нас =). Важно уяснить различие между литералом и непосредственным операндом. В случае непосредственного операнда его значение транслируется как составная часть машинной команды. В случае литерала его значение генерируется в виде константы в оперативной памяти, а ее адрес используется в качестве целевого адреса команды. Употребление литерала дает точно та кой же результат, как если бы программист явно определил константу и использовал в качестве операнда ее метку. (Действительно, сгенерированный объектный код для строк 45 и 215 в распечатке 5 идентичен объектному коду для соответствующих строк программы на рис.2.6.) Вам следует сравнить объектный код, сгенерированный для строк 45 и 55 (рис.2.10), для того, чтобы убедиться, что вы уяснили разницу в обработке литералов и непосредственных операндов. Все литеральные операнды, используемые в программе, объединяются в один или несколько литеральных пулов (literals pools). Обычно литеральный пул помещается в конце программы и распечатывается в ее листинге. В литеральном пуле для каждого литерала показаны назначенный ему адрес и сгенерированное значение. Пример такого литерального пула приведен на рис.2.10 (он расположен непосредственно после предложения END). В данном случае пул состоит только из одного литерала =X'05'. Однако в некоторых случаях хотелось бы размещать литеральный пул в другом месте объектной программы. Это можно сделать с помощью директивы ассемблера LTORG (строка 93 на рис.2.10). Когда ассемблер встречает предложение LTORG, он создает литеральный пул, содержащий все литеральные операнды, которые были использованы в программе с момента обработки предыдущего предложения LTORG (или с начала программы). Литеральный пул помещается в объектную программу в то место, где было встречено предложение LTORG (см. рис.2.10). Конечно, литералы, помещенные в литеральный пул по команде LTORG, не будут повторно помещаться в литеральный пул в конце программы. Если бы мы не использовали предложение LTORG в строке 93, то литерал =С'EOF' был бы помещен в литеральный пул в конце программы. В этом случае данный операнд получил бы адрес 1073 и мы не смогли бы использовать для него адресацию относительно счетчика команд. Конечно, все дело в том, что мы зарезервировали очень большое пространство для массива BUFFER. Расположив литеральный пул перед этим массивом, мы смогли избежать использования расширенного командного формата для ссылок на литералы. Обычно необходимость в команде, подобной LTORG, возникает тогда, когда желательно расположить литеральные операнды в непосредственной близости от команд, в которых они используются. Большинство ассемблеров умеют распознавать повторное использование литералов и хранят только один экземпляр каждого из них. Например, литерал =X'05' используется в нашей программе в строках 215 и 230. Однако для его хранения генерируется только одна константа и операнды обеих команд ссылаются на адрес этой константы. Простейший способ распознать повторное использование литерала заключается в сравнении определяющих их строк символов (в нашем случае это строка =Х'05'). Однако в некоторых случаях лучше сравнивать не определения, а сгенерированные коды. Например, литералы =C'EOF' и =X'454F46' определяют идентичные значения. Если бы ассемблер умел распознавать их эквивалентность, то он мог бы не заводить дублирующую константу. В то же время выигрыш от подобного способа обработки литералов обычно не столь велик, чтобы оправдать дополнительные усложнения ассемблера. Если для распознавания повторного использования литералов используется символьная строка, то следует быть внимательными при обработке литералов, значение которых зависит от месторасположения в программе. Предположим, что мы разрешаем использовать литералы, которые ссылаются на текущее значение счетчика размещений (часто обозначаются с помощью символа *). Такие литералы иногда бывают полезны для загрузки базового регистра. Например, предложения LDB = * BASE * расположенные в самом начале программы, обеспечивают загрузку в регистр В начального адреса программы. В дальнейшем это значение можно использовать для адресации относительно базы. Однако такое обозначение может вызвать трудности распознавания повторно используемых литералов. Если литерал =* появился бы в нашей программе в строке 13, то его значение было бы 0003. Если тот же операнд был бы задан в строке 55, то он определял бы операнд, имеющий значение 0020. В этом случае литеральные операнды имеют идентичные имена, нo различные значения. Следовательно, оба эти операнда должны быть помещены в литеральный пул. Те же самые проблемы возникают, если литерал ссылается на какой-либо другой объект, значение которого изменяется между двумя точками программы. Теперь мы готовы к тому, чтобы описать, как ассемблер обрабатывает литеральные операнды. Основной структурой данных, используемой при обработке литералов, является таблица литералов LITTAB. Для каждого использованного в программе литерала эта таблица содержит следующую информацию: имя литерала, его значение, длину и назначенный ему адрес. Очень часто LITTAB организуется в виде хеш-таблицы, в которой в качестве ключа используется имя литерала или его значение. Если во время первого просмотра ассемблер встречает литеральный операнд, то он пытается найти его в LITTAB. Если литерал уже присутствует в таблице, то никаких дополнительных действий не требуется. Если литерала в таблице еще нет, то он помещается в LITTAB (значение адреса пока остается неопределенным). Когда во время первого просмотра встречается предложение LTORG или конец программы, ассемблер просматривает таблицу литералов и назначает каждому литералу его адрес (за исключением тех литералов, которым адреса были назначены ранее). После назначения адреса для очередного литерала счетчик размещений продвигается на длину литерального операнда. Во время второго просмотра адреса литеральных операндов извлекаются из LITTAB и используются для генерации объектного кода. Значения литеральных операндов в литеральном пуле заносятся в соответствующие места объектной программы точно так же, как если бы эти значения были сгенерированы с помощью предложений BYTE или WORD. Если значение литерального операнда представляет собой программный адрес (например, значение счетчика размещений), то ассемблер должен сгенерировать для него соответствующую запись-модификатор в объектном представлении. Для того чтобы убедиться, что вы поняли, как создается и используется таблица литералов, вам следует применить описанную нами процедуру к исходной программе на рис.2.9. Объектный код и литеральные пулы, которые вы получите, должны быть такими, как показано на рис.2.10. 2.3.2. Средства определения имен До сих пор в рассмотренных нами ассемблерных программах мы имели дело только с одним способом определения символических имен пользователя. Эти имена появлялись в качестве меток команд или областей данных. Значением такой метки является адрес, назначаемый предложению, в котором она определена. Большинство ассемблеров предоставляет программисту дополнительные средства для определения имен и задания их значений. К числу обычно используемых директив ассемблера относится директива EQU (от EQUate). В общем виде это предложение записывается следующим образом: имя EQU значение Данное предложение определяет некоторое имя (т. е. вносит его в SYMTAB) и присваивает ему значение. Значение может задаваться в виде константы пли выражения, в котором могут использоваться константы и ранее определенные имена. Образование и использование выражений мы обсудим в следующем разделе. Одним из общих применений EQU является введение символических имен вместо числовых значений, чтобы упростить чтение программы. Например, в строке 133 программы на рис.2.5 для того, чтобы загрузить в регистр Т число 4096, мы использовали предложение +LDT #4096 Данная величина представляет собой максимальную длину записи, которую мы можем прочитать с помощью подпрограммы RDREC. Однако смысл этого предложения далеко не очевиден. Если мы включим в нашу программу предложение MAXLEN EQU 4096 то сможем записать строку 133 как +LDT #MAXLEN Когда ассемблер встретит предложение EQU, он занесет МАХLEN в таблицу имен (со значением 4096). Во время трансляции команды LDT ассемблер найдет MAXLEN в SYMTAB и использует его значение в качестве операнда. Получаемый при этом объектный код будет таким же, как и в предыдущей версии, однако исходная программа легче для понимания. Кроме того, если понадобится изменить значение величины, задающей максимальную длину записи, то проще сделать это в одном предложении EQU, чем искать все вхождения 4096 в исходной программе. Другое общее применение EQU заключается в определении мнемонических имен для регистров. Мы предположили, что наш ассемблер распознает стандартные мнемонические имена регистров - А, X, L и т. д. Однако представим себе, что ассемблер требует использовать для задания регистров их номера (например, в команде RMO). Тогда мы должны были бы писать RMO 0, 1 вместо RMO А, X. В этом случае программист мог бы включить в свою программу следующую последовательность предложений: А EQU 0 X EQU 1 L EQU 2 . . . В результате обработки этих предложений имена А, X, L,... были бы занесены в SYMTAB и имели бы значения 0, 1, 2,.... После этого команда, подобная RMO А, X, была бы допустима. Ассемблер, обрабатывая такое предложение, должен был бы просмотреть SYMTAB и при трансляции использовать для имен А и X соответствующие им значения 0 и 1. На машине, подобной УУМ, задание для регистров пользовательских имен большого смысла не имеет. Гораздо проще использовать символические имена, встроенные в ассемблер. К тому же стандартные имена (base, index и т. п.) отражают способ использования регистров. Рассмотрим, однако, машину, имеющую регистры общего назначения (например, System/370). Обычно для обозначения этих регистров используются номера вида 0, 1, 2,... или имена вида R0, R1, R2,.... В конкретной программе некоторые из них могут быть использованы как базовые регистры, другие - как индексные, третьи - как сумматоры и т. д. Более того, в различных программах функциональное распределение регистров может быть различным. В этом случае программист может определить имена, отражающие функциональное назначение регистров конкретной программы, с помощью следующих предложений: BASE EQU 1 COUNT EQU 2 INDEX EQU 3 Имеется еще одна общеупотребительная директива ассемблера, позволяющая косвенно задавать значения символическим именам. Обычно эта директива называется ORG (от ORiGin). Она имеет следующий вид: ORG значение где значение представляет собой константу или выражение, в котором могут быть использованы константы и ранее определенные имена. Если во время ассемблирования программы встречается данное предложение, то заданное им значение заносится в счетчик размещений (LOCCTR). Поскольку для определения значений имен используется LOCCTR, то предложение ORG распространяет свое влияние на все последующие определения меток. Конечно, ввиду того что счетчик размещений используется для управления распределением памяти в объектной программе, его изменение чаще всего приводит к ошибкам ассемблирования. Однако иногда предложение ORG может быть полезно. Предположим, что мы хотим определить таблицу, имеющую следующую структуру: STAB 100 элементов SYMBOL VALUЕ FLAGS
... ... ... В этой таблице SYMBOL представляет собой 6-байтовое поле для хранения имен; VALUE - 1-словное поле для хранения значения, присвоенного имени; FLAGS - 2-байтовое поле для спецификации типа и другой информации. Мы могли бы зарезервировать пространство для этой таблицы с помощью предложения STAB RESB 1100 Естественно, что мы хотим иметь возможность ссылаться на элементы таблицы с помощью индексной адресации (помещая в индексный регистр величину смещения требуемого элемента относительно начала таблицы). Так как нам нужен доступ к отдельным полям элементов, то мы должны определить метки SYMBOL, VALUE и FLAGS. С помощью предложений EQU это можно сделать следующим образом: SYMBOL EQU STAB VALUE EQU STAB+6 FLAGS EQU STAB+9 Теперь для ссылки на поле VALUE мы можем использовать предложения вида LDA VALUE,X Однако такой способ определения лишь задает метки, но не дает наглядного представления о структуре таблицы. С помощью предложения ORG мы можем определить те же имена следующим образом: STAB RESB 1100 ORG STAB SYMBOL RESB 6 VALUE RESW 1 FLAGS RESB 2 ORG STAB + 1100 Первое предложение ORG заносит в счетчик размещений начальный адрес таблицы STAB. В следующем предложении метке SYMBOL будет присвоено текущее значение LOCCTR (т. е. тот же адрес, что и STAB). Затем LOCCTR будет продвинут и его новое значение (STAB + 6) будет присвоено метке VALUE. Аналогично будет определено значение метки FLAGS. В результате эти метки получат такие же значения, как и при определении с помощью EQU, но теперь структура STAB стала очевидной. Последнее предложение ORG очень важно. Оно возвращает в LOCCTR значение, которое было в нем до первого предложения ORG - адрес первого свободного байта, следующего за таблицей STAB. Это необходимо сделать для того, чтобы в по следующих предложениях меткам, которые не являются частью STAB, были назначены правильные адреса. В некоторых ассемблерах предыдущее значение LOCCTR запоминается автоматически, и для его восстановления достаточно просто написать предложение ORG с пустым операндом. Определения, задаваемые предложениями EQU и ORG, содержат ограничения, являющиеся общими для всех директив ассемблера, в которых определяются имена. В случае EQU все метки, используемые в правой части предложения (т. е. все термы, используемые для определения значения нового имени), должны быть уже определены. Таким образом, последовательность предложений ALPHA RESW 1 BETA EQU ALPHA является допустимой, тогда как последовательность BETA EQU ALPHA ALPHA RESW 1 недопустима. Причина этого ограничения кроется в самом процессе распознавания имен. Так, во втором случае мы не сможем присвоить значение метке ВЕТА (так как метка ALPHA еще не определена), а алгоритм работы нашего двухпросмотрового ассемблера требует, чтобы все имена были определены во время первого просмотра. Сходные ограничения применяются и для предложения ORG: все имена, используемые для задания нового значения счетчика размещений, должны быть предварительно определены. Так, например, последовательность ORG ALPHA ВYТЕ1 RESB 1 ВYТЕ2 RESB 1 ВYТЕЗ RESB 1 ORG ALPHA RESW 1 нe может быть обработана. В данном случае ассемблер не знает (во время первого просмотра), какое значение следует установить в счетчик размещений в ответ на первое предложение ORG. В результате невозможно во время первого просмотра вычислить адреса для меток BYTE1, BYTE2, BYTE3. Может показаться, что данное ограничение возникло вследствие конкретного распределения функций между первым и вторым просмотрами нашего ассемблера. На самом деле это проблема носит более общий характер, и она связана с трудностями, возникающими при разрешении ссылок вперед. Легко можно убедиться, что последовательность предложений ALPHA EQU BETA BETA EQU DELTA DELTA RESW 1 нe может быть обработана с помощью обычного двухпросмотрового ассемблера при любом распределении функций между его первым и вторым просмотрами. В разд. 2.4.3 мы вкратце познакомимся, как осуществляется обработка подобных последовательностей в более сложных ассемблерах. Выражения Во всех предложениях языка ассемблера, рассмотренных нами ранеев качестве операндов использовались отдельные термы (константы, метки и т. п.). В большинстве ассемблеров наряду с одиночными термами разрешается использовать выражения. Каждое такое выражение вычисляется ассемблером во время трансляции, а затем полученное значение используется в виде адреса или непосредственного операнда. Обычно допускаются арифметические выражения, которые строятся по стандартным правилам с помощью операций +,-,*,/. Деление чаще всего определяется как целочисленное. В выражениях можно использовать константы, метки и специальные термы. Одним из таких общеупотребительных специальных термов является терм, ссылающийся на текущую величину счетчика размещений (часто обозначается как *). Значением этого терма является адрес, который будет присвоен очередной команде или области данных. Так, на рис.2.9 предложение 106 BUFEND EQU * устанавливает в качестве значения BUFEND адрес байта, расположенного непосредственно после поля, отведенного под буфер. В разд. 2.2 мы обсуждали проблемы, связанные с перемещением программ. Мы видели, что в объектной программе имеются адреса двух типов: относительные (их значения могут быть вычислены суммированием начального адреса программы с некоторым постоянным смещением) и абсолютные (не зависящие от начального адреса программы). Аналогично могут быть относительными или абсолютными термы и выражения. Константа - абсолютный терм. Метки команд и областей данных, а также ссылка на текущее значение счетчика размещений - относительные термы. Имена, определенные в предложениях EQU (или других подобных директивах ассемблера)могут быть как абсолютными, так и относительными в зависимости от выражений, определяющих их значения. Выражения классифицируются как абсолютные или относительные в зависимости от того, к какому типу относится его значение. Естественно, что выражение, состоящее только из абсолютных термов, является абсолютным выражением. Однако абсолютное выражение может содержать и относительные термы. Для этого необходимо, чтобы относительные термы образовывали пары, причем в каждую пару должен входить как терм со знаком плюс, так и терм со знаком минус. Совершенно необязательно, чтобы парные термы располагались друг за другом. Единственное требование иметь возможность так преобразовать выражение, чтобы все термы могли быть сгруппированы в пары, в каждую из которых входит как положительный, так и отрицательный терм. Относительные термы не могут быть использованы в операциях умножения и деления. Относительное выражение - это выражение, в котором все относительные термы, кроме одного, могут быть объединены в пары, как это было описано выше, причем терм, не вошедший ни в одну из пар, должен быть положительным. Как и в первом случае, относительные термы нельзя использовать в операциях умножения и деления. Выражения, которые не удовлетворяют ни условиям абсолютного выражения, ни условиям относительного выражения, отмечаются ассемблером как ошибочные. Хотя приведенные выше правила могут показаться произвольными, они на самом деле имеют глубокий смысл. К числу выражений, удовлетворяющих данным определениям, относятся только те выражения, значения которых не теряют смысла при перемещении программ. Относительный терм или выражение представляет собой некоторое значение, которое может быть записано в виде (S+r), где S - начальный адрес программы, а r - величина смещения относительно начального адреса. По этому обычно относительный терм задает некоторую точку внутри программы. Когда относительные термы комбинируются в пары, каждая из которых включает термы с противоположными знаками, величины S, определяющие начальный адрес программы, взаимно исключаются. В результате получается абсолютное значение. Рассмотрим, например, программу на рис.2.9. В выражении 107 MAXLEN EQU BUFEND - BUFFER обе метки BUFEND и BUFFER - относительные термы, каждый из которых определяет внутренний адрес программы. Однако само выражение (разность между двумя адресами) является величиной абсолютной и определяет длину в байтах области данных, отведенной под буфер. Значения таких выражений, как BUFEND + BUFFER, 100 - BUFFER или 3 * BUFFER, не представляют ни абсолютную величину, ни внутренний адрес программы. Зависимость этих значений от адреса начальной загрузки такова, что она не имеет связи с чем-либо в самой программе. Поскольку маловероятно, чтобы от таких выражений была какая-либо польза, они рассматриваются как ошибочные. Для того чтобы определить тип выражения, мы должны иметь информацию о типе всех имен, определенных в программе. Эта информация хранится в таблице имен в виде признака, определяющего тип значения (абсолютное или относительное). Так, для программы на рис.2.10 некоторые из элементов таблицы имен могли бы иметь вид
С помощью этой информации ассемблер может легко определить тип каждого выражения и сгенерировать в объектной программе записи-модификаторы для относительных выражений. В разд. 2.3.5 мы рассмотрим программы, состоящие из нескольких частей, каждая из которых может перемещаться независимо от других. Как мы увидим, в этом случае должны быть модифицированы правила для определения типа выражения. Программные блоки Во всех примерах, которые мы видели до сих пор, ассемблируемая программа обрабатывалась как единое целое. Хотя логически исходная программа содержит подпрограммы, области данных и т. п., она рассматривалась ассемблером как неделимый объект, который переводился в единственный блок объектного кода. Внутри этой объектной программы сгенерированные машинные команды и данные располагались в том же порядке, в котором они были написаны в исходной программе. Многие ассемблеры предоставляют более гибкие средства обработки исходной программы и соответствующего ей объектного кода. Одни из этих средств позволяют располагать сгенерированные машинные команды и данные в объектной программе в порядке, отличном от порядка, в котором расположены соответствующие им исходные предложения. Другие позволяют создавать несколько независимых частей объектной программы, Каждая из этих частей сохраняет свою индивидуальность и обрабатывается загрузчиком отдельно от других частей. Мы используем термин программные блоки (program blocks) для обозначения сегментов, расположенных в пределах одного объектного модуля, и термин управляющие секции (control sections) для обозначения сегментов, которые транслируются в независимые объектные модули. (Данная терминология, к сожалению, не является общепризнанной. Фактически в некоторых системах одни и те же средства языка ассемблера используются для обеспечения обеих этих логически различных функций.) В этом разделе мы рассмотрим как используются и обрабатываются ассемблером программные блоки. Разд. 2.3.5 посвящен обсуждению управляющих секций. На рис.2.11 показано, как с помощью программных блоков может быть записана наша программа. В данном случае используются три блока. Первый (непоименованный) программный блок содержит выполняемые команды. Второй (с именем CDATA) содержит все области данных, длина которых не превышает нескольких слов. Третий (с именем CBLKS) состоит из областей данных, занимающих значительный объем оперативной памяти. Некоторые из возможных доводов и пользу подобного разделения обсуждаются в данном разделе.
Строка Исходное предложение 5 COPY START 0 КОПИРОВАНИЕ ФАЙЛА 10 FIRST STL RETADR СОХРАНЕНИЕ АДРЕСА ВОЗВРАТА 15 CLOOP +JSUB RDREC ВВОД ВХОДНОЙ ЗАПИСИ 20 LDA LENGTH ПРОВЕРКА НА EOF (LENGTH = 0) 25 COMP #0 30 JEQ ENDFIL ВЫХОД, ЕСЛИ НАШЛИ EOF 35 JSUB WRREC ВЫВОД ВЫХОДНОЙ ЗАПИСИ 40 J CLOOP ЦИКЛ 45 ENDFIL LDA =C"EOF" ЗАНЕСЕНИЕ МАРКЕРА КОНЦА ФАЙЛА 50 STA BUFFER 55 LDA #3 УСТАНОВИТЬ LENGTH = 3 60 STA LENGTH 65 JSUB WRREC ЗАПИСЬ EOF 70 J @RETADR ВОЗВРАТ ИЗ ПРОГРАММЫ 92 USE CDATA 95 RETADR RESW 1 100 LENGTH RESW 1 ДЛИНА ЗАПИСИ 103 USE CBLKS 105 BUFFER RESB 4096 ДЛИНА БУФЕРА – 4096 БАЙТ 106 BUFEND EQU * АДРЕС ПЕРВОГО БАЙТА ПОСЛЕ БУФЕРА 107 MAXLEN EQU BUFEND-BUFFER МАКСИМАЛЬНАЯ ДЛИНА ЗАПИСИ 110 * 115 * ПОДПРОГРАММА ВВОДА ЗАПИСИ НА БУФЕР 120 * 123 USE 125 RDREC CLEAR X ОБНУЛЕНИЕ СЧЕТЧИКА ЦИКЛА 130 CLEAR A ОБНУЛЕНИЕ РЕГИСТРА А 132 CLEAR S ОБНУЛЕНИЕ РЕГИСТРА S 133 +LDT #MAXLEN 135 RLOOP TD INPUT ПРОВЕРКА УСТРОЙСТВА ВВОДА 140 JEQ RLOOP ЦИКЛ ДО ПОЛУЧЕНИЯ ГОТОВНОСТИ 145 RD INPUT ЧТЕНИЕ СИМВОЛА В РЕГИСТР А 150 COMP A,S ПРОВЕРКА НА КОНЕЦ ЗАПИСИ (Х"00") 155 JEQ EXIT ВЫХОД ИЗ ЦИКЛА ПО КОНЦУ ЗАПИСИ 160 STCH BUFFER,X ЗАПИСЬ СИМВОЛА В БУФЕР 165 TIXR T ЦИКЛ ДО ДОСТИЖЕНИЯ МАКСИМАЛЬНОЙ 170 JLT RLOOP ДЛИНЫ 175 EXIT STX LENGTH ЗАПОМИНАНИЕ ДЛИНЫ ЗАПИСИ 180 RSUB ВОЗВРАТ ИЗ ПОДПРОГРАММЫ 183 USE CDATA 185 INPUT BYTE X"F1" КОД УСТРОЙСТВА ВВОДА 195 * 200 * ПОДПРОГРАММА ВЫВОДА ЗАПИСИ ИЗ БУФЕРА 205 * 208 USE 210 WRREC CLEAR X ОБНУЛЕНИЕ СЧЕТЧИКА ЦИКЛА 212 LDT LENGTH 215 WLOOP TD =X"05" ПРОВЕРКА УСТРОЙСТВА ВЫВОДА 220 JEQ WLOOP ЦИКЛ ДО ПОЛУЧЕНИЯ ГОТОВНОСТИ 225 LDCH BUFFER,X ЧТЕНИЕ СИМВОЛА ИЗ БУФЕРА 230 WD =X"05" ВЫВОД СИМВОЛА 235 TIXR T ЦИКЛ, ПОКА НЕ БУДУТ ВЫВЕДЕНЫ 240 JLT WLOOP ВСЕ СИМВОЛЫ 245 RSUB ВОЗВРАТ ИЗ ПОДРОГРАММЫ 252 USE CDATA 253 LTORG 255 END FIRST
Рис. 2.11. Пример программы с несколькими программными блоками.
Директива ассемблера USE указывает на то, какие части исходной программы принадлежат к тем или иным блокам, Вначале предполагается, что все предложения исходной программы являются частью непоименованного (задаваемого по умолчанию) блока. Если не использовано ни одного предложения USE, то считается, что вся программа принадлежит одному этому блоку. Предложение USE в строке 92 сигнализирует о начале блока с именем СDАТА. К этому блоку относятся все последующие предложения исходной программы вплоть до следующего предложения USE в строке 103которое начинает блок с именем CBLKS. Предложение USE может также указывать на продолжение ранее начатого блока. Так, предложение в строке 123 продолжает непоименованный блок, а предложение в строке 183 - блок СDАТА. Как вы видите, каждый блок фактически может содержать несколько отдельных сегментов исходной программы. Ассемблер перегруппирует (логически) эти сегменты так, чтобы собрать вместе отдельные части каждого блока. Затем каждому из этих блоков будут назначены адреса в объектной программе в порядке появления блоков в исходной программе. Результат будет в точности таким же, как если бы программист собственноручно перегруппировал предложения исходной программы, собрав вместе предложения, принадлежащие каждому из блоков. Такая логическая перегруппировка осуществляется ассемблером во время первого просмотра. Для этого с каждым программным блоком связывается свой счетчик размещений. При объявлении нового блока связанный с ним счетчик размещений устанавливается в 0. Текущее значение счетчика размещений сохраняется при переключении на другой блок и восстанавливается, когда возобновляется ранее определенный блок. Таким образом, во время первого просмотра каждая метка программы получает адрес относительно начала содержащего ее блока. Когда метка заносится в таблицу имен, то вместе с присвоенным ей адресом заносится имя или номер блока, которому она принадлежит. По завершению первого просмотра значение счетчика размещений каждого блока указывает на длину этого блока. Затем ассемблер может назначить каждому блоку начальный адрес в объектной программе (начиная с относительного адреса 0). Во время второго просмотра для генерации объектного кода ассемблеру для каждого имени нужно знать значение адреса относительно начала объектной программы (а не относительно начала блока). Эту информацию можно легко получить из SYMTAB: достаточно просто прибавить к адресу метки начальный адрес ее блока.
Строка Адрес Исходное предложение Объектный код 5 0000 0 COPY START 0 10 0000 0 FIRST STL RETADR 172063 15 0003 0 CLOOP JSUB RDREC 4B2021 20 0006 0 LDA LENGTH 032060 25 0009 0 COMP #0 290000 30 000C 0 JEQ ENDFIL 332006 35 000F 0 JSUB WRREC 4B203B 40 0012 0 J CLOOP 3F2FEE 45 0015 0 ENDFIL LDA =С"EOF" 032055 50 0018 0 STA BUFFER 0F2056 55 001B 0 LDA #3 010003 60 001E 0 STA LENGTH 0F2048 65 0021 0 JSUB WRREC 4B2029 70 0024 0 J @RETADR 3E203F 92 0000 1 USE CDATA 95 0000 1 RETADR RESW 1 100 0003 1 LENGTH RESW 1 103 0000 2 USE CBLKS 105 0000 2 BUFFER RESB 4096 106 1000 2 BUFEND EQU * 107 1000 MAXLEN EQU BUFEND-BUFFER 110 * 115 * ПОДПРОГРАММА ВВОДА ЗАПИСИ НА БУФЕР 120 * 123 0027 0 USE 125 0027 0 RDREC CLEAR X B410 130 0029 0 CLEAR A B400 132 002B 0 CLEAR S B440 133 002D 0 +LDT #MAXLEN 75101000 135 0031 0 RLOOP TD INPUT E32038 140 0034 0 JEQ RLOOP 332FFA 145 0037 0 RD INPUT DB2032 150 003A 0 COMP A,S A004 155 003C 0 JEQ EXIT 332008 160 003F 0 STCH BUFFER,X 57A02F 165 0042 0 TIXR T B850 170 0044 0 JLT RLOOP 3B2FEA 175 0047 0 EXIT STX LENGTH 13201F 180 004A 0 RSUB 4F0000 183 0006 1 USE CDATA 185 0006 1 INPUT BYTE X"F1" F1 195 * 200 * ПОДПРОГРАММА ВЫВОДА ЗАПИСИ ИЗ БУФЕРА 205 * 208 004D 0 USE 210 004D 0 WRREC CLEAR X B410 212 004F 0 LDT LENGTH 772017 215 0052 0 WLOOP TD =X"05" E3201B 220 0055 0 JEQ WLOOP 332FFA 225 0058 0 LDCH BUFFER,X 53A016 230 005B 0 WD =X"05" DF2012 235 005E 0 TIXR T B850 240 0060 0 JLT WLOOP 3B2FEF 245 0063 0 RSUB 4F0000 252 0007 1 USE CDATA 253 LTORG 0007 1 * =C"EOF" 454F46 000A 1 * =X"05" 05 255 END FIRST
Рис.2.12. Объектный код для программы на рис.2.11.
На рис.2.12 показано, как этот процесс применяется к нашей модельной программе. В столбце "Адрес" приведены относительный адрес (внутри блока) каждого предложения и номер соответствующего ему блока (0 = непоименованный блок, 1 = CDATA, 2 = CBLKS). По существу это та же информация, которая хранится в SYMTAB для каждого имени. Обратите внимание, что для значения MAXLEN (строка 107) номер блока не указан. Это означает, что MAXLEN является абсолютной меткой, значение которой не связано ни с одним из блоков. В конце первого просмотра ассемблер создает рабочую таблицу, которая содержит начальные адреса и длины всех блоков. Для нашей модельной программы таблица выглядит так:
Рассмотрим теперь команду 20 0006 0 LDA LENGTH 032060 В SYMTAB значение ее операнда (метка LENGTH) определено как относительный адрес 0003, принадлежащий программному блоку 1 (CDATA). Начальный адрес CDATA равен 0066. Таким образом, требуемый целевой адрес для этой команды будет 0003 + 0066 = 0069. Команда должна ассемблироваться с использованием адресации относительно счетчика команд. Во время исполнения данной команды счетчик команд будет содержать адрес следующей команды (строка 25). Относительный адрес этой команды равен 0009 и принадлежит непоименованному блоку. Поскольку этот блок начинается с адреса 0000, то, следовательно, значение этого адреса будет просто равно 0009. Итак, требуемое смещение вычисляется: 0069 - 0009 = 60. Вычисление других адресов осуществля
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 212; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 52.15.49.90 (0.012 с.) |