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

Меню навигации для мобильных

ASM для STM32 :: изучаем и пробуем писать

Автор Slabovik, 19 Июль, 2023, 18:08

« предыдущая - следующая »

Slabovik

Вообще, с asm'ом для ARM-процов, коими являются всяческие STM32 одновременно и трудно, и вполне нормально.
Трудно в том плане, что доступные среды, которые "всё-в-одном" не имеют режимов "проект на ASM", а нормально потому что их Си-компиляторы имеют на выходе тот же asm-код, который на следующей ступени скармливается ассемблеру, а затем линковщику. Это значит, что компилятор ассемблера вполне имееются, язык декларирован и работать можно. GUI вот нету, только командная строка.

Ассемблер можно найти в папке bin после установки Segger или STM32Cube. Можно найти в интернете чистый "гнусный" инсталлятор - GNU C, но не для PC, а кроссплатформенный ARM GNU Toochain. Нужна сборка gcc-arm-none-eabi, где
    gcc - название компилятора;
    arm - архитектура целевого процессора;
    none - целевая ОС (здесь none = компилятор не вносит никакого дополнительного bootstrap кода от себя);
    eabi - код соответствует спецификации EABI (https://ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D1%8B%D0%B9_%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D1%84%D0%B5%D0%B9%D1%81_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9).

Взять можно, например, здесь: https://gnutoolchains.com/arm-eabi/ (внутри будут нужные none-eabi), либо ещё где.  К сожалению, ссылка на "гнездо" https://developer.arm.com/ тоже не работает, походу, заблокирована... (ps: выяснил, что в DNS от провайдера нет записи для этого сайта. Переключение на другой DNS-сервер решает проблему)

Нужны следующие файлы, минимальный набор:

arm-none-eabi-as.exe
arm-none-eabi-ld.exe
arm-none-eabi-objcopy.exe

и файлы для удобства (и не только)

arm-none-eabi-nm.exe
arm-none-eabi-objdump.exe
arm-none-eabi-size.exe

всего 6 штук. Лучше сложить их (скопировать) в папочку, в которой предполагается держать проект, а ещё лучше - в каждом проекте иметь свою копию этих бинарников. Это связано с тем, что иногда, с изменением версий, может меняться поведение компилятора на всякие там директивы и бывает, что поэтому проект вдруг перестаёт собираться.

Для компиляции надо иметь ещё два файла. Один - батник, в котором прописаны вызовы всего этого хозяйства (без него грустно т.к. нужно указывать много опций), а второй - информация для линковщика, в котором прописано, как правильно располагать в памяти контроллера написанный код. Вот, здесь (http://stm32asm.ru/) есть примеры таких файлов, свои я делал на их основе, так что принципиально они не отличаются, покажу их попозже.

Дальше... а дальше надо писать саму программу :)
Общением на форуме подпитываю свою эгоистичную, склонную к самолюбованию сущность.

Slabovik

#1
Как писать программу... даже не знаю, как сказать... всё очень просто. Программа представляет собой набор команд (инструкций), исполняемых непосредственно ядром процессора. Эти команды, можно сказать, являются элементарными. Процессор их читает с шины как коды, но для удобства в тексте программы мы пишем их мнемонические обозначения (хотя в принципе, никто не мешает нам писать прям вот так сами коды, но это чертовски неудобно). Как работает сам процессор, если будет интересно, можно разобрать позже. На деле там тоже ничего сложного.

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

Но вначале пара специальных символов, без которых никак: символ комментария: @
Всё, что написано правее этого "уха" и до самого конца строки компилятором игнорируется. Символ немного непривычный, потому что в других ассемблерах для этой цели использовались либо точка с запятой, либо '#', а может и ещё чего ('/*' и '*/').
Комментарии можно вставлять где угодно. Если он в самом начале строки - вся строка будет комментарием. Если он после команды - команда будет компилироваться, а начиная от @ будет проигнорировано.

Второй символ '#'. У него есть другие роли, но если он стоит самым первым в строке (совсем первым, а не после пробела или табуляции), то вся строка после него тоже комментарий.

Далее желательно указать используемый синтаксис, тип процессора, для которого компилируется программа, тип команд (да, у ARM ещё и с командами кавардак).

.syntax unified @ ситаксис
.cpu cortex-m3 @ это для STM32F103, которые в BluePill
.thumb @ тип команд (Thumb - это 16-битный код, он более компактный)
.thumb_func @ определяет упрощённое написание некоторых команд Thumb

Поскольку в один файл всю программу включить в подавляющем большинстве случаем не удаётся, есть замечательная директива

.include "filename"

Это значит, что в это место, где написано .include, при компиляции будет вставлено всё содержимое файла filename. Это удобно, потому что позволяет вынести с глаз долой целые "полотенца" различных определений, процедур и т.п. (в принципе, в Си делается точно также).

.equ имя, значение

присваивает имени заданное значение (выражение).
В качестве значения могут быть числа, математические зависимости либо другие имена (важно: они должны быть определены ранее по тексту). Фактически, при компиляции происходит замена имени на значение. По ходу текста (не исполнения!) программы имя можно многократно переопределять.

.equiv имя, значение

то же самое, но однажды определённое значение имени переопределить нельзя.

.set имя, значение

работает аналогично .equ

Тут многие наверное уже заметили, что у AVR слегка не так. Но на деле это синтаксические мелочи :)

Через .set и .equ очень удобно задавать имена ресурсам (портам) контроллера. Поскольку у ARM ресурсов много, списки получаются внушительными, выносятся в отдельные файлы и подключаются через .include в самом начале программы.

.org адрес @ тоже важная штука. Она устанавливает адресный счётчик в нужное нам положение. Ну, например

.org 0x0
команда 1
команда 2
.org 0x100
команда 3

будет означать, что "команда 1" расположится в памяти по адресу 0x0, команда 2 по адресу 0x0+'длина команды 1'
а команда 3 ляжет на адрес 0x100... очень удобно. Если при этом в промежутке между командой 2 и командой 3 ничего не будет определено, то там так и останется пустое место. Это работает как для кода, так и для размещения всяких переменных в ОЗУ или констант в памяти программ.

По-большому счёту, разобраться осталось с так называемыми сегментами. Сегменты - это условное разбиение на части кода и/или данных, которые линковщиком (который работает уже после компилятора) "укладываются" в определённые им области памяти. Для систем с Фон-Неймановской архитектурой (это там, где программный код и данные находятся в едином адресном пространстве) можно обходиться одним сегментом, а вот для Гарвардской архитектуры (данные и код не пересекаются и у каждого типа этих данных своя адресация) минимальное количество сегментов - два. Но обычно и там и там делают как минимум два сегмента, а когда надо - и больше.

.section .имя_секции [,"флаг"]

имена секция по-идее можно назначать произвольно (они потом участвуют в файле-конфигураторе линковщика), но так уж сложилось, что имя секции .text считается программым кодом, а секция .bss - озу. Почему .bss - я не знаю, тем более, что в одних источниках эта секция предназначена для инициализируемых данных, в других - просто озу. В любом случае, имя можно использовать любое, главное - потом это имя прописать в конфиг линковщика. Например, у себя я прописываю ещё секции .code и .const - они линкуются в программное ROM, а секцию .bss называю .sram или .data - они линкуются в область RAM (но с .data не точно, если не указать чётко, то она может прилинковаться в область ROM с кодом). В общем, тут насколько фантазии хватит.

"флаг" может конкретизировать предназначение секции. Например "x" укажет, что это точно секция кода.

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

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