Директивы Ассемблера AVR. Директивы ассемблера. ORG – Установить положение в сегменте

Метки
Метка в языке ассемблера может содержать следующие символы:


Буквы: от A до Z и от a до z
Цифры: от 0 до 9
Спецсимволы: знак вопроса (?)
точка (.) (только первый символ)
знак "коммерческое эт" (@)
подчеркивание (_)
доллар ($)

Первым символом в метке должна быть буква или спецсимвол. Цифра не может быть первым символом метки, а символы $ и? иногда имеют специальные значения и обычно не рекомендуются к использованию. Большие и маленькие буквы по умолчанию не различаются, но различие можно включить, задав ту или иную опцию в командной строке ассемблера. Максимальная длина метки - 31 символ. Примеры меток: COUNT, PAGE25, $E10. Рекомендуется использовать описательные и смысловые метки. Имена регистров, например, AX, DI или AL являются зарезервированными и используются только для указания соответствующих регистров.
Если метка располагается перед командой процессора, сразу после нее всегда ставится символ «:» (двоеточие), который указывает ассемблеру, что надо создать переменную с этим именем, содержащую адрес текущей команды:
some_loop:

loopne some_loop
Когда метка стоит перед директивой ассемблера, она обычно оказывается одним из операндов этой директивы и двоеточие не ставится:

codesg segment
lodsw ; cчитать слово из строки,
cmp ax,7 ; если это 7 - выйти из цикла
codesg ends
Рассмотрим директивы, работающие напрямую с метками и их значениями: LABEL, EQU и =.

Директива LABEL

Метка label тип Директива LABEL определяет метку и задает ее тип. Тип может быть одним из: BYTE (байт), WORD (слово), DWORD (двойное слово), FWORD (6 байт), QWORD (учетверенное слово), TBYTE (10 байт), NEAR (ближняя метка), FAR (дальняя метка). Метка получает значение, равное адресу следующей команды или следующих данных, и тип, указанный явно. В зависимости от типа команда
mov метка,0 запишет в память байт (слово, двойное слово и т.д.), заполненный нулями, а команда
call метка выполнит ближний или дальний вызов подпрограммы.

С помощью директивы LABEL удобно организовывать доступ к одним и тем же данным, как к байтам, так и к словам, определив перед данными две метки с разными типами.

Директива EQU

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

truth equ 1
message1 equ "Try again$"
var2 equ 4
cmp ax,truth ; cmp ax,1
db message1 ; db "Try again$"
mov ax,var2 ; mov ax, 4 Директива EQU чаще всего используется с целью введения параметров, общих для всей программы, аналогично команде #define препроцессора языка С.

Директива =

Директива = эквивалентна EQU, но определяемая ею метка может принимать только целочисленные значения. Кроме того, метка, указанная этой директивой, может быть переопределена.

Каждый ассемблер предлагает целый набор специальных предопределенных меток — это может быть текущая дата (@date или??date), тип процессора (@cpu) или имя того или иного сегмента программы, но единственная предопределенная метка, поддерживаемая всеми рассматриваемыми нами ассемблерами, — $ . Она всегда соответствует текущему адресу. Например, команда

Jmp $

выполняет безусловный переход на саму себя, так что создается вечный цикл из одной команды.

Чтобы получше уяснить все это дело я написал небольшую программку. Все тот же “Hello World”, но на новый лад:) Текст ниже:

Програ ассемблируется TASM и MASM, но EXE файлик, ассемблированный MASM на один байт больше. Замечаем что команду mov dx,offset msg, заменили на команду lea dx,msgb. LEA помещает адрес смещения указанных данных в DX, т.е. делает то же самое что и команда mov с offset. Рекомендую это посмотреть под отладчиком.



Смотрим внимательно листинги ассемблирования и находим разницу.



Интересно, что TASM ассемблировал команду LEA в данном случае как команду MOV (код операции BA), а MASM ассемблировал команду LEA в другой код операции - 8D16, что и увеличило размер программы на 1 байт. Почему он решил так сделать пока не знаю, но интересно было бы выяснить.

При написании программ на языке Ассемблер используются директивы, которые указывают компилятору положение программы в памяти, определяют макросы, инициализируют память и др. Список директив и их описание приведен в табл. 1.8. Запись всех директив начинается с точки. Кратко перечислим выполняемые директивами функции в каждом из сегментов.

Сегмент программы открывается директивой.CSEG. Если программа начинается с этого сегмента, директива может отсутствовать. В сегменте программы с помощью директивы.ORG можно указать начало сегмента.

Директива.DB в сегменте определяет один байт или группу байтов, констант, записываемых во Flash-память. Директива.DW определяет слово или группу слов, записываемых в память в качестве констант. Начало записи констант определяется меткой, стоящей перед соответствующей директивой. Перечисляемые константы разделяются запятыми.

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

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

Директива.INCLUDE с именем файла используется для включения в текст программы другого файла.

Таблица 1.8. Список директив

Директива

Описание

Резервировать байты в ОЗУ

Сегмент программы

Определить байт – константу во Flash-памяти или

Назначить регистру символическое имя

Определяет устройство, для которого компилируется

программа

Сегмент данных

Определяет слово во Flash-памяти или EEPROM

Конец макроса

Установить постоянное выражение

Сегмент EEPROM

Выход из файла

Вложить другой файл

Включить генерацию листинга

Включить разворачивание макросов в листинге

Начало макроса

Выключить генерацию листинга

Установить положение в сегменте

Установить для переменной эквивалентное выражение

Директивы.MACRO и.ENDMACRO обрамляют макроопределение. Макроопределение может иметь до 10 параметров с фиксированными именами @0,…,@9. При вызове макроопределения параметры задают в виде списка в порядке нумерации.

Сегмент данных начинается директивой.DSEG. В сегменте могут быть использованы директивы.ORG и.BYTE. Директива.BYTE определяет количество байтов, к которым будет производиться обращение при выполнении программы. Резервируемая область начинается по адресу, определяемому меткой перед директивой.

Сегмент типа EEPROM начинается директивой.ESEG. В сегменте могут быть использованы директивы.ORG, .DB, .DW. Директива.DB в сегменте определяет один или группу байтов, записываемых в EEPROM. Директива.DW определяет слово или группу слов, записываемых в память EEPROM парами по 2 байта. Начало записи байтов и слов определяется меткой, стоящей перед соответствующей директивой.

Директивы.LIST, .NOLIST, .LISTMAC используют для управления выводом листинга.

Ассемблеры MASM, TASM и WASM отличаются между собой. Однако создание простых программ для них практически не имеет отличий, за исключением самого ассемблирования и компоновки.

Итак, наша первая программа для MASM, TASM и WASM, которая выводит английскую букву «A» в текущей позиции курсора, то есть в левом верхнем углу экрана:

Model tiny .code ORG 100h start: MOV AH,2 MOV DL,41h INT 21h INT 20h END start Этот текст можно набрать в любом простом текстовом редакторе – например в БЛОКНОТЕ (NotePad) от WINDOWS (но не в Word и не в другом «навороченном»). Однако я рекомендую «продвинутый» текстовый редактор с подсветкой синтаксиса, например, PSPad (см. раздел ). Затем сохраняем этот файл с расширением.asm, например, в папке MYPROG. Назовем файл atest. Итак, мы получили: C:\MYPROG\atest.asm.

ПРИМЕЧАНИЕ
Обратите внимание, что в первой команде мы записали 2 вместо 02h. MASM, TASM и WASM, как и Emu8086, допускают такие «вольности». Хотя можно написать 02h – ошибки не будет.

Пояснения к программе :

.model tiny – 1-ая строка. Директива.model определяет модель памяти для конкретного типа файлов. В нашем случае это файл с расширением COM, поэтому выбираем модель tiny, в которой объединены сегменты кода, данных, и стека. Модель tiny предназначена для создания файлов типа СОМ.

.code – 2-ая строка. Эта директива начинает сегмент кода.

ORG 100h – 3-ая строка. Эта команда устанавливает значение программного счетчика в 100h, потому что при загрузке СОМ-файла в память, DOS выделяет под блок данных PSP первые 256 байт (десятичное число 256 равно шестнадцатеричному 100h). Код программы располагается только после этого блока. Все программы, которые компилируются в файлы типа СОМ, должны начинаться с этой директивы.

start: MOV AH, 02h – 4-я строка. Метка start располагается перед первой командой в программе и будет использоваться в директиве END, чтобы указать, с какой команды начинается программа. Инструкция MOV помещает значение второго операнда в первый операнд. То есть значение 02h помещается в регистр АН. Для чего это делается? 02h - это ДОСовская функция, которая выводит символ на экран. Мы пишем программу для DOS, поэтому используем команды этой операционной системы (ОС). А записываем мы эту функцию (а точнее ее номер) именно в регистр АН, потому что прерывание 21h использует именно этот регистр.

MOV DL, 41h – 5-я строка. Код символа «A» заносится в регистр DL. Код символа «A» по стандарту ASCII – это число 41h.

INT 21h – 6-я строка. Это и есть то самое прерывание 21h – команда, которая вызывает системную функцию DOS, заданную в регистре АН (в нашем примере это функция 02h). Команда INT 21h – основное средство взаимодействия программ с ОС.

INT 20h – 7-я строка. Это прерывание, которое сообщает операционной системе о выходе из программы, и о передаче управления консольному приложению. В том случае, если программа уже откомпилирована и запущена из ОС, команда INT 20h вернет нас в ОС (например, в DOS).

END start – 8-я строка. Директива END завершает программу, одновременно указывая, с какой метки должно начинаться ее выполнение.

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

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

Директива equ позволяет назначать имена переменных и констант. Теперь можно назначить переменной адрес в одном месте и пользоваться идентификатором переменной во всей программе. Правда за использование идентификатора именно в качестве переменной отвечает программист, тем не менее, если в процессе написания программы потребуется изменить адрес переменной, это можно сделать в одном месте программы, а не просматривать всю программу, разбираясь является ли в данной конкретной команде число 10 константой, адресом ячейки ли количеством повторов в цикле. Все необходимые изменения сделает сам транслятор. Пример назначения переменных приведён на примере, показанном в листинге 1.


Листинг 1. Назначение переменных при помощи директивы equ

Как видно на приведённом примере, использование идентификаторов значительно повышает понятность программы, так как в названии переменной отображается функция, за которую отвечает данная переменная.

При помощи директивы equ можно назначать не только переменные, но и константы. Как уже говорилось ранее, будет ли использован идентификатор как переменная или как константа зависит от команд и видов адресации , которые использует программист.

Один раз назначенный идентификатор уже не может быть изменён в дальнейшем и при повторной попытке назначения точно такого же имени идентификатора будет выдано сообщение об ошибке.

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

Константы, назначаемые директивой equ, могут быть использованы только в одной команде. Достаточно часто требуется работа с таблицей констант, такой как таблица перекодировки, таблицы элементарных функций или синдромы помехоустойчивых кодов. Такие константы используются не на этапе трансляции, а хранятся в памяти программ микроконтроллера. Для занесения констант в память программ микроконтроллера используются директивы db и dw.

Директива db используется для занесения в память программ однобайтных констант. Пример использования директивы db приведён в листинге 2.


Листинг 2. Назначение констант при помощи директивы db

В этом примере использована подпрограмма-функция , перекодирующая двоично-десятичное число в семисегментный код. В эту функцию двоично-десятичный код передаётся через аккумулятор и через этот же регистр возвращается семисегментный код в вызывающую программу.

В директиве db можно задавать сразу несколько констант, разделённых запятыми. Можно одновременно использовать все , но обычно имеет смысл снабдить каждую константу комментарием, как это сделано в предыдущем примере. Так программа становится более понятной и легче искать допущенную ошибку.

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

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

Директива dw позволяет заносить в память программ двухбайтные числа. В этой директиве, как и в директиве db числа можно заносить через запятую. Пример листинга фрагмента программы приведён на рисунке 4.

Рисунок 4. Применение директивы dw.

На рисунке 4 приведён фрагмент листинга программы для того, чтобы можно было проследить какие байты заносятся в память программ микроконтроллера. В самой правой колонке листинга приведены адреса в которые будут занесены числа, являющиеся операндами директивы dw. В следующей колонке приведены двухбайтовые числа, которые будут заноситься в память программ микроконтроллера. Обратите внимание, что несмотря на то, что первые два операнда состоят только из одной цифры, в память микроконтроллера заносятся четыре шестнадцатеричных цифры (двухбайтовое число).

При трансляции исходного текста программ предполагается, что первая команда расположена по нулевому адресу. Адрес последующих команд зависит от длины и количества предыдущих команд. Пример листинга начального участка программы приведён на рисунке 5.

Рисунок 5. Пример листинга программы.

Иногда требуется расположить команду по определённому адресу. Наиболее часто это требуется при использовании прерываний, когда первая команда программы-обработчика прерываний должна быть расположена точно на векторе прерывания . Это можно сделать используя команду nop для заполнения промежутков между векторами прерывания, но лучше воспользоваться директивой ORG.

Директива org предназначена для записи в счетчик адреса сегмента значения своего операнда. То есть при помощи этой директивы можно разместить команду (или данные) в памяти микроконтроллера по любому адресу. Пример использования директивы ORG для размещения подпрограмм обработки прерываний на векторах прерываний показан на рисунке 6.

Рисунок 6. Пример использования директивы ORG.

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

Директива using При использовании прерываний критичным является время, занимаемое программой, обработчиком прерываний. Это время можно значительно сократить, выделив для обработки прерываний отдельный банк регистров. Выделить отдельный банк регистров можно при помощи директивы USING. Номер банка используемых регистров указывается в директиве в качестве операнда. Пример использования директивы USING для подпрограммы обслуживания прерываний от таймера 0 приведён на рисунке 7.

Рисунок 7. Пример использования директивы USING.

Директива CALL. В системе команд микроконтроллера MCS-51 используется три команды безусловного перехода. Выбор конкретной команды зависит от расположения ее в памяти программ, однако программист обычно этого не знает. В результате во избежание ошибок приходится использовать самую длинную команду LJMP . Это приводит к более длинным программам и к дополнительной нагрузке на редактор связей. Транслятор сам может подобрать наилучший вариант команды безусловного перехода. Для этого вместо команды микроконтроллера следует использовать директиву call.

Остальные директивы предназначены для управления сегментами, и поэтому будут рассмотрены позднее при обсуждении работы с многомодульными программами.

Литература:

Вместе со статьей "Директивы языка программирования ASM-51" читают:


http://сайт/Progr/progr.php