Создание загрузочного модуля

(компоновка программы)

После того как мы устранили ошибки и получили объектный модуль, можно приступать к следующему шагу — созданию исполняемого (загрузочного) модуля, или, как еще называют этот процесс, к компоновке программы. Главная цель этого шага — преобразовать код и данные в объектных файлах в их перемещаемое выполняемое отображение. Чтобы понять, в чем здесь суть, нужно разобраться, зачем вообще разделяют процесс создания исполняемого модуля на два шага — трансляцию и компоновку. Это сделано намеренно для того, чтобы можно было объединять вместе несколько модулей (написанных на одном или нескольких языках). Формат объектного файла позволяет, при определенных условиях, объединить несколько отдельно оттранслированных исходных модулей в один модуль. При этом в функции компоновщика входит разрешение внешних ссылок (ссылок на процедуры и переменные) в этих модулях. Результатом работы компоновщика является создание загрузочного файла с расширением .ехе. После этого операционная система может загрузить такой файл в память и выполнить его.

Полный формат командной строки для запуска компоновщика достаточно сложен, но нам достаточно упрощенного формата:

TLINK [опции] список_объектных_файлов [,имя_загрузочного_модуля] [,имя_файла_карты] [,имя_файла_библиотеки]

Здесь:

§ опции — необязательные параметры, управляющие работой компоновщика. Список наиболее часто используемых опций приведен в приложении 1;

§ список_объектных_файлов — обязательный параметр, содержащий список компонуемых файлов с расширением .obj. Файлы должны быть разделены пробелами или знаком «+», к примеру

tlink /v prog + mdf + fdг

§ имя_загрузочного_модуля — необязательный параметр, обозначающий имя целевого исполняемого модуля. Если оно не указано, то имя загрузочного модуля будет совпадать с первым именем объектного файла из списка объектных файлов;

§ имя_файла_карты — необязательный параметр, наличие которого обязывает компоновщик создать специальный файл с картой загрузки. В ней перечисляются имена, адреса загрузки и размеры всех сегментов, входящих в программу;

§ имя_файла_библиотеки — необязательный параметр, который представляет собой путь к файлу библиотеки. Этот файл с расширением .lib создается и обслуживается специальной утилитой tlib.exe из пакета TASM. Данная утилита позволяет объединить часто используемые подпрограммы в виде объектных модулей в один файл. В дальнейшем можем указывать в командной строке tlink.exe имена нужных для компоновки объектных модулей и имя_файла_библиотеки, в которой следует искать подпрограммы с этими именами.

Так же как и для синтаксиса tasm. ехе, совсем не обязательно запоминать подробно синтаксис команды tlink.exe. Для того чтобы получить список опций программы tlink. ехе, достаточно просто запустить ее без указания параметров.

Для выполнения нашего примера запустим программу tlink.exe командной строкой вида

tlink.exe /v prg_3_1.obj

В результате получим исполняемый модуль с расширением .ехе — prg_3_1. ехе.

Получив исполняемый модуль, не спешите радоваться. К сожалению, устранение синтаксических ошибок еще не гарантирует того, что программа будет хотя бы запускаться, не говоря уже о правильности работы. Поэтому обязательным этапом процесса разработки является отладка.

На этапе отладки, используя описание алгоритма, выполняется контроль правильности функционирования как отдельных участков кода, так и всей программы в целом. Но даже успешное окончание отладки еще не является гарантией того, что программа будет работать правильно со всеми возможными исходными данными. Поэтому нужно обязательно провести тестирование программы, то есть проверить ее работу на «пограничных» и заведомо некорректных исходных данных. Для этого составляются тесты. Вполне возможно, что результаты тестирования вас не удовлетворят. В этом случае придется вносить поправки в код программы, то есть возвращаться к первому шагу процесса разработки (см. рис. 4.1).

Специфика программ на ассемблере состоит в том, что они интенсивно работают с аппаратными ресурсами компьютера. Это обстоятельство заставляет программиста постоянно отслеживать содержимое определенных регистров и областей памяти. Естественно, что человеку трудно следить за этой информацией с большой степенью детализации. Поэтому для локализации логических ошибок в программах используют специальный тип программного обеспечения — программные отладчики.

Отладчики бывают двух типов:

§ интегрированные — отладчик реализован в виде интегрированной среды типа среды для языков Turbo Pascal, Quick С и т. д.;

§ автономные — отладчик представляет собой отдельную программу.

Из-за того, что ассемблер не имеет своей интегрированной среды, для отладки написанных на нем программ используют автономные отладчики. К настоящему времени разработано большое количество таких отладчиков. В общем случае с помощью автономного отладчика можно исследовать работу любой программы, для которой создан исполняемый модуль, независимо от того, на каком языке был написан его исходный текст.

Рассмотрим отладчик Turbo Debugger (TD). Принципиально то, что основная информация о нем в той или иной степени относится и к другим отладчикам. Рассмотрим основные моменты работы с отладчиком TD.

Отладчик Turbo Debugger

Отладчик Turbo Debugger (TD), разработанный фирмой Borland International, представляет собой оконную среду отладки программ на уровне исходного текста на языках Pascal, С, ассемблер. Он позволяет решить две главные задачи:

§ определить место логической ошибки;

§ определить причину логической ошибки.

Перечислим некоторые возможности TD:

§ выполнение трассировки программы в прямом направлении, то есть последовательное исполнение программы, при котором за один шаг выполняется одна машинная инструкция;

§ выполнение трассировки программы в обратном направлении, то есть выполнение программы по одной команде, но в обратном направлении;

§ просмотр и изменение состояния аппаратных ресурсов микропроцессора во время покомандного выполнения программы.

Это позволяет определить место и источник ошибки в программе. Нужно сразу оговориться, что TD не позволяет вносить исправления в исходный текст программы. После определения причины ошибочной ситуации можно, при необходимости, не завершая работу отладчика, внести исправления прямо в машинный код и запустить программу на выполнение. После завершения работы отладчика эти изменения не будут сохранены, и нужно внести их повторно, но уже в исходный текст, и повторно создать загрузочный модуль.

Как правильно организовать процесс получения исполняемого модуля, чтобы можно было выполнять его отладку на уровне исходного текста, мы уже рассмотрели ранее. Вспомним ключевые моменты этого процесса:

§ В исходной программе должна быть обязательно определена метка для первой команды, с которой начнется выполнение программы. Такая метка может быть собственно меткой или, как мы видели на примере программы из листинга 3.1, именем процедуры. Имя этой метки обязательно нужно указать в конце программы в качестве операнда директивы END:

END имя_метки

В нашем случае эта метка является именем процедуры main.

§ Исходный модуль должен быть оттранслирован с опцией /zi:

tasm /zi имя_исходного_модуля , , ,

Применение опции /zi разрешает транслятору сохранить связь символических имен в программе и их смещений в сегменте кода, что позволит отладчику производить отладку, используя оригинальные имена.

§ Редактирование модуля должно быть осуществлено с опцией /v:

tlink /v имя_объектного_модуля

Опция /v указывает на необходимость сохранения отладочной информации в исполняемом файле.

§ Запуск отладчика удобнее производить из командной строки с указанием исполняемого модуля программы, которая подлежит отладке:

td имя_исполняемого_модуля

Кстати, сам файл отладчика td.exe логично также поместить в наш рабочий каталог WORK. Изначально файлы отладчика находятся в каталоге BIN пакета TASM. Если все же td.exe и исполняемый модуль при запуске будут находиться в разных каталогах, то в командной строке необходимо указать путь к этому модулю, например:

td c:\tasm\woгк\имя_модуля.ехе

При правильном выполнении перечисленных выше действий откроется окно отладчика TD под названием Module.

В этом окне можно видеть исходный текст программы prg_3_1. asm. Как он здесь оказался, ведь мы для программы td.exe указали только имя исполняемого модуля? Это как раз и есть результат действия опций /zi и /м для tasm и tlink соответственно. Их применение позволило сохранить информацию об использовавшихся в коде на ассемблере символических именах. Ради полноты эксперимента можно получить исполняемый модуль без задания этих опций. Проанализируйте результат. Но вернемся к окну Module. Здесь мы видим так называемый курсор выполнения (в виде треугольника). Он указывает на первую команду, подлежащую выполнению. Этой команде предшествует имя метки (в нашем случае роль метки выполняет имя процедуры). Это так называемая точка входа в программу. Если мы внимательно посмотрим конец исходного текста программы, то увидим, что это же имя записано в качестве операнда в заключительной директиве END. Это единственный способ сообщить загрузчику ОС о том, где в исходном тексте программы расположена точка входа в нее. В более сложных программах обычно вначале могут идти описания процедур, макрокоманд, и в этом случае без такого явного указания на первую исполняемую команду нам не обойтись.

Основную часть экрана отладчика обычно занимают одно или несколько окон. В каждый момент времени активным может быть только одно из них. Активизация любого окна производится щелчком мышью в любой видимой точке окна.

Управление работой отладчика ведется с помощью системы меню. Имеется два типа таких меню:

§ глобальное меню — находится в верхней части экрана и доступно постоянно. Вызов меню осуществляется нажатием клавиши F10, после чего следует выбрать нужный пункт этого меню;

§ локальное меню — для каждого окна отладчика можно вызвать его собственное меню, которое учитывает особенности этого окна. Вызвать данное меню можно, щелкнув в окне правой кнопкой мыши (либо сделав активным окно и нажав клавиши Alt-F10).

Теперь можно проверить правильность функционирования нашей программы.

Специфика программ на ассемблере в том, что делать выводы о правильности их функционирования можно, только отслеживая работу на уровне микропроцессора. При этом нас интересует прежде всего то, как программа использует микропроцессор и изменяет состояние его ресурсов и компьютера в целом.

Запустить программу на выполнение в отладчике можно в одном из четырех режимов:

§ режим безусловного выполнения;

§ выполнение по шагам;

§ выполнение до текущего положения курсора;

§ выполнение с установкой точек прерывания.

Рассмотрим эти режимы подробнее.

Режим безусловного выполнения целесообразно применять, когда требуется посмотреть на общее поведение программы. Для запуска программы в этом режиме необходимо нажать клавишу F9. В точках, где необходимо ввести данные, отладчик, в соответствии с логикой работы применяемого средства ввода, будет осуществлять определенные действия. Аналогичные действия отладчик выполняет для вывода данных. Для просмотра или ввода этой информации можно открыть окно пользователя (Window|User screen) (В скобках мы будем указывать последовательность обращения к меню для выполнения необходимой операции. Так, в этом случае необходимо выбрать в глобальном меню Window пункт User screen.), например, нажав клавиши Alt+F5. Если работа программы удовлетворяет вас, то на этом можно и закончить. В случае, если возникают какие-то проблемы или если нужно более детально изучить работу программы, применяются три следующих режима отладки.

Выполнение по шагам применяется для детального изучения работы программы. В этом режиме мы можем выполнить программу по командам. При этом можно наблюдать результат исполнения каждой команды. Для активизации этого режима нужно нажать клавиши F7 (Run|Trace into) или F8 (Run|Step over). Обе эти клавиши активизируют пошаговый режим; отличие их проявляется в том случае, когда в потоке команд встречаются команды перехода в процедуру или на прерывание. При использовании клавиши F7 отладчик осуществит переход на процедуру или прерывание и выполнит их по шагам. Если же используется клавиша F8, то вызов процедуры или прерывания отрабатывается как одна обычная команда и управление возвращается следующей команде программы. Здесь нужно отметить, что кроме окна Module при работе в этом режиме полезно использовать окно CPU, вызвать которое можно через глобальное меню командой View|CPU.

Это окно отражает состояние микропроцессора и состоит из 5 подокон:

§ окна с исходной программой в дизассемблированном виде. Это та же самая программа, что и в окне Module, но уже в машинном виде. Пошаговую отладку можно производить прямо в этом окне; строка с текущей командой подсвечивается;

§ Registers — окна регистров микропроцессора, отражающего текущее содержимое регистров. Заметьте, что по умолчанию отображаются регистры только i8086. Для того чтобы воспользоваться всеми регистрами i486 или Pentium, нужно задать режим их отображения. Для этого щелкните правой кнопкой мыши в области подокна регистров для вызова локального меню. В меню выберите команду Registers 32-bit — Yes;

§ окна флагов, которые отражает текущее состояние флагов микропроцессора в соответствии с их мнемоническими названиями;

§ окна стека Stack, отражающего содержимое памяти, выделенной для стека. Адрес области стека определяется содержимым регистров SS и SP;

§ окна с дампом памяти Dump, отражающего содержимое области памяти по адресу, который формируется из компонентов, указанных в левой части окна. В окне можно увидеть содержимое произвольной области памяти. Для этого нужно в локальном меню, вызываемом по щелчку правой кнопки мыши, выбрать нужную команду.

Некоторые из этих подокон можно использовать по-другому. Все-таки удобнее работать с исходным текстом в окне Module, чем с его дизассемблированным вариантом в окне CPU. Но в то же время нужно отслеживать и состояние микропроцессора, используя информацию из подокон окна CPU. Совместить возможности окон Module и CPU можно посредством пункта глобального меню View, в котором необходимо выбрать нужные имена подокон CPU (эти имена мы указали при перечислении подокон выше).

Выполнение до текущего положения курсора позволяет выполнить программу по шагам, начиная с произвольного места программы. Этот режим целесообразно использовать в том случае, если нас интересует только правильность функционирования некоторого участка программы. Для активизации этого режима необходимо установить курсор на нужную строку программы и нажать клавишу F4. Программа начнет выполнение и остановится на отмеченной команде, не выполнив ее. Далее вы можете использовать, при необходимости, пошаговый режим.

Выполнение с установкой точек прерывания позволяет выполнить программу с остановкой ее в строго определенных точках прерывания (breakpoints). Перед выполнением программы необходимо установить эти точки в программе, для чего следует перейти в нужную строку и нажать клавишу F2. Выбранные строки подсвечиваются другим цветом. Установленные ранее точки прерывания можно убрать — для этого нужно повторно выбрать нужные строки и нажать клавишу F2. После установки точек прерывания программа запускается на выполнение клавишей F9. На первой точке прерывания программа остановится. Теперь можно посмотреть состояние микропроцессора и памяти, а затем продолжить выполнение программы. Сделать это можно или в пошаговом режиме, или выполнить программу до следующей точки прерывания.

Прервать выполнение программы в любом из этих режимов можно, нажав Ctrl+F2.

Мы описали все основные моменты, связанные с отладкой программы, начиная с ее загрузки в отладчик и заканчивая способами исследования ее работы. Теперь нам нужно немного попрактиковаться, запуская программу prg_3_1.exe в различных режимах отладчика или используя комбинацию этих режимов.

Подведем некоторые итоги:

u Наиболее развитым и удобным для использования ассемблерным пакетом является транслятор фирмы Borland — TASM. Большое количество опций и директив управления листингом программы на ассемблере позволяют задать достаточно гибкую обработку исходного кода транслируемой программы.

u Процесс создания программ на ассемблере напоминает процесс создания программ на большинстве языков высокого уровня. Последние версии транслятора фирмы Borland, начиная с TASM 3.0, поддерживают все основные существующие на настоящий момент времени технологии программирования, включая объектно-ориентированную.

u Специфика разработки программы на ассемблере в том, что программист должен уделять внимание не только и не столько особенностям предметной области, сколько тому, как при моделировании наиболее эффективно и корректно использовать ресурсы микропроцессора.

u В результате работы транслятора создается листинг программы, содержащий разнообразную информацию о программе: объектный код, сообщения о синтаксических ошибках, таблицу символов и т. д. Имея небольшой опыт, из листинга можно извлечь массу полезной информации.

u Целями трансляции программы являются обнаружение синтаксических ошибок и генерация объектного модуля. После того как получен корректный объектный модуль, программу необходимо скомпоновать. Для этого применяется утилита TLINK, входящая в состав пакета TASM. Одно из основных ее назначений — разрешение внешних ссылок. Если наша целевая программа состоит из нескольких отдельно оттранслированных модулей и в них есть взаимные ссылки на переменные или модули, то TLINK разрешает их, формируя тем самым правильные перемещаемые адреса.

u Результатом работы утилиты TLINK является исполняемый (загрузочный) модуль, имеющий расширение .ехе. Его уже можно запускать на выполнение с надеждой, что он правильно выполнит задуманные вами действия. Так, возможно, и будет, когда у нас появится достаточно опыта и знаний. Пока же чаще всего мы будем сталкиваться с ситуацией, в которой машина погружается в глубокую задумчивость, выдает на экран какую-то бессмыслицу или вовсе перезагружается. Все это говорит о том, что нам предстоит долгая работа по поиску самых вредных ошибок — логических. К счастью, в нашем распоряжении есть специальный вид программного обеспечения — отладчики. Постараемся уделить им достойное внимание, и мы увидим, что большинство наших программных проблем будет снято.

После всего вышерассмотренного настало время подробно разобраться с самим языком ассемблера и с правилами оформления программ на нем.

Коротко вспомним информацию об ассемблере, которой мы обладаем на настоящий момент:

§ ассемблер является символическим аналогом машинного языка. По этой причине программа, написанная на ассемблере, должна отражать все особенности архитектуры микропроцессора: организацию памяти, способы адресации операндов, правила использования регистров и т. д. Из-за необходимости учета подобных особенностей ассемблер уникален для каждого типа микропроцессоров;

§ программа на ассемблере представляет собой совокупность блоков памяти, называемых сегментами памяти. Программа может состоять из одного или нескольких таких блоков-сегментов. Каждый сегмент содержит совокупность предложений языка, каждое из которых занимает отдельную строку кода программы;

§ предложения ассемблера бывают четырех типов:

• команды или инструкции, представляющие собой символические аналоги машинных команд. В процессе трансляции инструкции ассемблера преобразуются в соответствующие команды системы команд микропроцессора;

• макрокоманды — оформляемые определенным образом предложения текста программы, замещаемые во время трансляции другими предложениями;

• директивы, являющиеся указанием транслятору ассемблера на выполнение некоторых действий. У директив нет аналогов в машинном представлении;

• строки комментариев, содержащие любые символы, в том числе и буквы русского алфавита. Комментарии игнорируются транслятором.

Синтаксис асемблера

Предложения, составляющие программу, могут представлять собой синтаксическую конструкцию, соответствующую команде, макрокоманде, директиве или комментарию. Для того чтобы транслятор ассемблера мог распознать их, они должны формироваться по определенным синтаксическим правилам. Нам, конечно, было бы неплохо их знать. Лучше всего использовать формальное описание синтаксиса языка наподобие правил грамматики. Наиболее распространенные способы подобного описания языка программирования — синтаксические диаграммы и расширенные формы Бэкуса-Наура. Для практического использования более удобны синтаксические диаграммы. К примеру, синтаксис предложений ассемблера можно описать с помощью синтаксических диаграмм, показанных на следующих рисунках.

Создание загрузочного модуля

Рис. 5.1. Формат предложения ассемблера

Создание загрузочного модуля

Рис. 5.2. Формат директив

Создание загрузочного модуля

Рис. 5.3. Формат команд и макрокоманд

На этих рисунках:

§ имя метки — идентификатор, значением которого является адрес первого байта того предложения исходного текста программы, которое он обозначает;

§ имя — идентификатор, отличающий данную директиву от других одноименных директив. В результате обработки ассемблером определенной директивы этому имени могут быть присвоены определенные характеристики;

§ код операции (КОП) и директива — это мнемонические обозначения соответствующей машинной команды, макрокоманды или директивы транслятора;

§ операнды — части команды, макрокоманды или директивы ассемблера, обозначающие объекты, над которыми производятся действия. Операнды ассемблера описываются выражениями с числовыми и текстовыми константами, метками и идентификаторами переменных с использованием знаков операций и некоторых зарезервированных слов.

Как использовать синтаксические диаграммы? Для этого нужно всего лишь найти и затем пройти путь от входа диаграммы (слева) к ее выходу (направо). Если такой путь существует, то предложение или конструкция синтаксически правильны. Если такого пути нет, значит эту конструкцию компилятор не примет. При работе с синтаксическими диаграммами обращайте внимание на направление обхода, указываемое стрелками, так как среди путей могут быть и такие, по которым можно идти справа налево. По сути, синтаксические диаграммы отражают логику работы транслятора при разборе входных предложений программы. В дальнейшем синтаксические диаграммы будут помогать в сложных случаях описывать синтаксис конструкций ассемблера.

Допустимыми символами при написании текста программ являются:

1. Все латинские буквы A-Z, a-z. При этом заглавные и прописные буквы считаются эквивалентными.

2. Цифры от 0 до 9.

3. Знаки ?, @, $, _, .

4. Разделители: , . [ ] ( ) { }+ / * % ! ? \ = # А ^

Предложения ассемблера формируются из лексем, представляющих собой синтаксически неразделимые последовательности допустимых символов языка, имеющие смысл для транслятора. Лексемами являются:

§ идентификаторы — последовательности допустимых символов, использующиеся для обозначения таких объектов программы, как коды операций, имена переменных и названия меток. Правило записи идентификаторов заключается в следующем. Идентификатор может состоять из одного или нескольких символов. В качестве символов можно использовать буквы латинского алфавита, цифры и некоторые специальные знаки — _, ?, $, @. Идентификатор не может начинаться символом цифры. Длина идентификатора может быть до 255 символов, хотя транслятор воспринимает лишь первые 32, а остальные игнорирует. Регулировать длину возможных идентификаторов можно с использованием опции командной строки mv (см. приложение 1). Кроме этого существует возможность указать транслятору на то, чтобы он различал прописные и строчные буквы либо игнорировал их раз личие (что и делается по умолчанию). Для этого применяются опции командной строки /mu, /ml, /тх (см. приложение );

§ цепочки символов — последовательности символов, заключенные в одинарные или двойные кавычки;

§ целые числа в одной из следующих систем счисления: двоичной, десятичной, шестнадцатеричной. Отождествление чисел при записи их в программах на ассемблере производится по определенным правилам. Для шестнадцатеричных чисел эти правила были рассмотрены ранее. Десятичные числа не требуют для своего отождествления указания каких-либо дополнительных символов. Для отождествления в исходном тексте программы двоичных чисел необходимо после записи нулей и единиц, входящих в их состав, поставить латинское «b». К примеру, 10010101b.

Таким образом, мы разобрались с тем, как конструируются предложения программы ассемблера. Но это — лишь самый поверхностный взгляд. Практически каждое предложение содержит описание объекта, над которым или при помощи которого выполняется некоторое действие. Эти объекты называются операндами. Их можно определить так: операнды — это объекты (некоторые значения, регистры или ячейки памяти), на которые действуют инструкции или директивы, либо это объекты, которые определяют или уточняют действие инструкций или директив.

Операнды могут комбинироваться с арифметическими, логическими, побитовыми и атрибутивными операторами для расчета некоторого значения или определения ячейки памяти, на которую будет воздействовать данная команда или директива.

Рассмотрим классификацию операндов, поддерживаемых транслятором ассемблера.

§ Постоянные или непосредственные операнды — число, строка, имя или выражение, имеющие некоторое фиксированное значение. Имя не должно быть перемещаемым, то есть зависеть от адреса загрузки программы в память. К примеру, оно может быть определено операторами equ или =:

Создание загрузочного модуля

Это первый рассматриваемый нами фрагмент программы, содержащий машинные команды (не считая листинга 3.1, который преследовал другие цели). В свое время все они будут подробно описаны, пока же мы по мере необходимости будем пояснять их в комментариях. Для более подробной информации вы всегда можете обратиться к приложению, где приведены справочные сведения по всем машинным командам. В данном фрагменте определяются две константы, которые затем используются в качестве непосредственных операндов в командах пересылки mov и сложения add.

§ Адресные операнды — задают физическое расположение операнда в памяти с помощью указания двух составляющих адреса: сегмента и смещения (рис. 5.4).

§

Создание загрузочного модуля

Рис. 5.4. Синтаксис описания адресных операндов

К примеру:

Создание загрузочного модуля

Здесь третья команда mov имеет адресный операнд.

§ Перемещаемые операнды — любые символьные имена, представляющие некоторые адреса памяти. Эти адреса могут обозначать местоположение в памяти некоторой инструкции (если операнд — метка) или данных (если операнд — имя области памяти в сегменте данных). Перемещаемые операнды отличаются от адресных тем, что они не привязаны к конкретному адресу физической памяти. Сегментная составляющая адреса перемещаемого операнда неизвестна и будет определена после загрузки программы в память для выполнения. К примеру:

Создание загрузочного модуля

В этом фрагменте mas_w — символьное имя, значением которого является начальный адрес области памяти размером 25 слов. Полный физический адрес этой области памяти будет известен только после загрузки программы в память для выполнения.

§ Счетчик адреса — специфический вид операнда. Он обозначается знаком $. Специфика этого операнда в том, что когда транслятор ассемблера встречает в исходной программе этот символ, то он подставляет вместо него текущее значение счетчика адреса. Значение счетчика адреса, или как его иногда называют счетчика размещения, представляет собой смещение текущей машинной команды относительно начала сегмента кода. Мы упоминали о счетчике адреса ранее, когда обсуждали понятие листинга на примере программы (prg_3_1.asm). В формате листинга счетчику адреса соответствует вторая или третья колонка (в зависимости от того, присутствует или нет в листинге колонка с уровнем вложенности). Как видите из этого примера листинга, при обработке транслятором очередной команды ассемблера счетчик адреса увеличивается на длину сформированной машинной команды. Важно правильно понимать этот момент. К примеру, обработка директив ассемблера не влечет за собой изменения счетчика. Подумайте, почему? Директивы, в отличие от команд ассемблера, — это лишь указания транслятору на выполнение определенных действий по формированию машинного представления программы, и для них транслятором не генерируется никаких конструкций в памяти. В качестве примера использования в команде значения счетчика адреса можно привести следующий:

Создание загрузочного модуля

При использовании подобного выражения для перехода не забывайте о длине самой команды, в которой это выражение используется, так как значение счетчика адреса соответствует смещению в сегменте команд данной, а не следующей за ней команды. В нашем примере команда jmp занимает 2 байта. Но будьте осторожны, длина команды зависит от того, какие в ней используются операнды. Команда с регистровыми операндами будет короче команды, один из операндов которой расположен в памяти. В большинстве случаев эту информацию можно получить, зная формат машинной команды и анализируя колонку листинга с объектным кодом команды.

§ Регистровый операнд — это просто имя регистра. В программе на ассемблере можно использовать имена всех регистров общего назначения и большинства системных регистров.

Создание загрузочного модуля

§ Базовый и индексный операнды. Этот тип операндов используется для реализации косвенной базовой, косвенной индексной адресации или их комбинаций и расширений. Адресация будет рассмотрена позже.

§ Структурные операнды используются для доступа к конкретному элементу сложного типа данных, называемого структурой. Мы подробно разберемся со структурами позже.

§ Записи (аналогично структурному типу) используются для доступа к битовому полю некоторой записи.

Операнды являются элементарными компонентами, из которых формируется часть машинной команды, обозначающая объекты, над которыми выполняется операция. В более общем случае операнды могут входить как составные части в более сложные образования, называемые выражениями. Выражения представляют собой комбинации операндов и операторов, рассматриваемые как единое целое. Результатом вычисления выражения может быть адрес некоторой ячейки памяти или некоторое константное (абсолютное) значение.

Возможные типы операндов мы уже рассмотрели. Перечислим теперь возможные типы операторов ассемблера и синтаксические правила формирования выражений ассемблера. После этого можно будет считать, что мы полностью разобрались с правилами записи команд ассемблера. В табл. 5.2 приведены поддерживаемые языком ассемблера операторы и перечислены их приоритеты. Дадим краткую характеристику операторов.

§ Арифметические операторы. К ним относятся унарные операторы «+» и «-», бинарные «+» и «-», операторы умножения «*», целочисленного деления «/», получения остатка от деления «mod» (рис. 5.5). Эти операторы расположены на уровнях приоритета 6, 7, 8 в табл. 5.1.

Например:

Создание загрузочного модуля

Рис.5.5. Синтаксис арифметических операторов

§ Операторы сдвига выполняют сдвиг выражения на указанное количество разрядов (рис. 5.6).

Например:

Создание загрузочного модуля

Рис.5.6. Синтаксис операторов сдвига

§ Операторы сравнения (возвращают значение «истина» или «ложь») предназначены для формирования логических выражений (см. рис. 5.7 и табл. 5.1). Логическое значение «истина» соответствует цифровой единице, а «ложь» — нулю.

Например:

Создание загрузочного модуля

В этом примере, если значение tab_size больше или равно 50, то результат в al равен 0ffh, а если tab_size меньше 50, то al равно 00h. Команда cmp сравнивает значение al с нулем и устанавливает соответствующие флаги в flags/eflags. Команда je на основе анализа этих флагов передает или не передает управление на метку ml.

Создание загрузочного модуля

Рис. 5.7. Синтаксис операторов сравнения

Создание загрузочного модуля

§ Логические операторы выполняют над выражениями побитовые операции (рис. 5.8). Выражения должны быть абсолютными, то есть такими, численное значение которых может быть вычислено транслятором.

Например:

Создание загрузочного модуля

Рис. 5.8. Синтаксис логических операторов

Более подробно о правилах, в соответствии с которыми вычисляется результат логических операций, мы поговорим позже.

§ Индексный оператор [ ]. Не удивляйтесь, но скобки тоже являются оператором, и транслятор их наличие воспринимает как указание сложить значение выражение_1 за этими скобками с выражение_2, заключенным в скобки (рис. 5.9).

Например:

Создание загрузочного модуля

Рис. 5.9. Синтаксис индексного оператора

Заметим, что в литературе по ассемблеру принято следующее обозначение: когда в тексте речь идет о содержимом регистра, то его название берут в круглые скобки.

К примеру, в нашем случае запись в комментариях последнего фрагмента программы mas + (si) означает вычисление выражения: значение смещения символического имени mas плюс содержимое регистра si.

§ Оператор переопределения типа ptr применяется для переопределения или уточнения типа метки или переменной, определяемых выражением (рис. 5.10). Тип может принимать одно из следующих значений: byte, word, dword, qword, tbyte, near, far. Что означают эти значения, мы узнаем далее на этой лекции.

Например:

Создание загрузочного модуля

Установка windows 10 в 10 кликов ,создание загрузочной флешки или обновление


Похожие статьи.

Понравилась статья? Поделиться с друзьями: