Записки "маньяка" или почему асм
Асм для stm32? Все сразу кричат — МАНЬЯК!!! Давайте разберемся. Асм для AVR — не маньяк. Но сразу поправляют — для быстрых фрагментов и не больших программ. Почему не больших? И где критерий большая программа или нет. Ведь на асме и не большая может весить десятки страниц. А большая под С вроде как и не очень для асма (по коду). Сложность восприятия асм текста? Так документировать и разбивать на блоки, файлы, драйвера никто не запрещает. Да и пишется много чего на макросах, что уже и на С смахивает. Возможность использовать чужие наработки, библиотеки. Тут да, облом. На асме и свои то наработки часто переделывать приходиться для разных avrок. Но лично я не люблю использовать чужие наработки. Могу подсмотреть прицип или еще что, но не копи/пасте. Зато сам сделал — всегда сможешь повторить. Что еще «шьют» в минус асму? Реализация хитрых протоколов, драйверов типа USB, IP и др. + скорость разработки. Ну насчет первого не знаю, не пробовал, да и для любительских разработок это не сильно актуально как и, кстати, второе, но кто знает, может и возьмусь со временем. Не буду расписывать плюсы авровского асма — тут все ясно. А вот стоит ли заниматься этим под stm32? Смотрим. Флэша больше, озу больше, переферии больше. Переферия вся стандартная, аккуратно разложена по блокам с одинаковыми смещениями под однотипные регистры. Если чего в проце нет — то и блок не используется. Конечно, поначалу пугает количество этих самых регистров, но вспомните, когда Вы увидели первый раз дат на мегу128. А ведь Вас тоже уверяли, что все не так страшно, что будем использовать только то, что нужно а остальное не трогаем. Так же поступаем и в котексах. Теперь непосредственно ассемблер. У рискового процессора и асм рисковый. Т.е. стандартные чтение-модификация-запись. Правда, разработчики старались помочь программистам и придумали разные прибамбасы типа бит-бэнда и регистров для установки/снятия битов, но чаще все равно чт-мод-зап. Сам асм вцелом мне показался «кумедным»(укр. что-то вроде веселый+интересный+смешной) — это слово сразу почему-то пришло на ум. Где-то вроде как избыток, а чего-то вроде как и не хватает. Но немного освоившись, я понял что этот асм довольно гибкий и мощный (+ в связке с хорошим макро-языком кейла). Можно, например, забыть о постоянных вычислениях адреса смещения в таблицах данных или переходов, о сохранении регистров в прерываниях, о сложных ветвлениях программы. Плюс работа с 32х битными регистрами и любой тебе и аккумулятор, и индексный, и базовый. Красота! Есть, конечно, ограничения, но не будем прерывать эйфорию. Работа с битовыми полями, быстрые умножения и деления, условные блоки! Ну почему я должен от этого отказаться и перейти на С. Зачем мне эти страшные CMSIS. Эти дебри порока и разврата! Если серьезно, я когда посмотрел код, созданный с помощью библиотеки — мне стало страшно. Но это еще не все! Мне очень не понравился код после «нормального» С. Вот тогда я и решил попробовать асм. Понравилось. Может и Вам понравится. Может не для заработать, а для души. Хотя, кто знает.
Теперь о том за что я бы три недели назад многое отдал — макет(заготовка) проекта. На компиляцию первого проекта с одним «nop» ушло два дня. Но давайте по порядку. Использеум Keil, поскольку жтаг у меня для него. По той же причине камень — 103C8. Сначала все как обычно. Новый прект + startup + CMSIS. Зачем библиотеки, а с ними стартап завязан. Когда перепишем стартап и библиотеки не нужно будет подключать. Дальше создаем не С файл main.c а асм файл main.s. Все как обычно подключаем к нужным группам. Открываем наш асм файл и пишем первую программу. Для начала отгородим себе «площадь» для работы: AREA. Затем экспортируем метку __main для стартапа. Затем сама прога — nop nop и nop (нет операции — кто не в курсе). И обязательно закрываем программу: END. Имеем:
Компилим — F7. Все в порядке, хекс создан, размер кода: Program size: Code=68. 62 байтика ушло, видимо, стартапу и по 2 на каждый ноп. Запустим дебаг(Cntr+F5) и проследим (F11) путь проги по стартапу к нашим нопам. Да, следует отметить что кейл в асме строг. Все должно быть на своем месте — метки без пробелов(в начале) и двоеточий(в конце). Все что с пробелом спереди — или команда, или макрос. Теперь зациклим нашу программу.
b это такая себе команда перехода.; — стандартно коментарий.
Теперь добавим область переменных и занесем туда несколько констант.
Все очень просто, но как всегда везде есть нюансы. В командах загрузки ldr и сохранения str регистра, например, источник и приемник меняются местами, что поначалу дико, но зато удобно, а у mov есть ограничения. К тому же у команд могут быть суффиксы, влияющие на размер операнда, флаги, размер самих инструкций и условия их выполнения. Описание всего этого есть в мануалах на ядро (и на русском), поэтому не буду повторяться. Ну, пока все.
PS. Все, что написано выше, не является аксиомой или обучающим материалом, а есть лишь субъективным продуктом эксперементов. Все совпадения фрагментов кода и названий меток случайны.
Project: we.easyelectronics.ru/attachments/get/89
Теперь о том за что я бы три недели назад многое отдал — макет(заготовка) проекта. На компиляцию первого проекта с одним «nop» ушло два дня. Но давайте по порядку. Использеум Keil, поскольку жтаг у меня для него. По той же причине камень — 103C8. Сначала все как обычно. Новый прект + startup + CMSIS. Зачем библиотеки, а с ними стартап завязан. Когда перепишем стартап и библиотеки не нужно будет подключать. Дальше создаем не С файл main.c а асм файл main.s. Все как обычно подключаем к нужным группам. Открываем наш асм файл и пишем первую программу. Для начала отгородим себе «площадь» для работы: AREA. Затем экспортируем метку __main для стартапа. Затем сама прога — nop nop и nop (нет операции — кто не в курсе). И обязательно закрываем программу: END. Имеем:
AREA |header code|,CODE,READONLY
ENTRY
EXPORT __main
__main
nop
nop
nop
END
Компилим — F7. Все в порядке, хекс создан, размер кода: Program size: Code=68. 62 байтика ушло, видимо, стартапу и по 2 на каждый ноп. Запустим дебаг(Cntr+F5) и проследим (F11) путь проги по стартапу к нашим нопам. Да, следует отметить что кейл в асме строг. Все должно быть на своем месте — метки без пробелов(в начале) и двоеточий(в конце). Все что с пробелом спереди — или команда, или макрос. Теперь зациклим нашу программу.
AREA |header code|,CODE,READONLY
ENTRY
EXPORT __main
__main
nop
nop
nop
metka1 b metka1 ;зацикливаем
END
b это такая себе команда перехода.; — стандартно коментарий.
Теперь добавим область переменных и занесем туда несколько констант.
AREA |header code|,CODE,READONLY
ENTRY
EXPORT __main
__main
ldr r0,=str0
mov r1,#0xcb
strb r1,[r0] ;0xCB -> [str0]
mov r1,#0x1122
strh r1,[r0,#1] ;0x1122 -> [str1]
ldr r1,=0x33445566
str r1,[r0,#3] ;0x33445566 -> [str2]
metka1 b metka1 ;зацикливаем
AREA |header data|,DATA,READWRITE
str0 dcb 0
str1 dcw 0
str2 dcd 0
END
Все очень просто, но как всегда везде есть нюансы. В командах загрузки ldr и сохранения str регистра, например, источник и приемник меняются местами, что поначалу дико, но зато удобно, а у mov есть ограничения. К тому же у команд могут быть суффиксы, влияющие на размер операнда, флаги, размер самих инструкций и условия их выполнения. Описание всего этого есть в мануалах на ядро (и на русском), поэтому не буду повторяться. Ну, пока все.
PS. Все, что написано выше, не является аксиомой или обучающим материалом, а есть лишь субъективным продуктом эксперементов. Все совпадения фрагментов кода и названий меток случайны.
Project: we.easyelectronics.ru/attachments/get/89
- +4
- 29 марта 2011, 23:08
- psv
- 1
Файлы в топике:
test.zip
Лично мне было бы интересно увидеть расширенный курс под стм32 с ассемблером.
PS: не забудьте под кат статью убрать, а то огромная :)
PS: не забудьте под кат статью убрать, а то огромная :)
Ассемблер и Си — это просто инструменты в наших руках. Бесполезно возвышать одно над другим, нужно хорошо владеть обеими и грамотно использовать преимущества каждого. Вот тогда реально достигнуть высот в программировании МК :)
согласен, холивара не хочу просто пытаюсь объяснить, что этот асм не сложнее авровского, но гораздо удобнее
А с чего ему быть страшнее? Асмы все не страшные (ну может кроме х86, его хорошо только ICC знает, ну и еще VC). Правда, ассемблер ARM позабавнее (Cortex-M использует Thumb 2) — предикаты и все такое.
Алсо, чем он удобнее? Опять же, не вижу особой разницы.
Алсо, чем он удобнее? Опять же, не вижу особой разницы.
А чем страшен х86? По моему самый удобный, или я просто привык к нему. Когда то под дос много чего писал
В нем команд — как в японском иероглифов (ЕМНИП, более 5к команд). И сложные правила оптимизации (которые, к тому же, досконально знают только инженеры Intel). Так что неудивительно, что ICC (Intel C Compiler) по эффективности кода запинывает даже матерых ассемблерщиков, притом иногда — в разы.
Ну дык даже на регистр идет четыре вида адресации, вот так много команд. Но запомнить их совершенно не сложно — не так уж там много мнемоник. И писать на асме х86 очень легко и приятно. ИМХО самый удобный из всех ассемблеров на котором я писал.
Ну, во первых, даже с учетом этого команд дохрена — это все-таки CISC, причем с десятком-другим расширений, иные из которых по сотне своих команд прибавляют. Не помню, овер5к их с учетом различных способов адресации или нет.
Во вторых, оптимизация от этого проще не становится. Архитектура нынешних процов довольно мутная, так что AFAIK тягаться в производительности с ICC очень сложно. Разве что на специфичных задачах. У RSDN в тестах был даже случай, когда ICC ничтоже сумнящеся поделил на ноль и выиграл на этом при оптимизации порядка 10 раз по производительности (причем выдав правильный результат). Эту ошибку в коде их теста только BCC заметил :)
Ну и в третьих да, писать на нем вполне хорошо, все 5к команд для этого не нужны, но смысл в том есть только в достаточно специфических задачах (либо для дельфистов, когда хочется оптимизировать какой-нить матан или потоковую обработку — оптимизатором DCC не блещет, а SIMD и вовсе не знает).
Во вторых, оптимизация от этого проще не становится. Архитектура нынешних процов довольно мутная, так что AFAIK тягаться в производительности с ICC очень сложно. Разве что на специфичных задачах. У RSDN в тестах был даже случай, когда ICC ничтоже сумнящеся поделил на ноль и выиграл на этом при оптимизации порядка 10 раз по производительности (причем выдав правильный результат). Эту ошибку в коде их теста только BCC заметил :)
Ну и в третьих да, писать на нем вполне хорошо, все 5к команд для этого не нужны, но смысл в том есть только в достаточно специфических задачах (либо для дельфистов, когда хочется оптимизировать какой-нить матан или потоковую обработку — оптимизатором DCC не блещет, а SIMD и вовсе не знает).
Смотреть надо соглашение о вызовах в доках на компилер. Обычно используется нечто вроде register — аргументы распихиваются по регистрам (вроде начиная с R8 — это связано с особенностями Thumb, но не помню), что не влезло — в стек. Результат тоже в каком-то из регистров (R8 или R0 скорее всего). Конкретней можно узнать в доках на компилер (или заглянуть в ассемблерный листинг любой функции).
Ну сколько раз твердили миру… А мир не слушает.
Всё описанное в топике хорошо, но реализация структур данных на асме — имхо ещё то удовольствие. Если из простейшего — бинарное дерево и двусвязный список. Эти я ещё могу себе более-менее представить, а это размещение в памяти + функции для работы с ними. Но реализовывать например RB-tree или B+tree мне кажется лишним геморроем и будет делаться на C. ну или реализация таких смешных вещей как quicksort или heap sort… Их на C-то корректно реализовать непросто с первого раза и без багов, а на асме я честно говоря и не видел.
Дальше переносимость. Наработки бывают сделаны интересные и терять их при переходе с одного камня на другой неохота. Зря что ли C иногда называют «portable assembler»? Не зря. Если закладываться на портируемость и всё зависящее от камня вынести за тоненький уровень абстракции, то получается переносимая(с некоторыми вопросами конечно, но это в тонких моментах) библиотека. Если уж не между архитектурами, то в пределах одной архитектуры точно.
Ну и о нелюбви к чужому коду. Один факт замеченный многими и лично мной в том числе. В «своём» коде итоговое количество ошибок будет больше, нежели в том, что был взят «на стороне». Кроме случаев, когда этот код писало криворукое нечто, по иронии судьбы считающее себя программистом. Ну и такая мелочь как время… Когда пишешь за деньги, времени намного меньше и результат должен быть намного быстрее.
Всё описанное в топике хорошо, но реализация структур данных на асме — имхо ещё то удовольствие. Если из простейшего — бинарное дерево и двусвязный список. Эти я ещё могу себе более-менее представить, а это размещение в памяти + функции для работы с ними. Но реализовывать например RB-tree или B+tree мне кажется лишним геморроем и будет делаться на C. ну или реализация таких смешных вещей как quicksort или heap sort… Их на C-то корректно реализовать непросто с первого раза и без багов, а на асме я честно говоря и не видел.
Дальше переносимость. Наработки бывают сделаны интересные и терять их при переходе с одного камня на другой неохота. Зря что ли C иногда называют «portable assembler»? Не зря. Если закладываться на портируемость и всё зависящее от камня вынести за тоненький уровень абстракции, то получается переносимая(с некоторыми вопросами конечно, но это в тонких моментах) библиотека. Если уж не между архитектурами, то в пределах одной архитектуры точно.
Ну и о нелюбви к чужому коду. Один факт замеченный многими и лично мной в том числе. В «своём» коде итоговое количество ошибок будет больше, нежели в том, что был взят «на стороне». Кроме случаев, когда этот код писало криворукое нечто, по иронии судьбы считающее себя программистом. Ну и такая мелочь как время… Когда пишешь за деньги, времени намного меньше и результат должен быть намного быстрее.
Во. Позавчера только руки дошли до армов. И решил, также как и автор, начать с ассемблера. :) Начал с программинг мануала (PM0056) и с книги авторства Joseph Yiu. Также искал книгу ARM Assembly Language: Fundamentals and Techniques автор William Hohl — так и не нашел откуда стащить ее. Нашел ее на озоне, но там цена стоит 7355 рублей, хотя может это и ошибка…
После компиляции в Keil — Code=572, а не 68 как у автора… похоже что то не так сделал…
- ChipKiller
- 27 июня 2011, 11:16
- ↓
О, еще кто-то асм пробует. Обещаю, если пройдете весь путь — не пожалеете. А по поводу размера кода — не знаю, у меня настройки по умолчанию, может у Вас что-то из библиотеки попало. Посмотрите дебагером код — может лишнее найдете.
этот ассемблер для меня далеко не «первый» :) и гораздо бОльшая проблема не изучить новый ассемблер, а разобраться с фичами Keil. Слепил программулину на ассемблере, а вот стартануть в отладчике пока никак — выбрасывает на адрес 0х0000…
- ChipKiller
- 27 июня 2011, 22:02
- ↑
- ↓
Верно ли, что cortex-m3 поддерживает не весь набор ассемблерных инструкций arm а только лишь часть набора инструкций которая называется thumb2?
Только Thumb-2, да. Это подмножество смеси ARM и Thumb. Т.е. часть команд 16-битные, часть 32-битные. Это позволяет получить некоторый компромисс между плотностью кода, обеспечиваемой Thumb, и скоростью его работы, обеспечиваемой более мощными инструкциями ARM. Чистый ARM чуть быстрее, чистый Thumb чуть плотнее.
Вот это обстоятельство немного огорчает. Ведь часто говорится, ARM ассемблер тем и хорош, что он как бы ближе к Си. А в Thumb2 эта черта завуалирована.
На МК место ценнее, чем скорость. Да и вообще, ассемблер под ними редко кого волнует. Обычно волнует вопрос «во сколько байт скомпилится вон тот кусок на С и как быстро будет работать». Thumb-2 позволяет добиться компромисса в этом вопросе, это хорошо.
А чем он ближе к С? Набор команд ARM — вполне обычный ассемблер, за двумя исключениями — он трехадресный и все команды условные. Но в Thumb-2 вроде тоже с условными командами неплохо.
А чем он ближе к С? Набор команд ARM — вполне обычный ассемблер, за двумя исключениями — он трехадресный и все команды условные. Но в Thumb-2 вроде тоже с условными командами неплохо.
На ARM7 это выглядело так:
48: int a=30, b=70;
00000004 E3A0501E MOV R5,#0x1E
00000008 ---- Variable 'a' assigned to Register 'R5' ----
00000008 E3A04046 MOV R4,#0x46
0000000C ---- Variable 'b' assigned to Register 'R4' ----
49: a=a+20;
0000000C E2855014 ADD R5,R5,#0x0014 ; a
50: b=b-10;
00000010 E244400A SUB R4,R4,#0x000A ; b
а с gnu asm не пробовали?
я поставил coIDE (она бесплатная) к ней компилятор sourcery.mentor.com/sgpp/lite/arm/portal/package9743/public/arm-none-eabi/arm-2011.09-69-arm-none-eabi.exe
как мне теперь откомпилировать асм файл?
пытаюсь компильнуть
.globl main
.data
main:
b Start
а в ответ ни ошибок, ни файла
compile:
[mkdir] Skipping C:\CoIDE\workspace\test2\Debug\bin because it already exists.
[mkdir] Skipping C:\CoIDE\workspace\test2\Debug\obj because it already exists.
[cc] 1 total files to be compiled.
[cc] arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Wall -ffunction-sections -O0 -g3 -c -DM3 -IC:\CoIDE\workspace\test2 C:\CoIDE\workspace\test2\main.s
[cc] Starting link
[cc] arm-none-eabi-gcc -O0 -nostartfiles -Wl,-Map=test2.map -mcpu=cortex-m3 -mthumb -LC:\CoIDE\workspace\test2 -Wl,--gc-sections -Wl,-TC:\CoIDE\workspace\test2\link.ld -g -o test2.elf ..\obj\main.o
Program Size:
text data bss dec hex filename
0 0 0 0 0 test2.elf
BUILD SUCCESSFUL
Total time: 1 second
что делаю не так?
я поставил coIDE (она бесплатная) к ней компилятор sourcery.mentor.com/sgpp/lite/arm/portal/package9743/public/arm-none-eabi/arm-2011.09-69-arm-none-eabi.exe
как мне теперь откомпилировать асм файл?
пытаюсь компильнуть
.globl main
.data
main:
b Start
а в ответ ни ошибок, ни файла
compile:
[mkdir] Skipping C:\CoIDE\workspace\test2\Debug\bin because it already exists.
[mkdir] Skipping C:\CoIDE\workspace\test2\Debug\obj because it already exists.
[cc] 1 total files to be compiled.
[cc] arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Wall -ffunction-sections -O0 -g3 -c -DM3 -IC:\CoIDE\workspace\test2 C:\CoIDE\workspace\test2\main.s
[cc] Starting link
[cc] arm-none-eabi-gcc -O0 -nostartfiles -Wl,-Map=test2.map -mcpu=cortex-m3 -mthumb -LC:\CoIDE\workspace\test2 -Wl,--gc-sections -Wl,-TC:\CoIDE\workspace\test2\link.ld -g -o test2.elf ..\obj\main.o
Program Size:
text data bss dec hex filename
0 0 0 0 0 test2.elf
BUILD SUCCESSFUL
Total time: 1 second
что делаю не так?
для GCC расширения файла очень важно. «s» значит асм-файл который не надо прогонять через препроцессор, а «S» — надо. К тому же у вас main объявлен в секции data, а должен в text
попробуйте
;main.S
.globl main
.test
main:
b Start
И откомпилировать ручками:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -o main.o main.S
попробуйте
;main.S
.globl main
.test
main:
b Start
И откомпилировать ручками:
arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c -o main.o main.S
(сразу предупреждаю: все комменты не читал)
плюсы асма:
+ компактный код
+ максимальная эффективность
(^^^ при достаточном знании разработчика)
плюсы С:
+ компактный код
+ максимальная эффективность
^^^^ при достаточном знании разработчика
ПЛЮС скорость разработки.
просто в сях надо знать чуть больше. зато профитов больше на порядки.
плюсы асма:
+ компактный код
+ максимальная эффективность
(^^^ при достаточном знании разработчика)
плюсы С:
+ компактный код
+ максимальная эффективность
^^^^ при достаточном знании разработчика
ПЛЮС скорость разработки.
просто в сях надо знать чуть больше. зато профитов больше на порядки.
Комментарии (53)
RSS свернуть / развернуть