Э. Таненбаум - Архитектура компьютера (1127755), страница 146
Текст из файла (страница 146)
Если оба операнда команды А00 являются регистрами, длина команды составляет 2 байта, а код операции равен Ох01. Все комбинации кодов операций и операндов, которые В таблице кодов операций предусмотрена, по крайней мере, одна запись для каждого символьного кода операции ассемблера (табл. 7.5). В каждой записи содержится символьный код операции, два операнда, числовое значение кода операции, длина команды и номер типа, по которому можно определить, к какой группе относится код операции (коды операций делятся на группы в зависимости от числа и типа операндов).
574 Глава 7. Уровень ассемблера Таким образом, программисту не нужно писать директиву, чтобы разместить слово в памяти, присвоить ему значение 5, дать ему метку, а затем использовать эту метку в команде Е. Константы, для которых ассемблер автоматически резервирует память, называются литералами. Литералы упрощают чтение и понимание программы, делая значение константы очевидным в исходном операторе.
При первом проходе ассемблер должен создать таблицу всех литералов, которые используются в программе. Все три компьютера, которые мы взяли в качестве примеров, имеют команды с непосредственными адресами, поэтому их ассемблеры не поддерживают литералов. Команды с непосредственными адресами в настоящее время считаются вполне обычными, хотя раньше они рассматривались как нечто совершенно экзотическое. Вероятно, популярность литералов внушила разработчикам, что непосредственная адресация — очень хорошая идея.
Если литералы нужны, то во время ассемблирования сохраняется таблица литералов, в которой появляется новый элемент всякий раз, когда встречается литерал. После первого прохода таблица сортируются, и повторяющиеся элементы удаляются. В листинге 7.9 показан механизм, который лежит в основе первого прохода ассемблера. Названия команд выбраны таким образом, чтобы была ясна их суть. Этот листинг представляет собой хорошую отправную точку для изучения ассемблера.
Он достаточно короткий, понятный, и из него видно, каким должен быть следующий шаг — это написание процедур, которые упоминаются в данном листинге. Листинг 7.9. Первый проход простого ассемблера рцызс зсаг1с чозб рава опе(1 1 П Эта процедура - первый проход ассемблера Ьоо1еап логе зпрцг=ьгце; П Флаг, который останавливает первый проход 5тгтпр 1тпе. вуюЬо1. 11сега1. орсобе: П поля коианды зпт 1осатзоп соцптег, 1епдгп, ча1це. Суре: П переиенные Ттпа1 зпг ЕИО 5ТАТЕИЕИТ = -2: П сигналы окончания ввода П ассемблирование первой команды в ячейке О П общая инициализация 1осатзоп соцптег - О; зшгза11яе гашезО; ыш1е (логе зпрцгз 1 П щоге зпрцг с поиощвю директивы ЕИО П получает значение "лояв" соответствуют данному правилу, относятся к классу 19 и обрабатываются так же, как команда А00 с двумя регистрами в качестве операндов.
Класс команд идентифицирует процедуру, которая вызывается для обработки всех команд данного типа. В некоторых ассемблерах можно писать команды с применением непосредственной адресации, даже если соответствующей команды нет в выходном языке. Такие команды с «псевдонепосредственными» адресами обрабатываются следующим образом.
Ассемблер назначает область памяти для непосредственного операнда в конце программы и порождает команду, которая к нему обращается. Например, универсальная вычислительная машина 1ВМ 3090 не имеет команд с непосредственными адресами. Тем не менее для загрузки в регистр 14 константы 5 размером в полное слово программист может написать команду: Е 14.=Р'5' Процесс ассемблирования 575 1!Пе = геаб пехС 1ьпе(): 1епдгп = 0: Суре = 0; // считывание строки П в байт в коканде П тип коианды тт (1!пе зя пос соввепС(1ьпе) яувЬо1 = спесй Тот яувЬо1(1 !т (яувЬо1 != пц11Т ) ( !пе): // содершит ли строка метку? П если да, то записываются П сиивол и значение 1осас!оп соцпсег): (1ьпе): П содершит ли строка литерал? П если да. то он П сохраняется в таблице ептег пеи яувЬо1(яувЬо1.
1!Сета) - сйесд Тог 1ьсега1 !т (1ьсега1 != пЫ 1) епсег пеи 1ьсега1(1тсега)): П Тенер~ определяеи тип кода операции. -1 значит недопустииый код операции орсобе = ехСгасС орсобе(1!не): П определяен песте кода операции Суре =яеагсп орсобе СаЫ е(орсобе): П находим форнат, П например. ОР ИЕ61.ЯЕ62 !Т (Суре < 0) П если зто не код операции. П является ли он директивой? Суре = яеагсП ряецбо СаЫ е(орсобе): яи!Ссп(суре) ( // определяен длину команды саяе 1 1епОСП=ОеС 1епОСП от Суре1(1!пе): Ьгеай: саяе 2 1епОСП=ОеС 1епОСП ог Суре2(1зпе); Ьгеад; // другие варианты ) иг(Се Севр ЕЧ 1е(Суре.
орсобе. 1епОСП. 1!пе): П инфорнация для П второго прохода 1осастоп соцпсег = 1осастоп соцпсег т 1епОСП: // обновление счетчика // адресов коканд ьт (Суре == ЕИО 5ТАТЕМЕИТ) ( // завершился ли ввод? воге тпрцС = Та1яе; П если да, то выполняеи // слувебные действия: // переиатываен файл обратно // сортируем таблицу литералов // и удаляен из нее дубликаты геиьпб Севр Тот разя Сио(); яогс 1тбегаТ саЫ е(); гешоде гебцпбапС 1тСега1я() ) ) ) Некоторые процедуры будут относительно короткими, например, спер(( бог яушЬО1, которая просто возвращает в виде символьной строки имя, если таковое имеется, или ноль, если его нет. Другие процедуры, например деС 1епдСП ОТ Суре1 и деС 1епдСП ОТ Суре2, могут быть достаточно длинными и сами вызывать другие процедуры.
Естественно, на практике типов будет не два, а больше, — это зависит от ассемблируемого языка и от того, сколько типов команд предусмотрено в этом языке. Структурирование программ имеет и другие преимущества помимо простоты программирования. Если программа пишется группой людей, разнообразные процедуры могут быть поделены на фрагменты и распределены между программистами.
Все подробности получения входных данных скрыты в процедуре геаб пехС 1!пе. Если эти детали нужно изменить (например, из-за изменений в операционной системе), то это повлияет только на одну подчиненную процедуру, и никаких изменений в самой процедуре разя опе делать не нужно. 576 Глава 7. Уровень ассемблера Второй проход Цель второго прохода — создать объектную программу и напечатать протокол ассемблирования (если нужно). Кроме того, при втором проходе должна выводиться информация, необходимая для компоновки в один исполняемый файл процедур, которые ассемблировались в разное время.
В листинге 7.10 показана процедура для второго прохода. Листинг 7.10. Второй проход простого ассемблера рцЬ1!с зтаг!с чоы разя Сипы ( П Эта процедура — второй проход ассемблера Ьоо1еап щогезпрцт = Сгце: П флаг, останавливающий второй проход 5тгтп9 1зпе, орсобе: П поля коиаиды !пс 1осагтоп соцпгег, 1епОСЬ, Суре, П переиеииые в!па! тпг ЕИВ 5ТАТЕМЕИТ = -2; П сигналы окоичаиия ввода Г!па1 тпт МАХ СООЕ =1б; П иаксииапькое число байтов в коиакде бусе сооеИ = пеи ьусе(ИАх сббе), и число байтов в команде П в пороидеииои коде 1осаг!оп соцпгег = О: П ассеиблировакие первой П коиаиды в адресе О П щоге !прот с поиощью директивы ЕИО П получает значение "иощь" П считывание поля типа следующей строки П считывание поля кода операции П в следующей строке П считывакие поля дликы П в следующей строке П считывание самой входной строки ипз1е (щоге !пров( ( Суре = геаб гуре(); орсобе = геаб орсобе(1.
1епОСЬ = геаб 1епОСЬ(1: 1зпе = геаб 1тпе(1; тт (Суре ы 01 ( П тип О указывает ка коииеитарий По мере чтения программы во время первого прохода ассемблер должен анализировать каждую строку, чтобы найти код операции (например, АОО), определить ее тип Енабор операндов) и вычислить длину команды. Эта информация понадобится при втором проходе, поэтому ее лучше записать, чтобы не анализировать строку второй раз.
Однако переписывание входного файла потребует больше операций ввода-вывода. Что лучше — увеличить количество операций ввода-вывода, чтобы меньше времени тратить на анализ строк, или сократить количество операций ввода-вывода и потратить больше времени на анализ, зависит от быстродействия центрального процессора и дисковой памяти, эффективности файловой системы и некоторых других факторов. В нашем примере мы запишем временный файл, в который будут записываться тип, код и длина операции, а также сама входная строка. Именно эта строка и будет считываться при втором проходе, и читать файл по второму разу не потребуется.
После прочтения директивы ЕИО первый проход завершается. В этот момент можно сохранить таблицу символических имен и таблицу литералов, если это необходимо. В таблице литералов можно произвести сортировку и удалить дубликаты. Процесс ассемблирования 577 5НЗССП(СУРЕ) ( П порошдение выходного кода сазе П ена1 Суре)(орсобе, )епдСЬ, 11пе, собе); Ьгеах; сазе 2: ечаТ Суре2(орсопе, 1епдСЬ, 1)пе. соби); Ьгеад, П другие варианты ) ыг)ве оцврцмсобе): ГГ запись двоичного кода ыгзте 115тзпд(себе. 11пе): ГГ вывод на печать одной строки 1осатуоп соцпвег - 1осавзоп соцпвег + 1епдвл; П обновление счетчика П адресов нонанд 1Т (Суре -= ЕИЬ 5ТАТЕМЕИТ) ( П завершен ли ввод? шоте )прот - Уа)зе; П если да, то выполнвеи П слушебные операции ГГ завершение Т1П15П цр() ) Процедура второго прохода похожа на процедуру первого: строки считываются по одной и обрабатываются тоже по одной. Поскольку мы записали в начале каждой строки тип, код операции и длину (во временном файле), все они считываются и, таким образом, нам не нужно проводить анализ строк во второй раз.
Основная работа по порождению кода выполняется процедурами еча1 Суре1, ена1 Суре2 и т. д. Каждая из них обрабатывает определенную модель (например, код операции и два регистра-операнда). Полученный в результате двоичный код команды сохраняется в переменной сот(е. Затем совершается контрольное считывание. Желательно, чтобы процедура шг1бе со(зе просто сохраняла в буфере накопленный двоичный код и записывала файл на диск большими кусками — это снизит нагрузку на диск. Исходный оператор и выходной (объектный) код, полученный из него (в шестнадцатеричной системе), можно напечатать или поместить в буфер, чтобы напечатать потом. После смены значения счетчика адресов команд вызывается следующий оператор. До сих пор мы считали, что исходная программа не содержит ошибок. Однако любой, кто хоть когда-нибудь занимался программированием, знает, насколько это предположение не соответствует действительности.
Вот только наиболее распространенные ошибки; + используемый символ не определен; + символ определен более одного раза; + имя в поле кода операции не является допустимым кодом операции; + слишком мало операндов для данного кода операции; + слишком много операндов для данного кода операции; + восьмеричное число содержит цифру 8 или 9; + недопустимое применение регистра (например, переход к регистру); + отсутствует оператор ЕИЕ).