Одним из важнейших элементов этой части книги являются примеры программ в машинных кодах и на языке АССЕМБЛЕРа. В распечатках этих программ нам придется употреблять так
называемые ДИРЕКТИВЫ АССЕМБЛЕРа и сейчас, наверное, самое удобное время для того, чтобы дать представление о том, что это такое.
Мы рассмотрим следующие директивы: ORG, EQU, DEFB, DEFW, DEFM и END, но прежде чем начать их рассмотрение, надо твердо для себя понять:
1. Директивы АССЕМБЛЕРа не являются командами процессора Z8 0 и в этом смысле отношения к машинному коду Z8 0 не имеют.
2. АССЕМБЛЕР - это программа, которая переводит (транслирует) текст, написанный Вами в виде мнемоник в объектный код, являющийся машинным. И эти директивы АССЕМБЛЕРа - это некоторые команды ассемблирующей программе. Они не транслируются и в объектный код не войдут, но упростят Вам написание, и самое главное - чтение программы, записанной в мнемониках.
3. Программ-АССЕМБЛЕРов существует великое множество и каждая из них может иметь свои собственные директивы. Они могут иметь и одинаковые директивы, но предъявлять разные требования к их употреблению. Одним словом, конкретно способы использования директив АССЕМБЛЕРа Вам надо устанавливать по инструкции к ассемблирующей программе, которой Вы пользуетесь (напр. EDITAS, GENS 3, GENS 4, ZEUS и т.п.). И хотя стандартов не существует, тем не менее некоторые основополагающие понятия все же выделить можно, вот на них-то мы и остановимся.
3.1. Комментарии.
Мы начнем с самого простого - с комментариев. Они записываются после символа ";" (точка с запятой) .
Вам, конечно понятно, что все, что является комментариями, АССЕМБЛЕРом в машинный код не компилируется - это ни к чему. Они служат только для того, чтобы Вам было удобнее разбираться с листингом, который составил кто-то другой или Вы сами, но давным-давно.
Например:
10 60001 LD E,A 2 0
Как видите, строка может
; Загрузили в регистр E содер-; жимое аккумулятора. ; Уменьшили его на единицу.
остоять только из комментария.
Метки.
Метки значительно упрощают написание программ в мнемониках АССЕМБЛЕРа. В операциях перехода JP, JR, DJNZ, вызова подпрограмм CALL Вы можете не указывать адрес, в который Вы хотите совершить переход, а вместо него подставить метку. С другой стороны, когда будете писать команды для этого адреса, подставите метку и там, Например:
10 60001 BEGIN LD B,0 4
20 60003 AGAIN INC HL
40 60005 DJNZ, AGAIN
3.2.
250 260 270
60110 60111 60113
LD A,(HL) CP 80H JR NZ,BEGIN
Как видите, очень удобно. Сразу видно, что из строки 40 возврат осуществляется к метке AGAIN, если регистр B не достиг нуля. Из строки 270 возврат осуществляется к метке BEGIN.
Определенно имеет смысл выбирать для метки такое имя, которое соответствовало бы смыслу исполняемой операции - это облегчает чтение и понимание листинга программы.
При компиляции ассемблирующая программа сама подсчитает величины необходимых смещений в командах процессора и подставит их вместо меток. Так, например, в строке 40 вместо DJNZ AGAIN в объектный код пойдет DJNZ FCH, что то же самое.
В предыдущем примере мы использовали метки очень ограниченно. Дело в том, что и обращение по метке и сама метка находились в одной и той же процедуре. А как быть, если Вы хотите обратиться к метке, которая находится в другой процедуре, которую Вы написали и откомпилировали еще вчера, а как быть, если Вам надо сделать переход к процедуре ПЗУ и Вы при этом хотите воспользоваться меткой? В этом случае Вам поможет директива EQU. Она присваивает метке числовое значение. Конечно, при компиляции эта директива никак в машинный код не преобразовывается, но если по тексту программы есть ссылки на эту метку, то вместо нее будет подставлено значение, взятое из директивы EQU.
Например, Вам в Вашей программе неоднократно приходится вызывать процедуры ПЗУ, скажем CLEAR (1EACH=7 8 52) и OUT-LINE (1856H=6230) . Тогда в начале Вашей программы Вы задаете
например назвав их CLEAR
директивой |
значения своим меткам, н |
||
и OUT L. | |||
CLEAR |
EQU 7 8 52 |
||
OUT L |
EQU 62 3 0 |
||
LABEL |
EQU 60016 |
||
выз |
ываете эти |
процедуры или |
|
по метке. | |||
60001 |
LD HL, (LABEL) |
||
60004 |
LD BC, 0008 |
||
60007 |
LD DE, (04 52) |
||
60010 |
CALL CLEAR |
||
60013 |
CALL OUT L |
||
60016 | |||
Сразу |
должны Вас |
предупредить, |
|
примеры |
с точки зрения |
программной |
примеры того, как используются те или иные директивы АССЕМБЛЕРа и если Вам нужен в примерах реальный смысл, то Вы его получите чуть позже, в последующих главах, где мы будем разбирать практические приемы программирования.
Давайте еще раз взглянем на предыдущий пример. В строке 30 мы засылаем в регистровую пару HL то, что содержится в адресе, на который указывает метка LABEL, а она, согласно директиве EQU указывает на адрес 60016.
Итак, в ячейках 60016 и 60017 содержатся некоторые данные, которые впоследствии могут использоваться программой. Эти данные Вы можете заслать в ячейки сами перед компиляцией. И совсем не надо для этого привлекать машинный код. Первоначальные значения в ячейках памяти Вы можете выставить с помощью директив DEFB, DEFW и DEFM.
DEFB - DEFINE BYTE - задать байт.
DEFW - DEFINE WORD - задать "слово" ("слово" - это два последовательно расположенных байта. Обычно это адрес.) DEFM - DEFINE MESSAGE - задать сообщение (это несколько подряд идущих байтов) . Обычно ассемблирующие программы накладывают ограничение на то, сколько байтов можно задать одной директивой DEFM, скажем не более пяти. Но Вас это не должно волновать. Если Вы хотите задать длинное сообщение, то можете ставить подряд столько строк DEFM, сколько хотите.
Итак, DEFB задает один одиночный байт (0...255) , DEFW -два подряд идущих байта (0...65535), а DEFM - группу подряд идущих байтов - текстовое сообщение, числовая таблица и т. п.
В нашем предыдущем примере, если мы хотим хранить в адресе 60016 и 60017 некоторое двухбайтное число, строку 80 следовало бы записать, например так:
80 60016 DEFW 5C92H
90 60018
Предположим, Вы хотите начиная с адреса 60135 хранить слово "Spectrum".
Код буквы "S" Код буквы "p" "e" "c" "t" " r "
"u" "m"
60135
60136
60137
60138
60139
60140
60141
60142
53H 7 0H 65H 63H 7 4H 72H 75H 6DH
DEFB DEFB DEFB DEFB DEFB DEFB DEFB DEFB
можете его задать парами байтов:
Но проще и правильнее задать его как сообщение:
60135 DEFM 5370656374 ; "Spect"
60140 DEFM 72756D ; "rum"
Есть особый случай при программировании на АССЕМБЛЕРе, когда текст программы тоже приходится вводить через DEFB или DEFM. Это случай, когда Вы пишете программу для встроенного калькулятора. Ведь ассемблирующая программа может перевести в машинный код мнемоники АССЕМБЛЕРа, но она ничего не знает о кодах калькулятора и не знает его мнемоник. Код калькулятора -это внутреннее "Синклеровское" дело, его интерпретацией
занимаются программы, размещенные в ПЗУ и к процессору и к его командам код калькулятора не имеет никакого отношения. Посему ввести команды калькулятору в ассемблирующую программу Вам удастся только как последовательность независимых байтов, т. е. через DEFB или DEFM.
Мы с Вами в первой части книги употребляли мнемонические обозначения команд калькулятора, типа add, stk_data s_lt и т. п., и писали их с маленькой буквы в отличие от команд процессора. Но делали это ранее и будем делать впредь только ради понимания и удобства записи. Программа-АССЕМБЛЕР таких мнемоник не знает, их нет в ее словаре.
Итак, с помощью DEFB, DEFW и DEFM задают начальные значения программным переменным, вводят в программу таблицы, сообщения и любые прочие последовательности данных, даже
графику, а также такие кодовые последовательности, которые ассемблирующая программа не понимает, как команды АССЕМБЛЕРа.
3.5. Директивы ORG, END.
Нам осталось рассмотреть две самые тривиальные директивы. Директива ORG объявляет адрес, начиная с которого будет ассемблироваться программа. Она должна быть первой директивой в исходном тексте, хотя в принципе, перед ней могут быть комментарии.
Вы обратили внимание на то, что в вышеприведенных примерах мы слева писали столбец адресов, в которых будут размещаться те или иные команды. Так вот, этого при программировании на АСЕМБЛЕРе делать не надо. Достаточно в самом начале дать директиву
10 ORG 63000
и далее ассемблирующая программа сама рассчитает в какой ячейке памяти будет находиться та или иная команда. Это очень упрощает процесс программирования. А если Вы внесете изменения в готовый текст, АССЕМБЛЕР сам подправит все адреса.
Директива END отмечает конец программы. Если после него что-то еще и будет стоять, то АССЕМБЛЕР при компиляции это проигнорирует.
Вот пожалуй и все, что для начала стоит знать о директивах АССЕМБЛЕРа. Это не все директивы, какие могут встретиться в жизни, да и правила их использования для разных АССЕМБЛЕРов -разные, но по большому счету этот минимум удовлетворит 90 процентов Ваших потребностей в информации, а остальное Вы должны почерпнуть из инструкции к тому АССЕМБЛЕРу, с которым работаете.
При написании программ на языке Ассемблер используются директивы, которые указывают компилятору положение программы в памяти, определяют макросы, инициализируют память и др. Список директив и их описание приведен в табл. 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 используют для управления выводом листинга.
Язык программирования ассемблер всегда включает в себя машинные коды микроконтроллера, но этим не ограничивается набор команд этого языка. Дело в том, что нужно уметь управлять самим процессом трансляции программы. Полный список директив приведён в описании языка, а здесь будут подробно рассмотрены некоторые из них.
Первое, что неудобно при использовании только машинных команд — это необходимость помнить, какие данные в какой ячейке памяти находятся. При чтении программы трудно отличить константы от переменных, ведь они отличаются в командах только видом адресации . Преодолеть эту трудность можно при помощи идентификаторов. Можно назначить какой либо ячейке памяти идентификатор, и тем самым работать с этим идентификатором как с переменной.
Директива equ позволяет назначать имена переменных и констант. Теперь можно назначить переменной адрес в одном месте и пользоваться идентификатором переменной во всей программе. Правда за использование идентификатора именно в качестве переменной отвечает программист, тем не менее, если в процессе написания программы потребуется изменить адрес переменной, это можно сделать в одном месте программы, а не просматривать всю программу, разбираясь является ли в данной конкретной команде число 10 константой, адресом ячейки ли количеством повторов в цикле. Все необходимые изменения сделает сам транслятор. Пример назначения переменных приведён на примере, показанном в листинге 1.
Как видно на приведённом примере, использование идентификаторов значительно повышает понятность программы, так как в названии переменной отображается функция, за которую отвечает данная переменная.
При помощи директивы equ можно назначать не только переменные, но и константы. Как уже говорилось ранее, будет ли использован идентификатор как переменная или как константа зависит от команд и видов адресации , которые использует программист.
Один раз назначенный идентификатор уже не может быть изменён в дальнейшем и при повторной попытке назначения точно такого же имени идентификатора будет выдано сообщение об ошибке.
Директива set . Если требуется в различных местах программы назначать одному и тому же идентификатору различные числа, то нужно пользоваться директивой set. Использование этой директивы полностью идентично использованию директивы equ, поэтому иллюстрироваться примером не будет.
Константы, назначаемые директивой equ, могут быть использованы только в одной команде. Достаточно часто требуется работа с таблицей констант, такой как таблица перекодировки, таблицы элементарных функций или синдромы помехоустойчивых кодов. Такие константы используются не на этапе трансляции, а хранятся в памяти программ микроконтроллера. Для занесения констант в память программ микроконтроллера используются директивы db и dw.
Директива db используется для занесения в память программ однобайтных констант. Пример использования директивы db приведён в листинге 2.
В этом примере использована подпрограмма-функция , перекодирующая двоично-десятичное число в семисегментный код. В эту функцию двоично-десятичный код передаётся через аккумулятор и через этот же регистр возвращается семисегментный код в вызывающую программу.
В директиве 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
Директивы ассемблера
Компилятор поддерживает ряд директив. Директивы не транслируются непосредственно в код. Вместо этого они используются для указания положения в программной памяти, определения макросов, инициализации памяти и т.д. Все директивы предваряются точкой.
Список директив приведён в следующей таблице.
Директива | Описание |
BYTE | Зарезервировать байты в ОЗУ |
CSEG | Программный сегмент |
DB | Определить байты во флэш или EEPROM |
DEF | Назначить регистру символическое имя |
DEVICE | Определить устройство для которого компилируется программа |
DSEG | Сегмент данных |
DW | Определить слова во флэш или EEPROM |
ENDM, ENDMACRO | Конец макроса |
EQU | Установить постоянное выражение |
ESEG | Сегмент EEPROM |
EXIT | Выйти из файла |
INCLUDE | Вложить другой файл |
LIST | Включить генерацию листинга |
LISTMAC | Включить разворачивание макросов в листинге |
MACRO | Начало макроса |
NOLIST | Выключить генерацию листинга |
ORG | Установить положение в сегменте |
SET | Установить переменный символический эквивалент выражения |
BYTE - зарезервировать байты в ОЗУ. Директива BYTE резервирует байты в ОЗУ. В случае если вы хотите иметь возможность ссылаться на выделенную область памяти, то директива BYTE должна быть предварена меткой. Директива принимает один обязательный параметр, который указывает количество выделяемых байт. Эта директива может использоваться только в сегменте данных(смотреть директивы CSEG и DSEG). Выделенные байты не инициализируются.
Синтаксис:
МЕТКА: .BYTE выражение
DSEG var1: .BYTE 1 ; резервирует 1 байт для var1
table: .BYTE tab_size ; резервирует tab_size байт
Ldi r30,low(var1) ; Загружает младший байт регистра Z
Ldi r31,high(var1) ; Загружает старший байт регистра Z
Ld r1,Z ; Загружает var1 в регистр 1
DB - определить байты во флэш или EEPROM.
Директива DB резервирует крайне важно е количество байт в памяти программ или в EEPROM. В случае если вы хотите иметь возможность ссылаться на выделенную область памяти, то директива DB должна быть предварена меткой. Директива DB должна иметь хотя бы один параметр.
Размещено на реф.рф
Параметры, передаваемые директиве - это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-128..255), или в результате вычисления должно давать результат в данном же диапазоне, в противном случае число усекается до байта͵ причём БЕЗ выдачи предупреждений.
В случае если директива получает более одного параметра и текущим является программный сегмент, то параметры упаковываются в слова (первый параметр - младший байт). В случае если число параметров нечётно, то последнее выражение будет усечено до байта и записано как слово со старшим байтом равным нулю, даже если далее идет ещё одна директива DB.
Синтаксис:
МЕТКА: .DB список_выражений
CSEG consts: .DB 0, 255, 0b01010101, -128, 0xaa
const2: .DB 1,2,3
DW - определить слова во флэш или EEPROM.
Директива DW резервирует крайне важно е количество слов в памяти программ или в EEPROM. В случае если вы хотите иметь возможность ссылаться на выделенную область памяти, то директива DW должна быть предварена меткой. Директива DW должна иметь хотя бы один параметр.
Размещено на реф.рф
Данная директива должна быть размещена только в сегменте программ (CSEG) или в сегменте EEPROM (ESEG).
Параметры, передаваемые директиве, - это последовательность выражений разделённых запятыми. Каждое выражение должно быть или числом в диапазоне (-32768..65535), или в результате вычисления должно давать результат в данном же диапазоне, в противном случае число усекается до слова, причем БЕЗ выдачи предупреждений.
Синтаксис:
МЕТКА: .DW expressionlist
varlist:═ .DW 0, 0xffff, 0b1001110001010101, -32768, 65535
eevarlst: .DW 0,0xffff,10
· Сегменты
DSEG - сегмент данных. Директива DSEG определяет начало сегмента данных. Исходный файл может состоять из нескольких сегментов данных, которые объединяются в один сегмент при компиляции. Сегмент данных обычно состоит только из директив BYTE и меток. Сегменты данных имеют свои собственные побайтные счётчики положения. Директива ORG должна быть использована для размещения переменных в крайне важно м месте ОЗУ. Директива не имеет параметров.
Синтаксис:
ldi r30,low(var1) ; Загрузить младший байт регистра Z
ldi r31,high(var1) ; Загрузить старший байт регистра Z
ld r1,Z ; Загрузить var1 в регистр r1
CSEG - программный сегмент. Директива CSEG определяет начало программного сегмента. Исходный файл может состоять из нескольких программных сегментов, которые объединяются в один программный сегмент при компиляции. Программный сегмент является сегментом по умолчанию. Программные сегменты имеют свои собственные счётчики положения, которые считают не побайтно, а по словно. Директива ORG должна быть использована для размещения кода и констант в крайне важно м месте сегмента. Директива CSEG не имеет параметров.
Синтаксис:
DSEG ; Начало сегмента данных
vartab: .BYTE 4 ; Резервирует 4 байта в ОЗУ
CSEG ; Начало кодового сегмента
const: .DW 2 ; Разместить константу 0x0002 в памяти программ
mov r1,r0 ; Выполнить действия
ESEG - сегмент EEPROM. Директива ESEG определяет начало сегмента EEPROM. Исходный файл может состоять из нескольких сегментов EEPROM, которые объединяются в один сегмент при компиляции. Сегмент EEPROM обычно состоит только из директив DB, DW и меток. Сегменты EEPROM имеют свои собственные побайтные счётчики положения. Директива ORG должна быть использована для размещения переменных в крайне важно м месте EEPROM. Директива не имеет параметров.
Синтаксис:
DSEG ; Начало сегмента данных
var1: .BYTE 1 ; зарезервировать 1 байт для var1
table: .BYTE tab_size ; зарезервировать tab_size байт.
eevar1: .DW 0xffff ; проинициализировать 1 слово в EEPROM
ORG - Установить положение в сегменте.
Директива ORG устанавливает счётчик положения равным заданной величине, которая передаётся как параметр.
Размещено на реф.рф
Важно заметить, что для сегмента данных она устанавливает счётчик положения в SRAM (ОЗУ), для сегмента программ это программный счётчик, а для сегмента EEPROM это положение в EEPROM. В случае если директиве предшествует метка (в той же строке) то метка размещается по адресу указанному в параметре директивы. Перед началом компиляции программный счётчик и счётчик EEPROM равны нулю, а счётчик ОЗУ равен 32 (поскольку адреса 0-31 заняты регистрами). Обратите внимание что для ОЗУ и EEPROM используются побайтные счётчики а для программного сегмента - пословный.
Синтаксис:
ORG выражение
DSEG ; Начало сегмента данных
ORG 0x37 ; Установить адрес SRAM равным 0x37
variable: .BYTE 1 ; Зарезервировать байт по адресу 0x37H
CSEG .ORG 0x10 ; Установить программный счётчик равным 0x10
mov r0,r1 ; Данная команда будет размещена по адресу 0x10
Синтаксис:
· Макросы
MACRO - начало макроса. С директивы MACRO начинается определение макроса. В качестве параметра директиве передаётся имя макроса. При встрече имени макроса позднее в тексте программы компилятор заменяет это имя на тело макроса. Макрос может иметь до 10 параметров, к которым в его теле обращаются через @0-@9. При вызове параметры перечисляются через запятые. Определение макроса заканчивается директивой ENDMACRO.
По умолчанию в листинг включается только вызов макроса, для разворачивания макроса крайне важно использовать директиву LISTMAC. Макрос в листинге показывается знаком +.
Синтаксис:
MACRO макроимя
MACRO SUBI16 ; Начало макроопределения
subi @1,low(@0) ; Вычесть младший байт параметра 0 из параметра 1
sbci @2,high(@0) ; Вычесть старший байт параметра 0 из параметра 2
CSEG ; Начало программного сегмента
SUBI16 0x1234,r16,r17 ; Вычесть 0x1234 из пары r17:r16
ENDMACRO - конец макроса. Директива определяет конец макроопределения, и не принимает никаких параметров. Для информации по определению макросов смотрите директиву MACRO.
Синтаксис:
MACRO SUBI16 ; Начало определения макроса
subi r16,low(@0) ; Вычесть младший байт первого параметра
sbci r17,high(@0) ; Вычесть старший байт первого параметра
LISTMAC - включить разворачивание макросов в листинге. После директивы LISTMAC компилятор будет показывать в листинге содержимое макроса. По умолчанию в листинге показывается только вызов макроса и передаваемые параметры.
Синтаксис:
MACRO MACX ; Определение макроса
add r0,@0 ; Тело макроса
ENDMACRO ; Конец макроопределения
LISTMAC ; Включить разворачивание макросов
MACX r2,r1 ; Вызов макроса (в листинге будет показано тело макроса)
· Выражения
EQU - установить постоянное выражение. Директива EQU присваивает метке значение. Эта метка может позднее использоваться в выражениях. Метка которой присвоено значение данной директивой не должна быть переназначена и её значение не должна быть изменено.
Синтаксис:
EQU метка = выражение
EQU io_offset = 0x23
EQU porta = io_offset + 2
CSEG ; Начало сегмента данных
clr r2 ; Очистить регистр r2
SET - Установить переменный символический эквивалент выражения. Директива SET присваивает имени неĸᴏᴛᴏᴩᴏᴇ значение. Это имя позднее должна быть использовано в выражениях. Причем в отличие от директивы EQU значение имени должна быть изменено другой директивой SET.
Синтаксис:
SET имя = выражение
SET io_offset = 0x23
SET porta = io_offset + 2
CSE ; Начало кодового сегмента
clr r2 ; Очистить регистр 2
out porta,r2 ; Записать в порт A
DEF - назначить регистру символическое имя. Директива DEF позволяет ссылаться на регистр через неĸᴏᴛᴏᴩᴏᴇ символическое имя. Назначенное имя может использоваться во всей нижеследующей части программы для обращений к данному регистру. Регистр может иметь несколько различных имен. Символическое имя должна быть переназначено позднее в программе.
Синтаксис:
DEF Символическое_имя = Регистр
ldi temp,0xf0 ; Загрузить 0xf0 в регистр temp (R16)
eor temp,ior ; Регистры temp и ior складываются по исключающему или
DEVICE - определить устройство.
Директива DEVICE позволяет указать, для какого устройства компилируется программа. При использовании данной директивы компилятор выдаст предупреждение, в случае если будет найдена инструкция, которую не поддерживает данный микроконтроллер.
Размещено на реф.рф
Также будет выдано предупреждение, в случае если программный сегмент, либо сегмент EEPROM превысят размер, допускаемый устройством. В случае если же директива не используется, то все инструкции считаются допустимыми, и отсутствуют ограничения на размер сегментов.
Синтаксис:
DEVICE AT90S1200 | AT90S2313 | AT90S2323 | AT90S2333 | AT90S2343 | AT90S4414 | AT90S4433 | AT90S4434 | AT90S8515 | AT90S8534 | AT90S8535 | ATtiny11 | ATtiny12 | ATtiny22 | ATmega603 | ATmega103
DEVICE AT90S1200 ; Используется AT90S1200
push r30 ; инструкция вызовет предупреждение, AT90S1200 её не имеет
EXIT - выйти из файла. Встретив директиву EXIT, компилятор прекращает компиляцию данного файла. В случае если директива использована во вложенном файле (см. директиву INCLUDE), то компиляция продолжается со строки следующей после директивы INCLUDE. В случае если же файл не является вложенным, то компиляция прекращается.
Синтаксис:
EXIT ; Выйти из данного файла
INCLUDE - вложить другой файл. Встретив директиву INCLUDE компилятор открывает указанный в ней файл, компилирует его пока файл не закончится или не встретится директива EXIT, после этого продолжает компиляцию начального файла со строки следующей за директивой INCLUDE. Вложенный файл может также содержать директивы INCLUDE.
Синтаксис:
INCLUDE "имя_файла"
; файл iodefs.asm:
EQU sreg = 0x3f ; Регистр статуса
EQU sphigh = 0x3e ; Старший байт указателя стека
EQU splow = 0x3d ; Младший байт указателя стека
; файл incdemo.asm
INCLUDE iodefs.asm ; Вложить определения портов
· Листинги
LIST - включить генерацию листинга. Директива LIST указывает компилятору на крайне важно сть создания листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако данная директива используется совместно с директивой NOLIST для получения листингов отдельных частей исходных файлов.
Синтаксис: .LIST
NOLIST - выключить генерацию листинга. Директива NOLIST указывает компилятору на крайне важно сть прекращения генерации листинга. Листинг представляет из себя комбинацию ассемблерного кода, адресов и кодов операций. По умолчанию генерация листинга включена, однако должна быть отключена данной директивой. Кроме того данная директива должна быть использована совместно с директивой LIST для получения листингов отдельных частей исходных файлов
Синтаксис: .NOLIST
Пример: .NOLIST ; Отключить генерацию листинга
INCLUDE "macro.inc" ; Вложенные файлы не будут
INCLUDE "const.def" ; отображены в листинге
LIST ; Включить генерацию листинга
Директивы ассемблера - понятие и виды. Классификация и особенности категории "Директивы ассемблера" 2017, 2018.
Метки
Метка в языке ассемблера может содержать следующие символы:
Директива 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 байт. Почему он решил так сделать пока не знаю, но интересно было бы выяснить.