Перейти к содержанию

Система команд 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: r0–r31, указатели X, Y, Z и ограничения командТридцать два восьмибитных регистра; у классических AVR они занимают адреса 0x00–0x1F в памяти данных. Регистры r16–r31 работают с командами, содержащими константу. Пары r27:r26, r29:r28 и r31:r30 образуют указатели X, Y и Z.r0–r15без команд с константойr16–r31LDI, CPI, SUBI, ANDI, ORI…r00x00r10x01r20x02r30x03r40x04r50x05r60x06r70x07r80x08r90x09r100x0Ar110x0Br120x0Cr130x0Dr140x0Er150x0Fr160x10r170x11r180x12r190x13r200x14r210x15r220x16r230x17r240x18r250x19r26 · XL0x1Ar27 · XH0x1Br28 · YL0x1Cr29 · YH0x1Dr30 · ZL0x1Er31 · ZH0x1FR1:R0 —результат MULMULSU, FMUL,FMULS, FMULSU:только r16–r23Xr27:r26Yr29:r28Zr31:r30ADIW,SBIW
Регистровый файл AVR: адреса регистров в памяти данных классических AVR, указатели X, Y, Z и регистры, которыми ограничены отдельные команды

У классических 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.

Память данных ATmega328P: регистры, I/O, расширенный I/O и SRAMАдреса 0x0000–0x001F — регистры r0–r31, 0x0020–0x005F — 64 регистра ввода-вывода, 0x0060–0x00FF — 160 регистров расширенного ввода-вывода, 0x0100–0x08FF — 2048 байт SRAM. Масштаб не соблюдён.адрес данных32 регистраr0–r310x0000I/O 0x00–0x1Fбиты: SBI, CBI, SBIC, SBIS0x0020I/O 0x20–0x3F0x0040160 регистроврасширенного I/O0x0060SRAM, 2048 байтпеременные и стек0x01000x08FFстек64 регистра I/O: IN, OUT;адрес I/O = адрес данных − 0x20только LD, LDD, LDSи ST, STD, STSSP после сброса = RAMEND = 0x08FF;стек растёт к меньшим адресамLD/ST, LDD/STD и LDS/STS адресуют всё пространство 0x0000–0x08FF
Карта памяти данных ATmega328P (масштаб не соблюдён) и команды, которые достают до каждой области

Стек размещается в 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.

Регистр состояния SREG: биты I, T, H, S, V, N, Z, CБит 7 — I, глобальное разрешение прерываний; 6 — T, бит копирования; 5 — H, полуперенос; 4 — S, знак; 3 — V, переполнение дополнительного кода; 2 — N, отрицательный результат; 1 — Z, ноль; 0 — C, перенос.SREG: I/O 0x3F, адрес данных 0x5F (ATmega328P)7Iглобальноеразрешениепрерываний6Tбитдля BST/BLD5Hполупереносиз бита 34SзнакS = N ⊕ V3Vпереполнениедоп. кода2Nстарший битрезультата1Zрезультатравен нулю0Cпереносили заёмуправляющие битыфлаги результатаномер бита = s в BSET, BCLR, BRBS, BRBC
Биты регистра SREG. Номер бита — это параметр s в командах BSET, BCLR, BRBS и BRBC
ФлагБитКогда устанавливаетсяГде используется
C — перенос0перенос из бита 7 при сложении, заём при вычитании и сравнении, выдвинутый бит при сдвигемногобайтная арифметика (ADC, SBC, CPC), сравнения без знака (BRLO, BRSH)
Z — ноль1результат равен нулюBREQ, BRNE; в CPC, SBC и SBCI флаг Z только сбрасывается — так сравнивают многобайтные числа
N — минус2старший бит результата равен 1BRMI, BRPL
V — переполнение3результат знаковой операции не помещается в 8 бит, например 0x7F + 1BRVS, 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
AVRxmXMEGAдобавлены DES, XCH, LAS, LAC, LAT и SPM Z+; свои такты
AVRxttinyAVR 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
AVRrcATtiny4, 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, r17Rd и Rr, результат в Rd—
Непосредственныйldi r16, 0x3Fконстанта в коде командытолько r16–r31
Прямой I/Oin r16, PINBрегистр I/O по адресу AA = 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 + qq = 0…63, только Y и Z
Константа из Flashlpm r16, Z+байт Flash по адресу в ZZ — адрес байта, не слова
Прямой в памяти программjmp mainлюбой адрес Flash2 слова; нет при Flash до 8 КБ
Косвенный в памяти программijmpадрес слова в Zпервые 64K слов; дальше — EIJMP с регистром EIND
Относительныйrjmp loopPC + k + 1k = −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.

Арифметические и логические команды

Система команд AVR: Арифметические и логические команды
МнемоникаОперандыОписаниеОперацияФлагиТакты
ADDRd, Rrd, r = 0…31Сложение без переносаRd ← Rd + RrZ C N V S H1
ADCRd, Rrd, r = 0…31Сложение с переносомRd ← Rd + Rr + CZ C N V S H1
ADIWRd, Kd = 24, 26, 28, 30; K = 0…63Прибавить константу к регистровой пареНет в AVRrc.R[d+1]:Rd ← R[d+1]:Rd + KZ C N V S2
SUBRd, Rrd, r = 0…31Вычитание без заёмаRd ← Rd − RrZ C N V S H1
SUBIRd, Kd = 16…31, K = 0…255Вычесть константуRd ← Rd − KZ C N V S H1
SBCRd, Rrd, r = 0…31Вычитание с заёмомZ только сбрасывается: при нулевом результате сохраняет прежнее значение — удобно для многобайтных чисел.Rd ← Rd − Rr − CZ C N V S H1
SBCIRd, Kd = 16…31, K = 0…255Вычесть константу с заёмомZ только сбрасывается: при нулевом результате сохраняет прежнее значение — удобно для многобайтных чисел.Rd ← Rd − K − CZ C N V S H1
SBIWRd, Kd = 24, 26, 28, 30; K = 0…63Вычесть константу из регистровой парыНет в AVRrc.R[d+1]:Rd ← R[d+1]:Rd − KZ C N V S2
ANDRd, Rrd, r = 0…31Логическое ИRd ← Rd ∧ RrZ N V=0 S1
ANDIRd, Kd = 16…31, K = 0…255Логическое И с константойRd ← Rd ∧ KZ N V=0 S1
ORRd, Rrd, r = 0…31Логическое ИЛИRd ← Rd ∨ RrZ N V=0 S1
ORIRd, Kd = 16…31, K = 0…255Логическое ИЛИ с константойRd ← Rd ∨ KZ N V=0 S1
EORRd, Rrd, r = 0…31Исключающее ИЛИRd ← Rd ⊕ RrZ N V=0 S1
COMRdd = 0…31Инверсия всех битов (обратный код)Rd ← 0xFF − RdZ C=1 N V=0 S1
NEGRdd = 0…31Смена знака (дополнительный код)0x80 остаётся 0x80 и даёт V = 1; C = 0 только при нулевом результате.Rd ← 0x00 − RdZ C N V S H1
SBRRd, Kd = 16…31, K = 0…255Установить биты по маскесиноним ORI Rd, KRd ← Rd ∨ KZ N V=0 S1
CBRRd, Kd = 16…31, K = 0…255Сбросить биты по маскесиноним ANDI Rd, 0xFF − KRd ← Rd ∧ (0xFF − K)Z N V=0 S1
INCRdd = 0…31ИнкрементФлаг C не меняется — счётчик цикла не портит перенос.Rd ← Rd + 1Z N V S1
DECRdd = 0…31ДекрементФлаг C не меняется.Rd ← Rd − 1Z N V S1
TSTRdd = 0…31Проверка на ноль и знаксиноним AND Rd, RdRd ← Rd ∧ RdZ N V=0 S1
CLRRdd = 0…31Обнулить регистрсиноним EOR Rd, RdRd ← Rd ⊕ RdZ=1 N=0 V=0 S=01
SERRdd = 16…31Записать 0xFF в регистрсиноним LDI Rd, 0xFFRd ← 0xFF—1
MULRd, Rrd, r = 0…31Умножение без знака 8 × 8 → 16Нет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc.R1:R0 ← Rd × RrZ C2
MULSRd, Rrd, r = 16…31Умножение со знакомНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc.R1:R0 ← Rd × Rr (знак × знак)Z C2
MULSURd, Rrd, r = 16…23Умножение знакового Rd на беззнаковое RrНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc.R1:R0 ← Rd × Rr (знак × без знака)Z C2
FMULRd, Rrd, r = 16…23Дробное умножение без знака (формат 1.7)Нет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc.R1:R0 ← (Rd × Rr) << 1Z C2
FMULSRd, Rrd, r = 16…23Дробное умножение со знакомНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc.R1:R0 ← (Rd × Rr) << 1 (знак × знак)Z C2
FMULSURd, Rrd, r = 16…23Дробное умножение знакового на беззнаковоеНет в исходном ядре AVR, в классических ATtiny (ядро AVRe) и в AVRrc.R1:R0 ← (Rd × Rr) << 1 (знак × без знака)Z C2
DESKK = 0x0…0xFОдин раунд шифра DES над R15:R0Только XMEGA (AVRxm). 2 такта, если перед DES стоит другая команда.R15:R0 ← раунд K; H = 0 — шифрование, H = 1 — расшифровка—1 / 2только XMEGA

Переходы, вызовы, сравнения и пропуски

Система команд AVR: Переходы, вызовы, сравнения и пропуски
МнемоникаОперандыОписаниеОперацияФлагиТакты
RJMPkk = −2048…2047Относительный переходPC ← PC + k + 1—2
IJMP—Косвенный переход по адресу в ZPC(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
RCALLkk = −2048…2047Относительный вызов подпрограммыSTACK ← PC + 1, PC ← PC + k + 1—3 / 4xt: 2 / 3
ICALL—Косвенный вызов подпрограммы по ZSTACK ← 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 ← STACKI=14 / 5
CPSERd, Rrd, r = 0…31Сравнить и пропустить следующую команду, если равныесли Rd = Rr: PC ← PC + 2 или 3—1 / 2 / 3
CPRd, Rrd, r = 0…31Сравнение (вычитание без записи результата)Rd − RrZ C N V S H1
CPCRd, Rrd, r = 0…31Сравнение с учётом переносаZ только сбрасывается: при нулевом результате сохраняет прежнее значение — удобно для многобайтных чисел.Rd − Rr − CZ C N V S H1
CPIRd, Kd = 16…31, K = 0…255Сравнение с константойRd − KZ C N V S H1
SBRCRr, br = 0…31, b = 0…7Пропустить команду, если бит регистра сброшенесли Rr(b) = 0: PC ← PC + 2 или 3—1 / 2 / 3
SBRSRr, br = 0…31, b = 0…7Пропустить команду, если бит регистра установленесли Rr(b) = 1: PC ← PC + 2 или 3—1 / 2 / 3
SBICA, bA = 0…31, b = 0…7Пропустить команду, если бит регистра I/O сброшенесли I/O(A, b) = 0: PC ← PC + 2 или 3—1 / 2 / 3
SBISA, bA = 0…31, b = 0…7Пропустить команду, если бит регистра I/O установленесли I/O(A, b) = 1: PC ← PC + 2 или 3—1 / 2 / 3
BRBSs, ks = 0…7, k = −64…63Переход, если флаг SREG(s) установленесли SREG(s) = 1: PC ← PC + k + 1—1 / 2
BRBCs, ks = 0…7, k = −64…63Переход, если флаг SREG(s) сброшенесли SREG(s) = 0: PC ← PC + k + 1—1 / 2
BREQkk = −64…63Переход, если равносиноним BRBS 1, kесли Z = 1: PC ← PC + k + 1—1 / 2
BRNEkk = −64…63Переход, если не равносиноним BRBC 1, kесли Z = 0: PC ← PC + k + 1—1 / 2
BRCSkk = −64…63Переход, если перенос установленсиноним BRBS 0, kесли C = 1: PC ← PC + k + 1—1 / 2
BRCCkk = −64…63Переход, если перенос сброшенсиноним BRBC 0, kесли C = 0: PC ← PC + k + 1—1 / 2
BRSHkk = −64…63Переход, если больше или равно (без знака)синоним BRBC 0, kесли C = 0: PC ← PC + k + 1—1 / 2
BRLOkk = −64…63Переход, если меньше (без знака)синоним BRBS 0, kесли C = 1: PC ← PC + k + 1—1 / 2
BRMIkk = −64…63Переход, если результат отрицательныйсиноним BRBS 2, kесли N = 1: PC ← PC + k + 1—1 / 2
BRPLkk = −64…63Переход, если результат неотрицательныйсиноним BRBC 2, kесли N = 0: PC ← PC + k + 1—1 / 2
BRGEkk = −64…63Переход, если больше или равно (со знаком)синоним BRBC 4, kесли S = 0: PC ← PC + k + 1—1 / 2
BRLTkk = −64…63Переход, если меньше (со знаком)синоним BRBS 4, kесли S = 1: PC ← PC + k + 1—1 / 2
BRHSkk = −64…63Переход, если полуперенос установленсиноним BRBS 5, kесли H = 1: PC ← PC + k + 1—1 / 2
BRHCkk = −64…63Переход, если полуперенос сброшенсиноним BRBC 5, kесли H = 0: PC ← PC + k + 1—1 / 2
BRTSkk = −64…63Переход, если бит T установленсиноним BRBS 6, kесли T = 1: PC ← PC + k + 1—1 / 2
BRTCkk = −64…63Переход, если бит T сброшенсиноним BRBC 6, kесли T = 0: PC ← PC + k + 1—1 / 2
BRVSkk = −64…63Переход, если переполнениесиноним BRBS 3, kесли V = 1: PC ← PC + k + 1—1 / 2
BRVCkk = −64…63Переход, если нет переполнениясиноним BRBC 3, kесли V = 0: PC ← PC + k + 1—1 / 2
BRIEkk = −64…63Переход, если прерывания разрешенысиноним BRBS 7, kесли I = 1: PC ← PC + k + 1—1 / 2
BRIDkk = −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.

Пересылка данных

Система команд AVR: Пересылка данных
МнемоникаОперандыОписаниеОперацияФлагиТакты
MOVRd, Rrd, r = 0…31Копировать регистрRd ← Rr—1
MOVWRd, Rrd, r = 0, 2, 4…30Копировать регистровую паруНет в исходном ядре AVR и в AVRrc.R[d+1]:Rd ← R[r+1]:Rr—1
LDIRd, 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
LDRd, X / Y / Zd = 0…31Косвенная загрузка по указателюRd ← DS(X)—2
LDRd, X+ / Y+ / Z+d = 0…31Косвенная загрузка с постинкрементом указателяRd не может быть байтом того же указателя: у LD r26, X+ или LD r31, Z+ результат не определён.Rd ← DS(X), X ← X + 1—2
LDRd, −X / −Y / −Zd = 0…31Косвенная загрузка с предекрементом указателяТо же ограничение: у LD r27, −X или LD r30, −Z результат не определён.X ← X − 1, Rd ← DS(X)—2
LDDRd, 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
STX / Y / Z, Rrr = 0…31Косвенная запись по указателюDS(X) ← Rr—2xt: 1
STX+ / 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
STDY+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
LPMRd, Zd = 0…31Прочитать байт из FlashНет в исходном ядре AVR и в AVRrc.Rd ← PS(Z)—3
LPMRd, 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
ELPMRd, Zd = 0…31Прочитать байт из Flash с расширенным адресомRd ← PS(RAMPZ:Z)—3
ELPMRd, 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—перем.
SPMZ+Записать во 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: перем.
INRd, Ad = 0…31, A = 0…63Прочитать регистр I/ORd ← I/O(A)—1
OUTA, RrA = 0…63, r = 0…31Записать в регистр I/OI/O(A) ← Rr—1
PUSHRrr = 0…31Поместить регистр в стекSTACK ← Rr, SP ← SP − 1—2xt: 1
POPRdd = 0…31Извлечь регистр из стекаSP ← SP + 1, Rd ← STACK—2
XCHZ, Rdd = 0…31Обменять регистр с ячейкой SRAMТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D.DS(Z) ↔ Rd—2только XMEGA
LASZ, Rdd = 0…31Прочитать ячейку и установить в ней биты маски RdТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D.DS(Z) ← Rd ∨ DS(Z), Rd ← прежнее DS(Z)—2только XMEGA
LACZ, Rdd = 0…31Прочитать ячейку и сбросить в ней биты маски RdТолько часть XMEGA (AVRxm): есть, например, у ATxmega128A1U, нет у ATxmega128A1 и серии D.DS(Z) ← (0xFF − Rd) ∧ DS(Z), Rd ← прежнее DS(Z)—2только XMEGA
LATZ, 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.

Битовые операции и флаги

Система команд AVR: Битовые операции и флаги
МнемоникаОперандыОписаниеОперацияФлагиТакты
LSLRdd = 0…31Логический сдвиг влево (умножение на 2)синоним ADD Rd, RdC ← Rd(7), Rd(n+1) ← Rd(n), Rd(0) ← 0Z C N V S H1
LSRRdd = 0…31Логический сдвиг вправо (деление без знака на 2)Rd(7) ← 0, Rd(n) ← Rd(n+1), C ← Rd(0)Z C N=0 V S1
ROLRdd = 0…31Циклический сдвиг влево через переноссиноним ADC Rd, RdRd(0) ← C, Rd(n+1) ← Rd(n), C ← Rd(7)Z C N V S H1
RORRdd = 0…31Циклический сдвиг вправо через переносRd(7) ← C, Rd(n) ← Rd(n+1), C ← Rd(0)Z C N V S1
ASRRdd = 0…31Арифметический сдвиг вправо (деление со знаком на 2)Rd(n) ← Rd(n+1), C ← Rd(0), Rd(7) не меняетсяZ C N V S1
SWAPRdd = 0…31Поменять местами тетрадыRd(7:4) ↔ Rd(3:0)—1
SBIA, bA = 0…31, b = 0…7Установить бит в регистре I/OI/O(A, b) ← 1—2xt: 1
CBIA, bA = 0…31, b = 0…7Сбросить бит в регистре I/OI/O(A, b) ← 0—2xt: 1
BSTRr, br = 0…31, b = 0…7Скопировать бит регистра во флаг TT ← Rr(b)T1
BLDRd, bd = 0…31, b = 0…7Скопировать флаг T в бит регистраRd(b) ← T—1
BSETss = 0…7Установить флаг SREG с номером sSREG(s) ← 1SREG(s)1
BCLRss = 0…7Сбросить флаг SREG с номером sSREG(s) ← 0SREG(s)1
SEC—Установить флаг переноса Cсиноним BSET 0C ← 1C=11
CLC—Сбросить флаг переноса Cсиноним BCLR 0C ← 0C=01
SEZ—Установить флаг нуля Zсиноним BSET 1Z ← 1Z=11
CLZ—Сбросить флаг нуля Zсиноним BCLR 1Z ← 0Z=01
SEN—Установить флаг отрицательного результата Nсиноним BSET 2N ← 1N=11
CLN—Сбросить флаг отрицательного результата Nсиноним BCLR 2N ← 0N=01
SEV—Установить флаг переполнения Vсиноним BSET 3V ← 1V=11
CLV—Сбросить флаг переполнения Vсиноним BCLR 3V ← 0V=01
SES—Установить флаг знака Sсиноним BSET 4S ← 1S=11
CLS—Сбросить флаг знака Sсиноним BCLR 4S ← 0S=01
SEH—Установить флаг полупереноса Hсиноним BSET 5H ← 1H=11
CLH—Сбросить флаг полупереноса Hсиноним BCLR 5H ← 0H=01
SET—Установить флаг пользовательского бита Tсиноним BSET 6T ← 1T=11
CLT—Сбросить флаг пользовательского бита Tсиноним BCLR 6T ← 0T=01
SEI—Установить флаг глобального разрешения прерываний Iсиноним BSET 7Команда сразу после SEI выполняется раньше любого ожидающего прерывания.I ← 1I=11
CLI—Сбросить флаг глобального разрешения прерываний Iсиноним BCLR 7Прерывания запрещаются сразу, даже пришедшее одновременно с CLI не обслуживается.I ← 0I=01
  • xt — такты на ядре AVRxt (tinyAVR 0/1/2, megaAVR 0, AVR Dx/Ex), если они отличаются от классических megaAVR и tinyAVR.

Управление микроконтроллером

Система команд AVR: Управление микроконтроллером
МнемоникаОперандыОписаниеОперацияФлагиТакты
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

Команды-синонимы

Часть мнемоник — синонимы: ассемблер превращает их в код другой команды. Машинный код у синонима и базовой команды одинаковый, поэтому дизассемблер может показать любое из двух имён, а флаги и такты у них совпадают.

Команды-синонимы AVR и их базовые инструкции
СинонимТот же код, что уЧто делает
SBR Rd, KORI Rd, KУстановить биты по маске
CBR Rd, KANDI Rd, 0xFF − KСбросить биты по маске
TST RdAND Rd, RdПроверка на ноль и знак
CLR RdEOR Rd, RdОбнулить регистр
SER RdLDI Rd, 0xFFЗаписать 0xFF в регистр
LSL RdADD Rd, RdЛогический сдвиг влево (умножение на 2)
ROL RdADC Rd, RdЦиклический сдвиг влево через перенос
BREQ, BRNE, BRCS, BRCC, BRSH, BRLO, BRMI, BRPL, BRGE, BRLT, BRHS, BRHC, BRTS, BRTC, BRVS, BRVC, BRIE, BRIDBRBS s, k / BRBC s, kУсловный переход по одному флагу SREG; s — номер бита флага
SEC, CLC, SEZ, CLZ, SEN, CLN, SEV, CLV, SES, CLS, SEH, CLH, SET, CLT, SEI, CLIBSET s / BCLR sУстановка или сброс одного флага SREG

Условные переходы со знаком и без

После CP, CPI, SUB или SUBI переход выбирают по тому, как трактуются числа. Для условий «больше» и «меньше или равно» отдельных команд нет: операнды сравнения меняют местами.

Условие после cp Rd, RrЧисла без знакаЧисла со знаком
Rd = RrBREQBREQ
Rd ≠ RrBRNEBRNE
Rd ≥ RrBRSHBRGE
Rd < RrBRLOBRLT
Rd > Rrcp Rr, Rd и BRLOcp Rr, Rd и BRLT
Rd ≤ Rrcp Rr, Rd и BRSHcp 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 и возвратом:

T=3⏟RCALL+2⏟2×LDI+(4N−1)+4⏟RET=4N+8T = \underbrace{3}_{\text{RCALL}} + \underbrace{2}_{\text{2×LDI}} + (4N - 1) + \underbrace{4}_{\text{RET}} = 4N + 8

Для 1 мс при 16 МГц нужно 16 000 тактов: N=(16 000−8)/4=3998N = (16\,000 - 8) / 4 = 3998. Наибольшая задержка получается при 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 МГц.

Типичные ошибки

  1. Константа в регистр r0–r15. ldi r5, 10 не ассемблируется: LDI, CPI, SUBI, SBCI, ANDI, ORI, SBR, CBR и SER работают только с r16–r31, потому что в коде команды (1110 KKKK dddd KKKK у LDI) на номер регистра отведено 4 бита. Загрузите константу в старший регистр и скопируйте через MOV.
  2. ADIW и SBIW с другой парой или большой константой. Допустимы только r25:r24, X, Y и Z и константа 0…63: в коде 1001 0110 KKdd KKKK на пару отведено 2 бита, на константу — 6.
  3. SBI, CBI, SBIC и SBIS выше I/O 0x1F. В этих командах адрес 5-битный. Регистры I/O 0x20–0x3F читают и пишут через IN и OUT, расширенный I/O — через LDS и STS.
  4. I/O-адрес вместо адреса данных. IN и OUT принимают I/O-адрес 0x00–0x3F, а LD, ST, LDS и STS — адрес данных, у классических AVR на 0x20 больший. lds r16, 0x05 прочитает регистр r5, а не PORTB.
  5. Слишком далёкий переход. Условные переходы достают до −64…+63 слов, RJMP и RCALL — до −2048…+2047 слов. Лечится инверсией условия и переходом через RJMP или JMP.
  6. Адрес слова вместо адреса байта. Для LPM и ELPM указатель Z хранит адрес байта (в AVR Assembler — low(label * 2)), а IJMP и ICALL ждут в Z адрес слова. В GNU as метки — адреса байтов: для LPM пишут lo8(label), для IJMP и ICALL — pm_lo8(label) или lo8(gs(label)).
  7. RET вместо RETI в обработчике. На классических AVR флаг I останется сброшенным, и прерывания больше не придут. На AVRxt не сбросится флаг выполняющегося уровня в CPUINT.STATUS.
  8. Обработчик портит регистры. Сохранять нужно всё, что обработчик меняет: SREG и каждый использованный регистр, а в проекте на avr-gcc — также r0 и r1, если обработчик их трогает.
  9. SBI на регистре флагов у старых кристаллов. У ATmega8A SBI и CBI перезаписывают весь регистр I/O: sbi ADCSRA, ADSC для запуска АЦП запишет единицу и в уже установленный флаг ADIF и тем самым сбросит его вместе с ожидающим прерыванием. У ATmega328P SBI и CBI меняют только указанный бит.
  10. Указатель затирает сам себя. Сочетания вроде 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.

Источники

  1. Microchip. AVR Instruction Set Manual (DS40002198C) — редакция C, ноябрь 2024: операнды, флаги и такты по ядрам, приложение A о наборах команд кристаллов
  2. Microchip. ATmega48A/PA/88A/PA/168A/PA/328/P Data Sheet (DS40002061B) — ядро AVR, карта памяти данных, SREG, стек, время реакции на прерывание
  3. Microchip. ATmega8A Data Sheet (DS40001974B) — SBI и CBI на регистрах с флагами, начальное значение SP
  4. Microchip. ATmega4808/4809 Data Sheet (DS40002173C) — ядро AVRxt: карта памяти и I/O, контроллер прерываний CPUINT, запись Flash через NVMCTRL
  5. Microchip (Atmel). XMEGA AU Manual (8331F) — регистровый файл вне памяти данных, регистры I/O XMEGA с адреса 0x0000
  6. Microchip. AVR Assembler User Guide (DS40001917A) — директивы .db, .org, функции LOW и HIGH, адресация меток в сегменте кода
  7. AVR-LibC User Manual: FAQ «What registers are used by the compiler?» — соглашения avr-gcc о регистрах r0, r1 и передаче аргументов
  8. GNU Assembler: AVR Relocatable Expression Modifiers — lo8, hi8, pm_lo8, gs в синтаксисе GNU as