- •Эрни Каспер Программирование на языке Ассемблера для микроконтроллеров семейства i8051
- •1.Что нужно знать программисту о микроконтроллерах семейства i8051
- •1.1.Общие сведения об архитектуре i8051
- •1.2.Правила записи команд микроконтроллера семейства i8051 на Ассемблере
- •1.3.Форматы и способы адресации данных
- •1.4.Форматы и способы адресации команд
- •1.5.Команды пересылки информации
- •1.6.Команды поразрядной обработки информации
- •1.7.Команды арифметических операций
- •1.8.Управляющие команды
- •2.Директивы ассемблера для микроконтроллеров семейства i8051
- •2.1.Общие понятия о процессах трансляции и компоновки
- •2.2.Обработка имен транслятором и компоновщиком
- •2.3.Директивы резервирования памяти и инициализации данных
- •2.4.Использование выражений в операндах
- •2.5.Директивы условной трансляции
- •2.6.Директивы подстановок
- •2.7.Директивы управления вводом и выводом
- •Глава 3.
- •3.Кросс-средства фирмы 2500 a.D. Software, Inc. Для семейства i8051
- •Глава 4
- •4.Программирование арифметических действий
- •4.1.Кодирование информации в микроконтроллере
- •4.2.Арифметические действия с большими числами
- •4.3.Арифметические действия с отрицательными числами
- •4.4.Контроль точности при программировании арифметических операций
- •Глава 5
- •5.Программирование вычисления функций
- •5.1.Возведение в квадрат и извлечение квадратного корня
- •5.2.Переход от десятичной системы счисления к двоичной и обратно
- •5.3.Вычисление функций при помощи таблиц
- •5.4.Вычисление обратной функции по таблице прямой функции
- •5.5.Компенсация систематических погрешностей при помощи таблиц
- •Глава 6
- •6.Программирование фильтрации сигналов
- •6.1.Особенности цифровой фильтрации сигналов
- •6.2.Программирование простейших фильтров нижних частот
- •6.3.Программирование фильтра для оценки параметров сигнала
- •6.4.Программирование медианного фильтра
- •Глава 7
- •7.Программирование взаимодействия с внешними устройствами
- •7.1.Общие вопросы взаимодействия
- •7.2.Порядок выполнения прерываний в микроконтроллерах семейства i8051.
- •7.3.Синхронизация работы программы внешним или внутренним сигналом
- •7.4.Программирование приема информации от датчиков
- •7.5.Программирование выдачи команд на исполнительные устройства
- •7.6.Программирование ввода и вывода информации для пользователя
- •8.Несколько рекомендаций о стиле программирования
- •8.1.Стиль программирования и использование ресурсов
- •8.2.Оформление исходного текста программы
- •8.3.Системы обозначений, выражения и простые подстановки
- •8.4.Применение подпрограмм и сложных текстовых подстановок
2.2.Обработка имен транслятором и компоновщиком
Перевод мнемокодов и имен регистров ассемблерной команды в машинный код производится по заранее заданным таблицам с учетом способа адресации. Обработка символических имен, заданных программистом, представляет более сложную задачу. Программисту нужно понимать, каким образом эти имена обрабатываются транслятором и компоновщиком. Смысл обработки имен транслятором состоит в вычислении адресов, по которым программные объекты будут размещены в ОЗУ и ПЗУ микроконтроллера. По этим адресам определяются машинные коды команд, загружаемых в ПЗУ. Транслятор и компоновщик (независимо от используемого языка программирования) решают задачу вычисления правильных адресов данных и команды в соответствии с их размещением в памяти.
Начнем ознакомление с проблемой вычисления адресов команд и данных с понятия о сегментировании памяти. В отличие от процессоров, для микроконтроллеров сегментирование связано не со способом вычисления исполнительного адреса, а с форматом обрабатываемой информации. Обращение к встроенному ОЗУ и к функциональным регистрам микроконтроллера может производиться как для чтения, так и для записи. Адресное пространство для данных в байтовом и битовом форматах от 00 до FF соответствует размеру байта адресации. В ОЗУ можно адресовать 128 байт (адреса от 00 до 7F) и столько же битов. Битовое пространство занимает байтовые адреса от 20 до 2F, то есть 16 байт того же самого ОЗУ. Остальные адреса используются для обращения к функциональным регистрам. Обращение к внешним ЗУ может производиться только в байтовом формате, притом к ЗУ команд — только для чтения. Адресное пространство внешних ЗУ ограничено двумя байтами адресации, что соответствует 65536 байт (адреса от 0000 до FFFF).
В Ассемблере стандартными сегментами (они называются также секциями) являются сегменты CODE (предназначен для программы) и DATA (предназначен для данных). Две других секции BSECT (битовая) и RSECT (регистровая) введены для программирования обращений к ОЗУ в битовом и байтовом форматах соответственно. Все эти названия имеются в таблице имен приведенного примера. Любая часть транслируемого текста должна относиться к какой-либо секции, то есть находиться между открывающей и закрывающей строками директивы определения секции:
.CODE
/команды и директивы секции кода
.ENDS
.DATA
/директивы секции данных
.ENDS
.RSECT
/директивы регистровой секции
.ENDS
.BSECT
/директивы битовой секции
.ENDS
Если в начале исходного текста не указано имя секции, то транслятор по умолчанию считает открытой секцию кода (именно поэтому нам удалась трансляция приведенного примера). Допускается вложение одних секций в другие. Строка ENDS закрывает вложенную секцию. Если вложения секции не нужно, то для перехода к другой секции достаточно записать только открывающую строку директивы.
При желании можно задать дополнительные секции при помощи следующих директив:
имя:
.SECTION BIT ;Описывается новая битовая секция имя:
.SECTION REG ;Описывается новая регистровая секция
Эти директивы позволяют программисту задавать имена секций. Для открытия секции, определенной программистом, нужно записать имя секции в поле мнемокода. Допускается использование не более 256 секций в одном файле.
Транслятор переводит данные и команды в машинный формат, располагая их в той последовательности, в какой они будут храниться в отведенном для них адресном пространстве секции. Поскольку программа состоит из нескольких секций, то вычисление адресов для записи двоичных кодов ведется для каждой секции отдельно. Начальный адрес в каждой из секций равен 0. Для использования счетчика текущего адреса в секции применяется предопределенное символическое имя $ (знак доллара) или * (звездочка). Числовое значение содержимого счетчика после трансляции строки исходного текста увеличивается на количество байтов, добавленных в адресное пространство текущего сегмента командой или директивой. Программист может использовать содержимое счетчика текущего адреса в выражениях, входящих в операнды команд или директив, и даже изменять его содержимое при помощи директивы. Запись заданного адреса в счетчик производится директивой.
ORG адрес
Синонимом ORG является ORIGIN.
При обработке каждого оператора, которому соответствуют данные или команда, транслятор переводит их в двоичный код и записывает в отведенном для их секций адресном пространстве. Если оператор поименован (в поле метки есть имя), то транслятор записывает номер строки, имя объекта и содержимое счетчика адреса в таблицу имен. Конечно перед этим транслятор проверяет, нет ли этого имени в таблице. Если оно уже есть в таблице, то выдается сообщение об ошибке. Как только команда поименована, мы можем записывать в поле операнда управляющих команд ее адрес в символическом виде для передачи управления. Как только поименованы данные или отведенное для них место в памяти, мы можем записывать в поле операнда команды обработки информации их адрес в символическом виде (для чтения или записи).
Имя программного объекта может быть использовано в поле операнда только тогда, когда оно включено в таблицу символических имен. Если имя используется в той части программы, которая предшествует его определению, то транслятор не может использовать его при первом проходе, так что ему приходится читать исходный текст повторно после определения всех меток. Если и после повторного чтения программы не удалось обнаружить имя, записанное в операнде, то ввиду невозможности вычисления значения адресного выражения с этим именем транслятор выдает сообщение об ошибке.
Повторное использование имени в поле метки недопустимо, потому что нельзя создавать дополнительный программный объект с тем же самым именем. Исключения из правила уникальности используемых имен допускаются только в том случае, когда их повторное появление не нарушает этого правила. Это относится к директивам переопределяемых программных объектов и к локальным меткам. В таком случае при повторном использовании имени ранее созданный программный объект заменяется другим.
Имя должно начинаться с буквы и состоять из букв, цифр и может включать в себя некоторые другие символы. Его длина может быть очень большой, лишь бы оператор не вышел за пределы строки редактора, но только 32 первых символа будут иметь значение для таблицы имен. Большие и маленькие буквы в имени считаются различными. При соблюдении этих условий имя в поле метки считается нелокальным и включается в таблицу имен модуля. Имя, подставленное в поле метки, называется меткой (label). Метку можно начинать с любой колонки, если за именем следует двоеточие. Если двоеточие не используется, то первый символ имени должен располагаться в первой колонке.
Обычная метка наблюдаема (scope) во всем модуле. Однако при программировании можно пользоваться локальными метками, видимость которых ограничена двумя ближайшими нелокальными метками (предшествующей и последующей). Локальные метки могут быть использованы многократно, поэтому они не включаются в таблицу имен. Отличительным признаком локальной метки является символ $ в начале или конце имени, поэтому имя локальной метки может начинаться с цифры. Впрочем в качестве признака локальной метки можно использовать Другой символ, который указывается директивой
.LLCHAR символ
Эта директива изменяет символ-определитель локальной метки.
До сих пор речь шла об именах, используемых в пределах одного программного модуля. Если программа состоит более чем из одного модуля, то вычисление всех адресов на этапе трансляции невозможно. В связи с этим необходимо обратить внимание читателей на то, что адреса команд и данных на разных этапах подготовки программы к выполнению вычисляются с разной степенью определенности. Это касается размещения команд и данных в памяти и, как следствие, ссылок на адреса команд (из управляющих команд) или на адреса данных (из команд, обрабатывающих информацию). В связи с неполнотой информации о размещении данных и команд транслятор создает в объектных модулях три вида адресов:
• одна часть адресов определена окончательно и не подлежит изменению,
• другая часть адресов определена с точностью до размещения модулей в программе,
• третья часть адресов неизвестна, так как они находятся в других модулях (так называемые внешние ссылки).
Так что при трансляции каждого оператора, порождающего машинный код, транслятор прежде всего проверяет, какого рода адреса используются в операторе, по какому адресу этот код должен быть записан и какое символическое представление объекта программы должно быть сохранено для последующей сборки.
При использовании нескольких модулей большинство адресов, вычисленных транслятором, не являются окончательными и подлежит уточнению в процессе сборки. При сборке объектных модулей производится их перекомпоновка с целью объединения частей программы, содержащих одноименные секции, что позволяет вычислить все адреса. Имена секций сохраняются в объектных файлах для того, чтобы при сборке модулей объединять одноименные секции. При трансляции по умолчанию используется относительный режим, чтобы при сборке не происходило наложения адресных пространств. В относительном режиме сборка производится так, чтобы адресное пространство очередного модуля располагалось непосредственно за адресным пространством предыдущего. Таким образом для каждой из секций размер адресного пространства программы равен сумме адресных пространств модулей, если программист не задал смещения для собираемых частей секций.
При необходимости можно обеспечить фиксированные (не изменяемые компоновщиком) адреса в пределах 0 страницы ЗУ переключением транслятора в абсолютный режим директивой
.ABSOLUTE
Абсолютный режим допустим только для данных. Выполняемые команды всегда должны ассемблироваться в относительном режиме. Для возврата к относительному режиму используется директива
.RELATIVE
При переходе из одной секции в другую атрибуты ABSOLUTE и RELATIVE не изменяются.
В современных компьютерах большинство программ должно быть перемещаемыми, то есть работоспособными при их загрузке в произвольную часть ОЗУ. В микроконтроллерах все адреса окончательно определяются уже в процессе редактирования связей. Совершенно ясно, что транслятор может определить самостоятельно только первые два из трех перечисленных выше видов адреса. Притом адреса второго вида в объектном коде помечаются специальными символами, обнаружение которых используется компоновщиком для пересчета. Чтобы транслятор не выдавал сообщения об ошибках по третьему виду адресов, программист должен включить в программу соответствующие директивы. Связи между модулями осуществляются по данным и по управлению. Эти связи осуществляются при помощи символических имен, которые в модулях, вызывающих связь, объявляются как внешние при помощи директивы
.EXTERNAL список
В списке должны быть перечислены имена, определяемые в других модулях. Регистры могут быть заданы как внешние двумя путями. Если директива используется в регистровой секции, то внешние имена определяются как регистровые. Во всех остальных секциях необходимо перед каждым именем регистровой переменной в списке вставить слово REG. Синонимами EXTERNAL являются EXTERN и XREF.
Вызываемые имена должны быть определены в соответствующих модулях как доступные при помощи директивы
.PUBLIC список
Определив имя как доступное, можно ссылаться на него из других модулей. Синонимами PUBLIC являются XDEF и GLOBAL, хотя последний мнемокод в других диалектах Ассемблера используется для объявления и внешних, и доступных имен.
Существует еще один способ сделать имена доступными для использования другими модулями. Для этого используется директива
.GLOBALS ON
Транслятор считает все имена в поле меток команд следующих за этой Директивой глобальными, доступными для ссылки из других модулей.
Эта директива не действует на локальные метки. Ее действие отменяется переходом к трансляции другого модуля или директивой
.GLOBAL OFF
Имена, использованные в разных модулях и не внесенные в соответствующие списки, не включаются в объектные файлы. В этом случае при компоновке редактор связей выдает сообщение об ошибке.
Использование промежуточного объектного файла при переводе исходного текста в исполняемую программу характерно для большинства современных языков программирования. Хотя получение исполняемой программы непосредственно из исходного текста в принципе возможно, по ряду причин сложилась практика двухступенчатого перехода. В случае программирования для микроконтроллеров это оправдано тем, что для разработки более или менее сложных программ удобно сначала отладить отдельные функционально законченные модули независимо друг от друга. Кроме того набор отлаженных объектных файлов, которые могут использоваться во многих программах, можно объединить при помощи программы-библиотекаря в специальный файл с расширением LIB. После отладки всех модулей их объектные файлы (включая библиотечные) собираются в один исполняемый файл для комплексной отладки программы.
Транслятор может обрабатывать несколько последовательно расположенных модулей исходного текста, составляющих один файл. Для этого в начале каждого модуля нужно записать директиву
.MODULE
а в конце модуля — директиву
.ENDMOD
Вложение модулей в другие модули не допускается. Транслятор осуществляет перевод модулей в машинный код независимо от других модулей. Количество модулей в файле не ограничивается. Выходной файл в этом случае имеет расширение РАК. Эта пара директив предназначена для получения объектных файлов, пригодных для работы с программой-библиотекарем. Их использование позволяет транслировать всю библиотеку в целом, вместо того чтобы транслировать каждую подпрограмму отдельно, а затем собирать объектные модули в один файл. Обычно библиотеки составляются из большого числа небольших по размерам программ. Когда компоновщик не может найти глобальное имя (идентификатор) в каком-либо из компонуемых файлов, он может продолжить его поиск в библиотеках.
