AVR — Assembler!

Изначально это были две большие статьи про два свободных ассемблера под Linux, один AVRA , использует тот же синтаксис что и в учебниках по AVR Atmel, другой GNU As более универсален и применяется в основном в ассемблерных вставках. Статья большая, в ней есть свои недочеты и я буду рад вашим комментариям и исправлениям.

AVRA

Если вы все таки , решились писать на ассемблере в linux не используя wine и AVRStudio , вам сюда. Итак существует проект AVRA (GPL v2), и предполагается заменив AVRASM v1 от Atmel на AVRA, вы также спокойно будете компилировать свой код как и раньше…. но так как проект ,то ли не набрал людей, либо о нем мало кто знал, но сейчас везде используется AVRASM v2 а про этот пакет почти позабыли.

Установка

Нам понадобиться avra-1.3.0 …почему? потому как она спокойно воспринимает записи #ifndef ,#define ,#pragma в заголовочных файлах, эти директивы появились в AVRASMv2 , вы можете исправить на .ifndef , .define, а #pragma игнорировать, но в версии 1.3.0 это уже учтено.

К сожалению описание установки устарело… поэтому пришлось поставить кривым способом(18,03,2011). Качаем архив и распаковываем его. Зайдя в папку с файлами перенесите все файлы в папку /avra-1.3.0/src, следующей командой:

for i in `ls --color=no |grep -v "src"`
do 
mv $i src
done

Далее создаем make файл

cd src
aclocal
autoconf
automake -a

На последнем он немного ругнется о том что не хватает

  • Makefile.am: required file `./NEWS’ not found
  • Makefile.am: required file `./ChangeLog’ not found
./configure

А теперь РЕДАКТИРУЕМ Makefile.in удаляя из него слова NEWS ChangeLog

make

К сожалению install корректно не работает поэтому копируем все заголовочные файлы и исполняемый!

sudo cp avra /usr/bin/
sudo cp -r includes/ /usr/share/avra/includes

Все осталось написать простую программу…. например такую.

.includepath "/usr/share/avra/includes/" ;Папка с файлами заголовками
.include "m16def.inc" ; Используем ATMega16
; RAM
;=====================================================
.DSEG
; Сегмент ОЗУ
; FLASH
;===================================================
.CSEG
; Код овый сегмент
; EEPROM
;==================================================
.ESEG
; Сегмент EEPROM
; FLASH
;===================================================
main:
;LDI r17,Low(RAMEND);
;OUT SP, r17
;LDI r17,High(RAMEND);
;OUT SPH, r17
ldi r19 , 0xFF ;
out DDRB, r19
REB:
ldi r17, 0xFF
Program:
out PORTB, r17
rjmp Program

У меня не было тестовой программы… так что я слепил её из того что валялось в сети, и она даже не мигает диодами, а просто светит всем портом B. За нормальными инструкциями обращайтесь к профессионалам))) Сохраните это с расширением .s, например first.s, и перейдя в папку с программой введите:

avra first.s

Ну и должно выестись что нибудь вроде

Used memory blocks:

Assembly complete with no errors.
Segment usage:
   Code      :         5 words (10 bytes)
   Data      :         0 bytes
   EEPROM    :         0 bytes

А вот если у вас выскочит что то вроде /usr/share/avra/m16def.inc(534) : Error : Line to long То вам придется открыть этот файл(m16def.inс) и от редактировать этот длиннющий комментарий в 534 строке, после того как мы его сократим все должно работать.

Подсветка синтаксиса AVR Assembler в gedit

Тут долго искать не пришлось, один немец уже сделал все за нас, так что скачайте архив

cp asm.lang /usr/share/gtksourceview-2.0/language-specs/
cp assembler.xml /usr/share/gtksourceview-2.0/styles/ # это стиль и не факт что он вам понадобится

Перевод документации.

Введение

AVRA является ассемблером для AVR микроконтроллеров фирмы Atmel, и он почти полностью согласован с собственным Atmel ассемблером AVRASM32. Принципы программирования и концепции основаны на языке ANSI «C».

Первоначальный вариант AVRA был написан John Anders Haugum. Он выпустил все версии вплоть до v0.7. Все последующие версии были выпущены Tobias Weber.

Различия между AVRA и AVRASM32

Существуют некоторые различия между оригинальным Atmel-овским ассемблером AVRASM32 и AVRA. В основном AVRA создана для того чтобы заменить AVRASM32 без специальных изменений в вашей текущем Atmel AVR Studio окружении. Параметры командной строки были адаптированы на столько на сколько это возможно. Переход к строке содержащей ошибку напрямую, двойным щелчком на сообщении об ошибке в окне вывода(output window) работает так же как с AVRASM32.

Различия в деталях

  • Поддержка некоторых дополнительных директив препроцессора.
.define, .undef, .ifdef, .ifndef, .if, .else, .endif, .elif, .elseif, .warning
  • Не все параметры командной строки поддерживаются. Указание от дельного файла для eeprom(-e) не поддерживается. Все данные для eeprom помещаются в файл называемый program.eep.hex и всегда в Intel hex формате. Другие форматы hex файлов, помимо Intel на данный момент не поддерживаются.
  • Опережающие ссылки(ссылка на класс, переменную или функцию, которые объявлены, но еще не определены) не поддерживаются для .ifdef и .ifndef директив. Это гарантирует , что директивы такие как .ifdef и .undef будут работать правильно. Если вы знакомы с языком программирования C , вы легко это получите в AVRA. Смотри главу «Техника программирования» для получения большей информации о том как писать правильный код.
  • Расширенная поддержка макросов в AVRA имеет некоторые новые функции для написания гибких макросов. Это должно увеличить возможность повторного использования кода например построить вашу собственную библиотеку.
  • Поддержка отладки, AVRA создает coff файл каждый раз когда сборка завершается успешно. Это файл так же как и в AVR Studio или любой другой coff совместимом от ладчике используется для имитации или эмуляции программы.
  • Мета теги времени сборки. Это поможет вам отслеживать версии вашего программного обеспечения , а также может быть использовано для создания заказных серийных номеров.

Совместимость

С Авра написан на ANSI C, и может быть скомпилирован на большинстве платформ. Если у вас возникли проблемы компиляции AVRA, пожалуйста, оставьте сообщение на доске объявлений на SourceForge или от правте письмо авторам AVRA.

Техники программирования:Использование директив

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

Директива .define

Чтобы задать константу, используйте .define . Это то же самое что и .equ, только более C стиле. Имейте ввиду что AVRA не чувствительна к регистру. Не смешивайте .def и .def ine, потому что .def используется только для регистров. Это сделано для обратной совместимост и с Atmel-овским AVRASM32. Вот пример того как использовать .define .

.define network 1

Теперь «network» установлена значение 1. Вы можете собрать определенные части вашего кода в зависимости от определителей или переключателей. Вы можете проверить ваши определенные переменные на существование (.ifdef и .ifndef ), а также на значение которое они подcтавляю. В следующем коде показан способ предотвратить сообщение об ошибке при тестировании неопределенной константы. Условные директивы всегда должны заканчиваться директивой .endif

.ifndef network
.define net work 0 
.endif

Директивы .if и .else

Три строчки последнего примера устанвливают значения по умолчанию для «network». В следующем примере, вы увидете как мы можем использовать значения по умолчанию. Если не была определена ранее, она устанавливается в ноль. Теперь вы можете проверить , входит ли поддержка сети в процесс сборки.

.if network = 1
.include "include\tcpip.asm"
.else
.include "include\dummynet.asm"
.endif

Во второй части листинга выше вы видите использование .else, которая определяет часть которая будет выполнена если выражение в предыдущем .if не равно. Вы также можете использовать другое выражение для проверки другого выражения. Для этой цели используется .elif , что означает «else if». Всегда закрывайте эту условную часть «.endif »

Директива .error

Эта директива может быть использована чтобы выдавать ошибки, если часть кода достигла того, чего не должна была достигнута. В следующем примере показано как остановить процесс сборки ,если имеет значение то что значение не было установлено.

.ifndef network
.error "network is not configured!" ;the assembler stops here

Директивы .nolist и .list

Вывод списка файлов может быть остановлено этими двумя директивами. После того как только avra обнаруживает при сборке .nolist , это останавливает вывод списка файлов. После того как директива .list обнаружена, она продолжает нормальный вывод файлов.

Директива .includepath

По умолчанию, любой файл, который включается изнутри файла с исходным кодом, должен иметь либо только имя файла полный абсолютный путь к файлу. С директивой .includepath вы можете установить дополнительные пути поиска файлов. Кроме того вы можете добавить множество нужных вам путей. Убедитесь что нет схожих имен файлов в разных включениях, потому что , тогда не ясно какой из них avra использовать.

GNU As

Это перевод следующей страницы руководства по GNU Assembler для AVR.

Ассемблер AVRA поддерживает синтаксис Atmel ассемблера, но проблема заключается в том что с помощью него нельзя сделать файл с отладочной информацией и заниматься пошаговой отладкой, а также когда вы приступите к изучению ассемблерных вставок в GCC вам все равно придется учить GNU As и это хорошее вложение сил так как GNU As есть под все платформы и сильно переучиваться вам не придется.

Вам необходимо будет поставить пакеты binutils-avr gcc-avr avr-libs, первые два просто собранные под другую целевую архитектуру инструменты, последний же содержит информацию о микроконтроллерах. Советую не собирать с помощью avr-as , а воспользоваться инструкцией описанной в руководстве ниже

avr-gcc -mmcu=atmega16a example.S -o example.o #создаем объектный файл
avr-objcopy -O ihex example.o example.hex #создаем файл для прошивки

Введение

Существует несколько причин писать код для AVR микроконтроллеров используя только ассемблерный код. Среди них:

  • Код для устройств не обладающих RAM и таким образом не поддерживаемые компилятором C
  • Код для критичных ко времени выполнения, приложений
  • Специальные хитрости, которые нельзя совершить в С

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

Хотя avr-libc предназначен в первую очередь для поддержки программирования микроконтроллеров AVR с использованием языка C (и C++),также есть ограниченная поддержка использования ассемблера напрямую. Преимуществами этого являются:

Использование С-шного препроцессора и следовательно, возможность использовать те же символические константы, которые доступны для C программ, также гибкая концепция макросов, которая может использовать любой С-шный идентификатор как макрос (в то время как концепция ассемблерных макросов в основном ориентирована на использование макросов вмест о ассемблера???)

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

Вызов компилятора

Для достижения цели изложенной в настоящем документе, ассемблер и компоновщик, обычно не вызываются вручную, вмест о этого используется интерфейс С компилятора (avr-gcc), который в свою очередь , при необходимост и вызывает ассемблер и компоновщик. Этот подход обладает следующими преимущест вами:

  • Будет всего одна , вызываемая напрямую программа — avr-gcc,независимо от фактически используемого языка.
  • Автоматически будет вызваться препроцессор С и будет включать в себя необходимые параметры, для поиска включаемых файлов в файловой системе
  • Компоновщик будет вызваться автоматически и будет включать в себя соответствующие параметры для поиска дополнительных библиотек а также приложений запускающих код (crtXXX.o) и сценарии компоновщика

Обратите внимание, что С препроцессор вызывается автоматически когда имя файла содержащее ассемблерный код, оканчивается расширение .S(заглавная буква «s») Это также относится к операционным системам, использующим не чувствительные к регистру файловые системы, так как фактически принимаемое решение основывается на имени файла указанного в командной строке а не на фактическом имени файла(Прим. пробовать на системах чувствительных к регистру, у меня выдало ошибку).

Кроме того язык может быть задан явно с помощью параметра -x assembler-with-cpp

Пример программы

Описанный ниже пример, снабженный комментариями, является простым генератором прямоугольных импульсов(меандр) на 100кГц, используется AT90S1200 с частотой кристалла 10,7МГц. Pin6 будет использоваться для вывода сигнала.

#include <avr/io.h>      ; Note [1]
work = 16                ; Note [2]
tmp = 17

inttmp = 19
intsav = 0
SQUARE = PD6             ; Note [3]

; Note [4]:
tmconst = 10700000 / 200000 ; 100 kHz => 200000 edges/s
fuzz= 8 ; clocks in ISR unt il TCNT0 is set
.section .text
.global main ; Note [5]
main:
        rcall ioinit
1:
        rjmp 1b ; Note [6]
        .global TIMER0_OVF_vect ; Note [7]
TIMER0_OVF_vect :
        ldi inttmp, 256 - tmconst + fuzz
        out _SFR_IO_ADDR(TCNT0), inttmp ; Note [8]
        in  int sav, _SFR_IO_ADDR(SREG) ; Note [9]
        sbic _SFR_IO_ADDR(PORTD), SQUARE
        rjmp 1f
        sbi _SFR_IO_ADDR(PORTD), SQUARE
        rjmp 2f

1:      cbi _SFR_IO_ADDR(PORTD), SQUARE
2:
        out _SFR_IO_ADDR(SREG), intsav
        reti
ioinit :
        sbi _SFR_IO_ADDR(DDRD), SQUARE
        ldi work, _BV(TOIE0)
        out _SFR_IO_ADDR(TIMSK), work
        ldi work, _BV(CS00)     ; tmr0: CK/1
        out _SFR_IO_ADDR(TCCR0), work
        ldi work, 256 - tmconst
        out _SFR_IO_ADDR(TCNT0), work
        sei 
        ret
        .global__vector_default ; Note [10]
__vector_default :
        ret i

        .end

Note 1

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

Note 2

Назначение регистрам «символьных» имен, для локального использования. Ещё как вариант , можно использовать вместо этого макросы препроцессора

#define work 16

Note 3

Номер нашего бита для вывода меандра. Отметим что правая часть содержит CPP макрос, который будет заменен на свое настоящее значение (6 в нашем случае), когда его передадут ассемблеру

Note 4

Ассемблер использует целочисленные операции с целыми числами размер которых задается (32 бит и больше) при оценке выражения. Это контрастирует с С в котором используется тип int по умолчанию для расчета констант в целочисленных выражениях.

Для того чтобы получить 100кГц на выходе, нам необходимо переключать вывод PD6 200000 раз в секунду. Так как мы используем timer 0 (Прим. встроенный таймер — вызывающий прерывание при прохождении указанного количества такт ов) без какой либо опции предварительного масштабирования для того чтобы получить заданную частоту и точность, мы уже столкнулись с местами критичными для временного исполнения: При приеме и обработке прерывания таймера, таймер по прежнему продолжает считать. Когда мы будем загружать регистр TCCNT 0 , мы должны учесть количест вот актов необходимых для прерывания и инструкций по перезагрузке TCCNT 0 (4 такт а на переход к обработке прерывания, 2 такта на команду rjmp с вектора прерывания и 2 такт а для двух инструкций по перезагрузке TCCNT 0). Это то что записано в константу fuzz

Note 5

Внешние функции должны быть объявлены в .global main — являющейся точкой входа в приложение,в которую перейдет указатель из процедуры инициализации в cts1200.o

Note 6

Основной цикл — перескакивает к самому себе. Генератор меандров сам по себе полностью создан(обрабатывается), выполненяя прерывания при переполнении timer0. Использование инструкций сна(использование режима ожидания) было бы замечательной идеей, но вероятно не сохранило бы много энергии так как в любом случае обработка прерываний выполняется довольно часто.

Note 7

Функций обработки прерываний могут обладать именами функций которые обычно доступны программам на С. Затем компоновщик поместит их в соответствующие слот вектора прерывания. Обратите внимание что необходимо указать .global в порядке указания целей. Это будет работать только если он подключен . Обратите внимание что ассемблер и компоновщик не могут проверить правильность написания функции прерывания, поэтому необходимо перепроверить её. (При анализе полученного с помощью avr-objdump или avr-nm кода, должен появится вектор __vector_N , где N это небольшое целое число).

Note 8

Как объясняется в разделе посвященном специальным функциям работы с регистрами, фактический адрес порт а ввода-вывода должен быть получен использованием макроса __SFR_IO_ADDR. (AT90S1200 не обладает RAM , таким образом связанный с отображением в память регистров ввода-вывода не доступен. В любом случае это было бы гораздо медленней чем использовать инструкции in/out). Поскольку операция перезагрузки TCCNT0 критична по времени, она выполняется перед сохранением SREG. Очевидно, что это требует чтобы выполняемые инструкции не изменяли флаг биты в SREG

Note 9

Обработка прерываний не должна влиять на глобальное состояние процессора. Таким образом, как правило необходимо сохранять по крайне мере состояние флаг битов SREG. (Заметим, что в обсуждаемом здесь примере, нижеследующие инструкции не изменяют состояние SREG, но обычно это не так) также вы должны быть уверены что регистры используемые внутри прерывания не противоречат , тем что используются вне прерывания. В случае с устройствами с отсутствующей RAM как AT90S1200, это можно сделать только путем согласования набора регистров используемых для обработки прерывания;там нету никаких способов «сохранить» регистр где-нибудь.

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

Note 10

Как объясняется в главе Прерывания , глобальный, «срабатывающий на все» обработчик прерываний, который реагирует на все не использованные вектора прерываний, может быть описан используя имя __vector_default. Он должен быть .global и очевидно, должен заканчиваться инструкцией reti (По умолчанию, вмест о этого происходит переход к адресу 0)

Псевдооперации и операторы

Доступные в ассемблере псевдооперации описаны в руководстве к GNU Assembler(gas). В Интернете руководст во расположено как часть текущей версии пакета binutils(Прим. на компьютере находится в пакетах вроде binutils-doc)

Т.к. gas ведет свое происхождение от оригинальной Unix, его псевдооперации и общий синтаксис ассемблера немного отличаются от того что используется в других ассемблерах (Прим. вот пример статьи сравнения ) Описание числовых констант следует С-шному обозначению (префикс 0х для обозначения шестнадцатеричных констант ), выражения используют С-подобный синтаксис.

Некоторые общие псевдооперации включают в себя:

  • .byte закрепляет за обозначение однобайтовую константу
  • .acii закрепляет за обозначением оканчивающуюся на \0 строку символов (С-строка)
  • .data переключает на .data секцию(инициализация RAM значений)
  • .text переключает на .text секцию(код и ROM переменные)
  • .set объявляет символ как постоянную выражение (идентичен .equ)
  • .global (или .globl) объявляет глобальные переменные, которые видит компоновщик (например точки входа в функции, глобальные переменные)
  • .extern объявляет внешне определенный символ-переменную; это полезно писать только в качестве комментария т.к. когда gas сталкивается с неизвестным ему символом, он все равно ищет их в глобальном масштабе.

Обратите внимание что в gas также доступна псевдооперация .org ,но это довольно бессмысленно в окружении ассемблера использующего перемещаемые объектные файлы, так что компоновщик сам определяет конечную позицию некоторых объектов ROM или RAM.

По мимо архитектурно независимых стандарт ных операторов, доступны некоторые специфичные для AVR операторы, они к сожалению ещё не описаны в официальной документации(все описано в разделе 9.4.2.3 официальной документации к GNU As). Наиболее заметные операторы:

  • lo8 возвращает младшие 8 бит от 16-битного целого;
  • hi8 возвращает старшие 8 бит от 16-битного целого;
  • pm принимает адрес в программной(ROM) памяти и конвертирует его в адрес (RAM). Это подразумевает деление на 2 так как AVR обрабатывает ROM адреса как 16-разрядные слова(например инструкции IJMP или ICALL), а также эта инструкция может работать с перемещаемыми символами на правой стороне(???)

Пример

ldi r24, lo8(pm(somefunc))
ldi r25, hi8(pm(somefunc))
call somet hing

Этот код передает адрес функции somefunc как первый параметр функции something.

Как видно в статье некоторых моментах я не разобрался попытаюсь исправить это поспрашивав , людей знающих. В любом случае жду ваших комментариев, следующая статья будет про отладку ассемблерного кода в gdb. Документацию смотреть можно с помощью команды info раздел As или GNU As , или через Emacs (С-h i), поиск и там и там Ctrl-S(и повторные нажатия для перехода к следующему вхождению)

4 Comments

  1. Александр

    20.09.2013 at 11:14

    Для тех убунтоидов, которым лень собирать avra 1.3.0, и делать подсветку синтаксиса в gedit руками, я уже собрал соответствующие пакеты для Ubuntu 12.04 и 12.10. Пакеты лежат на launchpad — https://launchpad.net/~richkofsky/+archive/avr

    • Sergey

      20.09.2013 at 20:52

      Круто, спасибо! Надо будет помучать gdb на тему отладки coff выхлопа avra

  2. Александр

    20.09.2013 at 11:22

    Вдогонку.
    По поводу подсветки синтаксиса, у немца есть маленькая ошибка — инстпукция cbr не подсвечивалась.
    В пакете уже поправлено.

    • Сергей

      14.11.2014 at 00:46

      Спасибо, установил себе и все начало выделять, а до этого в списке подсветки исходного кода не было AVR Assembler

Добавить комментарий

Your email address will not be published.

*


*

© 2017 Crafting.be

Theme by Anders NorenUp ↑