Как дизассемблировать hex файл в avr studio

Обновлено: 11.05.2024

AVR AVRISP STK500 V3.0 USB ISP Programmer for AVR IC
Люди помогите плз. не могу разобраться. приобрел этот чудный девайс (AVR AVRISP STK500 V3.0 USB.

Анализ стека AVR / AVR stack analysis
Привет! Уперся я в стек, и решил понять что почем. Нашел вот такой вот скриптик.

AVR Atmega324PU не прошивается AVR ISP Mk2
Добрый день. На плату впаян данный микроконтроллер в корпусе tqfp. При подключении программатора.

Дизассемблер? hex --> asm
Привет всем! Собственно,чем дизассемблировать? Есть hex-файл - программа написана на С, не.

дизассемблированием переводят прошивку на язык ассемблера.
текст на си из прошивки не получить Подскажите где можно почитать как выковырять прошивку из чипа и дизассемблировать ее. Желательно разбираться с программой на языке "С". Прочитать прошивку можно с помощью любого программатора. Конечно в том случае, если не установлена защита от чтения. А после этого модно дизассемблировать программу. Результатом работы дизассемблера является текст программы на ассемблере, или листинг программы.
А причем тут программа на языке С? Хмм.
При дисассемблировании- то куча проблем. для x86 скомпиллированный заново исходник запросто может не заработать.
А тут СИ.
и для МК работает? Хмм.
При дисассемблировании- то куча проблем. для x86 скомпиллированный заново исходник запросто может не заработать.

Хмм. сложный вариант. Советую прочитать, что такое ассемблер.
Вас послушаешь, дак и зашифровать - расшифровать бинарник - проблем не оберёшься.
Ассемблирование - абсолютно обратимый процесс. Теряются только коменты, да названия меток.

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

Програмисты на ПК вообще называют С кроссплатформенным ассемблером и боятся.

Вообще-то я как раз програмист на ПК. с 1993г.
Что такое ассемблер меня учить не надо.
>>Ассемблирование - абсолютно обратимый процесс
в командах x86 -нет

. С кроссплатформенным ассемблером и боятся - что это за бред?

есть куча прог разной степени кривости(нормальной вообще не встречал, хотя в приватах может что и ходит) которые переводят язык из асма на си, но они все для х86.. для микроконтроллера - 99% что не найдете. Работайте с дизасмовым кодом в IDA - логику оно дает понять.

to Johmmy0007
вы не забывайте, при работе под виндой код может не работать из-за неправильного заполнения, например таблицы импорта. сам код вполне получается рабочим..

>>есть куча прог разной степени кривости(нормальной вообще не встречал
и не встретишь. Никогда.
Ибо полностью автоматическое дисассемблерование x86 НЕВОЗМОЖНО. Из-за разности длины команд x86 невозможно правильно отделить команды от данных, не напрягая логику.
даже очень мощный интеллект IDA заставит сильно работать головой и ручками. Про СИ, соответственно, вообще молчу.
В МК восстановление листинга asm куда проще. мож кто сделает и си (кому делать нечего).

Wymdows? Вы о чём?
Даже простейшие COM-программы для DOS(там нет таблицы импорта -голый код) запросто могут не заработать после малейшего исправления

>>Ассемблирование - абсолютно обратимый процесс
в командах x86 -нет Из-за разности длины команд x86 невозможно правильно отделить команды от данных, не напрягая логику. Вы хотите сказать что нельзя из бинарника точный асм-листинг получить? Интересно, а как проц x86 код выполняет, может быть там какая-то особая нигия недюжая логика?

Вы когда- нибудь сами дизассемблировали программу?
Вы можете не верить мне, но имя КРИС КАСПЕРСКИ вам о чём-нибудь говорит?

Например, ADD AX,1 может быть ассемблирована в следующие коды микропроцессора x86:
Код:

05 01 00
83 C0 01
81 C3 01 00

ADD - Add
05 iw ADD AX,imm16 Add imm16 to AX
05 id ADD EAX,imm32 Add imm32 to EAX
81 /0 iw ADD r/m16,imm16 Add imm16 to r/m16
81 /0 id ADD r/m32,imm32 Add imm32 to r/m32
83 /0 ib ADD r/m16,imm8 Add sykn-extendid imm8 to r/m16
83 /0 ib ADD r/m32,imm8 Add sykn-extendid imm8 to r/m32 Интересно, а как проц x86 код выполняет, может быть там какая-то особая нигия недюжая логика?

Да нет там никакой нигии. В 1 байте команды всегда присутствует код операции, по которому легко определить, сколько байт в команде, и какие из них являются данными (для загрузки в регистр, например).
Опять же, если, например, в коде программы встречаются блоки данных, то перед ними, как правило, должна быть команда возврата или перехода. Или команда обмена регистровой пары со счетчиком команд.
Иногда программисты специально запутывали свои программы для усложнения взлома.
Типичный прием: где-то в начале или середине подпрограммы грузим какое - то значение в одну из регистровых пар. Потом в другом месте (чтобы было неочевидно), кладем эту пару на стек. Когда же в конце подпрограмма делает возврат, она возвращается не туда, откуда была вызвана, а совершенно в другое место. При желании и это легко находится простым анализом обращений к стеку в теле подпрограммы. При желании все решаемо. Просто при больших обьемах иногда проще свое написать, чем ковырять чужое. Но, например, для обхода защит, или изменения некоторых параметров, когда нужен не весь код, а лишь его кусочек, под MSDOS особых проблем не возникало.

В 80х годах, когда я сделал свою первую персоналку, из за нехватки готовых программ и собственного опыта, мне приходилось дизассемблировать и переделывать программы для 8080. Естественно, программу - дизассемблер тоже пришлось писать самому, на Бэйсике Микро-80. Эта была моя вторая серьезная программа (первая - полный рассчет силового трансформатора по задаваемым параметрам). Довольно быстро этот дизассемблер оброс дополнительными функциями и возможностями. Например, позволял перекомпилировать программу для загрузки по другим адресам, автоматически пересчитывая все адреса вызовов и переходов, отыскивать в коде куски текста, или вызова какой - нибудь подпрограммы из разных мест, замену или вставку кусков кода с пересчетом всех адресов, проставлял метки в дизассемблированном листинге, и многое другое. Несколько лет я ее развивал и наращивал, превратив в мощный инструмент. Текст этой программы на Бэйсике насчитывал потом уже несколько тысяч строк.
С помощью этой программы, например, я дизассемблировал и переделывал операционки CM/M-80 и MicroDOC.
Занимался иногда дизассемблированием и позже, уже для Х86. Так что при желании, то, что сделано одним программистом, другой завсегда сломать сможет. Просто ему это не всегда нужно.

Так как микроконтроллеры AVR имеют Гарвардскую архитектуру, для каждого типа памяти SRAM, FLASH, EEPROM у них выделено отдельное адресное пространство. Каждая из этих типов памяти имеет собственное специальное назначение. Если данные не изменяются в течение работы программы, то они должны быть сохранены в памяти либо FLASH, либо EEPROM. В другом случае, если данные переменной часто используются и постоянно изменяются, то такая переменная должна быть сохранена в памяти SRAM. Где какие данные разместить - решает разработчик программы. Важное правило, которое следует учитывать - работа с разными типами памяти требует разных операций и способов доступа.

Обычно когда на C просто определяют переменную наподобие int a, компилятор автоматически помещает её в SRAM. Но если Вы захотите, то можете разместить константы в памяти EEPROM и даже в памяти FLASH. Для памяти SRAM для размещения переменных не нужно указывать никакого специального синтаксиса, но для EEPROM и FLASH нужно использовать специальные методы для декларации переменных.

Несмотря на указание const при декларации констант, компилятор все равно для их хранения использует ОЗУ (при старте программы они просто копируются из flash в RAM). Это несомненно полезно с точки зрения быстродействия кода, но для программ, активно использующих ОЗУ и/или имеющих большой объем констант (например, строковых), памяти ОЗУ может оказаться недостаточно. Обойти проблему позволяет атрибут PROGMEM и подпрограммы для работы с данными из flash. Их можно использовать, если включить заголовочный файл avr\pgmspace.h.

Функции заголовка подробно описаны в C:\WinAVR-20090313\doc\avr-libc\avr-libc-user-manual\pgmspace_8h.html. Принцип работы gcc, описание проблемы подробно описаны в файле C:\WinAVR-20090313\doc\avr-libc\avr-libc-user-manual\pgmspace.html. Далее дан его почти дословный перевод.

[Данные в памяти программ]

Многие микроконтроллеры AVR оборудованы недостаточым количеством памяти RAM для хранения данных и констант, однако они имеют в своем распоряжении гораздо больший объем памяти программ (flash). Во flash вполне могли бы поместиться константы, что сэкономит драгоценное место в RAM. Однако микроконтроллеры AVR имеют гарвардскую архитектуру, в которой четко разделены память программ (flash) и память данных (RAM), и каждая имеет свое отдельное адресное пространство. Имеется связанная с этим некоторая проблема, чтобы сохранять данные констант во flash, и затем считывать эти данные в программе AVR.

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

Некоторые компиляторы C (например IAR Embedded Workbench for AVR) используют нестандартные ключевые слова, либо расширяют стандартный синтаксис. Набор инструментов WinAVR/gcc используют другой способ.

Компилятор GCC имеет специальное ключевое слово __attribute__, которое используется для подсоединения различных атрибутов к функциям, определениям, переменным и типам. Это ключевое слово сопровождается спецификацией атрибута в двойных круглых скобках. В AVR GCC имеется специальный атрибут progmem. Он используется при декларации данных, и говорит компилятору поместить данные в памяти программ (flash).

Библиотека AVR-Libc предоставляет простой макрос PROGMEM, который задает синтаксис GCC-атрибута progmem. Эта макрокоманда была создана для удобства конечного пользователя, как мы увидим далее. Макрос PROGMEM задан в заголовочном файле < avr/pgmspace.h >. Поскольку сложно модифицировать GCC для создания нового расширения синтаксиса C, вместо этого avr-libc имеет макросы для получения данных из flash. Они также размещены в заголовке < avr/pgmspace.h >.

[О ключевом слове const]

Многие пользователи полагают, что использование ключевого слова const декларирует размещение данных в памяти программ (flash). Это происходит из-за неверного понимания назначения ключевого слова const.

Ключевое слово const говорит компилятору, что данные "только для чтения", и не более того. Это упрощает для компилятора некоторые преобразования, и предотвращает некорректное использование этих переменных. Например, const используется многими функциями в качестве модификатора типа параметра. Это говорит компилятору, что функция будет использовать этот параметр только для чтения, и не будет изменять содержимое параметра.

Таким образом, const всего лишь указывает на метод использования данных, и совсем не говорит о том, где должны эти данные храниться. Если это слово использовать как средство определить хранение данных, то мы окажемся в проигрыше, так как это изменит его семантику в других ситуациях, например в параметре функции.

[Сохранение данных в памяти программ и получение их оттуда]

Предположим, у Вас есть некоторые глобальные данные:

И далее код будет получать эти данные, например так:

Теперь Вы хотите сохранить данные в памяти программ (flash). Используйте макрос PROGMEM и поместите его в декларацию переменной, но перед инициализатором:

Теперь Ваши данные хранятся в памяти программ. Можно скомпилировать, слинковать, и проверить карту памяти - массив mydata будет лежать в правильной секции.

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

Решение проблемы довольно простое. Сначала нам надо получить адрес необходимых данных. Он равен &(mydata[i][j]). После этого можно использовать макрос для чтения данных из памяти программ по этому адресу:

Имеются различные макросы pgm_read_* для чтения данных разного типа и размера. Все они принимают адрес, указывающий на память программ (flash), и возвращают данные, сохраненные по этому адресу. Макросы обеспечивают для этого генерацию корректного кода.

[Сохранение строк в памяти программ и получение их оттуда]

Предположим, у нас есть массив строк:

Теперь добавляем макро PROGMEM и ключевое слово const:

Верно? Нет! К сожалению, атрибуты GCC затрагивают только объявление, к которому они присоединены. В этом случае мы действительно поместили переменную string_table, т. е. сам массив, в память программ, но не сами строки. Строки так и остались в памяти данных (RAM), что наверное не совсем то, то Вы хотели. Чтобы поместить строки во flash, нужно явно объявить каждую строку:

И потом использовать новые символы в массиве:

Теперь мы разместили массив string_table во flash, и массив string_table является массивом указателей на строки. Каждый указатель при этом указывает на строку во flash, где строка и хранится.

Например, Вы хотите скопировать строку из flash в буфер RAM (например в автоматическую переменную внутри функции, расположенную в стеке). Для это нужно сделать следующее:

Смысл приведенного кода очевиден - получение данных из массива происходит через указатель, выбираемый как 16-битное беззнаковое целое макросом pgm_read_word. Далее строка копируется функцией strcpy_P. Имеется множество функций для манипуляции строками в памяти программ с индексом _P, работающих так же, как и обычные строковые функции. Все эти функции с индексом _P также определены в заголовке <avr/pgmspace.h>.

[Предостережение]

Макрос и функции, используемые для получения данных из flash, генерируют некоторый дополнительный код, который больше по объему, чем код доступа к памяти RAM. Таким образом, это создает дополнительный расход памяти программ и замедление работы кода. Этот дополнительный расход и замедление достаточно малы, поэтому выигрыш при размещении данных во flash получается значительный. Однако об этом необходимо знать, чтобы при необходимости минимизировать количество обращений к памяти внутри одной функции и/или цикла. В этом может помочь поучительный просмотр дизассемблированного кода компилятора.

Я не очень большой фанат компании Microsoft, однако следует признать, что они сделали действительно потрясающую среду разработки Visual Studio. Особенно мне нравится в Visual Studio фича intellisense, которая автоматически дает подсказки по именам членов классов, функциям и полям структур, и для проектов AVR большого размера действительно предпочтительнее использовать Visual Studio IDE в сравнении с простым текстовым редактором.

Здесь приведена краткая инструкция (перевод статьи [1]) - как настроить среду Visual Studio 2008/2010 для использования тулчейна WinAVR и компилирования Ваших программ AVR для получения файлов в формате Intel Hex [2] (прошивка кода firmware микроконтроллера). Этот формат подходит для загрузки программы в память AVR/Arduino с использованием AVRDUDE и/или другого Вашего программатора (например, USBasp, AVRISP-mkII, JTAGICE mkII [3]).

Примечание: предполагается, что у Вас уже установлены тулчейн AVR GCC (в составе пакета WinAVR или Atmel Studio), и конечно же среда разработки Microsoft Visual Studio.

[Шаг 1. Создание Makefile-проекта]

Запустите Visual Studio, зайдите в меню Файл -> Создать -> Проект. выберите раздел Установленные шаблоны -> Visual C++ -> Проект, использующий makefile:

AVR-Visual-Studio-create-makefile-project-1

Введите имя проекта (Имя:), выберите папку, где будет расположен каталог проекта (Расположение:), уберите галочку "Создать каталог для решения", кликните OK.

Запустится мастер настройки проекта (см. скриншоты).

AVR-Visual-Studio-create-makefile-project-2

Настройте в окне "Параметры конфигурации отладки" следующие опции:

1. Командная строка построения: make

2. Команды очистки: make clean

3. Командная строка перестроения: make all

Примечание: можно ввести список команд, по одной в строке списка. В качестве команды перестроения здесь введены 2 команды, которые будут выполнены друг за другом:

AVR-Visual-Studio-edit-project-options-3

4. Вывод (для отладки): имя для выходного файла прошивки. Обязательно укажите расширение файла *.hex (GenericHID.hex, к примеру).

5. Путь поиска включений: %AVR32_HOME%\avr\include

Примечание: здесь %AVR32_HOME% это переменная окружения, в которой задан каталог установки WinAVR (например, C:\WinAVR-20100110) или тулчейна из Atmel Studio (например, c:\Program Files\Atmel\AVR Tools\AVR Toolchain). Вместо переменной окружения можно указать просто реальный путь до тулчейна.

AVR-Visual-Studio-create-makefile-project-3

Кликните Далее. В окне "Параметры конфигурации выпуска" поставьте галочку "Как в конфигурации отладки". Кликните Готово.

AVR-Visual-Studio-create-makefile-project-4

[Шаг 2. Сконфигурируйте проект]

Среда Visual Studio автоматически создала для Вас пустой makefile-проект. Теперь его нужно немного настроить, чтобы можно было начать писать программу для AVR.

Сделайте правый клик на названии проекта (myAVRproj) в дереве Обозревателя решений, и выберите в контекстном меню Свойства. Откроется окно редактирования свойств проекта с активной конфигурацией Debug.

AVR-Visual-Studio-edit-project-options-1

В разделе Свойства конфигурации -> Общие из выпадающего списка "Поддержка общеязыковой среды выполнения (CLR)" (Common Language Runtime Support (/clr)) выберите вариант Поддержка общеязыковой среды выполнения (CLR). Включение этой опции предоставляет изящную поддержку со стороны Intellisense.

Перейдите в раздел Свойства конфигурации -> NMake и убедитесь, что введенные здесь значения соответствуют необходимым командам make для сборки, очистки и перестроения (часто проекты поставляются с готовым Makefile, и команды в них могут отличаться). Также проверьте имя выходного hex-файла и убедитесь, что пути поиска включаемых файлов соответствуют ожидаемым или добавлены в соответствующие поле ввода. В строке ввода может быть несколько путей поиска, отделенных друг от друга точкой с запятой ';'. Если что-то не так, то исправьте.

AVR-Visual-Studio-edit-project-options-2

[Шаг 3. Создание и добавление Makefile]

Создайте файл Makefile для проекта, как Вы это обычно делаете. Лично я предпочитаю использовать готовые Makefile, которые генерирует система AVR Studio, или беру готовый Makefile из разных опубликованных AVR-проектов. Например, множество проектов с отличными Makefile можно найти в составе библиотек V-USB и LUFA [4]. В этом примере я буду использовать готовый Makefile проекта USB HID устройства из библиотеки LUFA-140928. Сам проект и его makefile находятся в папке Demos\Device\ClassDriver\GenericHID. Сделайте копию содержимого этой папки в папку Вашего проекта, который Вы только что создали. В моем примере папка проекта находится в каталоге c:\TEMP\myAVRproj (у Вас это может быть любой другой каталог на диске).

AVR-Visual-Studio-add-makefile-1

Файл Makefile обычно должен быть расположен в том же каталоге, где находятся компилируемые файлы исходного кода.

Перед использованием makefile проверьте все его опции, чтобы они соответствовали Вашему компилируемому проекту. Опции makefile редактируются простым текстовым редактором. Здесь я рассмотрю в качестве примера настройку опций для микроконтроллера AT90USB162 и тактовой частоты 16 МГц (макетная плата AVR-USB162).

LUFA_PATH. здесь должен быть указан полный или относительный путь до каталога lufa-LUFA-140928/LUFA. Пример:

MCU. Здесь нужно указать тип микроконтроллера. Название микроконтроллера нужно вводить маленькими буквами. Пример:

BOARD. Здесь указывается символическое название целевого устройства, для которого компилируется проект. Для макетной платы это MICROSIN162:

F_CPU. Здесь указывается тактовая частота микроконтроллера в Герцах. Она зависит от установленного кварцевого резонатора и коэффициента деления прескалера AVR. Для приложений устройств USB на микроконтроллере AT90USB162 допустимы тактовые частоты ядра 8 или 16 МГц. Пример установки тактовой частоты 16 МГц:

После того, как Вы скопировали файл makefile в каталог проекта, добавьте его в каталог Файлы ресурсов проекта Visual Studio. Для этого в Обозревателе решений сделайте правый клик на папке Файлы ресурсов в дереве проекта, и выберите Добавить -> Существующий элемент, и затем в открывшемся диалоге выбора файла выберите файл makefile проекта и кликните на кнопку Добавить.

AVR-Visual-Studio-add-makefile-2

Как вариант можно просто перетащить в Проводнике файл makefile в папку проекта Файлы ресурсов.

После этого будут работать команды меню Построение -> Очистить решение, Построение -> Построить решение (F7). Однако для удобства редактирования модулей кода их следует добавить в проект.

[Шаг 4. Добавление файлов исходного кода]

Перетащите файлы исходного кода с расширением *.c (для нашего примера Descriptors.c, GenericHID.c) в папку "Файлы исходного кода" Обозревателя решений. Заголовочные файлы с расширением *.h (Descriptors.h, GenericHID.h) перетащите в папку "Заголовочные файлы" Обозревателя решений.

AVR-Visual-Studio-add-source-files

[Шаг 5. Перепрошивка микроконтроллера]

Теперь для полного счастья осталось настроить функцию перепрограммирования микроконтроллера прямо из среды Visual Studio. Для этого через меню Сервис -> Внешние инструменты нужно добавить запуск утилиты для программатора [3]. В этом примере вместо программатора я буду использовать встроенный загрузчик (USB bootloader) Atmel DFU, а в качестве утилиты программирования буду использовать утилиту командной строки Flip DFU (batchisp.exe [5]).

Зайдите в меню Сервис -> Внешние инструменты, нажмите кнопку Добавить.

AVR-Visual-Studio-create-Flip-DFU-tool1

Для добавленного пункта меню Сервис отредактируйте следующие параметры:

Название: введите Flip DFU (можно ввести произвольное имя).


Ассемблер считается сложным языком программирования. Хотя, припоминая, к слову, функциональное программирование, где нет ни переменных, ни циклов, а вместо них одни рекурсии и функции, я бы поспорил. Но, мне кажется, что я знаю причину такого мнения. Если взять рядовую книжку по предмету, то прежде чем написать свой blink, сперва придется прочитать добрую сотню страниц, если не больше, про архитектуру, регистры, шины, порты и т.д. Не самое легкое чтиво, надо сказать. Взяв для сравнения книжку Кернигана&Ритчи мы найдем "Hello World!" на первой же странице. Весомый аргумент в пользу Си, на мой взгляд.

В предыдущем посте я упоминал, что размер прошивки с программой Blink написанной на Си для ATtiny45 составляет 82 байта. В AVR каждая инструкция вместе с аргументом занимает два байта, т.е. одно слово. 82 делим на 2, получаем 41 ассемблерная инструкция. Наверно, должен сразу оговориться. Если вы не получаете за это деньги, то изучать Ассемблер нет никакого практического смысла, языка Си хватит "за глаза" для написания своих прошивок. НО. иногда желание заглянуть "под капот" бывает слишком сильным;)

Если уж экспериментировать с ассемблером, то для начала, разумно будет выбрать чип попроще. Мой выбор пал на ATtiny13. Его флеш-память составляет всего 1 Кбайт. Опять же 1024 делим пополам, получаем размер программы до 500 инструкций, что для ассемблера совсем немного. По правде говоря, многие устройства работают на этом чипе, на мой взгляд один из самых популярных микроконтроллеров. По счастливой случайности, распиновка ATtiny13 совпадает с распиновкой ATtiny45, следовательно и исходник blink.c не меняется:

Ах, чуть не забыл. Про книжки по ассемблеру AVR.

Как видно, для ATtiny13 blink весит уже всего 68 байт против 82 для ATyiny45. Заглядываем под капот:

Итого 34 команды если пересчитаете нумерованые строки. т.е 68/2, все сходится. Сначала содержимое скорее всего не очень понятно, поэтому для внесения ясности, будет кстати вспомнить, что у нас имеется исходник программы и объектный файл. Запустив avr-objdump с опцией -S мы получим "смешаный код". Исходник и соответствующую трансляцию на ассемблере:

Прямо говоря не густо, ну чтож бывает. Обратите внимание на блок между main и exit, выделенный зеленым. Это и есть наша программа Blink на AVR Assembler. Все что выше зеленого блока, это таблица векторов прерываний, которая по спецификации должна идти перед программой. Это т.н. "шапка".

Теперь если вдуматься, то получим следующую структуру программы:



Как видете, все сводится к дюжине команд. В AVR есть две группы регистров: регистры общего назначения (РОН) и регистры ввода-вывода (РВВ).


r25,24,r18 и т.д. это регистры общего назначения.
0х17 и 0х18 это регистры ввода-вывода DDRB и PORTB соответственно.
sbi - устанавливает бит РВВ,
ldi - присваивает значение РОН,
in - копирует значение РВВ в РОН,
out - обратная операция,
eor - исключающее или,
subi - вычитание числа из РОН,
sbci - вычитание с флагом переноса,
brne - переход по условию: если результат предыдущей опереции не ноль,
rjmp - относительный переход. В качестве операнда смещение относительно текущего адреса в байтах

Наиболее интересный код здесь во второй фигурной скобке, который реализует задержку по времени. Этот алгоритм подробно разобран в книге Юрия Ревича, глава пятая "задержка". Только берите второе издание книги, в первом содержится опечатка: используется несуществующая команда suci вместо subi.

Вкратце. Нам нужно выполнить задержку на заданное время, мы знаем сколько времени выполняется каждая команда. Задержка выполняется последовательным вычитаним еденицы из числа до достижении нуля. комбинация команд: (subi + sbci + sbci) реализует декремент трехбайтного числа. Сначала вычитается младший байт, если он обнуляется, то вычитается еденица из среднего байта, если и он обнуляется от вычитается еденица из старшего байта. Если старший байт доходит до нуля, то условие перехода для оператора brne перестает выполнятся и программа выходит из цикла. В книге Юрия Ревича, вместо brne используется brcc. При этом операторе, цикл выполняет на одну итерацию больше, т.е. пока из нуля не вычитают еденицу.

Теперь считаем. Частота микроконтроллера 1 000 000 тактов в секунду. Цикл вычитания трехбайтного числа выполняется за: sbui, sbci - по одному такту, brne - два такта. Итого пять тактов. Т.е. надо вычитать число 1000000/5.

Смотрим на программу. Младший байт в r18 - 0x3F, средний байт в r19 - 0х0D, старший байт в r24 - 0x03. Итоговое число - 0x030D3F

Читайте также: