Использование сегментов в ассемблере
Как только встал вопрос о транслировании программы по частям возникает вопрос как с этими частями работать. Справедливости ради необходимо отметить, что даже когда мы не задумываемся о сегментах, в программе присутствует два сегмента: память программ и память данных. Если внимательно присмотреться к программе, то можно обнаружить, что кроме команд в памяти программ хранятся константы, то есть в памяти программ располагаются по крайней мере два сегмента: программа и данные. Чередование программы и данных может привести к нежелательным последствиям. Вследствие каких либо причин случайно данные могут быть выполнены в качестве программы или наоборот программа может быть воспринята и обработана как данные.
Рисунок V.3.1. Разбиение памяти программ и памяти данных на сегменты.
Перечисленные выше причины приводят к тому, что желательно явным образом выделить по крайней мере три сегмента:
программу;
переменные;
константы.
Наиболее простой способ определения сегментов это использование абсолютных сегментов памяти. При этом способе распределение памяти ведётся вручную точно также, как это делалось при использовании директивы EQU. В этом случае начальный адрес сегмента жёстко задаётся программистом и он же следит за тем, чтобы сегменты не перекрывались друг с другом в памяти микроконтроллера. Использование абсолютных сегментов позволяет более гибко работать с памятью данных, так как теперь байтовые переменные в памяти данных могут быть назначены при помощи директивы резервирования памяти DS, а битовые переменные при помощи директивы резервирования битов DBIT.
Для определения абсолютных сегментов памяти используются директивы:
BSEG - абсолютный сегмент в области битовой адресации
CSEG - абсолютный сегмент в области памяти программ
DSEG - абсолютный сегмент в области внутренней памяти данных
ISEG - абсолютный сегмент в области внутренней памяти данных с косвенной адресацией
XSEG - абсолютный сегмент в области внешней памяти данных
Директива BSEG позволяет определить абсолютный сегмент во внутренней памяти данных с битовой адресацией по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Использование битовых переменных позволяет значительно экономить внутреннюю память программ микроконтроллера. Пример использования директивы BSEG для объявления битовых переменных приведён на рисунке V.2.5.
Рисунок V.2.5. Пример использования директивы BSEG для объявления битовых переменных.
Директива CSEG позволяет определить абсолютный сегмент в памяти программ по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы CSEG для размещения подпрограммы обслуживания прерывания от таймера 0 приведён на рисунке V.3.3.
Рисунок V.3.3. Пример использования директивы CSEG для размещения подпрограммы обслуживания прерывания.
Директива DSEG позволяет определить абсолютный сегмент во внутренней памяти данных по определённому адресу. Предполагается, что к этому сегменту будут обращаться команды с прямой адресацией. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы DSEG для объявления байтовых переменных приведён на рисунке V.3.4.
Рисунок V.3.4. Пример использования директивы DSEG для объявления байтовых переменных.
В приведённом примере предполагается, что он связан с примером, приведённом на рисунке 2. То есть команды, изменяющие битовые переменные RejInd, RejPriem или Flag одновременно будут изменять содержимое переменной Rejim, и наоборот команды работающие с переменной Rejim одновременно изменяют содержимое флагов RejInd, RejPriem или Flag. Такое объявление переменных позволяет написать наиболее эффективную программу управления контроллером и подключенными к нему устройствами.
Директива ISEG позволяет определить абсолютный сегмент во внутренней памяти данных по определённому адресу. Напомню, что внутренняя память с косвенной адресацией в два раза больше памяти с прямой адресацией. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы ISEG для объявления байтовых переменных приведён на рисунке V.3.5.
Рисунок V.3.5. Пример использования директивы ISEG для объявления байтовых переменных.
Директива XSEG позволяет определить абсолютный сегмент во внешней памяти данных по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. До недавнего времени использование внешней памяти не имело смысла, так как это значительно увеличивало габариты и цену устройства. Однако в последнее время ряд фирм стал размещать на кристалле значительные объёмы ОЗУ, доступ к которому осуществляется как к внешней памяти. Так как использование этой директивы не отличается от использования директивы DSEG, то отдельный пример приводиться не будет.
Использование абсолютных сегментов позволяет облегчить работу программиста по распределению памяти микроконтроллера для различных переменных. Однако в большинстве случаев абсолютный адрес переменной нас совершенно не интересует. Исключение составляют только регистры специальных функций. Так зачем же вручную задавать начальный адрес сегментов?
Еще одна ситуация, когда нас не интересует начальный адрес сегмента – это программные модули. Как уже говорилось ранее, в программные модули обычно выносятся подпрограммы. Естественно, что где конкретно будут находиться эти подпрограммы в адресном пространстве микроконтроллера, нас тоже мало интересует.
Если абсолютные адреса переменных или участков программ не интересны, то можно воспользоваться перемещаемыми сегментами. Имя перемещаемого сегмента задается директивой segment.
Директива segment позволяет определить имя сегмента и область памяти, где будет размещаться данный сегмент памяти. Для каждой области памяти определено ключевое слово:
· data – размещает сегмент во внутренней памяти данных с прямой адресацией;
· idata – размещает сегмент во внутренней памяти данных с косвенной адресацией;
· bit – размещает сегмент во внутренней памяти данных с битовой адресацией;
· xdata – размещает сегмент во внешней памяти данных;
· code – размещает сегмент в памяти программ;
После определения имени сегмента можно использовать этот сегмент при помощи директивы rseg. Использование сегмента зависит от области памяти, для которой он предназначен. Если это память данных, то в сегменте объявляются байтовые или битовые переменные. Если это память программ, то в сегменте размещаются константы или участки кода программы. Пример использования директив segment и rseg для объявления битовых переменных приведен на рисунке 8.31.
_data segment idata
public VershSteka
rseg _data
buferKlav: ds 8
VershSteka:
End
Рис. V.3.6. Пример использования директив 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
Рис. V.3.7. Пример использования директив segment и rseg для объявления битовых переменных