Ассемблер STM8

В процессе освоения STM-ок, мне очень не хватало описания ассемблера от них. Речь идёт не о мнемониках инструкций вычислительного ядра микроконтроллеров, а о принципах построения ассемблерных программ в STVD. После ассемблера ядра AVR, в STM-ном ассемблере для меня всё выглядело до безобразия нелогично. Какое-то время даже хотелось написать свой ассемблер :) Теперь я постараюсь облегчить забег по граблям тем, кто будет осваивать ассемблер для STM8.

Начало и конец ассемблерного файла
Первым делом, этот ассемблер требует, чтобы было указано целевое семейство микроконтроллеров. Это может быть st7 или stm8. При этом ассемблер выберет необходимый .TAB-файл (лежит в каталоге ..\st_toolset\asm). Указание целевого семейства обязательно находится в самой первой строчке файла и начинается с самого первого символа. Конец файла обязательно осигналивается ключевым словом END. Притом этот end в строке должен быть не первым. В начале строки необходимо поставить пробел или символ табуляции. И строчка со словом END не должна быть последней. Следом должна быть ещё хотя бы одна строка, а можно и не одну написать. И там можно писать любые глупости: компилятор на них не обратит внимания!

Текст программы
Строка программы, кроме служебных, имеет вид:
[label[:]]<space>[opcode]<space>[operand]<space>[;comment]

На месте <space> могут быть пробелы или символы табуляции. В строке не обязательно присутствуют все эти элементы.
Метки
Метка, если она есть, пишется начиная с самого первого символа строки! В конце метки можно как ставить двоеточие, так и не ставить. Ассемблер переживёт оба варианта. Метка может содержать:
  • Латинские буквы верхнего и нижнего регистра (A-Z и a-z)
  • Цифры (0-9). Первый символ в наименовании метки не может быть цифрой
  • Знак прочерка (_)
Кроме того, к метке могут быть добавлены атрибуты.
Размерность метки. Дело в том, что вычислительное ядро STM8 поддерживает несколько режимов адресации, различающихся разрядностью выставляемого на адресную шину адреса. И связанные с этим инструкции, как правило, тоже имеют несколько вариантов с различного размера адресами.
  • .B — адрес имеет размерность 8 бит. Может указывать на адреса $0000-$00FF, т.е. все старшие разряды заполняются нулями. Позволяет сократить работу с переменными, размещёнными в сегменте RAM0. Больше, по-моему, практической пользы не имеет. О сегментах речь идёт ниже
  • .W — адрес имеет размерность 16 бит. В памяти, например, STM8L152 это позволяет обратиться куда угодно. Если аттрибут размера не указан, по умолчанию считается, что это .W
  • .L — адрес имеет размерность 32 бита. Насколько я понимаю, это необходимо для самых больших и толстых микроконтроллеров, у которых 64k адресного пространства на всё их богатство недостаточно.

Инструкции
Строка не может начинаться с инструкции. Если строка не содержит метки, то начинаться должна с пробела или символа табуляции. Поставить на данное место строки можно:
  • Мнемонику ассемблерной инструкции
  • Директиву ассемблера
  • Название макроса, который надо подставить
Мнемоники инструкций мы обсуждать не будем, а о директивах ассемблера и макросах поговорим ниже.
Операнды
  • Числа, в т.ч. адреса
  • Строки
  • Текущее значение программного счётчика. Для этого используется символ «звёздочка». JPF *, например. Если формат представления чисел установить любой другой, кроме MOTOROLA, то будет не звёздочка, а знак доллара — $
  • Арифметические выражения
Числа задаются в десятичном или шестнадцатеричном виде, можно ещё в двоичном. По умолчанию, STM-ный ассемблер использует формат MOTOROLA для представления чисел. Можно поменять на формат INTEL, TEXAS или ZILOG. Для этого существуют директивы INTEL, MOTOROLA, TEXAL, ZILOG. Работают директивы до тех пор, пока не будет объявлена другая.
В записях ассемблера STM, если число — это адрес, то записывается адрес без каких-либо префиксов и суффиксов. А если это константа, то перед константой ставится символ решётся. Вот фрагмент кода из предыдущей статьи, копирующий часть программы из флеша в RAM

;... всякие инициализации и переключения мы опускаем
	;Переносим исполняемый код в SRAM
	LDW	X,#Main            ;По метке Main начинается тот самый цикл
;обратите внимание на символ решётки перед указанием метки!
	LDW	Y,#$0100           ;По адресу 0100h (это в RAM) будет начинаться программа
CopyToRAM:
	LD		A,(X)  
	LD		(Y),A    
	INCW	X
	INCW	Y
	CPW		X,#AfterMain
	JRULT	CopyToRAM	
	
	JPF	$0100        ;Передаём управление в копию кода в RAM

;...

Main:
	MOV PC_ODR,#0
	MOV PC_ODR,#128
	MOV PC_ODR,#0
	MOV PC_ODR,#128
	MOV PC_ODR,#0
	MOV PC_ODR,#128
	MOV PC_ODR,#0
	MOV PC_ODR,#128
	jra Main            ;Тут специально команда относительного перехода, чтобы не возиться с адресами
AfterMain:        ;Метка стоит сразу после нужного фрагмента кода


Строки заключаются в двойные кавычки. Одиночные символы — в одинарные. В одинарных кавычках может быть один символ (1 байт), два символа (2 байта) или целых 4 символа (4 байта), но не более. Кроме того, используются хорошо знакомые любителям Сей служебные символы:
  • ‘\0’ — пустой символ. ASCII код 0
  • ‘\b’ — забой, он же BackSpave. ASCII код 8
  • ‘\t’ — горизонтальная табуляций. ASCII код 9
  • ‘\n’ — новая строка. ASCII код 10
  • ‘\f’ — новая страница. ASCII код 12
  • ‘\r’ — возврат каретки. ASCII код 13
  • ‘\”’ — двойная кавычка. ASCII код 34
  • ‘\’ — одиночная кавычка. ASII код 39
  • ‘\\’ — дробная черта. ASCII код 92


Арифметические выражения могут содержать числа, метки, скобки и операторы. Скобки понимает круглые "()" и фигурные "{}". Фигурные скобки обрамляют всё арифметическое выражение, содержащее внутренние скобки. Допускается до 8 уровней вложения. Операторы выполняются в 4 очереди.
Первая очередь вычислений
Вторая и третья очередь вычислений
Четвёртая очередь вычислений

И требуется всегда заключать арифметические выражения в скобки.

#define SIZE 128
DS.W SIZE+1        ;Так выдаст ошибку
DS.W {SIZE+1}      ;А так не выдаст 


Комментарии начинаются с точки с запятой. Никаких других вариантов нет. Многострочных комментариев не признаёт.

Сегментация памяти
Ассемблер STM8 делит память на сегменты. Всего в пределах проекта может быть до 128 сегментов. В отдельных сегментах располагаются коды программ, данные в оперативной памяти, различные таблицы в памяти FLASH или EEPROM. Кроме того, никто не запрещает нам разбить уже объявленный сегмент на несколько ещё.

Объявление сегмента имеет вид:
[<name>] SEGMENT [<align>] [<combine>] '<class>' [cod]

<name> — необязательный параметр. Длина до 12 символов. При его использовании, компоновщик (linker) будет группировать одноимённые сегменты в пределах класса ('<class>').
<align> — необязательный параметр, указывающий компоновщику, как выравнивать адреса. Варианты:
byte — без выравнивания
word — выравнивание по два байта ($1001 -> $1002)
long — выравнивание по четыре байта ($1001 -> $1004)
para — выравнивание по 16-байтовым параграфам ($1001 -> $1010)
64 — выравнивани по 64 байта ($1001 -> $1040)
128 — выравнивание по 128 байт ($1001 -> $1080)
page — выравнивание по 256-байтовым страницам ($1001 -> $1100)
1k — выравнивание по килобайтам ($1001 -> $1400)
4k — выравнивание по 4 килобайта ($1001 -> $2000)
<combine> — необязательный параметр, описывающий расположение сегмента в адресном пространстве. Варианты:
at — используется один раз при первом объявлении сегмента. Указывает адрес начала (и, опционально) конца сегмента. Признаёт только шестнадцатеричную запись. Притом никаких префиксов не требует

        segment at 0-FF 'ram0'
        segment at 100-7FF 'ram1'


common — указывает, что этот сегмент имеет общие адреса с другим. Вот пример из авторского описания этого ассемблера:

dat1 segment byte at: 10 'DATA'
ds.w
com1 segment common 'DATA'
.lab1 ds.w 4
com1 segment common 'DATA'
.lab2 ds.w 2
com2 segment common 'DATA'
.lab3 ds.w
com2 segment common 'DATA'
.lab4 ds.w 2
dat2 segment 'DATA'
.lab5 ds.w 2

;The values for labels lab1, lab2, lab3, lab4, and lab5
;are 12, 12, 1A, 1A and 1E, respectively.

Одновременно в одной строке at и common писать нельзя, поэтому сначала пишут объявление с ключевым словом AT, а затем — все со словом COMMON.
com1 segment byte at: 10 'DATA'
com1 segment common 'DATA'
;....
com1 segment common 'DATA'

<cod> — необязательный параметр, указывающий компоновщику, чтобы записывал код для данного класса в отдельный файл. Имеет значение от 0 до 9.

Кроме того, при хранении кода в одном сегменте, а выполнении из другого, можно указать ассемблеру, что исполняться код будет из другого места, нежели хранится. Иначе первый же абсолютный переход по метке, находящейся в таком коде, вернёт программу в ту область памяти, из которой исполняемый код был скопирован. Опять же пример из авторского описания
segment byte at: 0 'code'
segment byte at: 8000 'ram'
segment 'ram>code'
label1:nop

Смысл действа в том, что разместят ассемблер с компоновщиком этот код в одном месте, а метка label1 будет «смотреть» в совсем другое. Которое будет актуально, когда мы скопируем исполняемый код в то место, откуда собираемся его исполнять.

Макросы
Задаются макросы ключевыми словами MACRO и MEND.

<название> MACRO [список параметров, под запятую]
           <тело макроса> 
           MEND

Пример:
add16   MACRO first,second,result
        ld A,first
        adc A,second
        ld result,A
        MEND

Ключевое слово LOCAL указывает, что метка, объявленная с ним, является «местной» для макроса. При каждой очередной подстановке ассемблер будет к названию метки добавлять число от 0000 до FFFF. Пример из авторского описания:
getio MACRO
        LOCAL loop
loop ld A,$C000
     jra loop
    MEND

Существуют директивы компилятора
#IFB — true, если параметр пропущен
#IFDEF — true, если параметр определён
#IFLAB — true, если параметр является меткой
#IFIDN — true, если два строковых параметра (после #IFIDN идут через пробел)
Также к директивам условной компиляции относятся #ELSE и #ENDIF, а ещё #IF, #IF1 и #IF2 (#IF1 работает при первом проходе ассемблера, #IF2 — при втором)

Про условную компиляцию, думаю, расписывать тут не стОит, если будет надо — напишем отдельную статью



Директивы ассемблера, а также мненомики инструкций и их шестнадцатеричное представление будет позже. Хочу их в HELP-файл оформить, а не в статью. Убеждён, это будет удобнее для использования!
  • +5
  • 26 января 2012, 20:17
  • Deer

Комментарии (56)

RSS свернуть / развернуть
Мда… После AVR и MSP430 и правда странновато… Впрочем, STM — «не ассемблерная» архитектура. Они все на высшие уровни напирают, как я глядел.
0
  • avatar
  • _YS_
  • 26 января 2012, 21:12
Ну так и про AVR когда-то твердили, что у них система инструкций оптимизирована для ЯВУ… Только настолько разная организация, что для того, чтобы ЯВУ мог полностью использовать все особенности процессора, он должен быть очень умным…
0
Нет, я не об этом. У AVR система инструкций оптимизирована для ЯВУ. Но при этом синтаксис (даже не система команд сама по себе, а именно синтаксис ассемблера) все же построен так, что и человеку писать вполне удобно.

А вот в ST, как видно, просто забили на удобство разработки на асме для человека, и сфокусировались на понятности трансляторам ЯВУ.
0
А, ну это да! Здесь я долго ругался!
Но это повод написать чуть погодя ещё одну чрезвычайно интересную статью! :)
0
Ну давайте, давайте. :)
0
Будет, будет!

Набираю материала для неё :)
0
А вот в ST, как видно, просто забили на удобство разработки на асме
… ИМХО правильней сказать, что ST просто забили… и не только на ассемблер :) На счет системы команд — история идет от «двух подходов» Intel & Motorola и всякие ЯВУ тут ни при чем.
0
В мане так и написано что заточен под Си/С++
0
Про AVR-то тоже написано. И это ояень хорошо заметно там. Но, как мы с _YS_ обсудили, синтаксис ассемблера здесь намного более безумный! Если у тех же AVR, да и не только у них, он, как выразился DI, простой как лопата — берёшь и копаешь, то тут что-то посложнее лопаты, но ещё далеко не бульдозер :)
0
В «PM0044» я встретил инструкции JRF и JRT. При каких условиях осуществляется переход?
0
JRT и JRF? Хм… интересные команды! Обе условный переход. НО в JRT переход всегда произойдёт, а в JRF всегда не произойдёт
0
Бред!
0
Бред-не бред, но воображение у инженеров ST Microelectronics богатое, это мы все и до этого знали! :)
0
Такое ощущение, что набор команд генерился какой-то тулзой на полном автомате, вместе с описанием проца.
+1
А может (затаив дыхание) и вся фирма ST Microelectronics была сгенерирована автоматом? :)
0
На самом деле STMicroelectronics — хардверное воплощение Skynet…
+1
o_O
0
Более того, мнемоники JRT и JRA (относительный безусловный переход) имеют одинаковый опкод: 20h, т.е. по сути, это одна и та же команда!
0
Логично. Но что такое JRT?
0
JRxx — это Jump Relative if xx
JRA — Jump Always
JREQ — Jump if Equal
JRNE — Jump if Not Equal
и т.д. По этой логике,
JRT — Jump if True
JRF — Jump if False
0
Ну да. В доках видел =).
0
А JRF? NOP?) Или просто не ассемблируется?
0
А вот и нет! Опкод NOP-а 9D, опкод JRF — 21. Т.е. разные!
0
Забавно. А опкод 21 еще какая-нить инструкция имеет?
0
Нет. Больше ни у кого нет. Все JRxx начинаются на 2, и больше повторяющихся нет. На всякий случай методично пересмотрел всё-всё-всё описание системы команд. Больше нигде не встречается!
0
Быть может что бы на лету «глушить» переходы в отладчике?
0
А чорт его знает! Нам же ведь никто не запрещает флаги переставлять во время отладки!
0
Опкод JRF — 21h
0
сорь, не туда глянул =) сравнивались то JRT и JRA…
0
Я тут думал, думал тоже, посмотрел опкоды, есть подозрение, что, во-первых, оставались лишние биты (для системности, для «красивости»), а во-вторых, задел на будущее для оптимизации конвейера и предсказания ветвления (чтобы в кэш, например, или на конвейер, грузились команды как с текущей ветки, так и стой, которая указана на JRF. Например, подготовиться к возможному переходу, когда шина ещё не занята. Т.е. дополнительная возможность оптимизации. С другой стороны, не логично: остались переходы, на которые однобайтных опкодов не хватило, лучше бы на них отвели этот опкод.
0
Полезная статься. Лень было искать документацию про выражения.
+1
Доставь плиз еще статью про архитектуру ядра STM8 и про его адресные пространства.
0
  • avatar
  • Vga
  • 26 января 2012, 23:33
Хорошо. Пожелание учтём!
0
Встроенный в STVD ассемблер — самая глючная х… ня, которую доводилось встречать. Если у Вас много лишнего времени и есть желание находить все новые и новые баги, то Вы нашли, что искали :)… если есть необходимость писать рабочие программы, то лучше использовать ассемблер от Cosmik,IAR и т.д.
+1
если есть необходимость писать рабочие программы, то лучше использовать
Си
от Cosmik,IAR и т.д.
+2
… ассемблеры ST и Cosmica не совместимы, поэтому автору неплохо было-бы упомянуть о каком именно ассемблере идет речь. На счет С могу согласиться, но иногда некоторую «экзотическую» процедуру проще написать на ассемблере и включить ее в С-программу, поэтому знание ассемблера не повредит ИМХО…
0
но иногда некоторую «экзотическую» процедуру проще написать на ассемблере и включить ее в С-программу
Ну дык там автоматом возьмут и ассемблер, который в комплекте с С, т.е. IAR/Cosmic.
Кстати, а почему никто не вспоминает про Raisonance?
0
Ну дык там автоматом возьмут и ассемблер, который в комплекте с С
возьмут, только «автомата» не получится из-за несовместимости ассемблеров. При использовании ассемблера часто приходится обращаться к спец. регистрам (описание которых находится в папке \asm\include\*.asm). Для того чтобы в ручную не набивать имена спец. регистров написал утилиту, которая преобразует файлы \asm\include\*.asm от ST в формат понятный Cоsmicу st2cosmic.rar
0
А нафига делали столько вариантов? Чтобы запутать пользователя?

И как кстати можно сделать разные ассемблеры? Это же всего лишь мнемоники, присвоенные опкодам, которые намертво сидят в железе.
0
Вот так и можно сделать, что архетиктуры у железа разные и наборы опкодов, и их доступность.
И множество компиляторов, одни под конкретную архетиктуру, другие стремились охватить как можно больше архетиктур «Чтобы не запутать пользователя» при миграции между архетиктурами.
+1
архетиктуры у железа разные и наборы опкодов, и их доступность.
я говорю про разные варианты ассемблеров одного и того же железа. Вот ChipKiller писал:
Встроенный в STVD ассемблер — самая глючная х… ня, которую доводилось встречать. Если у Вас много лишнего времени и есть желание находить все новые и новые баги, то Вы нашли, что искали :)… если есть необходимость писать рабочие программы, то лучше использовать ассемблер от Cosmik,IAR и т.д.

Не понимаю все равно. Смультиплексировать линии на такой то адрес в памяти и подать строб чтения можно одним единственным способом — на кристалле же уже все сформировано. А как мы назовем код операции уже забота ассемблера. Максимум, что может быть — это разница в написании самой мнемоники: JMP или BR, например, или требование всяких отступов, наличие лишней строчки в конце программы, директивы препроцессора. Но сам подход к общению с процом и периферией то не должен отличаться.
Или все отличия асма STVD от остальных и заключается в этих формальностях типа лишних отступов, других директив и прочей хрени?
0
я говорю про разные варианты ассемблеров одного и того же железа.
Мой пост состоит из 2-х частей. Первая объясняет почему появилась вторая:
И множество компиляторов, одни под конкретную архетиктуру, другие стремились охватить как можно больше архетиктур «Чтобы не запутать пользователя» при миграции между архетиктурами.
0
Или все отличия асма STVD от остальных и заключается в этих формальностях
… директивы — это основа ассемблера и вовсе не «формальности»
0
Товарищи, использующие STVD, подскажите как там объявлять переменные, и указывать их размер. И как потом узнать где он размещает переменную, что бы следить за ней во время отладки?
0
STVD в «чистом» виде лучше не использовать — ибо очень кривой ассемблер. Скачайте Cosmic или что нибудь другое — избавите себя от кучи неприятных неожиданностей…
0
Скачал Cosmic, по ассемблеру для него то же информации не много… Полноценного мануала даже не нашел под STM8.
0
после установки Cosmicа, загляните в папку Docs — там есть три прекрасных pdf_a
0
Можно пару самых злостных костылей для примера? А то аж интересно стало.
+1
Один прикол и я могу поведать, правда не знаю откуда ноги растут у проблемы. Ставлю предделитель таймера, запуская таймер, а таймер работает без предделителя. Но досчитав до конца, снова начинает счет уже с предделителем.
0
STVD ассемблер неправильно генерит адреса переходов и вызовов, что касается дебагера, то «уронить» его можно даже случайно…
0
Это не баг а фича. там есть флаг «кеширования».
0
возможно, что и так, но после работы на Cosmic, пользоваться встроенным ассемблером STVD желания больше не возникало. Единственное неудобство было с использованием .inc-файлов — написал утилиту-конвертер и теперь с этим проблем нет…
0
«Хочу их в HELP-файл оформить, а не в статью.» Год прошел, где же обещанный HELP?
0
А разве кто-то обещал? Написано же «Хочу», а хотеть можно вечность.
0
Автор пишет «Хочу их в HELP-файл оформить, а не в статью».
А до этого "… будет позже".
0
Ну так, позже и будет. Гораздо позже :)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.