Система команд AVR
Система команд AVR — 119 мнемоник ассемблера 8-битных микроконтроллеров ATtiny, ATmega, XMEGA и AVR Dx/Ex, из них 41 синоним. Для каждой команды ниже — что она делает, какие флаги меняет и сколько тактов выполняется на классических ATmega и ATtiny и на новых tinyAVR 0/1/2, megaAVR 0 и AVR Dx.
Обновлено Редакция GAW
Система команд AVR — набор машинных инструкций 8-битного RISC-ядра, общего для ATtiny, ATmega, XMEGA и современных серий AVR Dx и Ex. Ядро работает с 32 регистрами общего назначения (урезанное AVRrc — с 16), почти все команды занимают одно 16-битное слово и выполняются за 1–2 такта. Ниже — справочник по документу Microchip DS40002198C (редакция ноября 2024 года): 119 мнемоник с операндами, флагами и тактами, режимы адресации, примеры с подсчётом тактов и частые ошибки.
Коротко
- 32 регистра r0–r31 по 8 бит; пары r27:r26, r29:r28 и r31:r30 — 16-битные указатели X, Y и Z.
- Команды 16-битные, четыре из них занимают два слова: JMP, CALL, LDS и STS. Счётчик команд PC считает слова, а не байты.
- Такты на ATmega: логика и арифметика — 1 (ADIW, SBIW и MUL — 2), условный переход — 1 или 2, LD и ST — 2, LPM — 3, CALL, RET и RETI — 4 (5 при Flash больше 128 КБ).
- SREG — флаги I, T, H, S, V, N, Z и C. При входе в прерывание SREG автоматически не сохраняется.
- Константы принимают только r16–r31 (LDI, CPI, SUBI, SBCI, ANDI, ORI), ADIW и SBIW — только пары r25:r24, X, Y и Z.
- Шесть версий ядра: исходное AVR 1995 года, AVRe — классические ATtiny без умножения, AVRe+ — megaAVR, AVRxm — XMEGA, AVRxt — tinyAVR 0/1/2, megaAVR 0 и AVR Dx/Ex, AVRrc — ATtiny4/5/9/10/20/40/102/104 с 16 регистрами.
Архитектура ядра AVR
AVR построен по гарвардской схеме: у памяти программ (Flash) и памяти данных раздельные шины. Пока выполняется одна команда, следующая уже выбирается из Flash — этот одноступенчатый конвейер и даёт характерную для AVR производительность, близкую к 1 млн команд в секунду на каждый мегагерц тактовой частоты.
Flash организована 16-битными словами: 32 КБ памяти ATmega328P — это 16K слов. Поэтому счётчик команд, адреса в JMP и CALL и смещения в RJMP, RCALL и условных переходах считаются в словах, а указатель Z в командах чтения Flash (LPM, ELPM) содержит адрес байта. Путаница этих двух адресов — одна из самых частых ошибок, о ней ниже.
Арифметические и логические команды работают только с регистрами: АЛУ берёт оба операнда из регистрового файла и за один такт записывает результат обратно. Данные из SRAM сначала загружают в регистр командами LD или LDS, обрабатывают и сохраняют обратно через ST или STS — классическая схема load/store. У классических ATtiny и ATmega регистры r0–r31 отображены на первые 32 адреса памяти данных, так что до любого из них можно добраться и через указатель. У XMEGA и AVRxt регистровый файл лежит в отдельном адресном пространстве.
У классических AVR за регистрами в памяти данных идут 64 регистра ввода-вывода (I/O), а у кристаллов с большим числом периферии — ещё и расширенный I/O, после которого начинается SRAM. У каждого регистра из первых 64 два адреса: I/O-адрес для команд IN, OUT, SBI, CBI, SBIC и SBIS и адрес данных, на 0x20 больший, для LD, ST, LDS и STS. Например, PORTB у ATmega328P — это I/O 0x05 и адрес данных 0x25. У XMEGA и AVRxt пространство данных начинается прямо с регистров I/O, и оба адреса совпадают: у ATmega4809 регистры 0x00–0x3F доступны командам IN и OUT, а расширенный I/O занимает адреса 0x0040–0x0FFF.
Стек размещается в SRAM и растёт от старших адресов к младшим. Указатель стека SP — пара регистров SPH:SPL с I/O-адресами 0x3E и 0x3D. Даташиты требуют задать стек до первого вызова подпрограммы и до разрешения прерываний. У ATmega328P SP после сброса уже равен RAMEND (0x08FF), а у ATmega8A таблица регистров в даташите даёт начальное значение 0, хотя текст того же раздела называет RAMEND, — поэтому SP надёжнее загрузить явно. PUSH уменьшает SP на 1, а CALL, RCALL, ICALL и прерывание кладут в стек 2 байта адреса возврата (3 байта на кристаллах с 22-битным PC, например ATmega2560).
Регистр состояния SREG
SREG хранит признаки результата последней операции АЛУ и бит глобального разрешения прерываний. Флаги обновляются после каждой арифметической и логической команды, поэтому отдельное сравнение часто не нужно: после DEC сразу можно ставить BRNE.
| Флаг | Бит | Когда устанавливается | Где используется |
|---|---|---|---|
| C — перенос | 0 | перенос из бита 7 при сложении, заём при вычитании и сравнении, выдвинутый бит при сдвиге | многобайтная арифметика (ADC, SBC, CPC), сравнения без знака (BRLO, BRSH) |
| Z — ноль | 1 | результат равен нулю | BREQ, BRNE; в CPC, SBC и SBCI флаг Z только сбрасывается — так сравнивают многобайтные числа |
| N — минус | 2 | старший бит результата равен 1 | BRMI, BRPL |
| V — переполнение | 3 | результат знаковой операции не помещается в 8 бит, например 0x7F + 1 | BRVS, BRVC |
| S — знак | 4 | всегда N ⊕ V — истинный знак с учётом переполнения | сравнения со знаком: BRGE, BRLT |
| H — полуперенос | 5 | перенос из бита 3 в бит 4, при вычитании — заём | двоично-десятичная (BCD) арифметика |
| T — пользовательский бит | 6 | командами BST и SET | перенос одного бита между регистрами: BST и затем BLD |
| I — разрешение прерываний | 7 | командой SEI, на классических AVR — и RETI | при I = 0 не обслуживается ни одно маскируемое прерывание |
Варианты ядра и объём памяти
Microchip различает шесть версий 8-битного ядра (таблица 5-1 в DS40002198C). Машинный код совместим между ними, за редкими исключениями у AVRrc, но не каждая команда есть в каждом кристалле, а такты у новых ядер отличаются.
| Ядро | Где применяется | Чем отличается набор команд |
|---|---|---|
| AVR | ранние кристаллы, например ATtiny12 и ATtiny26 | исходный набор 1995 года: нет MOVW, JMP, CALL, SPM; LPM загружает только R0 |
| AVRe | классические ATtiny: ATtiny13A, ATtiny85, ATtiny2313 | добавлены MOVW, LPM Rd, Z(+), JMP, CALL, SPM, BREAK; умножения нет |
| AVRe+ | megaAVR: ATmega8A, ATmega328P, ATmega2560 | добавлены MUL, MULS, MULSU и дробные FMUL; у больших кристаллов — ELPM, EIJMP, EICALL |
| AVRxm | XMEGA | добавлены DES, XCH, LAS, LAC, LAT и SPM Z+; свои такты |
| AVRxt | tinyAVR 0/1/2, megaAVR 0, AVR DA, DB, DD, DU, EA, EB, SD | команды AVRe+, у AVR Dx, Ex и SD ещё SPM Z+; быстрее ST, PUSH, вызовы, SBI и CBI, медленнее LDS |
| AVRrc | ATtiny4, 5, 9, 10, 20, 40, 102, 104 | только 16 регистров r16–r31; нет ADIW, SBIW, MOVW, MUL, LPM, LDD, STD, JMP и CALL; LDS и STS однословные |
Внутри одного ядра набор команд зависит ещё и от объёма Flash — по приложению A справочника:
- JMP и CALL есть только у кристаллов с Flash больше 8 КБ. При 4K слов (8 КБ) RJMP и RCALL с диапазоном ±2K слов и так достают до любого адреса, поэтому у ATtiny13A, ATtiny85, ATmega8A, ATmega48 и ATmega88, а также у tinyAVR 0/1/2 с Flash до 8 КБ этих команд нет.
- ELPM нужна при Flash больше 64 КБ: указатель Z адресует только 64 КБ, старшие биты адреса берутся из регистра RAMPZ. Команда есть у ATmega128, ATmega1284P, ATmega2560, AVR128DA.
- EIJMP и EICALL — только у кристаллов с Flash больше 128 КБ и 22-битным PC: ATmega2560, ATmega2561 и XMEGA со 128 КБ основной Flash и больше (вместе с загрузочной секцией выходит больше 128 КБ), например ATxmega128A1U.
- BREAK нет у кристаллов с исходным ядром AVR (ATtiny11, 12, 15 и 26), у ATmega8, ATmega8A, ATmega8515 и ATmega8535, а также у ATtiny4, 5, 9, 10 и 20.
- SPM нет у tinyAVR 0/1/2 и megaAVR 0. У ATmega4809, например, Flash пишется через контроллер NVMCTRL, а буфер страницы заполняется обычными командами записи в память данных, куда отображена Flash.
Режимы адресации AVR
| Режим | Пример | Что адресует | Ограничения |
|---|---|---|---|
| Регистровый, один регистр | inc r16 | регистр Rd | — |
| Регистровый, два регистра | add r16, r17 | Rd и Rr, результат в Rd | — |
| Непосредственный | ldi r16, 0x3F | константа в коде команды | только r16–r31 |
| Прямой I/O | in r16, PINB | регистр I/O по адресу A | A = 0…63; битовые команды — A = 0…31 |
| Прямой в памяти данных | lds r16, 0x0100 | любой байт памяти данных | 2 слова |
| Косвенный | ld r16, X | байт по адресу из X, Y или Z | — |
| Косвенный с постинкрементом | ld r16, Y+ | байт по адресу, затем указатель + 1 | удобен для массивов |
| Косвенный с предекрементом | st -Z, r16 | указатель − 1, затем доступ | программный стек |
| Косвенный со смещением | ldd r16, Y+5 | байт по адресу Y + q | q = 0…63, только Y и Z |
| Константа из Flash | lpm r16, Z+ | байт Flash по адресу в Z | Z — адрес байта, не слова |
| Прямой в памяти программ | jmp main | любой адрес Flash | 2 слова; нет при Flash до 8 КБ |
| Косвенный в памяти программ | ijmp | адрес слова в Z | первые 64K слов; дальше — EIJMP с регистром EIND |
| Относительный | rjmp loop | PC + k + 1 | k = −2048…2047 слов; у условных переходов −64…63 |
Режимы со смещением и автоинкрементом заменяют отсутствующие у AVR команды работы с памятью: структура адресуется через Y и LDD/STD, массив обходится через X+ без отдельного увеличения указателя. Пять режимов доступа к памяти данных (прямой, косвенный, со смещением, с преддекрементом и постинкрементом) работают для всего адресного пространства — у классических AVR от регистров до конца SRAM.
Таблица команд AVR
Таблицы собраны по разделам 5 и 6 справочника DS40002198C. Колонка «Такты» дана для классических megaAVR и tinyAVR: у ядер AVRe и AVRe+ такты одинаковые. Если на ядре AVRxt (tinyAVR 0/1/2, megaAVR 0, AVR Dx и Ex) время другое, оно показано пометкой «xt». Такты XMEGA (AVRxm) и AVRrc отличаются сильнее; они приведены только для команд, которых нет у других ядер, и в примечаниях. В колонке «Флаги» перечислены биты SREG, которые меняет команда; «=0» и «=1» — флаг всегда сбрасывается или устанавливается. Под операндами указаны допустимые значения: d и r — номера регистров, K — константа, k — адрес или смещение в словах (у LDS и STS — адрес байта в памяти данных), A — адрес I/O, b — номер бита, q — смещение, s — номер бита SREG.
Арифметические и логические команды
| Мнемоника | Операнды | Описание | Операция | Флаги | Такты |
|---|---|---|---|---|---|
ADD | Rd, Rrd, r = 0…31 | Сложение без переноса | Rd ← Rd + Rr | Z C N V S H | 1 |
ADC | Rd, Rrd, r = 0…31 | Сложение с переносом | Rd ← Rd + Rr + C | Z C N V S H | 1 |
ADIW | Rd, Kd = 24, 26, 28, 30; K = 0…63 | Прибавить константу к регистровой пареНет в AVRrc. | R[d+1]:Rd ← R[d+1]:Rd + K | Z C N V S | 2 |
SUB | Rd, Rrd, r = 0…31 | Вычитание без заёма | Rd ← Rd − Rr | Z C N V S H | 1 |
SUBI | Rd, Kd = 16…31, K = 0…255 | Вычесть константу | Rd ← Rd − K | Z C N V S H | 1 |
SBC | Rd, Rrd, r = 0…31 | Вычитание с заёмомZ только сбрасывается: при нулевом результате сохраняет прежнее значение — удобно для многобайтных чисел. | Rd ← Rd − Rr − C | Z C N V S H | 1 |
SBCI | Rd, Kd = 16…31, K = 0…255 | Вычесть константу с заёмомZ только сбрасывается: при нулевом результате сохраняет прежнее значение — удобно для многобайтных чисел. | Rd ← Rd − K − C | Z C N V S H | 1 |
SBIW | Rd, Kd = 24, 26, 28, 30; K = 0…63 | Вычесть константу из регистровой парыНет в AVRrc. | R[d+1]:Rd ← R[d+1]:Rd − K | Z C N V S | 2 |
AND | Rd, Rrd, r = 0…31 | Логическое И | Rd ← Rd ∧ Rr | Z N V=0 S | 1 |
ANDI | Rd, Kd = 16…31, K = 0…255 | Логическое И с константой | Rd ← Rd ∧ K | Z N V=0 S | 1 |
OR | Rd, Rrd, r = 0…31 | Логическое ИЛИ | Rd ← Rd ∨ Rr | Z N V=0 S | 1 |
ORI | Rd, Kd = 16…31, K = 0…255 | Логическое ИЛИ с константой | Rd ← Rd ∨ K | Z N V=0 S | 1 |
EOR | Rd, Rrd, r = 0…31 | Исключающее ИЛИ | Rd ← Rd ⊕ Rr | Z N V=0 S | 1 |
COM | Rdd = 0…31 | Инверсия всех битов (обратный код) | Rd ← 0xFF − Rd | Z C=1 N V=0 S | 1 |
NEG | Rdd = 0…31 | Смена знака (дополнительный код)0x80 остаётся 0x80 и даёт V = 1; C = 0 только при нулевом результате. | Rd ← 0x00 − Rd | Z C N V S H | 1 |
SBR | Rd, Kd = 16…31, K = 0…255 | Установить биты по маскесиноним ORI Rd, K | Rd ← Rd ∨ K | Z N V=0 S | 1 |
CBR | Rd, Kd = 16…31, K = 0…255 | Сбросить биты по маскесиноним ANDI Rd, 0xFF − K | Rd ← Rd ∧ (0xFF − K) | Z N V=0 S | 1 |
INC | Rdd = 0…31 | ИнкрементФлаг C не меняется — счётчик цикла не портит перенос. | Rd ← Rd + 1 | Z N V S | 1 |
DEC | Rdd = 0…31 | ДекрементФлаг C не меняется. | Rd ← Rd − 1 | Z N V S | 1 |
TST | Rdd = 0…31 | Проверка на ноль и знаксиноним AND Rd, Rd | Rd ← Rd ∧ Rd | Z N V=0 S | 1 |
CLR | Rdd = 0…31 | Обнулить регистрсиноним EOR Rd, Rd | Rd ← Rd ⊕ Rd | Z=1 N=0 V=0 S=0 | 1 |
SER | Rdd = 16…31 | Записать 0xFF в регистрсиноним LDI Rd, 0xFF | Rd ← 0xFF | — | 1 |
MUL | Rd, Rrd, r = 0…31 | Умножение без знака 8 × 8 → 16Нет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc. | R1:R0 ← Rd × Rr | Z C | 2 |
MULS | Rd, Rrd, r = 16…31 | Умножение со знакомНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc. | R1:R0 ← Rd × Rr (знак × знак) | Z C | 2 |
MULSU | Rd, Rrd, r = 16…23 | Умножение знакового Rd на беззнаковое RrНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc. | R1:R0 ← Rd × Rr (знак × без знака) | Z C | 2 |
FMUL | Rd, Rrd, r = 16…23 | Дробное умножение без знака (формат 1.7)Нет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc. | R1:R0 ← (Rd × Rr) << 1 | Z C | 2 |
FMULS | Rd, Rrd, r = 16…23 | Дробное умножение со знакомНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc. | R1:R0 ← (Rd × Rr) << 1 (знак × знак) | Z C | 2 |
FMULSU | Rd, Rrd, r = 16…23 | Дробное умножение знакового на беззнаковоеНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc. | R1:R0 ← (Rd × Rr) << 1 (знак × без знака) | Z C | 2 |
DES | KK = 0x0…0xF | Один раунд шифра DES над R15:R0Только XMEGA (AVRxm). 2 такта, если перед DES стоит другая команда. | R15:R0 ← раунд K; H = 0 — шифрование, H = 1 — расшифровка | — | 1 / 2только XMEGA |
Переходы, вызовы, сравнения и пропуски
| Мнемоника | Операнды | Описание | Операция | Флаги | Такты |
|---|---|---|---|---|---|
RJMP | kk = −2048…2047 | Относительный переход | PC ← PC + k + 1 | — | 2 |
IJMP | — | Косвенный переход по адресу в Z | PC(15:0) ← Z, PC(21:16) ← 0 | — | 2 |
EIJMP | — | Расширенный косвенный переход (EIND:Z)Только кристаллы с Flash больше 128 КБ (22-битный PC), например ATmega2560. | PC(15:0) ← Z, PC(21:16) ← EIND | — | 2 |
JMP32 бита | kk = 0…4M − 1 | Абсолютный переходНет на кристаллах с Flash до 8 КБ (ATtiny85, ATmega8) и в AVRrc. | PC ← k | — | 3 |
RCALL | kk = −2048…2047 | Относительный вызов подпрограммы | STACK ← PC + 1, PC ← PC + k + 1 | — | 3 / 4xt: 2 / 3 |
ICALL | — | Косвенный вызов подпрограммы по Z | STACK ← PC + 1, PC(15:0) ← Z, PC(21:16) ← 0 | — | 3 / 4xt: 2 / 3 |
EICALL | — | Расширенный косвенный вызов (EIND:Z)Только кристаллы с 22-битным PC. | STACK ← PC + 1, PC(15:0) ← Z, PC(21:16) ← EIND | — | 4xt: 3 |
CALL32 бита | kk = 0…64K − 1; при 22-битном PC 0…4M − 1 | Абсолютный вызов подпрограммыНет на кристаллах с Flash до 8 КБ и в AVRrc. | STACK ← PC + 2, PC ← k | — | 4 / 5xt: 3 / 4 |
RET | — | Возврат из подпрограммы | PC ← STACK | — | 4 / 5 |
RETI | — | Возврат из обработчика прерыванияSREG не восстанавливается. На AVRxm/AVRxt флаг I не трогает: там он не сбрасывается при входе в прерывание. | PC ← STACK | I=1 | 4 / 5 |
CPSE | Rd, Rrd, r = 0…31 | Сравнить и пропустить следующую команду, если равны | если Rd = Rr: PC ← PC + 2 или 3 | — | 1 / 2 / 3 |
CP | Rd, Rrd, r = 0…31 | Сравнение (вычитание без записи результата) | Rd − Rr | Z C N V S H | 1 |
CPC | Rd, Rrd, r = 0…31 | Сравнение с учётом переносаZ только сбрасывается: при нулевом результате сохраняет прежнее значение — удобно для многобайтных чисел. | Rd − Rr − C | Z C N V S H | 1 |
CPI | Rd, Kd = 16…31, K = 0…255 | Сравнение с константой | Rd − K | Z C N V S H | 1 |
SBRC | Rr, br = 0…31, b = 0…7 | Пропустить команду, если бит регистра сброшен | если Rr(b) = 0: PC ← PC + 2 или 3 | — | 1 / 2 / 3 |
SBRS | Rr, br = 0…31, b = 0…7 | Пропустить команду, если бит регистра установлен | если Rr(b) = 1: PC ← PC + 2 или 3 | — | 1 / 2 / 3 |
SBIC | A, bA = 0…31, b = 0…7 | Пропустить команду, если бит регистра I/O сброшен | если I/O(A, b) = 0: PC ← PC + 2 или 3 | — | 1 / 2 / 3 |
SBIS | A, bA = 0…31, b = 0…7 | Пропустить команду, если бит регистра I/O установлен | если I/O(A, b) = 1: PC ← PC + 2 или 3 | — | 1 / 2 / 3 |
BRBS | s, ks = 0…7, k = −64…63 | Переход, если флаг SREG(s) установлен | если SREG(s) = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRBC | s, ks = 0…7, k = −64…63 | Переход, если флаг SREG(s) сброшен | если SREG(s) = 0: PC ← PC + k + 1 | — | 1 / 2 |
BREQ | kk = −64…63 | Переход, если равносиноним BRBS 1, k | если Z = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRNE | kk = −64…63 | Переход, если не равносиноним BRBC 1, k | если Z = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRCS | kk = −64…63 | Переход, если перенос установленсиноним BRBS 0, k | если C = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRCC | kk = −64…63 | Переход, если перенос сброшенсиноним BRBC 0, k | если C = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRSH | kk = −64…63 | Переход, если больше или равно (без знака)синоним BRBC 0, k | если C = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRLO | kk = −64…63 | Переход, если меньше (без знака)синоним BRBS 0, k | если C = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRMI | kk = −64…63 | Переход, если результат отрицательныйсиноним BRBS 2, k | если N = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRPL | kk = −64…63 | Переход, если результат неотрицательныйсиноним BRBC 2, k | если N = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRGE | kk = −64…63 | Переход, если больше или равно (со знаком)синоним BRBC 4, k | если S = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRLT | kk = −64…63 | Переход, если меньше (со знаком)синоним BRBS 4, k | если S = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRHS | kk = −64…63 | Переход, если полуперенос установленсиноним BRBS 5, k | если H = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRHC | kk = −64…63 | Переход, если полуперенос сброшенсиноним BRBC 5, k | если H = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRTS | kk = −64…63 | Переход, если бит T установленсиноним BRBS 6, k | если T = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRTC | kk = −64…63 | Переход, если бит T сброшенсиноним BRBC 6, k | если T = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRVS | kk = −64…63 | Переход, если переполнениесиноним BRBS 3, k | если V = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRVC | kk = −64…63 | Переход, если нет переполнениясиноним BRBC 3, k | если V = 0: PC ← PC + k + 1 | — | 1 / 2 |
BRIE | kk = −64…63 | Переход, если прерывания разрешенысиноним BRBS 7, k | если I = 1: PC ← PC + k + 1 | — | 1 / 2 |
BRID | kk = −64…63 | Переход, если прерывания запрещенысиноним BRBC 7, k | если I = 0: PC ← PC + k + 1 | — | 1 / 2 |
- xt — такты на ядре AVRxt (tinyAVR 0/1/2, megaAVR 0, AVR Dx/Ex), если они отличаются от классических megaAVR и tinyAVR.
- 1 / 2 — условие не выполнено / переход выполнен.
- 1 / 2 / 3 — без пропуска / пропуск однословной команды / пропуск двухсловной (JMP, CALL, LDS, STS).
- У вызовов и возвратов второе число — для кристаллов с 22-битным счётчиком команд (Flash больше 128 КБ).
- «32 бита» — команда занимает два слова Flash.
Пересылка данных
| Мнемоника | Операнды | Описание | Операция | Флаги | Такты |
|---|---|---|---|---|---|
MOV | Rd, Rrd, r = 0…31 | Копировать регистр | Rd ← Rr | — | 1 |
MOVW | Rd, Rrd, r = 0, 2, 4…30 | Копировать регистровую паруНет в исходном ядре AVR и в AVRrc. | R[d+1]:Rd ← R[r+1]:Rr | — | 1 |
LDI | Rd, Kd = 16…31, K = 0…255 | Загрузить константу | Rd ← K | — | 1 |
LDS32 бита | Rd, kd = 0…31, k = 0…65535 | Загрузить байт из памяти данных по прямому адресуВ AVRrc своя 16-битная LDS: d = 16…31, адреса 0x40…0xBF, 2 такта. | Rd ← DS(k) | — | 2xt: 3 |
LD | Rd, X / Y / Zd = 0…31 | Косвенная загрузка по указателю | Rd ← DS(X) | — | 2 |
LD | Rd, X+ / Y+ / Z+d = 0…31 | Косвенная загрузка с постинкрементом указателяRd не может быть байтом того же указателя: у LD r26, X+ или LD r31, Z+ результат не определён. | Rd ← DS(X), X ← X + 1 | — | 2 |
LD | Rd, −X / −Y / −Zd = 0…31 | Косвенная загрузка с предекрементом указателяТо же ограничение: у LD r27, −X или LD r30, −Z результат не определён. | X ← X − 1, Rd ← DS(X) | — | 2 |
LDD | Rd, Y+q / Z+qd = 0…31, q = 0…63 | Косвенная загрузка со смещениемТолько Y и Z; нет в AVRrc. | Rd ← DS(Y + q) | — | 2 |
STS32 бита | k, Rrk = 0…65535, r = 0…31 | Записать байт в память данных по прямому адресуВ AVRrc своя 16-битная STS: r = 16…31, адреса 0x40…0xBF, 1 такт. | DS(k) ← Rr | — | 2 |
ST | X / Y / Z, Rrr = 0…31 | Косвенная запись по указателю | DS(X) ← Rr | — | 2xt: 1 |
ST | X+ / Y+ / Z+, Rrr = 0…31 | Косвенная запись с постинкрементом указателяRr не может быть байтом того же указателя: у ST X+, r26 или ST Y+, r29 результат не определён. | DS(X) ← Rr, X ← X + 1 | — | 2xt: 1 |
ST | −X / −Y / −Z, Rrr = 0…31 | Косвенная запись с предекрементом указателяТо же ограничение: у ST −X, r27 или ST −Z, r30 результат не определён. | X ← X − 1, DS(X) ← Rr | — | 2xt: 1 |
STD | Y+q / Z+q, Rrq = 0…63, r = 0…31 | Косвенная запись со смещениемТолько Y и Z; нет в AVRrc. | DS(Y + q) ← Rr | — | 2xt: 1 |
LPM | — | Прочитать байт из Flash в R0Z — байтовый адрес, доступны первые 64 КБ Flash. Нет в AVRrc. | R0 ← PS(Z) | — | 3 |
LPM | Rd, Zd = 0…31 | Прочитать байт из FlashНет в исходном ядре AVR и в AVRrc. | Rd ← PS(Z) | — | 3 |
LPM | Rd, Z+d = 0…31 | Прочитать байт из Flash с постинкрементом ZНет в исходном ядре AVR и в AVRrc. У LPM r30, Z+ и LPM r31, Z+ результат не определён. | Rd ← PS(Z), Z ← Z + 1 | — | 3 |
ELPM | — | Прочитать байт из Flash в R0 с расширенным адресомТолько кристаллы с Flash больше 64 КБ. | R0 ← PS(RAMPZ:Z) | — | 3 |
ELPM | Rd, Zd = 0…31 | Прочитать байт из Flash с расширенным адресом | Rd ← PS(RAMPZ:Z) | — | 3 |
ELPM | Rd, Z+d = 0…31 | Прочитать байт из Flash с расширенным адресом и постинкрементомУ ELPM r30, Z+ и ELPM r31, Z+ результат не определён. | Rd ← PS(RAMPZ:Z), RAMPZ:Z ← RAMPZ:Z + 1 | — | 3 |
SPM | — | Записать во Flash: стирание страницы, буфер, запись страницыНет в исходном ядре AVR, в AVRrc, в tinyAVR 0/1/2 и megaAVR 0: у последних Flash пишется через NVMCTRL. | PS(RAMPZ:Z) ← R1:R0 | — | перем. |
SPM | Z+ | Записать во Flash с увеличением Z на 2Только XMEGA и AVR Dx, Ex, SD (ядра AVRxm и AVRxt); у tinyAVR 0/1/2 и megaAVR 0 нет. | PS(RAMPZ:Z) ← R1:R0, Z ← Z + 2 | — | —xt: перем. |
IN | Rd, Ad = 0…31, A = 0…63 | Прочитать регистр I/O | Rd ← I/O(A) | — | 1 |
OUT | A, RrA = 0…63, r = 0…31 | Записать в регистр I/O | I/O(A) ← Rr | — | 1 |
PUSH | Rrr = 0…31 | Поместить регистр в стек | STACK ← Rr, SP ← SP − 1 | — | 2xt: 1 |
POP | Rdd = 0…31 | Извлечь регистр из стека | SP ← SP + 1, Rd ← STACK | — | 2 |
XCH | Z, Rdd = 0…31 | Обменять регистр с ячейкой SRAMТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D. | DS(Z) ↔ Rd | — | 2только XMEGA |
LAS | Z, Rdd = 0…31 | Прочитать ячейку и установить в ней биты маски RdТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D. | DS(Z) ← Rd ∨ DS(Z), Rd ← прежнее DS(Z) | — | 2только XMEGA |
LAC | Z, Rdd = 0…31 | Прочитать ячейку и сбросить в ней биты маски RdТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D. | DS(Z) ← (0xFF − Rd) ∧ DS(Z), Rd ← прежнее DS(Z) | — | 2только XMEGA |
LAT | Z, Rdd = 0…31 | Прочитать ячейку и инвертировать в ней биты маски RdТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D. | DS(Z) ← Rd ⊕ DS(Z), Rd ← прежнее DS(Z) | — | 2только XMEGA |
- xt — такты на ядре AVRxt (tinyAVR 0/1/2, megaAVR 0, AVR Dx/Ex), если они отличаются от классических megaAVR и tinyAVR.
- Такты LD, LDD, LDS, ST, STD, STS даны для внутренней SRAM, не для внешней памяти; на AVRxt обращение к Flash и EEPROM через память данных длится минимум на такт дольше.
- перем. — зависит от времени стирания и записи Flash.
- «32 бита» — команда занимает два слова Flash.
Битовые операции и флаги
| Мнемоника | Операнды | Описание | Операция | Флаги | Такты |
|---|---|---|---|---|---|
LSL | Rdd = 0…31 | Логический сдвиг влево (умножение на 2)синоним ADD Rd, Rd | C ← Rd(7), Rd(n+1) ← Rd(n), Rd(0) ← 0 | Z C N V S H | 1 |
LSR | Rdd = 0…31 | Логический сдвиг вправо (деление без знака на 2) | Rd(7) ← 0, Rd(n) ← Rd(n+1), C ← Rd(0) | Z C N=0 V S | 1 |
ROL | Rdd = 0…31 | Циклический сдвиг влево через переноссиноним ADC Rd, Rd | Rd(0) ← C, Rd(n+1) ← Rd(n), C ← Rd(7) | Z C N V S H | 1 |
ROR | Rdd = 0…31 | Циклический сдвиг вправо через перенос | Rd(7) ← C, Rd(n) ← Rd(n+1), C ← Rd(0) | Z C N V S | 1 |
ASR | Rdd = 0…31 | Арифметический сдвиг вправо (деление со знаком на 2) | Rd(n) ← Rd(n+1), C ← Rd(0), Rd(7) не меняется | Z C N V S | 1 |
SWAP | Rdd = 0…31 | Поменять местами тетрады | Rd(7:4) ↔ Rd(3:0) | — | 1 |
SBI | A, bA = 0…31, b = 0…7 | Установить бит в регистре I/O | I/O(A, b) ← 1 | — | 2xt: 1 |
CBI | A, bA = 0…31, b = 0…7 | Сбросить бит в регистре I/O | I/O(A, b) ← 0 | — | 2xt: 1 |
BST | Rr, br = 0…31, b = 0…7 | Скопировать бит регистра во флаг T | T ← Rr(b) | T | 1 |
BLD | Rd, bd = 0…31, b = 0…7 | Скопировать флаг T в бит регистра | Rd(b) ← T | — | 1 |
BSET | ss = 0…7 | Установить флаг SREG с номером s | SREG(s) ← 1 | SREG(s) | 1 |
BCLR | ss = 0…7 | Сбросить флаг SREG с номером s | SREG(s) ← 0 | SREG(s) | 1 |
SEC | — | Установить флаг переноса Cсиноним BSET 0 | C ← 1 | C=1 | 1 |
CLC | — | Сбросить флаг переноса Cсиноним BCLR 0 | C ← 0 | C=0 | 1 |
SEZ | — | Установить флаг нуля Zсиноним BSET 1 | Z ← 1 | Z=1 | 1 |
CLZ | — | Сбросить флаг нуля Zсиноним BCLR 1 | Z ← 0 | Z=0 | 1 |
SEN | — | Установить флаг отрицательного результата Nсиноним BSET 2 | N ← 1 | N=1 | 1 |
CLN | — | Сбросить флаг отрицательного результата Nсиноним BCLR 2 | N ← 0 | N=0 | 1 |
SEV | — | Установить флаг переполнения Vсиноним BSET 3 | V ← 1 | V=1 | 1 |
CLV | — | Сбросить флаг переполнения Vсиноним BCLR 3 | V ← 0 | V=0 | 1 |
SES | — | Установить флаг знака Sсиноним BSET 4 | S ← 1 | S=1 | 1 |
CLS | — | Сбросить флаг знака Sсиноним BCLR 4 | S ← 0 | S=0 | 1 |
SEH | — | Установить флаг полупереноса Hсиноним BSET 5 | H ← 1 | H=1 | 1 |
CLH | — | Сбросить флаг полупереноса Hсиноним BCLR 5 | H ← 0 | H=0 | 1 |
SET | — | Установить флаг пользовательского бита Tсиноним BSET 6 | T ← 1 | T=1 | 1 |
CLT | — | Сбросить флаг пользовательского бита Tсиноним BCLR 6 | T ← 0 | T=0 | 1 |
SEI | — | Установить флаг глобального разрешения прерываний Iсиноним BSET 7Команда сразу после SEI выполняется раньше любого ожидающего прерывания. | I ← 1 | I=1 | 1 |
CLI | — | Сбросить флаг глобального разрешения прерываний Iсиноним BCLR 7Прерывания запрещаются сразу, даже пришедшее одновременно с CLI не обслуживается. | I ← 0 | I=0 | 1 |
- xt — такты на ядре AVRxt (tinyAVR 0/1/2, megaAVR 0, AVR Dx/Ex), если они отличаются от классических megaAVR и tinyAVR.
Управление микроконтроллером
| Мнемоника | Операнды | Описание | Операция | Флаги | Такты |
|---|---|---|---|---|---|
BREAK | — | Точка останова для внутрисхемного отладчикаЕсли отладка не включена или кристалл заблокирован, выполняется как NOP. Нет в исходном ядре AVR (ATtiny11/12/15/26), в ATmega8/8A, ATmega8515/8535 и ATtiny4/5/9/10/20. | останов CPU для отладчика | — | 1 |
NOP | — | Нет операции | — | — | 1 |
SLEEP | — | Перейти в режим сна, заданный в регистре управления | см. описание режимов сна в даташите | — | 1 |
WDR | — | Сбросить сторожевой таймер | перезапуск WDT | — | 1 |
Команды-синонимы
Часть мнемоник — синонимы: ассемблер превращает их в код другой команды. Машинный код у синонима и базовой команды одинаковый, поэтому дизассемблер может показать любое из двух имён, а флаги и такты у них совпадают.
| Синоним | Тот же код, что у | Что делает |
|---|---|---|
SBR Rd, K | ORI Rd, K | Установить биты по маске |
CBR Rd, K | ANDI Rd, 0xFF − K | Сбросить биты по маске |
TST Rd | AND Rd, Rd | Проверка на ноль и знак |
CLR Rd | EOR Rd, Rd | Обнулить регистр |
SER Rd | LDI Rd, 0xFF | Записать 0xFF в регистр |
LSL Rd | ADD Rd, Rd | Логический сдвиг влево (умножение на 2) |
ROL Rd | ADC Rd, Rd | Циклический сдвиг влево через перенос |
BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT, BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRID | BRBS s, k / BRBC s, k | Условный переход по одному флагу SREG; s — номер бита флага |
SEC, CLC, SEZ, CLZ, SEN, CLN, SEV, CLV, SES, CLS, SEH, CLH, SET, CLT, SEI, CLI | BSET s / BCLR s | Установка или сброс одного флага SREG |
Условные переходы со знаком и без
После CP, CPI, SUB или SUBI переход выбирают по тому, как трактуются числа. Для условий «больше» и «меньше или равно» отдельных команд нет: операнды сравнения меняют местами.
Условие после cp Rd, Rr | Числа без знака | Числа со знаком |
|---|---|---|
Rd = Rr | BREQ | BREQ |
Rd ≠ Rr | BRNE | BRNE |
Rd ≥ Rr | BRSH | BRGE |
Rd < Rr | BRLO | BRLT |
Rd > Rr | cp Rr, Rd и BRLO | cp Rr, Rd и BRLT |
Rd ≤ Rr | cp Rr, Rd и BRSH | cp Rr, Rd и BRGE |
Разница видна на одной и той же паре байтов 0x9C и 0x64. Без знака это 156 и 100: после cp флаг C = 0, и BRSH перейдёт. Со знаком это −100 и +100: разность 0x38 положительна (N = 0), но возникло переполнение (V = 1), поэтому S = N ⊕ V = 1 и перейдёт BRLT. Проверка одного N командой BRMI здесь дала бы неверный ответ.
Условный переход достаёт только до −64…+63 слов от следующей команды. Если цель дальше, условие инвертируют и перепрыгивают через безусловный переход:
cpi r16, 10
brlo skip ; inverted condition instead of "brsh far_away"
rjmp far_away ; reaches +-2K words
skip:
Примеры с подсчётом тактов
Такты ниже — для ATmega328P (ядро AVRe+) с кварцем 16 МГц: один такт длится 62,5 нс. Синтаксис — AVR Assembler из Microchip Studio; отличия ассемблера GNU as, с которым работает avr-gcc, отмечены отдельно.
Сложение 16-битных чисел: ADD и ADC
; r25:r24 = r25:r24 + r23:r22
; 0x12F0 + 0x0135 = 0x1425
add r24, r22 ; 1 cycle: 0xF0 + 0x35 = 0x125 -> r24 = 0x25, C = 1
adc r25, r23 ; 1 cycle: 0x12 + 0x01 + C = 0x14
Младшие байты складывает ADD, старшие — ADC с учётом переноса: 2 такта и 4 байта кода. Для 32-битных чисел добавляется по ADC на каждый следующий байт. Команды сложения с произвольной константой у AVR нет (ADIW прибавляет к паре регистров только 0…63), её заменяет вычитание отрицательного числа:
; r25:r24 = r25:r24 + 1000
subi r24, low(-1000) ; -1000 = 0xFC18: subtract 0x18
sbci r25, high(-1000) ; subtract 0xFC with borrow
Так можно только с регистрами r16–r31, а флаг C после такой пары получается инвертированным относительно переноса настоящего сложения. Для пар r25:r24, X, Y, Z и констант 0…63 короче ADIW: adiw r24, 50 — одно слово и 2 такта.
Задержка 1 мс при 16 МГц
; ATmega328P at 16 MHz: exactly 16 000 cycles = 1 ms, including RCALL and RET
delay_1ms: ; called with "rcall delay_1ms": 3 cycles
ldi r24, low(3998) ; 1
ldi r25, high(3998) ; 1
delay_loop:
sbiw r24, 1 ; 2
brne delay_loop ; 2 while looping, 1 on exit
ret ; 4
Проход цикла SBIW + BRNE занимает 4 такта, но на последнем BRNE не переходит и выполняется за 1 такт, поэтому N проходов дают 4N − 1 тактов. Вместе с вызовом, двумя LDI и возвратом:
Для 1 мс при 16 МГц нужно 16 000 тактов: . Наибольшая задержка получается при N = 0: SBIW превращает ноль в 0xFFFF, цикл проходит 65 536 раз, и подпрограмма длится 262 152 такта, или 16,38 мс. Прерывания удлиняют задержку на время своих обработчиков. На ядре AVRxt RCALL на такт короче, и та же подпрограмма займёт 15 999 тактов.
Таблица во Flash: LPM Z+
; Show ten 7-segment codes from flash on PORTD (AVR Assembler syntax)
ldi ZL, low(digits * 2) ; labels in the code segment are word addresses,
ldi ZH, high(digits * 2) ; LPM needs a byte address
ldi r17, 10
next:
lpm r16, Z+ ; 3 cycles: r16 = code, Z = Z + 1
out PORTD, r16 ; 1
; ... delay to keep the digit visible
dec r17 ; 1
brne next ; 2 while looping
stop:
rjmp stop ; the table below is data, never execute it
digits: ; 0..9, common cathode, bits = gfedcba
.db 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F
В AVR Assembler счётчик адресов сегмента кода считает слова, поэтому метка digits — адрес слова, а для LPM его умножают на 2. В GNU as метки содержат адрес байта, и запись другая: ldi r30, lo8(digits). Директива .db с нечётным числом байтов дополняет последнее слово нулевым байтом и выдаёт предупреждение. Без учёта задержки проход цикла — 7 тактов: LPM 3, OUT 1, DEC 1, BRNE 2.
Атомарная установка бита: SBI против «чтения—изменения—записи»
; Set PB5 on ATmega328P (PORTB = I/O 0x05)
sbi PORTB, 5 ; one instruction, 2 cycles (1 on AVRxt), atomic
; The same as read-modify-write: 3 instructions, 3 cycles
in r16, PORTB ; an interrupt that changes PORTB right here...
ori r16, (1 << 5)
out PORTB, r16 ; ...loses its change on this write
Прерывание обслуживается только между командами, поэтому SBI атомарна. В варианте IN, ORI, OUT обработчик, который изменил другой бит PORTB между чтением и записью, потеряет своё изменение. Для регистров за пределами I/O 0x1F SBI недоступна — например, TIMSK1 у ATmega328P лежит в расширенном I/O по адресу 0x6F, — и участок защищают запретом прерываний:
; Enable the Timer1 overflow interrupt: TIMSK1 is extended I/O (data address 0x6F)
in r17, SREG ; 1 remember the I flag
cli ; 1
lds r16, TIMSK1 ; 2
ori r16, (1 << TOIE1) ; 1
sts TIMSK1, r16 ; 2
out SREG, r17 ; 1 restore I as it was
Это 8 тактов против 2 у SBI. Восстановление SREG вместо безусловной SEI не включит прерывания там, где они были запрещены до этого участка.
Умножение 8 × 8 командой MUL
; 200 * 150 = 30000 = 0x7530
ldi r16, 200
ldi r17, 150
mul r16, r17 ; 2 cycles: r1:r0 = 0x7530 (r1 = 0x75, r0 = 0x30)
movw r24, r0 ; 1: product to r25:r24
clr r1 ; 1: avr-gcc expects r1 = 0
Произведение всегда попадает в R1:R0, даже если множители лежат в других регистрах. Флаг Z показывает нулевое произведение, C — его старший бит 15. В чистом ассемблере r0 и r1 — обычные регистры, но avr-gcc держит в r1 ноль (__zero_reg__): ассемблерная функция, вызываемая из C, или вставка с MUL обязана обнулить r1 перед возвратом. MULS работает с r16–r31, MULSU и дробные FMUL — только с r16–r23. У классических ATtiny с ядром AVRe умножения нет, его делают сдвигами и сложениями.
Сохранение SREG в обработчике прерывания
; Timer0 overflow handler on ATmega328P; r20 is reserved for the counter
TIM0_OVF:
push r16 ; 2
in r16, SREG ; 1
push r16 ; 2
inc r20 ; 1 the real work; changes Z, N, V, S
pop r16 ; 2
out SREG, r16 ; 1
pop r16 ; 2
reti ; 4
Даже одна команда INC меняет флаги Z, N, V и S. Без сохранения SREG основная программа, прерванная между CP и BRNE, после возврата перешла бы по чужим флагам. Сохранение и восстановление стоят 10 тактов. К ним добавляются минимум 4 такта реакции на прерывание, 3 такта на JMP в векторе и 4 такта RETI — не меньше 21 такта сверх полезной работы, или 1,31 мкс при 16 МГц.
Типичные ошибки
- Константа в регистр r0–r15.
ldi r5, 10не ассемблируется: LDI, CPI, SUBI, SBCI, ANDI, ORI, SBR, CBR и SER работают только с r16–r31, потому что в коде команды (1110 KKKK dddd KKKKу LDI) на номер регистра отведено 4 бита. Загрузите константу в старший регистр и скопируйте через MOV. - ADIW и SBIW с другой парой или большой константой. Допустимы только r25:r24, X, Y и Z и константа 0…63: в коде
1001 0110 KKdd KKKKна пару отведено 2 бита, на константу — 6. - SBI, CBI, SBIC и SBIS выше I/O 0x1F. В этих командах адрес 5-битный. Регистры I/O 0x20–0x3F читают и пишут через IN и OUT, расширенный I/O — через LDS и STS.
- I/O-адрес вместо адреса данных. IN и OUT принимают I/O-адрес 0x00–0x3F, а LD, ST, LDS и STS — адрес данных, у классических AVR на 0x20 больший.
lds r16, 0x05прочитает регистр r5, а не PORTB. - Слишком далёкий переход. Условные переходы достают до −64…+63 слов, RJMP и RCALL — до −2048…+2047 слов. Лечится инверсией условия и переходом через RJMP или JMP.
- Адрес слова вместо адреса байта. Для LPM и ELPM указатель Z хранит адрес байта (в AVR Assembler —
low(label * 2)), а IJMP и ICALL ждут в Z адрес слова. В GNU as метки — адреса байтов: для LPM пишутlo8(label), для IJMP и ICALL —pm_lo8(label)илиlo8(gs(label)). - RET вместо RETI в обработчике. На классических AVR флаг I останется сброшенным, и прерывания больше не придут. На AVRxt не сбросится флаг выполняющегося уровня в CPUINT.STATUS.
- Обработчик портит регистры. Сохранять нужно всё, что обработчик меняет: SREG и каждый использованный регистр, а в проекте на avr-gcc — также r0 и r1, если обработчик их трогает.
- SBI на регистре флагов у старых кристаллов. У ATmega8A SBI и CBI перезаписывают весь регистр I/O:
sbi ADCSRA, ADSCдля запуска АЦП запишет единицу и в уже установленный флаг ADIF и тем самым сбросит его вместе с ожидающим прерыванием. У ATmega328P SBI и CBI меняют только указанный бит. - Указатель затирает сам себя. Сочетания вроде
ld r26, X+,ld r31, -Zилиst Y+, r28, где регистр данных — половина того же указателя, по справочнику дают неопределённый результат. То же уlpm r30, Z+иelpm r31, Z+.
Для остальных МК семейства — архитектура, периферия, программаторы — смотрите раздел об AVR. Последовательные интерфейсы, с которыми чаще всего работают из ассемблера, разобраны в статьях про I²C и RS-232.
Частые вопросы
Сколько команд у микроконтроллеров AVR?
В справочнике Microchip DS40002198C описано 119 мнемоник, из них 41 — синонимы вроде CLR, LSL и BREQ, которые ассемблер превращает в код другой команды. Полный набор есть только у части XMEGA с Flash от 128 КБ, например у ATxmega128A1U: команды DES, XCH, LAS, LAC и LAT есть только у XMEGA, а умножения нет у классических ATtiny. В даташитах счёт ведётся по-своему: у ATtiny13A и ATtiny85 заявлено 120 команд, у ATmega8A — 130, у ATmega328P — 131, у ATmega2560 — 135.
Почему LDI не работает с регистрами r0–r15?
В 16-битном коде LDI на номер регистра отведено всего 4 бита, поэтому команда адресует только 16 регистров — r16–r31. Так же устроены CPI, SUBI, SBCI, ANDI, ORI, SBR, CBR и SER. Чтобы записать константу в r0–r15, загрузите её в любой регистр r16–r31 и скопируйте командой MOV.
Чем RET отличается от RETI?
Обе команды забирают адрес возврата из стека. RETI на классических AVR вдобавок устанавливает флаг I, сброшенный аппаратно при входе в прерывание, а на ядрах AVRxm и AVRxt сбрасывает в контроллере прерываний флаг выполняющегося уровня. Если завершить обработчик командой RET, на классическом AVR прерывания останутся запрещены, а на AVRxt контроллер будет считать обработчик незавершённым.
Сколько тактов выполняется команда AVR?
Большинство арифметических, логических и битовых операций над регистрами занимает 1 такт (ADIW, SBIW и умножения — 2): пока одна команда выполняется, следующая уже выбирается из Flash. На классических ATmega RJMP и IJMP выполняются за 2 такта, условный переход — за 1 или 2, LD, ST, LDS и STS — за 2, LPM — за 3, CALL, RET и RETI — за 4 (5 на кристаллах с Flash больше 128 КБ). На ядре AVRxt ST, PUSH, SBI и CBI выполняются за 1 такт, RCALL — за 2, а LDS, наоборот, за 3.
Как перевести адрес регистра I/O в адрес памяти данных?
На классических ATtiny и ATmega — прибавить 0x20. PORTB у ATmega328P имеет I/O-адрес 0x05 и адрес данных 0x25. Команды IN, OUT, SBI, CBI, SBIC и SBIS используют I/O-адрес, а LD, ST, LDS и STS — адрес данных. Регистры расширенного I/O с адресами данных от 0x60, например TIMSK1 (0x6F) или ADCSRA (0x7A), доступны только командами LD, ST, LDS и STS. У XMEGA и AVRxt, например у ATmega4809, I/O-адрес и адрес данных совпадают.
Есть ли у AVR команда деления?
Нет, аппаратного деления нет ни в одном ядре 8-битных AVR. Деление выполняется подпрограммой из сдвигов и вычитаний, а компилятор подставляет вызов такой подпрограммы из своей библиотеки. Деление на степень двойки заменяют сдвигами: LSR для чисел без знака и ASR для чисел со знаком. ASR округляет вниз: −3 превращается в −2, тогда как в языке C −3 / 2 даёт −1.
Источники
- Microchip. AVR Instruction Set Manual (DS40002198C) — редакция C, ноябрь 2024: операнды, флаги и такты по ядрам, приложение A о наборах команд кристаллов
- Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — ядро AVR, карта памяти данных, SREG, стек, время реакции на прерывание
- Microchip. ATmega8A Data Sheet (DS40001974B) — SBI и CBI на регистрах с флагами, начальное значение SP
- Microchip. ATmega4808/4809 Data Sheet (DS40002173C) — ядро AVRxt: карта памяти и I/O, контроллер прерываний CPUINT, запись Flash через NVMCTRL
- Microchip (Atmel). XMEGA AU Manual (8331F) — регистровый файл вне памяти данных, регистры I/O XMEGA с адреса 0x0000
- Microchip. AVR Assembler User Guide (DS40001917A) — директивы .db, .org, функции LOW и HIGH, адресация меток в сегменте кода
- AVR-LibC User Manual: FAQ «What registers are used by the compiler?» — соглашения avr-gcc о регистрах r0, r1 и передаче аргументов
- GNU Assembler: AVR Relocatable Expression Modifiers — lo8, hi8, pm_lo8, gs в синтаксисе GNU as