Pull to refresh

Тюнинг toolchain для Arduino для продолжающих

Reading time13 min
Views9.2K
Давным-давно случилось мне поработать над проектом с Arduino, где были довольно специфические требования к предсказуемости генерации кода, а работать с чёрным ящиком местами раздражало. Так родилась идея несколько поднастроить процесс сборки и внедрить некоторые дополнительные шаги при сборке.

Как известно, язык Wiring на самом деле является полноценным C++, из которого убрана поддержка исключений ввиду их тяжеловесности для среды выполнения и спрятаны некоторые тонкости, связанные со структурой программы, такие как реализация функции main, а разработчику отданы функции setup и loop, которые вызываются из main.

А это означает, что можно пользоваться плюшками C++, даже если о них никто не рассказал.

Всё описанное в статье относится к версиям среды Arduino как минимум от 1.6. Я делал всё под Windows, но для для Linux/MacOS подходы остаются теми же, просто меняются технические детали.

Итак, приступаем.

В среде версии 1.6 по умолчанию используется компилятор GCC версии 4.8, в котором по умолчанию не включена поддержка С++11, но нам же хочется auto, varadic templates и прочие плюшки, не правда ли? А в среде версии 1.8 компилятор уже версии 4.9, поддерживающий С++11 по умолчанию, но и его можно заставить использовать С++14 с его rvalue reference, константными выражениями, разделителями разрядов, двоичными литералами и прочими радостями.

Чтобы добавить поддержку более новых стандартов, чем включены по умолчанию, надо в каталоге установки Arduino открыть файл hardware\arduino\avr\platform.txt, содержащий настройки компиляции, найти там параметр compiler.cpp.flags и указать желаемый стандарт языка: -std=gnu++11 заменить на -std=gnu++14. Если параметра нету — добавить, например в конце строки.

После сохранения файла можно попробовать пересобрать скетч — среда подхватит новые настройки сразу же.

Здесь же можно изменить настройки оптимизации, например вместо более компактного кода изменением опции -Os на -Ofast включить генерацию более быстрого кода.

Эти настройки глобальные, поэтому применимы в рамках всех проектов.

Дальше я решил получить более детальный контроль над результатом работы компилятора и добавил некоторый аналог postbuild-событий. Для этого в тот же файл надо добавить параметр recipe.hooks.postbuild.0.pattern, значение которого должно содержать имя файла, который будет запущен после сборки, и аргументы командной строки. Я присвоил ему значение "{compiler.path}stat.bat" "{build.path}/{build.project_name}". Поскольку такого файла пока нет, его надо создать в каталоге hardware\tools\avr\bin (с именем, очевидно, stat.bat).

Напишем туда следующие строки:

"%~dp0\avr-objdump.exe" -d -C %1.elf > %1.SX
"%~dp0\avr-nm.exe" %1.elf -nCS > %1.names

Первая строка запускает декомпиляцию уже собранной и оптимизированной программы с помощью objdump, создавая выходной файл с именем проекта и дополнительным расширением .SX (выбор произвольный, но будем считать, что S — традиционно ассемблер, а X указывает на факт некоторого преобразования).

Вторая строка извлекает символьные имена областей памяти запуском команды nm и формирует отчёта в файле с дополнительным расширением .names, при этом сортировка выполняется по адресам.

Здесь надо ещё добавить, что сборка ведётся во временном каталоге, и чтобы его посмотреть, нужно зайти в настройки Arduino и включить чекбокс «Показать подробный вывод» -> «Компиляция», после чего запустить эту самую компиляцию и посмотреть, где идёт сборка. В моём случае (IDE версии 1.8.5) это был каталог вида %TEMP%\arduino_build_[random].

Читать дизассемблированный код в обычной жизни — удовольствие сомнительное, но может кому-то понадобится (мне доводилось).

Файл имён же в целом более полезен. В спойлере — тестовый скетч, с которым выполнялись опыты:

Тестовый скетч
int counter = 1;

void test(auto&& x) {
  static unsigned long time;
  
  long t = millis();
  Serial.print(F("Counter: "));
  Serial.println(counter);
  Serial.print(F(" At "));
  Serial.print(t);
  Serial.print(F(" value is "));
  Serial.println(x);
  Serial.print(F("Time between iterations: "));
  Serial.println(t - time);
  Serial.println();
  time = t;
}

void setup() {
  Serial.begin(9600);
}

void loop() {  
  delay(100);
  test(digitalRead(A0));
}


Получившийся файл имён целиком тоже под спойлером:

Файл имён целиком

w serialEvent()
00000000 W __heap_end
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 W __vector_default
00000000 T __vectors
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
00000068 T __trampolines_end
00000068 T __trampolines_start
00000068 0000001a t test(int)::__c
00000082 0000000b t test(int)::__c
0000008d 00000005 t test(int)::__c
00000092 0000000a t test(int)::__c
0000009c 00000014 T digital_pin_to_timer_PGM
000000b0 00000014 T digital_pin_to_bit_mask_PGM
000000c4 00000014 T digital_pin_to_port_PGM
000000d8 0000000a T port_to_input_PGM
000000e2 T __ctors_start
000000e4 T __ctors_end
000000e4 T __dtors_end
000000e4 T __dtors_start
000000e4 W __init
000000f0 00000016 T __do_copy_data
00000106 00000010 T __do_clear_bss
0000010e t .do_clear_bss_loop
00000110 t .do_clear_bss_start
00000116 00000016 T __do_global_ctors
00000134 T __bad_interrupt
00000134 W __vector_1
00000134 W __vector_10
00000134 W __vector_11
00000134 W __vector_12
00000134 W __vector_13
00000134 W __vector_14
00000134 W __vector_15
00000134 W __vector_17
00000134 W __vector_2
00000134 W __vector_20
00000134 W __vector_21
00000134 W __vector_22
00000134 W __vector_23
00000134 W __vector_24
00000134 W __vector_25
00000134 W __vector_3
00000134 W __vector_4
00000134 W __vector_5
00000134 W __vector_6
00000134 W __vector_7
00000134 W __vector_8
00000134 W __vector_9
00000138 00000012 T setup
0000014a 000000d8 T loop
00000222 00000094 T __vector_16
000002b6 00000018 T millis
000002ce 00000046 T micros
00000314 00000050 T delay
00000364 00000076 T init
000003da 00000052 t turnOffPWM
0000042c 00000052 T digitalRead
0000047e 00000016 T HardwareSerial::available()
00000494 0000001c T HardwareSerial::peek()
000004b0 00000028 T HardwareSerial::read()
000004d8 000000b8 T HardwareSerial::write(unsigned char)
00000590 000000a4 T HardwareSerial::flush()
00000634 0000001c W serialEventRun()
00000650 00000042 T HardwareSerial::_tx_udr_empty_irq()
00000692 000000d0 T HardwareSerial::begin(unsigned long, unsigned char)
00000762 00000064 T __vector_18
000007c6 0000004c T __vector_19
00000812 00000014 T Serial0_available()
00000826 00000086 t _GLOBAL__sub_I___vector_18
000008ac 00000002 W initVariant
000008ae 00000028 T main
000008d6 00000058 T Print::write(unsigned char const*, unsigned int)
000008ff W __stack
0000092e 0000004a T Print::print(__FlashStringHelper const*)
00000978 00000242 T Print::print(long, int)
00000bba 00000016 T Print::println()
00000bd0 0000029a T Print::println(int, int)
00000e6a 0000013a T Print::println(unsigned long, int)
00000fa4 00000002 t __empty
00000fa4 00000002 W yield
00000fa6 00000044 T __udivmodsi4
00000fb2 t __udivmodsi4_loop
00000fcc t __udivmodsi4_ep
00000fea 00000004 T __tablejump2__
00000fee 00000008 T __tablejump__
00000ff6 T _exit
00000ff6 W exit
00000ff8 t __stop_program
00000ffa A __data_load_start
00000ffa T _etext
00001016 A __data_load_end
00800100 D __data_start
00800100 00000002 D counter
00800102 00000010 V vtable for HardwareSerial
0080011c B __bss_start
0080011c D __data_end
0080011c D _edata
0080011c 00000004 b test(int)::time
00800120 00000001 b timer0_fract
00800121 00000004 B timer0_millis
00800125 00000004 B timer0_overflow_count
00800129 0000009d B Serial
008001c6 B __bss_end
008001c6 N _end
00810000 N __eeprom_end



Как это читать? Давайте разбираться.

В контроллерах AVR две независимые области памяти: память программы и память данных. Соотвественно, указатель на код и указатель на данные — это не одно и то же. Про EEPROM пока молчу, там отдельный разговор про адресацию. В дампе память программы расположена по смещению 0 и с него начинается выполнение, и там же располагается таблица векторов прерываний, по 4 байта на элемент. В отличие от x86, каждый вектор прерываний содержит не адрес обработчика, а инструкцию, которую надо выполнить. Обычно (я не видел других вариантов) — jmp, то есть переход в нужный адрес. К векторам прерываний мы ещё потом ненадолго вернёмся, а пока смотрим дальше.

Память данных состоит из двух частей: регистровый файл со смещения 0 и данные в оперативной памяти (SRAM) со смещения 0x100. Когда выводится дамп памяти, то к адресам SRAM добавляется смещение 0x00800000, которое ничего не значит по сути, но просто удобно. Отмечу также, что смещение EEPROM имеет значение 0x00810000, но нам это не нужно.

Давайте начнём с данных, тут всё немного проще и привычнее, а потом вернёмся к коду. Первый столбец — смещение, второй — размер, дальше тип (пока не обращаем внимание), дальше до конца строки — имя.

Идём на смещение 0x00800100 и видим там записи:

00800100 D __data_start
00800100 00000002 D counter
00800102 00000010 V vtable for HardwareSerial
0080011c D __data_end

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

Дальше идём (пускай вас не смущает, что __bss_start и __data_end идут в произвольном порядке — они имеют общий адрес).

0080011c B __bss_start
0080011c 00000004 b test(int)::time
00800120 00000001 b timer0_fract
00800121 00000004 B timer0_millis
00800125 00000004 B timer0_overflow_count
00800129 0000009d B Serial
008001c6 B __bss_end

Это секция неинициализированных данных (аналог .bss). Она даётся даром, поскольку заполнена нулевыми байтами. Здесь несколько вспомогательных переменных, имена который начинаются с timer0_, глобальный объект Serial и наша статическая локальная переменная.

Маленькое лирическое отступление: обратите внимание, что объект Serial изначально пуст, и он будет проинициализирован, когда на нём будет вызван метод begin. Для этого есть несколько причин. Во-первых, он может вообще не использоваться (тогда он вообще не будет создан и место под него выделяться не будет), а во-вторых, мы же знаем, что порядок инициализации глобальных объектов не определён, и до входа в main строго говоря контроллер не инициализирован корректно.

По более высоким адресам располагается стек. Если объём памяти контроллера 2 килобайта (платы на базе процессора atmega328), то последний адрес будет 0x008008FF. С этого адреса начинается стек и растёт он в сторону уменьшения адресов. Нет никаких барьеров, и в принципе стек может наползти на переменные, испортив их. Или данные могут испортить стек — как повезёт. В этом случае ничего хорошего не произойдёт. Можно это назвать неопределённым поведением, но лучше всегда в памяти оставлять достаточно места для стека. Сколько — сложно сказать. Чтобы хватило на локальные переменные и кадры стека всех вызовов плюс для самого тяжёлого обработчика прерывания со всеми вложенными вызовами. Я, честно говоря, более-менее честного решения относительно того, как диагностировать использование стека, не нашёл.

Давайте вернёмся к коду. Когда я говорил, что там хранится код, я немного лукавил. На самом деле там могут храниться и данные, доступные только для чтения, но извлекаются они оттуда специальной инструкцией процессора LPM. Так вот, константные данные можно размещать там и размещать даром, потому как на их инициализацию не тратится время. Обратите внимание на макросы F(...) в коде. Они как раз объявляют строки размещёнными в программной памяти и дальше делает немножко магии. Не будем вдаваться в подробности магии, а поищем их в памяти. Такие макросы идут в функции test, и в дампе мы их увидим как

00000068 0000001a t test(int)::__c
00000082 0000000b t test(int)::__c
0000008d 00000005 t test(int)::__c
00000092 0000000a t test(int)::__c

Несмотря на то, что они имеют одинаковые имена, они находятся по разным адресам. Потому что макрос F(...) создаёт область видимости в фигурных скобках, и переменные (точнее константы) друг другу не мешают. Если посмотреть дизассемблерный дамп, то это и будут адреса строк в программной памяти.

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

Дамп памяти пустого скетча
w serialEventRun()
00000000 W __heap_end
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 a __tmp_reg__
00000000 W __vector_default
00000000 T __vectors
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
00000001 a __zero_reg__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003d a __SP_L__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003e a __SP_H__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
0000003f a __SREG__
00000068 T __ctors_end
00000068 T __ctors_start
00000068 T __dtors_end
00000068 T __dtors_start
00000068 W __init
00000068 T __trampolines_end
00000068 T __trampolines_start
00000074 00000010 T __do_clear_bss
0000007c t .do_clear_bss_loop
0000007e t .do_clear_bss_start
0000008c T __bad_interrupt
0000008c W __vector_1
0000008c W __vector_10
0000008c W __vector_11
0000008c W __vector_12
0000008c W __vector_13
0000008c W __vector_14
0000008c W __vector_15
0000008c W __vector_17
0000008c W __vector_18
0000008c W __vector_19
0000008c W __vector_2
0000008c W __vector_20
0000008c W __vector_21
0000008c W __vector_22
0000008c W __vector_23
0000008c W __vector_24
0000008c W __vector_25
0000008c W __vector_3
0000008c W __vector_4
0000008c W __vector_5
0000008c W __vector_6
0000008c W __vector_7
0000008c W __vector_8
0000008c W __vector_9
00000090 00000002 T setup
00000092 00000002 T loop
00000094 00000002 W initVariant
00000096 00000028 T main
000000be 00000094 T __vector_16
00000152 00000076 T init
000001c8 T _exit
000001c8 W exit
000001ca t __stop_program
000001cc A __data_load_end
000001cc a __data_load_start
000001cc T _etext
000008ff W __stack
00800100 B __bss_start
00800100 D _edata
00800100 00000001 b timer0_fract
00800101 00000004 B timer0_millis
00800105 00000004 B timer0_overflow_count
00800109 B __bss_end
00800109 N _end
00810000 N __eeprom_end

Немало, неправда ли?

А вот код:

Дизассемблированный код
/empty.cpp.elf:     file format elf32-avr

Disassembly of section .text:

00000000 <__vectors>:
   0:	0c 94 34 00 	jmp	0x68	; 0x68 <__ctors_end>
   4:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
   8:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
   c:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  10:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  14:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  18:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  1c:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  20:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  24:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  28:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  2c:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  30:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  34:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  38:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  3c:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  40:	0c 94 5f 00 	jmp	0xbe	; 0xbe <__vector_16>
  44:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  48:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  4c:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  50:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  54:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  58:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  5c:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  60:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>
  64:	0c 94 46 00 	jmp	0x8c	; 0x8c <__bad_interrupt>

00000068 <__ctors_end>:
  68:	11 24       	eor	r1, r1
  6a:	1f be       	out	0x3f, r1	; 63
  6c:	cf ef       	ldi	r28, 0xFF	; 255
  6e:	d8 e0       	ldi	r29, 0x08	; 8
  70:	de bf       	out	0x3e, r29	; 62
  72:	cd bf       	out	0x3d, r28	; 61

00000074 <__do_clear_bss>:
  74:	21 e0       	ldi	r18, 0x01	; 1
  76:	a0 e0       	ldi	r26, 0x00	; 0
  78:	b1 e0       	ldi	r27, 0x01	; 1
  7a:	01 c0       	rjmp	.+2      	; 0x7e <.do_clear_bss_start>

0000007c <.do_clear_bss_loop>:
  7c:	1d 92       	st	X+, r1

0000007e <.do_clear_bss_start>:
  7e:	a9 30       	cpi	r26, 0x09	; 9
  80:	b2 07       	cpc	r27, r18
  82:	e1 f7       	brne	.-8      	; 0x7c <.do_clear_bss_loop>
  84:	0e 94 4b 00 	call	0x96	; 0x96 <main>
  88:	0c 94 e4 00 	jmp	0x1c8	; 0x1c8 <_exit>

0000008c <__bad_interrupt>:
  8c:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>

00000090 <setup>:
  90:	08 95       	ret

00000092 <loop>:
  92:	08 95       	ret

00000094 <initVariant>:
  94:	08 95       	ret

00000096 <main>:
  96:	0e 94 a9 00 	call	0x152	; 0x152 <init>
  9a:	0e 94 4a 00 	call	0x94	; 0x94 <initVariant>
  9e:	0e 94 48 00 	call	0x90	; 0x90 <setup>
  a2:	80 e0       	ldi	r24, 0x00	; 0
  a4:	90 e0       	ldi	r25, 0x00	; 0
  a6:	89 2b       	or	r24, r25
  a8:	29 f0       	breq	.+10     	; 0xb4 <main+0x1e>
  aa:	0e 94 49 00 	call	0x92	; 0x92 <loop>
  ae:	0e 94 00 00 	call	0	; 0x0 <__vectors>
  b2:	fb cf       	rjmp	.-10     	; 0xaa <main+0x14>
  b4:	0e 94 49 00 	call	0x92	; 0x92 <loop>
  b8:	0e 94 49 00 	call	0x92	; 0x92 <loop>
  bc:	fb cf       	rjmp	.-10     	; 0xb4 <main+0x1e>

000000be <__vector_16>:
  be:	1f 92       	push	r1
  c0:	0f 92       	push	r0
  c2:	0f b6       	in	r0, 0x3f	; 63
  c4:	0f 92       	push	r0
  c6:	11 24       	eor	r1, r1
  c8:	2f 93       	push	r18
  ca:	3f 93       	push	r19
  cc:	8f 93       	push	r24
  ce:	9f 93       	push	r25
  d0:	af 93       	push	r26
  d2:	bf 93       	push	r27
  d4:	80 91 01 01 	lds	r24, 0x0101
  d8:	90 91 02 01 	lds	r25, 0x0102
  dc:	a0 91 03 01 	lds	r26, 0x0103
  e0:	b0 91 04 01 	lds	r27, 0x0104
  e4:	30 91 00 01 	lds	r19, 0x0100
  e8:	23 e0       	ldi	r18, 0x03	; 3
  ea:	23 0f       	add	r18, r19
  ec:	2d 37       	cpi	r18, 0x7D	; 125
  ee:	20 f4       	brcc	.+8      	; 0xf8 <__vector_16+0x3a>
  f0:	01 96       	adiw	r24, 0x01	; 1
  f2:	a1 1d       	adc	r26, r1
  f4:	b1 1d       	adc	r27, r1
  f6:	05 c0       	rjmp	.+10     	; 0x102 <__vector_16+0x44>
  f8:	26 e8       	ldi	r18, 0x86	; 134
  fa:	23 0f       	add	r18, r19
  fc:	02 96       	adiw	r24, 0x02	; 2
  fe:	a1 1d       	adc	r26, r1
 100:	b1 1d       	adc	r27, r1
 102:	20 93 00 01 	sts	0x0100, r18
 106:	80 93 01 01 	sts	0x0101, r24
 10a:	90 93 02 01 	sts	0x0102, r25
 10e:	a0 93 03 01 	sts	0x0103, r26
 112:	b0 93 04 01 	sts	0x0104, r27
 116:	80 91 05 01 	lds	r24, 0x0105
 11a:	90 91 06 01 	lds	r25, 0x0106
 11e:	a0 91 07 01 	lds	r26, 0x0107
 122:	b0 91 08 01 	lds	r27, 0x0108
 126:	01 96       	adiw	r24, 0x01	; 1
 128:	a1 1d       	adc	r26, r1
 12a:	b1 1d       	adc	r27, r1
 12c:	80 93 05 01 	sts	0x0105, r24
 130:	90 93 06 01 	sts	0x0106, r25
 134:	a0 93 07 01 	sts	0x0107, r26
 138:	b0 93 08 01 	sts	0x0108, r27
 13c:	bf 91       	pop	r27
 13e:	af 91       	pop	r26
 140:	9f 91       	pop	r25
 142:	8f 91       	pop	r24
 144:	3f 91       	pop	r19
 146:	2f 91       	pop	r18
 148:	0f 90       	pop	r0
 14a:	0f be       	out	0x3f, r0	; 63
 14c:	0f 90       	pop	r0
 14e:	1f 90       	pop	r1
 150:	18 95       	reti

00000152 <init>:
 152:	78 94       	sei
 154:	84 b5       	in	r24, 0x24	; 36
 156:	82 60       	ori	r24, 0x02	; 2
 158:	84 bd       	out	0x24, r24	; 36
 15a:	84 b5       	in	r24, 0x24	; 36
 15c:	81 60       	ori	r24, 0x01	; 1
 15e:	84 bd       	out	0x24, r24	; 36
 160:	85 b5       	in	r24, 0x25	; 37
 162:	82 60       	ori	r24, 0x02	; 2
 164:	85 bd       	out	0x25, r24	; 37
 166:	85 b5       	in	r24, 0x25	; 37
 168:	81 60       	ori	r24, 0x01	; 1
 16a:	85 bd       	out	0x25, r24	; 37
 16c:	ee e6       	ldi	r30, 0x6E	; 110
 16e:	f0 e0       	ldi	r31, 0x00	; 0
 170:	80 81       	ld	r24, Z
 172:	81 60       	ori	r24, 0x01	; 1
 174:	80 83       	st	Z, r24
 176:	e1 e8       	ldi	r30, 0x81	; 129
 178:	f0 e0       	ldi	r31, 0x00	; 0
 17a:	10 82       	st	Z, r1
 17c:	80 81       	ld	r24, Z
 17e:	82 60       	ori	r24, 0x02	; 2
 180:	80 83       	st	Z, r24
 182:	80 81       	ld	r24, Z
 184:	81 60       	ori	r24, 0x01	; 1
 186:	80 83       	st	Z, r24
 188:	e0 e8       	ldi	r30, 0x80	; 128
 18a:	f0 e0       	ldi	r31, 0x00	; 0
 18c:	80 81       	ld	r24, Z
 18e:	81 60       	ori	r24, 0x01	; 1
 190:	80 83       	st	Z, r24
 192:	e1 eb       	ldi	r30, 0xB1	; 177
 194:	f0 e0       	ldi	r31, 0x00	; 0
 196:	80 81       	ld	r24, Z
 198:	84 60       	ori	r24, 0x04	; 4
 19a:	80 83       	st	Z, r24
 19c:	e0 eb       	ldi	r30, 0xB0	; 176
 19e:	f0 e0       	ldi	r31, 0x00	; 0
 1a0:	80 81       	ld	r24, Z
 1a2:	81 60       	ori	r24, 0x01	; 1
 1a4:	80 83       	st	Z, r24
 1a6:	ea e7       	ldi	r30, 0x7A	; 122
 1a8:	f0 e0       	ldi	r31, 0x00	; 0
 1aa:	80 81       	ld	r24, Z
 1ac:	84 60       	ori	r24, 0x04	; 4
 1ae:	80 83       	st	Z, r24
 1b0:	80 81       	ld	r24, Z
 1b2:	82 60       	ori	r24, 0x02	; 2
 1b4:	80 83       	st	Z, r24
 1b6:	80 81       	ld	r24, Z
 1b8:	81 60       	ori	r24, 0x01	; 1
 1ba:	80 83       	st	Z, r24
 1bc:	80 81       	ld	r24, Z
 1be:	80 68       	ori	r24, 0x80	; 128
 1c0:	80 83       	st	Z, r24
 1c2:	10 92 c1 00 	sts	0x00C1, r1
 1c6:	08 95       	ret

000001c8 <_exit>:
 1c8:	f8 94       	cli

000001ca <__stop_program>:
 1ca:	ff cf       	rjmp	.-2      	; 0x1ca <__stop_program>


Задействовано прерывание таймера, 9 байт на обслуживание этого таймера, и некоторое количество инфраструктурного кода.

На сегодня, пожалуй, хватит. Надеюсь, кому-нибудь это пригодится.
Tags:
Hubs:
+15
Comments18

Articles

Change theme settings