При описании первого блока переменных в Программе 8.1 явно указывается, что он начинается с регистра h’26’. Во всех последующих директивах cblock указание адреса можно опустить, если новые переменные должны располагаться сразу же после уже описанных. Таким образом, переменная I:2 размещается в регистрах h’27’:h’28’, a COUNT:1 — в регистре h’29’. Такой подход обеспечивает гораздо большую гибкость по сравнению с ручным распределением регистров самим программистом, так как при изменении какого-либо модуля или добавлении новых элементов распределение памяти изменится автоматически. Кроме того, изменение начального адреса какого-либо блока, скажем с регистра h’26’ на регистр h’20’, автоматически размещает все переменные программы по новым адресам.
Также для именования (присваивания имен) различных объектов программы можно использовать директиву #define. Например, строка вида
#define 6,7 BUZZER
позволяет нам писать bsf BUZZER вместо bsf 6,7, например для включения звукового излучателя, подключенного к выводу 7 порта В (регистр h’06’).
Чтобы проиллюстрировать еще одну возможность ассемблера, подпрограмма из Программы 8.1 размещается, начиная с адреса h’200’ памяти программ. Это осуществляется использованием директивы org (см. также Программу 7.1 на стр. 215). В результате данной операции метке программы SQR_ROOT было присвоено значение h’200’.
В последней строке Программы 8.1 размещается директива end. Эта директива указывает ассемблеру игнорировать весь текст, располагающийся ниже ее, т. е. прекратить трансляцию.
Разумеется, для работы символьного транслятора требуется больше вычислительных ресурсов, нежели для простого шестнадцатеричного загрузчика, особенно это касается памяти и устройств резервного хранения. До появления в конце 1970-х персональных компьютеров для выполнения ассемблирования требовались мэйнфреймы, мини-компьютеры или же специальные системы разработки микропроцессоров/микроконтроллеров. Естественно, эти решения были весьма дорогостоящими, и по причине невозможности использования указанных вычислительных средств повсеместно использовалось ручное кодирование.
Программы-трансляторы, вообще говоря, выполняют две задачи:
1. Преобразование различных мнемоник команд и меток в их эквивалентные значения в машинном коде.
2. Размещение команд и данных по заданным адресам.
Для большинства программ, выполняющихся на микроконтроллерах PIC младшего и среднего уровней, более чем достаточно возможностей абсолютного ассемблера. Чтобы облегчить понимание процесса трансляции, изображенного на Рис. 8.2, мы рассмотрим все этапы преобразования нашей программы, начиная с создания файла с исходным кодом и заканчивая итоговым файлом с абсолютным машинным кодом. Перемещаемый ассемблер мы затронем чуть позже.
Рис. 8.2.
Редактирование
Прежде всего исходный файл необходимо создать. Для этого используется
Типичная строка файла с исходным кодом имеет следующий формат:
За исключением строк, в которых содержится только комментарий, все строки должны содержать инструкцию (либо исполняемую микроконтроллером команду, либо директиву) и соответствующий операнд или операнды. Все метки должны начинаться с 1-й позиции строки; если метка отсутствует, то первым символом строки должен быть пробел или символ табуляции, индицирующие этот факт. Метка может состоять из 32 алфавитно-цифровых символов, символов подчеркивания или символов вопроса. При этом первым символом метки обязательно должна быть буква или символ подчеркивания. Обычно метки нечувствительны к регистру символов. Метка строки соответствует адресу памяти программ первой следующей за ней исполняемой команды. Метка должна отделяться от последующей команды или директивы пробелом, двоеточием или даже символом новой строки.
Необязательный