Beginner’s introduction to avr assembler language

Содержание

Оговорочки

Хочу сразу оговориться, что правильно говорить не «ассемблер» (assembler), а «язык ассемблера» (assembly language), потому как ассемблер – это транслятор кода на языке ассемблера (т.е. по сути, программа MASM, TASM, fasm, NASM, UASM, GAS и пр., которая компилирует исходный текст на языке ассемблера в объектный или исполняемый файл). Тем не менее, из соображения краткости многие, говоря «ассемблер» (асм, asm), подразумевают именно «язык ассемблера».

Синтаксис директив, стандартных макросов и пр. структурных элементов различных диалектов (к примеру, MASM, fasm, NASM, GAS), могут отличаться довольно существенно. Мнемоники (имена) инструкций (команд) и регистров, а также синтаксис их написания для одного и того же процессора примерно одинаковы почти во всех диалектах (заметным исключением среди популярных ассемблеров является разве что GAS (GNU Assembler) в режиме синтаксиса AT&T для x86, где к именам инструкций могут добавляться суффиксы, обозначающие размер обрабатываемых ими данных, что бывает довольно удобно, но там есть и другие нюансы, сбивающие с толку программиста, привыкшего к классическому ассемблеру, к примеру, иной порядок указания операндов, хотя всё это лечится специальной директивой переключения в режим классического синтаксиса Intel).

Поскольку ассемблер – самый низкоуровневый язык программирования, довольно проблематично написать код, который корректно компилировался бы для разных архитектур процессоров (например, x86 и ARM), для разных режимов одного и того же процессора (16-битный реальный режим, 32-битный защищённый режим, 64-битный long mode; а ещё код может быть написан как с использованием различных технологий вроде SSE, AVX, FMA, BMI и AES-NI, так и без них) и для разных операционных систем (Windows, Linux, MS-DOS). Хоть иногда и можно встретить «универсальный» код (например, отдельные библиотеки), скажем, для 32- и 64-битного кода ОС Windows (или даже для Windows и Linux), но это бывает нечасто. Ведь каждая строка кода на ассемблере (не считая управляющих директив, макросов и тому подобного) – это отдельная инструкция, которая пишется для конкретного процессора и ОС, и сделать кроссплатформенный вариант можно только с помощью макросов и условных директив препроцессора, получая в итоге порой весьма нетривиальные конструкции, сложные для понимания.

Условные переходы

Все команды этой группы выполняют переход (PC ← PC + A + 1) при разных условиях.

Мнемоника Описание Условие Флаги
BRBC s, A Переход если флаг S сброшен Если SREG(S) = 0
BRBS s, A Переход если флаг S установлен Если SREG(S) = 1
BRCS A Переход по переносу Если C = 1
BRCC A Переход если нет переноса Если C = 0
BREQ A Переход если равно Если Z = 1
BRNE A Переход если не равно Если Z = 0
BRSH A Переход если больше или равно Если C = 0
BRLO A Переход если меньше Если C = 1
BRMI A Переход если отрицательное значение Если N = 1
BRPL A Переход если положительное значение Если N = 0
BRGE A Переход если больше или равно (со знаком) Если (N и V) = 0
BRLT A Переход если меньше (со знаком) Если (N или V) = 1
BRHS A Переход по половинному переносу Если H = 1
BRHC A Переход если нет половинного переноса Если H = 0
BRTS A Переход если флаг T установлен Если T = 1
BRTC A Переход если флаг T сброшен Если T = 0
BRVS A Переход по переполнению дополнительного кода Если V = 1
BRVC A Переход если нет переполнения дополнительного кода Если V = 0
BRID A Переход если прерывания запрещены Если I = 0
BRIE A Переход если прерывания разрешены Если I = 1
SBRC Rd, K Пропустить следующую команду если бит в регистре очищен Если Rd = 0
SBRS Rd, K Пропустить следующую команду если бит в регистре установлен Если Rd = 1
SBIC A, b Пропустить еследующую команду если бит в регистре ввода/вывода очищен Если I/O(A, b) = 0
SBIS A, b Пропустить следующую команду если бит в регистре ввода/вывода установлен Если I/O(A, b) = 1

Общая информация

Компилятор среды разработки
ATMEL AVR Studioтранслирует исходные коды с языка ассемблера в
объектный код и в
прошивку
.hex
для загрузки в микроконтроллер. Полученный объектный код можно использовать в
симуляторе AVR Studio, либо в эмуляторе ATMEL AVR In-Circuit
Emulator.

AVR Studio имеет объем для скачивания около 80 Мб
! Но вы можете скачать всего 4 Мб это
VMLAB —
с нем есть тот же ассемблер и великолепный
симулятор.

Компилятор генерирует код, который не требует линковки.

Компилятор работает под Microsoft Windows 3.11, Microsoft
Windows95 и Microsoft Windows NT. Кроме этого есть консольная версия
для MS-DOS.

Набор инструкций семейства микроконтроллеров AVR описан в данном
документе кратко, для более полной информации по инструкциям
обращайтесь к полному описанию инструкций и документации по
конкретному микроконтроллеру.

═Инструкции ветвления

Мнемоника Операнды Описание Операция Флаги Циклы
RJMP k Относительный переход PC = PC + k +1 None 2
IJMP Нет Косвенный переход на (Z) PC = Z None 2
EIJMP Нет Расширенный косвенный переход на (Z) STACK = PC+1, PC(15:0) = Z, PC(21:16) = EIND None 2
JMP k Переход PC = k None 3
RCALL k Относительный вызов подпрограммы STACK = PC+1, PC = PC + k + 1 None 3/4*
ICALL Нет Косвенный вызов (Z) STACK = PC+1, PC = Z═ None 3/4*
EICALL Нет Расширенный косвенный вызов (Z) STACK = PC+1, PC(15:0) = Z, PC(21:16) =EIND None 4*
CALL k Вызов подпрограммы STACK = PC+2, PC = k None 4/5*
RET Нет Возврат из подпрограммы PC = STACK None 4/5*
RETI Нет Возврат из прерывания PC = STACK I 4/5*
CPSE Rd,Rr Сравнить, пропустить если равны═ if (Rd ==Rr) PC = PC 2 or 3 None 1/2/3
CP Rd,Rr Сравнить Rd -Rr Z,C,N,V,H,S 1
CPC Rd,Rr Сравнить с переносом Rd — Rr — C Z,C,N,V,H,S 1
CPI Rd,K8 Сравнить с константой Rd — K Z,C,N,V,H,S 1
SBRC Rr,b Пропустить если бит в регистре очищен if(Rr(b)==0) PC = PC + 2 or 3 None 1/2/3
SBRS Rr,b Пропустить если бит в регистре установлен if(Rr(b)==1) PC = PC + 2 or 3 None 1/2/3
SBIC P,b Пропустить если бит в порту очищен if(I/O(P,b)==0) PC = PC + 2 or 3 None 1/2/3
SBIS P,b Пропустить если бит в порту установлен if(I/O(P,b)==1) PC = PC + 2 or 3 None 1/2/3
BRBC s,k Перейти если флаг в SREG очищен if(SREG(s)==0) PC = PC + k + 1 None 1/2
BRBS s,k Перейти если флаг в SREG установлен if(SREG(s)==1) PC = PC + k + 1 None 1/2
BREQ k Перейти если равно if(Z==1) PC = PC + k + 1 None 1/2
BRNE k Перейти если не равно if(Z==0) PC = PC + k + 1 None 1/2
BRCS k Перейти если перенос установлен if(C==1) PC = PC + k + 1 None 1/2
BRCC k Перейти если перенос очищен if(C==0) PC = PC + k + 1 None 1/2
BRSH k Перейти если равно или больше if(C==0) PC = PC + k + 1 None 1/2
BRLO k Перейти если меньше if(C==1) PC = PC + k + 1 None 1/2
BRMI k Перейти если минус if(N==1) PC = PC + k + 1 None 1/2
BRPL k Перейти если плюс if(N==0) PC = PC + k + 1 None 1/2
BRGE k Перейти если больше или равно (со знаком) if(S==0) PC = PC + k + 1 None 1/2
BRLT k Перейти если меньше (со знаком) if(S==1) PC = PC + k + 1 None 1/2
BRHS k Перейти если флаг внутреннего переноса установлен if(H==1) PC = PC + k + 1 None 1/2
BRHC k Перейти если флаг внутреннего переноса очищен if(H==0) PC = PC + k + 1 None 1/2
BRTS k Перейти если флаг T установлен if(T==1) PC = PC + k + 1 None 1/2
BRTC k Перейти если флаг T очищен if(T==0) PC = PC + k + 1 None 1/2
BRVS k Перейти если флаг переполнения установлен if(V==1) PC = PC + k + 1 None 1/2
BRVC k Перейти если флаг переполнения очищен if(V==0) PC = PC + k + 1 None 1/2
BRIE k Перейти если прерывания разрешены if(I==1) PC = PC + k + 1 None 1/2
BRID k Перейти если прерывания запрещены if(I==0) PC = PC + k + 1 None 1/2

Быть или не быть?

Так, нужно ли изучать ассемблер современному программисту? Если вы уже не новичок в программировании, и у вас серьёзные амбиции, то изучение ассемблера, внутреннего устройства операционных систем и функционирования железа (особенно процессоров, памяти), а также использование различных инструментов для дизассемблирования, отладки и анализа кода полезно тем, кто хочет писать действительно эффективные программы. Иначе будет сложно в полной мере понять, что происходит «под капотом» любимого компилятора (хотя бы в общих чертах), как оптимизировать программы на любом языке программирования и какой приём стоит предпочесть. Необязательно погружаться слишком глубоко в эту тему, если вы пишете на Python или JavaScript. А вот если ваш язык – C или C++, хорошенько изучить ассемблер будет полезно.

Вместе с тем, необходимо помнить не только о «тактике», но и о «стратегии» написания кода, поэтому не менее важно изучать и алгоритмы (правильный выбор которых зачастую более важен для создания эффективных программ, нежели низкоуровневая оптимизация), шаблоны проектирования и многие другие технологии, без которых программист не может считать себя современным

О программе

В SASM Вы можете легко разрабатывать и выполнять программы, написанные на языках ассемблера NASM, MASM, GAS, FASM. Вводите код в форму и запускайте приложение. В Windows также возможен запуск приложения в отдельном окне. Входные данные указывайте в поле «Ввод». В поле «Вывод» Вы сможете увидеть результат работы программы. При этом все сообщения и ошибки компиляции будут выводиться в форму снизу. Вы можете сохранять исходный или скомпилированный (exe) код программы в файл, а также загружать свои программы из файла.

Программа поддерживает работу с несколькими проектами – новые файлы открываются и создаются в новых вкладках. При выходе из программы текущий набор открытых файлов сохраняется. При следующем запуске Вы сможете восстановить предыдущую сессию. В параметрах настраивается шрифт, цветовая схема и текст, отображающийся при создании нового файла. Интерфейс программы доступен на восьми языках (русский, английский, турецкий (спасибо Ali Goren), китайский (спасибо Ahmed Zetao Yang), немецкий (спасибо Sebastian Fischer), итальянский (спасибо Carlo Dapor), польский (спасибо Krzysztof Rossa), иврит (спасибо Elian Kamal), испанский (спасибо Mariano Cordoba)). Все окна в программе плавающие, с возможностью закрепления в одной из множества позиций. Имеется возможность переназначения горячих клавиш.

Стандартное меню «Правка» дополнено возможностью комментирования/раскомментирования выделенного куска кода и создания/удаления отступа в 4 пробела (Tab/Shift+Tab).

В SASM вы можете находить ошибки в своих программах с помощью интерфейса к отладчику gdb. В программе можно просматривать значения регистров и переменных, а также устанавливать точки останова и перемещаться по отлаживаемой программе. Дополнительно имеется возможность выполнять произвольные команды отладчика gdb, результаты которых будут отображаться в логе.

SASM полностью поддерживает работу с четырьмя ассемблерами NASM, MASM, GAS, FASM в двух режимах — x64 и x86, переключаться между которыми можно в настройках на вкладке «Построение». Там же можно изменить опции ассемблера и компоновщика и выбрать, какие программы будут использоваться для ассемблирования и компоновки.

10.49 LSL

Logical Shift Left. This instruction is a preferred synonym for instructions with shifted register operands.

Syntax

where:

is an optional suffix. If S is specified, the condition flags are updated on
the result of the operation.
is the destination register.
is the register holding the first operand. This operand is shifted left.
is a register holding a shift value to apply to the value in
. Only the least
significant byte is used.
is a constant shift. The range of values permitted is 0-31.

Thumb instructions must not use PC or SP.

You cannot specify zero for the value in an
instruction in an IT block.

Use of SP and PC in ARM instructions

You can use SP in these ARM instructions but this is deprecated in ARMv6T2 and
above.

You cannot use PC in instructions with the
syntax. You can use PC for and
in the other syntax, but this is
deprecated in ARMv6T2 and above.

If you use PC as , the value used is the address of the
instruction plus 8.

If you use PC as :

  • Execution branches to the address corresponding to the result.
  • If you use the S suffix, the SPSR of the current mode is
    copied to the CPSR. You can use this to return from exceptions.

    The ARM instruction always disassembles to the preferred form
    .

Caution

Do not use the S suffix when using PC as
in User mode or System mode. The assembler cannot warn
you about this because it has no information about what the processor mode is
likely to be at execution time.

You cannot use PC for or any operand in the
instruction if it has a register-controlled shift.

Condition flags

If S is specified, the instruction updates
the N and Z flags according to the result.

The C flag is unaffected if the shift value is 0. Otherwise, the C flag is updated to
the last bit shifted out.

16-bit instructions

The following forms of this instruction are available in Thumb code, and are 16-bit
instructions:

and
must both be Lo registers.
This form can only be used outside an IT block.

and
must both be Lo registers.
This form can only be used inside an IT block.

and
must both be Lo registers.
This form can only be used outside an IT block.

and
must both be Lo registers.
This form can only be used inside an IT block.

Architectures

This ARM instruction is available in all architectures.

This 32-bit Thumb instruction is available in ARMv6T2 and above.

This 16-bit Thumb instruction is available in ARMv4T and above.

    LSLS    r1, r2, r3

Арифметические операции

Мнемоника Описание Операция Флаги
ADD Rd, Rr Сложение двух регистров Rd ← Rd + Rr Z, C, N, V, H
ADC Rd, Rr Сложение двух регистров с переносом Rd ← Rd + Rr + С Z, C, N, V, H
SUB Rd, Rr Вычитание двух регистров Rd ← Rd — Rr Z, C, N, V, H
SBC Rd, Rr Вычитание двух регистров с заёмом Rd ← Rd — Rr — С Z, C, N, V, H
ADIW Rd, K Сложение регистровой пары с константой R(d+1):Rd ← R(d+1):Rd + K Z, C, N, V, S
SBIW Rd, K Вычитание константы из регистровой пары R(d+1):Rdl ← R(d+1):Rd — K Z, C, N, V, S
SUBI Rd, K Вычитание константы из регистра Rd ← Rd — K Z, C, N, V, H
SBCI Rd, K Вычитание константы из регистра с заёмом Rd ← Rd — K — С Z, C, N, V, H
INC Rd Инкремент регистра Rd ← Rd + 1 Z, N, V
DEC Rd Декремент регистра Rd ← Rd – 1 Z, N, V
MUL Rd, Rr Умножение чисел без знака R1:R0 ← Rd * Rr Z, C
MULS Rd, Rr Умножение чисел со знаком R1:R0 ← Rd * Rr Z, C
MULSU Rd, Rr Умножение числа со знаком с числом без знака R1:R0 ← Rd * Rr Z, C
FMUL Rd, Rr Умножение дробных чисел без знака R1:R0 ← (Rd * Rr) << 1 Z, C
FMULS Rd, Rr Умножение дробных чисел со знаком R1:R0 ← (Rd * Rr) << 1 Z, C
FMULSU Rd, Rr Умножение дробного числа со знаком с числом без знака R1:R0 ← (Rd * Rr) << 1 Z, C

═Арифметические и логические инструкции

Мнемоника Операнды Описание Операция Флаги Циклы
ADD═ Rd,Rr═ Суммирование без переноса Rd = Rd + Rr═ Z,C,N,V,H,S═ 1
ADC Rd,Rr Суммирование с переносом Rd = Rd + Rr + C Z,C,N,V,H,S 1
SUB Rd,Rr Вычитание без переноса Rd = Rd — Rr Z,C,N,V,H,S 1
SUBI Rd,K8 Вычитание константы Rd = Rd — K8 Z,C,N,V,H,S 1
SBC Rd,Rr Вычитание с переносом Rd = Rd — Rr — C Z,C,N,V,H,S 1
SBCI Rd,K8 Вычитание константы с переносом Rd = Rd — K8 — C Z,C,N,V,H,S 1
AND Rd,Rr Логическое И Rd = Rd ╥ Rr Z,N,V,S═ 1
ANDI Rd,K8 Логическое И с константой Rd = Rd ╥ K8 Z,N,V,S 1
OR Rd,Rr Логическое ИЛИ Rd = Rd V Rr Z,N,V,S 1
ORI Rd,K8 Логическое ИЛИ с константой Rd = Rd V K8 Z,N,V,S 1
EOR Rd,Rr Логическое исключающее ИЛИ Rd = Rd EOR Rr Z,N,V,S 1
COM Rd Побитная Инверсия Rd = $FF — Rd Z,C,N,V,S 1
NEG Rd Изменение знака (Доп. код) Rd = $00 — Rd Z,C,N,V,H,S 1
SBR Rd,K8 Установить бит (биты) в регистре Rd = Rd V K8 Z,C,N,V,S 1
CBR Rd,K8 Сбросить бит (биты) в регистре Rd = Rd ╥ ($FF — K8) Z,C,N,V,S 1
INC Rd Инкрементировать значение регистра Rd = Rd + 1 Z,N,V,S 1
DEC Rd Декрементировать значение регистра Rd = Rd -1 Z,N,V,S 1
TST Rd Проверка на ноль либо отрицательность Rd = Rd ╥ Rd Z,C,N,V,S 1
CLR Rd Очистить регистр Rd = 0 Z,C,N,V,S 1
SER Rd Установить регистр Rd = $FF None 1
ADIW Rdl,K6 Сложить константу и слово Rdh:Rdl = Rdh:Rdl + K6═ Z,C,N,V,S 2
SBIW Rdl,K6 Вычесть константу из слова Rdh:Rdl = Rdh:Rdl — K 6 Z,C,N,V,S 2
MUL Rd,Rr Умножение чисел без знака R1:R0 = Rd * Rr Z,C 2
MULS Rd,Rr Умножение чисел со знаком R1:R0 = Rd * Rr Z,C 2
MULSU Rd,Rr Умножение числа со знаком с числом без знака R1:R0 = Rd * Rr Z,C 2
FMUL Rd,Rr Умножение дробных чисел без знака R1:R0 = (Rd * Rr) << 1 Z,C 2
FMULS Rd,Rr Умножение дробных чисел со знаком R1:R0 = (Rd *Rr) << 1 Z,C 2
FMULSU Rd,Rr Умножение дробного числа со знаком с числом без знака R1:R0 = (Rd * Rr) << 1 Z,C 2

Логические операции

Мнемоника Описание Операция Флаги
AND Rd, Rr Логическое «И» двух регистров Rd ← Rd and Rr Z, N, V
ANDI Rd, K Логическое «И регистра и константы Rd ← Rd and K Z, N, V
EOR Rd, Rr Исключающее «ИЛИ» двух регистров Rd ← Rd xor Rr Z, N, V
OR Rd, Rr Логическое «ИЛИ» двух регистров Rd ← Rd or Rr Z, N, V
ORI Rd, K Логическое «ИЛИ» регистра и константы Rd ← Rd or K Z, N, V
COM Rd Перевод в обратный код Rd ← 0xFF — Rd Z, C, N, V
NEG Rd Перевод в дополнительный код Rd ← 0 — Rd Z, C, N, V, H
CLR Rd Очистка регистра Rd ← Rd xor Rd Z, N, V
SER Rd Установка всех разрядов регистра Rd ← 0xFF
TST Rd Проверка регистра на отрицательное (нулевое) значение Rd ← Rd and Rd Z, N, V

Different registers

.DEF AnotherRegister = R15
   LDI AnotherRegister, 150

There is one exception from that rule: setting a register to Zero. This command    CLR MyPreferredRegister

  • ANDI Rx,K ; Bit-And of register Rx with a constant value K,
  • CBR Rx,M ; Clear all bits in register Rx that are set to one
    within the constant mask value M,
  • CPI Rx,K ; Compare the content of the register Rx with a
    constant value K,
  • SBCI Rx,K ; Subtract the constant K and the current value
    of the carry flag from the content of register Rx and store the result in register Rx,
  • SBR Rx,M ; Set all bits in register Rx to one, that are one
    in the constant mask M,
  • SER Rx ; Set all bits in register Rx to one (equal to
    LDI Rx,255),
  • SUBI Rx,K ; Subtract the constant K from the content of
    register Rx and store the result in register Rx.

Commands sorted by function

Function Subfunction Command Flags Clk
Registerset Z N V 1
255   1
Constant   1
Copy Register => Register   1
SRAM => Register, direct   2
SRAM => Register   2
SRAM => Register and INC   2
DEC, SRAM => Register   2
SRAM, displaced => Register   2
Port => Register   1
Stack => Register   2
Program storage Z => R0   3
Register => SRAM, direct   2
Register => SRAM   2
Register => SRAM and INC   2
DEC, Register => SRAM   2
Register => SRAM, displaced   2
Register => Port   1
Register => Stack   2
Add 8 Bit, +1 Z N V 1
8 Bit Z C N V H 1
8 Bit + Carry Z C N V H 1
16 Bit, constant Z C N V S 2
Subtract 8 Bit, -1 Z N V 1
8 Bit Z C N V H 1
8 Bit, constant Z C N V H 1
8 Bit — Carry Z C N V H 1
8 Bit — Carry, constant Z C N V H 1
16 Bit Z C N V S 2
Shift logic, left Z C N V 1
logic, right Z C N V 1
Rotate, left over Carry Z C N V 1
Rotate, right over Carry Z C N V 1
Arithmetic, right Z C N V 1
Nibble exchange   1
Binary And Z N V 1
And, constant Z N V 1
Or Z N V 1
Or, constant Z N V 1
Exclusive-Or Z N V 1
Ones-complement Z C N V 1
Twos-complement Z C N V H 1
Bitschange Register, set Z N V 1
Register, clear Z N V 1
Register, copy to T-Flag T 1
Register, copy from T-Flag   1
Port, set   2
Port, clear   2
Statusbitset Zero-Flag Z 1
Carry Flag C 1
Negative Flag N 1
Twos complement carry Flag V 1
Half carry Flag H 1
Signed Flag S 1
Transfer Flag T 1
Interrupt Enable Flag I 1
Statusbitclear Zero-Flag Z 1
Carry Flag C 1
Negative Flag N 1
Twos complement carry Flag V 1
Half carry Flag H 1
Signed Flag S 1
Transfer Flag T 1
Interrupt Enable Flag I 1
Compare Register, Register Z C N V H 1
Register, Register + Carry Z C N V H 1
Register, constant Z C N V H 1
Register, ≤0 Z N V 1
ImmediateJump Relative   2
Indirect, Address in Z   2
Subroutine, relative   3
Subroutine, Address in Z   3
Return from Subroutine   4
Return from Interrupt I 4
ConditionedJump Statusbit set   1/2
Statusbit clear   1/2
Jump if equal   1/2
Jump if equal   1/2
Jump if carry   1/2
Jump if carry clear   1/2
Jump if equal or greater   1/2
Jump if lower   1/2
Jump if negative   1/2
Jump if positive   1/2
Jump if greater or equal (Signed)   1/2
Jump if lower than zero (Signed)   1/2
Jump on half carry set   1/2
Jump if half carry clear   1/2
Jump if T-Flag set   1/2
Jump if T-Flag clear   1/2
Jump if Twos complement carry set   1/2
Jump if Twos complement carry clear   1/2
Jump if Interrupts enabled   1/2
Jump if Interrupts disabled   1/2
ConditionedJumps Registerbit=0   1/2/3
Registerbit=1   1/2/3
Portbit=0   1/2/3
Portbit=1   1/2/3
Compare, jump if equal   1/2/3
Others No Operation   1
Sleep   1
Watchdog Reset   1

Подробнее об адресах

Адрес может передаваться несколькими способами:

  1. В виде имени переменной, которая в ассемблере является синонимом адреса.
  2. Если переменная является массивом, то обращение к элементу массива происходит через имя его переменной и смещения. Для этого существует 2 формы: и <имя>. Следует учитывать, что смещение — это не индекс в массиве, а размер в байтах. Программисту самому необходимо понимать, на сколько нужно сделать смещение в байтах, чтобы получить нужный элемент массива.
  3. Можно использовать регистры. Для обращения к памяти, в которой хранится регистр, нужно использовать квадратные скобки: , .
  4. [] — квадратные скобки допускают применение сложных выражений внутри себя для вычисления адреса: .

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

Помимо этого, в ассемблер существуют сокращения: r — для регистров, m — для памяти и i — для операнда. Эти сокращения используются с числами 8, 16 и 32 для указания размера операнда: r8, m16, i32 и т. д.

add i8/i16/i32, m8/m16/m32 ;суммирование операнда с ячейкой памяти

Немного фактов и цифр

В качестве примера того, почему ассемблер — это хорошо, могу привести Дисплейный модуль 128х128,
на микроконтроллере ATMega328P, работающий на частоте 20 МГц. Модуль, вообщем, работает и не тормозит, но захотелось мне его переделать под ATMega8A
на 16 МГц. Преимущества последней очевидны — она штатно умеет работать на максимальной частоте при питании от 3.3В (ATMega328P по даташиту при
таком напряжении гарантированно будет работать только на 10 МГц), стоит в раза в 3-4 дешевле, да и достать её легче. Вообщем, захотелось мне
сделать для ZX-магнитофона дисплейный модуль с питанием от 3.3В на ATMega8A, возможно, с немного урезанным функционалом, но с максимально
компактной и быстрой прошивкой. Прошивка модуля на ATMega328P на тот момент имела размер в почти 20Кб (из которых ровно 7Кб занимали шрифты, 5х7
и 13х15). Шрифты были ужаты до 3.5 КБ путём оптимизации их формата. В результате под код осталось чуть более 4кб флеша (с учётом того,
что надо ещё оставить место для bootloader-а). В итоге, мне удалось впихнуть всю прошивку в примерно 3Кб, сохранив основной функционал, а
именно рисование точек и линий, рисование и заливка прямоугольников, окружностей и текста (шрифтами 5х7 и 13х15, латиница + кириллица + основные
символы). При этом был удалён код работы с клавиатурой, пищалкой и подсветкой (только потому, что в случае ZX-магнитофона он не нужен), но оставшихся
свободных полутора килобайт без проблем хватит на эти вещи. При этом производительность новой прошивки должна быть ощутимо выше, т.к., чем меньше
кода, тем быстрее он будет выполняться.

Инструментарий

Дальнейшее теоретическое изучение ассемблера может быть трудным, поэтому стоит задуматься об инструментах, используемых для разработки программ с его помощью. Здесь будет приведен лишь краткий список популярных средств:

  • Borland Turbo Assembler (TASM) — один из самых популярных инструментов. Хорошо подходит для разработки под DOS и плохо — под Windows.
  • Microsoft Macro Assembler (MASM) — это пакет для разработки на ассемблере в среде Windows. Существует как отдельно, так и в виде встроенной функции в среде Visual Studio. Ассемблер и языки высокого уровня часто совместимы. В том смысле, что последние могут использовать ассемблер напрямую. Например, С++.
  • Netwide Assembler (NASM) — популярный свободный ассемблер для архитектуры Intel.

Существует множество инструментов. При этом следует сделать особую пометку о том, что нет единого стандарта синтаксиса ассемблера. Есть 2 наиболее применимых: AT&T-синтаксис, ориентированный на процессоры производства не Intel, и, соответственно, Intel-синтаксис.

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

Язык ассемблер. Команды и основы ассемблера на News4Auto.ru.

Наша жизнь состоит из будничных мелочей, которые так или иначе влияют на наше самочувствие, настроение и продуктивность. Не выспался — болит голова; выпил кофе, чтобы поправить ситуацию и взбодриться — стал раздражительным. Предусмотреть всё очень хочется, но никак не получается. Да ещё и вокруг все, как заведённые, дают советы: глютен в хлебе — не подходи, убьёт; шоколадка в кармане — прямой путь к выпадению зубов. Мы собираем самые популярные вопросов о здоровье, питании, заболеваниях и даем на них ответы, которые позволят чуть лучше понимать, что полезно для здоровья.

10.42 LDR (immediate offset)

Load with immediate offset, pre-indexed immediate offset, or post-indexed immediate offset.

Syntax

where:

can be any one of:
unsigned Byte (Zero extend to 32 bits on loads.)
signed Byte ( only.
Sign extend to 32 bits.)
unsigned Halfword (Zero extend to 32 bits on loads.)
signed Halfword ( only.
Sign extend to 32 bits.)
omitted, for Word.
is an optional condition code.
is the register to load.
is the register on which the memory address is based.
is an offset. If is
omitted, the address is the contents of .
is the additional register to load for doubleword
operations.

Not all options are available in every instruction set and
architecture.

Offset ranges and architectures

The following table shows the ranges of offsets and availability of these instructions:

Table 10-10 Offsets and architectures, LDR, word, halfword, and
byte

Instruction Immediate offset Pre-indexed Post-indexed Arch.
ARM, word or byte –4095 to 4095 –4095 to 4095 –4095 to 4095 All
ARM, signed byte, halfword, or signed halfword –255 to 255 –255 to 255 –255 to 255 All
ARM, doubleword –255 to 255 –255 to 255 –255 to 255 5E
Thumb 32-bit encoding, word, halfword, signed halfword, byte, or signed byte –255 to 4095 –255 to 255 –255 to 255 T2
Thumb 32-bit encoding, doubleword –1020 to 1020 –1020 to 1020 –1020 to 1020 T2
Thumb 16-bit encoding, word 0 to 124 Not available Not available T
Thumb 16-bit encoding, unsigned halfword 0 to 62 Not available Not available T
Thumb 16-bit encoding, unsigned byte 0 to 31 Not available Not available T
Thumb 16-bit encoding, word, is SP 0 to 1020 Not available Not available T

Notes about the Architectures column

Entries in the Architecture column indicate that the instructions are
available as follows:

All

All versions of the ARM architecture.

5E

The ARMv5TE, ARMv6*, and ARMv7 architectures.

T2

The ARMv6T2 and above architectures.

T

The ARMv4T, ARMv5T*, ARMv6*, and ARMv7 architectures.

must be different from in
the pre-index and post-index forms.

For Thumb instructions, you must not specify SP or PC for
either or .

For ARM instructions:

  • must be an even-numbered register.

  • must not be LR.

  • ARM strongly recommends that you do not use for
    .
  • must be .

Use of PC

In ARM code you can use PC for in
word instructions and PC for in
instructions.

Other uses of PC are not permitted in these ARM instructions.

In Thumb code you can use PC for in
word instructions and PC for in
instructions. Other uses of PC in these Thumb instructions are not permitted.

Use of SP

You can use SP for .

In ARM code, you can use SP for in word instructions. You
can use SP for in non-word instructions in ARM code but this
is deprecated in ARMv6T2 and above.

In Thumb code, you can use SP for in word instructions
only. All other use of SP for in these instructions are not
permitted in Thumb code.

Examples

    LDR     r8,        ; loads R8 from the address in R10.
    LDRNE   r2,!   ; (conditionally) loads R2 from a word
                            ; 960 bytes above the address in R5, and
                            ; increments R5 by 960.
Related reference

10.8 Condition code suffixes

 
For word loads, Rt can be the PC. A load to the PC causes a
branch to the address loaded. In ARMv4, bits of the address loaded must be
0b00. In ARMv5T and above, bits must not be 0b10, and if bit is 1,
execution continues in Thumb state, otherwise execution continues in ARM
state.

 
Must be divisible by 4.

 
Rt and Rn must be in the range R0-R7.

 
Must be divisible by 2.

Общая информация

Компилятор транслирует исходные коды с языка ассемблера в объектный код.
Полученный объектный код можно использовать в симуляторе ATMEL AVR Studio, либо
в эмуляторе ATMEL AVR In-Circuit Emulator. Компилятор также генерирует код,
который может быть непосредственно запрограммирован в микроконтроллеры AVR.

Компилятор генерирует код, который не требует линковки.

Компилятор работает под Microsoft Windows 3.11, Microsoft Windows95 и
Microsoft Windows NT. Кроме этого есть консольная версия для MS-DOS.

Набор инструкций семейства микроконтроллеров AVR описан в данном документе
кратко, для более полной информации по инструкциям обращайтесь к полному
описанию инструкций и документации по конкретному микроконтроллеру.

Почему ассемблер для AVR — это хорошо

Не смотря на все утверждения о том, что современные компиляторы научились отлично оптимизировать код лучше человека, это не так. По кр.мере,
применительно к AVR GCC. Практика показывает, что создаваемый им код может быть ощутимо улучшен по размеру (и, соответственно, скорости выполнения).
Взять, например, обработчики прерываний, которые должны сохранять все используемые регистры на старте и восстанавливать их при завершении.
Так вот тут AVR GCC часто использует в обработчике регистров больше, чем надо (т.е., использует несколько разных регистров там, где можно было
бы использовать один и тот же регистр повторно). И, соответственно, имеем лишнюю работу по сохранению/восстановлению из стека (это при том,
что операции со стеком занимают по два машинных цикла). Оптимизация обработчиков прерываний особенно важна если они вызываются десятки тысяч
раз в секунду, тогда даже устранение одной лишней пары PUSH/POP даст ощутимую экономию ресурсов CPU.

Другой пример — функции, работающие с десятком (примерно) переменных и аргументов. GCC может свободно использовать большую часть верхних регистров
(r18 — r31, подробнее см. тут) в коде Си-функции не заботясь о их сохранении. Если же этих
регистров ему немного не хватило, компилятор вызывает довольно “жирные” подпрограммы и
при входе в функцию и выходе из неё. Функции эти сохраняют на входе стеке и восстанавливают на
выходе все нижние регистры (все, без разбора). Это также даёт ощутимый оверхед. При том, что часто этой же функции, переписанной руками,
будет достаточно доступных регистров, и сохранять вообще ничего не придётся.

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

Итого, на ассемблере имеет смысл почти всегда писать обработчики прерываний и наиболее требовательные к производительности функции. Также на нём
можно писать совсем простые прошивки. И, да, если писать код целиком на ассемблере, то можно свободно использовать в нём дополнительные регистры
r0-r17, которые GCC использует по своему усмотрению (и при написании ассемблерных процедур, вызываемых из Си-кода, программист должен заботиться о
сохранении и восстановлении этих регистров).

Один наглядный пример

В качестве примера рассмотрим простейший обработчик прерывания, который инкрементирует uint16-переменную при переполнении
таймера. С-код тривиален:

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

Что тут происходит:

  • Зачем-то сохраняется регистр r1, который в обработчике не используется. Далее r1 зануляется
  • Зачем-то сохраняется регистр r0, а затем SREG
  • Выполняется инкремент счётчика
  • Сохранённые регистры восстанавливаются из стека, делается возврат из прерывания

Итого 19 команд и 46 байт. При том, что сохранять r0 и r1 тут нет никакой необходимости, обработчик можно было бы переписать так:

Итого получается 14 команд (при том, что мы избавились от 4х лишних push/pop команд, выполнение которых занимает по 2 такта).
Если бы в обработчике не было команды adiw, и SREG бы не модифицировался, то можно было бы убрать ещё 4 инструкции.
GCC же тут использует стандартный шаблон для обработчика прерываний, в котором сохраняет всё, что может быть изменено в Си-коде.

Стек

Это область памяти, выделенная для работы процедур. Особенность стека заключается в том, что последние данные, записанные в него, доступны для чтения первыми. Или иными словами: первые записи стека извлекаются последними. Представить этот процесс себе можно в качестве башни из шашек. Чтобы достать шашку (нижнюю шашку в основание башни или любую в середине) нужно сначала снять все, которые лежат сверху. И, соответственно, последняя положенная на башню шашка, при разборе башни снимается первой. Такой принцип организации памяти и работы с ней продиктован ее экономией. Стек постоянно очищается и в каждый момент времени одна процедура использует его.

Рассмотрим команды ассемблера на практическом примере.

С использованием среды разработки TASMED или любого текстового редактора набираем код. Программа, задаст вопрос на английском языке о половой принадлежности (имеется ввиду ваш биологический пол при рождении). Если вы нажмете m (Man), будет выведено приветствие с мужчиной, если w (Woman), то с женщиной, после этого программа прекратит работу. Если будет нажата любая другая клавиша, то программа предположит, что имеет дело с гоблином, не поверит и будет задавать вам вопросы о половой принадлежности, пока вы не ответите верно.

;goblin.asm
.model tiny ; for СОМ
.code ; code segment start
org 100h ; offset in memory = 100h (for COM)

start: main proc
begin:
mov ah,09h
mov dx,offset prompt
int 21h
inpt:
mov ah,01h
int 21h
cmp al,’m’
je mode_man
cmp al,’w’
je mode_woman
call goblin
jmp begin
mode_man:
mov addrs,offset man; указатель на процедуру в addrs
jmp cont
mode_woman:
mov addrs,offset woman; указатель на процедуру в addrs
cont:
call word ptr addrs; косвенный вызов процедуры
mov ax,4c00h
int 21h
main endp

man proc
mov ah,09h
mov dx,offset mes_man
int 21h
ret
man endp

woman proc
mov ah,09h
mov dx,offset mes_womn
int 21h
ret
woman endp

goblin proc
mov ah,09h
mov dx,offset mes_gobl
int 21h
ret
goblin endp

;DATA
addrs dw 0;for procedure adress
prompt db ‘Are you Man or Woman [m/w]? : $’
mes_man db 0Dh,0Ah,»Hello, Strong Man!»,0Dh,0Ah,’$’ ; строка для вывода. Вместо ASCII смвола ‘$’ можно написать машинный код 24h
mes_womn db 0Dh,0Ah,»Hello, Beautyful Woman!»,0Dh,0Ah,’$’ ; строка для вывода
mes_gobl db 0Dh,0Ah,»Hello, Strong and Beautyful GOBLIN!»,0Dh,0Ah,24h ; строка для вывода. 24h = ‘$’ .
len = $ — mes_gobl
end start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

;goblin.asm

.modeltiny; for СОМ

.code; code segment start

org100h; offset in memory = 100h (for COM)

startmainproc

begin

movah,09h

movdx,offsetprompt

int21h

inpt

movah,01h

int21h

cmpal,’m’

jemode_man

cmpal,’w’

jemode_woman

callgoblin

jmpbegin

mode_man

movaddrs,offsetman; указатель на процедуру в addrs

jmpcont

mode_woman

movaddrs,offsetwoman; указатель на процедуру в addrs

cont

callwordptraddrs; косвенный вызов процедуры

movax,4c00h

int21h

mainendp

manproc

movah,09h

movdx,offsetmes_man

int21h

ret

manendp

womanproc

movah,09h

movdx,offsetmes_womn

int21h

ret

womanendp

goblinproc

movah,09h

movdx,offsetmes_gobl

int21h

ret

goblinendp

 
;DATA

addrsdw;for procedure adress

promptdb’Are you Man or Woman [m/w]? : $’

mes_mandb0Dh,0Ah,»Hello, Strong Man!»,0Dh,0Ah,’$’; строка для вывода. Вместо ASCII смвола ‘$’ можно написать машинный код 24h

mes_womndb0Dh,0Ah,»Hello, Beautyful Woman!»,0Dh,0Ah,’$’; строка для вывода

mes_gobldb0Dh,0Ah,»Hello, Strong and Beautyful GOBLIN!»,0Dh,0Ah,24h; строка для вывода. 24h = ‘$’ .

len=$-mes_gobl

endstart