Дата последнего обновления файла 02.12.2005

Использование сегментов в языке программирования ассемблер

Необходимо отметить, что даже когда мы не задумываемся о сегментах, в программе присутствует два сегмента: сегмент кода программы и сегмент данных. Если внимательно присмотреться к программе, то можно обнаружить, что кроме кодов команд в памяти программ хранятся константы, то есть в памяти программ микроконтроллера располагаются, по крайней мере, два сегмента: программа и данные. Чередование программы и констант может привести к нежелательным последствиям. Вследствие каких-либо причин данные могут быть случайно выполнены в качестве программы или наоборот программа может быть воспринята и обработана как данные.

Рисунок 1. Разбиение памяти программ и памяти данных на сегменты.

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

  1. программы;
  2. стека;
  3. переменных;
  4. констант.

Пример размещения сегментов в адресном пространстве памяти программ и внутренней памяти данных приведен на рисунке1. На этом рисунке видно, что при использовании нескольких сегментов переменных во внутренней памяти данных редактор связей может разместить меньший из них на месте неиспользованных банков регистров. Под сегмент стека обычно отводится вся область внутренней памяти, не занятая переменными. Это позволяет создавать программы с максимальным уровнем вложенности подпрограмм. Сегмент переменных, расположенный на рисунке 1 во внешней памяти данных, при использовании современных микросхем, таких как AduC842, может находиться в ОЗУ, расположенном на кристалле микроконтроллера.

Абсолютные сегменты памяти

Наиболее простой способ определения сегментов это использование абсолютных сегментов памяти. При этом способе распределение памяти ведётся вручную точно также, как это делалось при использовании директивы EQU. В этом случае начальный адрес сегмента жёстко задаётся программистом и он же следит за тем, чтобы сегменты не перекрывались друг с другом в памяти микроконтроллера. Использование абсолютных сегментов позволяет более гибко работать с памятью данных, так как теперь байтовые переменные в памяти данных могут быть назначены при помощи директивы резервирования памяти DS, а битовые переменные при помощи директивы резервирования битов DBIT.

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

  1. BSEG - абсолютный сегмент в области битовой адресации
  2. CSEG - абсолютный сегмент в области памяти программ
  3. DSEG - абсолютный сегмент в области внутренней памяти данных
  4. ISEG - абсолютный сегмент в области внутренней памяти данных с косвенной адресацией
  5. XSEG - абсолютный сегмент в области внешней памяти данных

Директива BSEG позволяет определить абсолютный сегмент во внутренней памяти данных с битовой адресацией по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Использование битовых переменных позволяет значительно экономить внутреннюю память программ микроконтроллера. Пример использования директивы BSEG для объявления битовых переменных приведён на рисунке 2.

  BSEG AT 8     ;Сегмент начинается с восьмого бита
RejInd   DBIT 1 ;Флаг режима индикации
RejPriem DBIT 1 ;Флаг режима приема
Flag     DBIT 1 ;Флаг общего назначения  

Рисунок 2. Пример использования директивы BSEG для объявления битовых переменных.

Директива CSEG позволяет определить абсолютный сегмент в памяти программ по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы CSEG для размещения подпрограммы обслуживания прерывания от таймера 0 приведён на рисунке 3.

;Перезагрузка таймера-----------------------------------------
CSEG AT 0bh ;Вектор прерывания от таймера 0
IntT0:
mov TL0, #LOW(-(F_ZQ/12)*10-2) ;Настроить таймер
mov TH0, #HIGH(-(F_ZQ/12)*10-2) ;на период 10мс
reti

Рисунок 3. Пример использования директивы CSEG для размещения подпрограммы обслуживания прерывания.

Директива DSEG позволяет определить абсолютный сегмент во внутренней памяти данных по определённому адресу. Предполагается, что к этому сегменту будут обращаться команды с прямой адресацией. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы DSEG для объявления байтовых переменных приведён на рисунке 4.

DSEG AT 20   ;Разместить сегмент в битовом пространстве микроконтроллера
             ;(для возможности одновременно битовой и байтовой адресации)
RejInd DS 1  ;Переменная, отображающая состояние программ обслуживания аппаратуры
Rejim DS 1   ;Переменная, отображающая режимы работы
Massiv DS 10 ;Десятибайтовый массив

Рисунок 4. Пример использования директивы DSEG для объявления байтовых переменных.

В приведённом примере предполагается, что он связан с примером, приведённом на рисунке 2. То есть команды, изменяющие битовые переменные RejInd, RejPriem или Flag одновременно будут изменять содержимое переменной Rejim, и наоборот команды работающие с переменной Rejim одновременно изменяют содержимое флагов RejInd, RejPriem или Flag. Такое объявление переменных позволяет написать наиболее эффективную программу управления контроллером и подключенными к нему устройствами.

Директива ISEG позволяет определить абсолютный сегмент во внутренней памяти данных по определённому адресу. Напомню, что внутренняя память с косвенной адресацией в два раза больше памяти с прямой адресацией. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. Пример использования директивы ISEG для объявления байтовых переменных приведён на рисунке 5.

  ISEG AT 80 ;Разместить сегмент в диапазоне адресов, совмещенных с SFR

Bufer DS 10 ;Десятибайтовый массив
Stack DS 245 ;Стек

Рисунок 5. Пример использования директивы ISEG для объявления байтовых переменных.

Директива XSEG позволяет определить абсолютный сегмент во внешней памяти данных по определённому адресу. Эта директива не назначает имени сегменту, то есть объединение сегментов из различных программных модулей невозможно. Для определения конкретного начального адреса сегмента применяется атрибут AT. Если атрибут AT не используется, то начальный адрес сегмента предполагается равным нулю. До недавнего времени использование внешней памяти не имело смысла, так как это значительно увеличивало габариты и цену устройства. Однако в последнее время ряд фирм стал размещать на кристалле значительные объёмы ОЗУ, доступ к которому осуществляется как к внешней памяти. Так как использование этой директивы не отличается от использования директивы DSEG, то отдельный пример приводиться не будет.

Использование абсолютных сегментов позволяет облегчить работу программиста по распределению памяти микроконтроллера для различных переменных. Однако в большинстве случаев абсолютный адрес переменной нас совершенно не интересует. Исключение составляют только регистры специальных функций. Так зачем же вручную задавать начальный адрес сегментов?

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

Перемещаемые сегменты памяти

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

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

  • data - размещает сегмент во внутренней памяти данных с прямой адресацией;
  • idata - размещает сегмент во внутренней памяти данных с косвенной адресацией;
  • bit - размещает сегмент во внутренней памяти данных с битовой адресацией;
  • xdata - размещает сегмент во внешней памяти данных;
  • code - размещает сегмент в памяти программ;

Директива rseg После определения имени сегмента можно использовать этот сегмент при помощи директивы rseg. Использование сегмента зависит от области памяти, для которой он предназначен. Если это память данных, то в сегменте объявляются байтовые или битовые переменные. Если это память программ, то в сегменте размещаются константы или участки кода программы. Пример использования директив segment и rseg для объявления битовых переменных приведен на рисунке 6.

_data segment idata
  public VershSteka, buferKlav

;Определение переменных----------------------------
  rseg  _data
buferKlav:  ds 8
VershSteka:
  End

Рисунок 6. Пример использования директив segment и rseg для объявления байтовых переменных

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

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

Еще один пример использования директив segment и rseg приведен на рисунке 7. В этом примере директива segment используется для объявления сегмента битовых переменных.

_bits segment bit
  public knIzm,strVv

;Определение битовых переменных -------------------------------------
  rseg _bits
knIzm:   dbit 1
strVv:   dbit 1
   end

Рисунок 7. Пример использования директив segment и rseg для объявления битовых переменных

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

Пример использования перемещаемых сегментов в исходном тексте программы приведен на рисунке 8.

_code segment code

;Старт программы----------------------------------------------
  CSEG AT  0   ;Вектор рестарта процессора
reset:
  jmp main

;Начало основной программы микроконтроллера -------------------------
  rseg _code
main:
  MOVX @DPTR,A
  mov  SP,#VershSteka      ;Настроить указатель стека на вершину стека
  call init                ;Настроить микроконтроллер
;--------------------------------------------------------------------

Рисунок 8. Пример использования директив segment и rseg в программном модуле

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

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

Литература:

  1. Микушин А.В. Сединин В.И. Однокристальный микроконтроллер семейства MCS-51 фирмы INTEL 8xC51GB Новосибирск: СибГУТИ, 2001
  2. Микушин А.В. Сединин В.И. Программирование микропроцессорных систем на языке ASM-51 Новосибирск: СибГУТИ, 2003
  3. Микушин А.В. Занимательно о микроконтроллерах. СПб, БХВ-Петербург, 2006.
  4. Микушин А.В., Сажнев А.М., Сединин В.И. Цифровые устройства и микропроцессоры. СПб, БХВ-Петербург, 2010.

Вместе со статьей "Использование сегментов в языке программирования ассемблер" читают:

Языки программирования для микроконтроллеров
http://digteh.ru/Progr/progr.php

Язык программирования ASM-51
http://digteh.ru/MCS51/ASM51/asm51.php

Реализация подпрограмм на языке ASM51
http://digteh.ru/MCS51/ASM51/func.php

Многомодульные программы на языке программирования ассемблер
http://digteh.ru/MCS51/ASM51/manymod.php


Автор Микушин А. В. All rights reserved. 2001 ... 2017

Предыдущие версии сайта:
http://neic.nsk.su/~mavr
http://digital.sibsutis.ru/

Поиск по сайту сервисом Яндекс

Поиск по сайту сервисом ГУГЛ

пЕИРХМЦ@Mail.ru


Яндекс.Метрика
Rambler's Top100