Многомодульные программы
Дата последнего обновления файла 21.01.05
Многомодульные программы.
Разбиение исходного текста программы на несколько файлов делает этот текст более понятным для программиста или нескольких программистов, участвующих в создании программного продукта. Однако остаётся нерешёнными ещё несколько задач:
- Программа-транслятор работает со всем исходным текстом целиком, ведь она соединяет все файлы перед трансляцией вместе. Поэтому время трансляции исходного текста программы остаётся значительным (и даже возрастает). В то же самое время программа никогда не переписывается целиком. Обычно изменяется только небольшой участок программы.
- При назначении переменных их количество ограничено программой-транслятором и может быть исчерпано при написании программы.
- Различные программисты, участвующие в создании программного продукта могут назначать одинаковые имена для своих переменных и при попытке соединения файлов в единую программу обычно возникают проблемы.
Все эти проблемы могут быть решены при раздельной трансляции программы. То есть было бы неплохо уметь транслировать каждый файл с исходным текстом программы отдельно и соединять затем готовые оттранслированные участки программы.
На первый взгляд раздельная трансляция не должна вызывать каких либо проблем. Однако это не так. При компиляции исходного текста программы транслятор составляет таблицу ссылок на константы, переменные и команды. Если при втором просмотре исходного текста программы, во время которого формируется объектный модуль, транслятор не обнаружит имени переменной или метки в своей таблице, то будет сформировано сообщение об ошибке и объектный модуль будет стёрт с диска компьютера.
Для того, чтобы транслятор вместо формирования сообщения об ошибке записал в объектный модуль информацию, необходимую для редактора связей, нужно использовать специальные директивы ссылок на внешние переменные или метки. Обычно эти директивы называются PUBLIC (общие) и EXTRN (внешние). Для ссылки на переменную или метку используется директива EXTRN.
В этой директиве перечисляются через запятую метки и переменные, точное значение которых редактор связей должен получить из другого модуля и модифицировать все команды, в которых эти метки или переменные используются. Пример использования директивы EXTRN на языке программирования ASM-51:
EXTRN DATA (BufInd, ERR)
EXTRN CODE (Podprogr) Для того, чтобы редактор связей мог осуществить связывание модулей в единую программу, переменные и метки, объявленные по крайней мере в одном из модулей как EXTRN, в другом модуле должны быть объявлены как доступные для всех модулей при помощи директивы PUBLIC . Пример использования директивы PUBLIC на языке программирования ASM-51:
PUBLIC BufInd, Parametr
PUBLIC Podprogr, ?Podprogr?Byte
Использование нескольких модулей при написании программы увеличивает скорость трансляции и, в конечном итоге, скорость написания программы. Однако объявления переменных и имён подпрограмм внешних модулей загромождают исходный текст модуля. Кроме того, при использовании чужих модулей трудно объявить переменные и подпрограммы без ошибок. Поэтому обычно объявления переменных, констант и предварительные объявления подпрограмм хранят во включаемых файлах, которые называются файлами-заголовками. Правилом хорошего тона считается при разработке программного модуля сразу же написать файл-заголовок для этого модуля, который может быть использован программистами, работающими с Вашим программным модулем.
Для объединения нескольких модулей в исполняемую программу имена всех модулей передаются в редактор связей rl51.exe в качестве параметров при запуске этой программы. Пример вызова редактора связей из командной строки DOS для объединения трёх модулей:
rl51.exe progr.obj, modul1.obj, modul2.obj
В результате работы редактора связей в этом примере будет создан исполняемый модуль с именем progr. Формат записи информации в этом файле остаётся прежним - объектный. Это позволяет объединять модули по частям, то есть при желании можно из нескольких мелких модулей получить один более крупный.
В настоящее время часто пользуются интегрированными средами программирования, такими как Franclin или keil C, в состав которых входят язык программирования ASM?51. В этих программах создание строки вызова редактора связей производится автоматически при настройке программного проекта, а вызов этой строки производится при трансляции программного проекта. Настройка программного проекта происходит при подключении к проекту новых программных модулей и при изменении свойств программного проекта, таких как разрешение или запрет создания карты памяти программы, выбор папки для хранения выходных файлов, разрешение или запрет помещения в выходной файл отладочной информации разрешение или запрет создания загрузочного HEX файла.
[ Назад] [ Содержание] [ Вперёд]
Такое объявление переменных позволяет написать наиболее эффективную программу управления контроллером и подключенными к нему устройствами.
Директива ISEG позволяет определить абсолютный сегмент во внутренней памяти данных по определённому адресу. Напомню, что внутренняя память с косвенной адресацией в два раза больше памяти с прямой адресацией. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы ISEG для объявления байтовых переменных приведён на рисунке 5.
Рисунок 5. Пример использования директивы ISEG для объявления байтовых переменных.
Директива XSEG позволяет определить абсолютный сегмент во внешней памяти данных по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. До недавнего времени использование внешней памяти не имело смысла, так как это значительно увеличивало габариты и цену устройства. Однако в последнее время ряд фирм стал размещать на кристалле значительные объёмы ОЗУ, доступ к которому осуществляется как к внешней памяти. Так как использование этой директивы не отличается от использования директивы DSEG, то отдельный пример приводиться не будет.
Использование абсолютных сегментов позволяет облегчить работу программиста по распределению памяти микроконтроллера для различных переменных. Однако в большинстве случаев абсолютный адрес переменной нас совершенно не интересует. Исключение составляют только регистры специальных функций. Так зачем же вручную задавать начальный адрес сегментов?
Еще одна ситуация, когда нас не интересует начальный адрес сегмента – это программные модули.
Как уже говорилось ранее, в программные модули обычно выносятся подпрограммы. Естественно, что где конкретно будут находиться эти подпрограммы в адресном пространстве микроконтроллера, нас тоже мало интересует.
Если абсолютные адреса переменных или участков программ не интересны, то можно воспользоваться перемещаемыми сегментами. Имя перемещаемого сегмента задается директивой segment.
Директива segment позволяет определить имя сегмента и область памяти, где будет размещаться данный сегмент памяти. Для каждой области памяти определено ключевое слово:
- data – размещает сегмент во внутренней памяти данных с прямой адресацией;
- idata – размещает сегмент во внутренней памяти данных с косвенной адресацией;
- bit – размещает сегмент во внутренней памяти данных с битовой адресацией;
- xdata – размещает сегмент во внешней памяти данных;
- code – размещает сегмент в памяти программ;
_data segment idata
public VershSteka
;Определение переменных----------------------------
rseg _data
buferKlav: ds 8
VershSteka:
End
Рис. 8.31. Пример использования директив segment и rseg для объявления байтовых переменных
В этом примере объявлена строка buferKlav, состоящая из восьми байтовых переменных. Кроме того, в данном примере объявлена переменная VershSteka, соответствующая последней ячейке памяти, используемой для хранения переменных.
Переменная VershSteka может быть использована для начальной инициализации указателя стека для того, чтобы отвести под стек максимально доступное количество ячеек внутренней памяти. Это необходимо для того, чтобы избежать переполнения стека при вложенном вызове подпрограмм.
Объявление и использование сегментов данных в области внутренней или внешней памяти данных не отличается от приведенного примера за исключением ключевого слова, определяющего область памяти данных.
Еще один пример использования директив segment и rseg приведен на рисунке 8.32. В этом примере директива segment используется для объявления сегмента битовых переменных.
_bits segment bit
public knIzm,strVv
;Определение битовых переменных -------------------------------------
rseg _bits
knIzm: dbit 1
strVv: dbit 1
end
Рис. 8.32. Пример использования директив segment и rseg для объявления битовых переменных
[ Назад] [ Содержание]