Arm команди асемблера. Вивчення системи команд процесора ARM
Привіт всім!
За діяльністю я програміст на Java. Останні місяці роботи змусили мене познайомитися з розробкою під Android NDK і відповідно. нативних додатківна С. Тут я зіткнувся з проблемою оптимізації бібліотек Linux. Багато хто виявився абсолютно не оптимізований під ARM і сильно навантажував процесор. Раніше я практично не програмував на асемблері, тому спочатку було складно почати вивчати цю мову, але все ж таки я вирішив спробувати. Цю статтю написано, так би мовити, від новачка для новачків. Я постараюся описати ті основи, які вже вивчив, сподіваюся, когось це зацікавить. Крім того, радий конструктивній критиці з боку професіоналів.
Вступ
Отже, спочатку розберемося що таке ARM. Вікіпедія дає таке визначення:Архітектура ARM (Advanced RISC Machine, Acorn RISC Machine, вдосконалена RISC-машина) - сімейство 32-бітних і 64-бітових мікропроцесорних ядер, що ліцензуються розробки компанії ARM Limited. Компанія займається виключно розробкою ядер та інструментів для них (компілятори, засоби налагодження тощо), заробляючи на ліцензуванні архітектури стороннім виробникам.
Якщо хто не знає, зараз більша частина мобільних пристроїв, планшети розроблені саме на цій архітектурі процесорів. Основною перевагою даного сімейства є низьке енергоспоживаннязавдяки чому він часто використовується в різних вбудованих системах. Архітектура розвивалася з часом, і починаючи з ARMv7 було визначено 3 профілю: 'A'(application) - додатки, 'R'(real time) - у час,'M'(microcontroller) - микроконтроллер. Історію розробки цієї технології та інші цікаві дані ви можете прочитати у Вікіпедії або погугливши в інтернеті. ARM підтримує різні режими роботи (Thumb та ARM, крім того останнім часом з'явився Thumb-2, що є сумішшю ARM та Thumb). У цій статті розглянемо власне режим ARM, у якому здійснюється 32-бітовий набір команд.
Кожен процесор ARM створений з наступних блоків:
- 37 регістрів (з яких видимих при розробці лише 17)
- Арифметико-логічний пристрій (АЛУ) - виконує арифметичні та логічні завдання
- Barrel shifter - пристрій, створений для переміщення блоків даних на певну кількість біт
- The CP15 - спеціальна система, що контролює ARM співпроцесори
- Декодер інструкцій - займається перетворенням інструкції на послідовність мікрооперацій
Конвеєрне виконання (Pipeline execution)
В ARM процесорах використовується 3-стадійний конвеєр (починаючи з ARM8 був реалізований 5-стадійний конвеєр). Розглянемо простий конвеєр з прикладу процесора ARM7TDMI. Виконання кожної інструкції складається з трьох ступенів:1. Етап вибірки (F)
На цьому етапі інструкції надходять із ОЗП в конвеєр процесора.
2. Етап декодування (D)
Інструкції декодуються та розпізнається їх тип.
3. Етап виконання (E)
Дані надходять в ALU і виконуються та отримане значення записується в заданий регістр.
Але при розробці треба враховувати, що є інструкції, які використовують кілька циклів виконання, наприклад, load(LDR) або store. У такому разі етап виконання (E) поділяється на етапи (E1, E2, E3...).
Умовне виконання
Одна з найважливіших функцій ARM асемблера – умовне виконання. Кожна інструкція може виконуватися умовно, і для цього використовуються суфікси. Якщо суфікс додається до назви інструкції, перш ніж виконати її, відбувається перевірка параметрів. Якщо параметри не відповідають умові, інструкція не виконується. Суфікси:MI - негативне число
PL - позитивне або нуль
AL - виконувати інструкцію завжди
Суфіксів умовного виконання набагато більше. Інші суфікси та приклади прочитати в офіційній документації: ARM документація
А тепер настав час розглянути…
Основи синтаксису ARM асемблера
Тим, хто раніше працював із асемблером, цей пункт можна фактично пропустити. Для решти опишу основи роботи з цією мовою. Отже, кожна програма на асемблері складається з інструкцій. Інструкція створюється таким чином:(мітка) (інструкція | операнди) (@ коментар)
Мітка – необов'язковий параметр. Інструкція – безпосередньо мнемоніка інструкції процесору. Основні інструкції та їх використання буде розібрано далі. Операнди - константи, адреси регістрів, адреси оперативної пам'яті. Коментар - необов'язковий параметр, який впливає виконання програми.
Імена регістрів
Дозволено такі імена регістрів:1.r0-r15
3.v1-v8 (змінні регістри, з r4 по r11)
4.sb and SB (статичний регістр, r9)
5.sl і SL (r10)
6.fp and FP (R11)
7.ip and IP (R12)
8.sp and SP (R13)
9.lr та LR (r14)
10.pc and PC (програмний лічильник, r15).
Змінні та костанти
В асимблері ARM, як і будь-якій (практично) іншій мові програмування можуть використовуватися змінні та константи. Вони поділяються на такі типи:- Числові
- Логічні
- Рядкові
a SETA 100; створюється числова змінна "a" зі значенням 100.
Рядкові змінні:
improb SETS "literal"; створюється змінна improb з значенням «literal». УВАГА! Значення змінної не може перевищувати 5120 символів.
У логічних змінних відповідно використовуються значення TRUE та FALSE.
Приклади інструкцій ARM асемблера
У цій таблиці я зібрав основні інструкції, яка буде потрібна для подальшої розробки (на самому базовому етапі:):Щоб закріпити використання основних інструкцій, давайте напишемо кілька простих прикладів, але спочатку нам знадобиться arm toolchain. Я працюю в Linux тому вибрав: frank.harvard.edu/~coldwell/toolchain (arm-unknown-linux-gnu toolchain). Ставиться він простіше простого, як будь-яка інша програма на Linux. У моєму випадку (Russian Fedora) знадобилося лише встановити rpm пакети із сайту.
Тепер настав час написати найпростіший приклад. Програма буде абсолютно марною, але головне, що працюватиме:) Ось код, який я вам пропоную:
start: @ Необов'язковий рядок, що означає початок програми mov r0, #3 @ Завантажуємо в регістр r0 значення 3 mov r1, #2 @ Робимо теж саме з регістром r1, тільки тепер із значенням 2 add r2, r1, r0 @ Складаємо значення r0 і r1, відповідь записуємо в r2 mul r3, r1, r0 @ Помножуємо значення регістру r1 на значення регістра r0, відповідь записуємо в r3 stop: b stop @ Рядок завершення програми
Компілюємо програму до отримання.bin файлу:
/usr/arm/bin/arm-unknown-linux-gnu-as -o arm.o arm.s /usr/arm/bin/arm-unknown-linux-gnu-ld -Ttext=0x0 -o arm.elf arm .o /usr/arm/bin/arm-unknown-linux-gnu-objcopy -O binary arm.elf arm.bin
(код у файлі arm.s, а toolchain в моєму випадку лежить у директорії /usr/arm/bin/)
Якщо все пройшло успішно, у вас буде 3 файли: arm.s (власне код), arm.o, arm.elf, arm.bin (власне програма, що виконується). Для того, щоб перевірити роботу програми, не обов'язково мати власний arm пристрій. Достатньо встановити QEMU. Для довідки:
QEMU – вільна програма з відкритим вихідним кодомдля емуляції апаратного забезпечення різних платформ.Включає емуляцію процесорів Intel x86 та пристроїв введення-виведення. Може емулювати 80386, 80486, Pentium, Pentium Pro, AMD64 та інші x86-сумісні процесори; PowerPC, ARM, MIPS, SPARC, SPARC64, m68k – лише частково.
Працює на Syllable, FreeBSD, FreeDOS, Linux, Windows 9x, Windows 2000, Mac OS X, QNX, Android та ін.
Отже, для емуляції арм знадобиться qemu-system-arm. Цей пакет є в yum, тому тим, у кого Fedora, можна не морочитися і просто виконати команду:
yum install qemu-system-arm
Далі треба запустити емулятор ARM так, щоб він виконав нашу програму arm.bin. Для цього створимо файл flash.bin, який буде флеш-пам'яттю для QEMU. Зробити це дуже просто:
dd if=/dev/zero of=flash.bin bs=4096 count=4096 dd if=arm.bin of=flash.bin bs=4096 conv=notrunc
Тепер вантажимо QEMU з отриманою flash пам'яттю:
qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
На виході ви отримаєте щось на кшталт цього:
$ qemu-system-arm -M connex -pflash flash.bin -nographic -serial /dev/null
QEMU 0.15.1 monitor - type "help" for more information
(qemu)
Наша програма arm.bin мала змінити значення чотирьох регістрів, отже для перевірки правильності роботи давайте подивимося на ці самі регістри. Робиться це дуже простою командою: info registers
На виході ви побачите всі 15 ARM регістрів, причому чотири з них будуть змінені значення. Перевірте:) Значення регістрів збігаються з тими, які можна очікувати після виконання програми:
(qemu) info registers R00=00000003 R01=00000002 R02=00000005 R03=00000006 R04=00000000 R05=00000000 R06=00000000 R000 000000 R10=00000000 R11=00000000 R12=00000000 R13=00000000 R14=00000000 R15=00000010 PSR=400001d3 -Z-- A svc32
P.S. У цій статті я постарався описати основи програмування на ARM асемблер. Сподіваюсь вам сподобалось! Цього вистачить для того, щоб далі заглиблюватись у нетрі цієї мови та писати на ній програми. Якщо все вийде, писатиму далі про те, що дізнаюся сам. Якщо є помилки, прошу не штовхати, тому що я новачок в асемблері.
Спочатку ARMдосить незвичний асемблер (якщо переучуватися з x86, MCS51або AVR). Але в нього досить проста та логічна організація, тому засвоюється швидко.
Документації російською по асемблеру зовсім мало. Можу порадити зайти на 2 посилання (може, Ви знайдете більше, і підкажете мені? Буду вдячний):
Архітектура та система команд RISС-процесорів сімейства ARM - http://www.gaw.ru/html.cgi/txt/doc/micros/arm/index.htm
Осягаємо асемблер ARM, з циклу статей GBA ASM, автор Mike H, пров. Aquila - http://wasm.ru/series.php?sid=21.
Останнє посилання мені дуже допомогло, розвіяло туман =). Друге, що добре може допомогти - це, як не дивно, C-компілятор IAR Embedded Workbench for ARM(Далі просто IAR EW ARM). Справа в тому, що він з давніх часів вміє (як і всі компілятори, що поважають себе, втім) компілювати C-код в код асемблера, який, у свою чергу, так само легко компілюється асемблером IAR в об'єктний код. Тому немає нічого кращого написати найпростішу функцію на C, скомпілювати її в асемблер, і одразу стане зрозуміло, яка команда асемблера що робить, як передаються аргументи і як повертається результат. Вбиваєте відразу двох зайців - навчаєтесь асемблеру і заразом отримуєте інформацію, як інтергрувати асемблерний код у проект на C. Я тренувався на функції підрахунку CRC16, і в результаті отримав її повноцінну версіюна асемблері.
Ось вихідна функція на C (u16 означає unsigned short, u32 - unsigned int, u8 - unsigned char):
// файл crc16.c
u16 CRC16 (void* databuf, u32 size)
{
u16 tmpWord, crc16, idx;
u8 bitCnt;
#define CRC_POLY 0x1021;
Crc16 = 0;
idx=0;
while (size! = 0)
{
/* складемо xor старший байт crc16 і вхідний байт */
tmpWord = (crc16>>8) ^ (*(((u8*)databuf)+idx));
/* результат запишемо в старший байт crc16 */
tmpWord<<= 8;
crc16 = tmpWord + (0x00FF & crc16);
for (bitCnt=8;bitCnt!=0;bitCnt--)
{
/* перевіримо старший розряд акумулятора CRC */
if (crc16 & 0x8000)
{
crc16<<= 1;
crc16 ^= CRC_POLY;
}
else
crc16<<= 1;
}
idx++;
size--;
}
return crc16;
}
Змусити генерувати код асемблера IAR EW ARM дуже просто. У опціях файлу crc16.c (доданого до проекту) поставив галку Override inherited settings, а потім на закладці List поставив 3 галки - Output assembler file, Include sourceі Include call frame information(хоча останню галку, напевно, можна не ставити – вона генерує купу непотрібних CFI-Директив). Після компіляції вийшов файл папка_проекту \ewp\at91sam7x256_sram\List\crc16.s. Цей файл також легко можна додати в проект, як і файл C (він буде нормально компілюватися).
Звичайно, коли я підсунув C-компілятор необрізаний варіант C-коду, то він мені видав такий лістинг асемблера, що я нічого в ньому не зрозумів. Але коли викинув із функції всі C-оператори, окрім одного, стало зрозуміліше. Потім крок за кроком додавав C-оператори, і ось у результаті вийшло:
; файл crc16.s
NAME crc16
PUBLIC CRC16
CRC_POLY EQU 0x1021
SECTION `.text`:CODE:NOROOT(2)
ARM
// u16 CRC16 (void* databuf, u32 size)
;R0 - результат повернення, CRC16
;R1 - параметр size
R2 - параметр databuf (він був при вході в R0)
;R3, R12 - тимчасові регістри
CRC16:
PUSH (R3, R12); методом тику з'ясував, що R3 і R13 зберігати
; не обов'язково. Але вирішив зберегти на всякий
; випадок.
MOVS R2,R0 ;тепер R2==databuf
MOV R3, #+0
MOVS R0, R3; crc16 = 0
CRC16_LOOP:
CMP R1, # + 0; всі байти обробили (size = = 0)?
BEQ CRC16_RETURN ;якщо так, то вихід
LSR R3, R0, # +8; R3 = crc16>>8
LDRB R12, ;R12 = *databuf
EOR R3, R3, R12; R3 = * databuf ^ HIGH (crc16)
LSL R3, R3, # +8; R3<<= 8 (tmpWord <<= 8)
AND R0, R0, #+255; crc16 &= 0x00FF
ADD R0, R0, R3; crc16 = tmpWord + (0x00FF & crc16)
MOV R12, # +8; bitCnt = 8
CRC16_BIT_LOOP:
BEQ CRC16_NEXT_BYTE ;bitCnt == 0?
TST R0,#0x8000 ;Ще в повному обсязі біти оброблені.
BEQ CRC16_BIT15ZERO ;Перевіряємо старший біт crc16.
LSL R0, R0, # +1; crc16<<= 1
MOV R3, # + (LOW (CRC_POLY)); crc16 ^ = CRC_POLY
ORR R3, R3, # + (HIGH (CRC_POLY)<< 8) ;
EOR R0, R3, R0;
B CRC16_NEXT_BIT
CRC16_BIT15ZERO:
LSL R0, R0, # +1; crc16<<= 1
CRC16_NEXT_BIT:
SUBS R12, R12, # +1; bitCnt--
B CRC16_BIT_LOOP;
CRC16_NEXT_BYTE:
ADD R2,R2,#+1;databuf++
SUBS R1,R1,#+1 ;size--
B CRC16_LOOP ;цикл по всіх байтах
CRC16_RETURN:
POP (R3, R12); відновлюємо регістри
BX LR ;вихід із підпрограми, R0==crc16
Компілятор C від IAR робить напрочуд хороший код. Мені зовсім мало вдалося його оптимізувати. Викинув лише зайвий тимчасовий регістр, який хотів використати компілятор (він чомусь взяв як зайвий тимчасовий регістр LR, хоча R3 і R12 було достатньо), а також прибрав пару зайвих команд, які перевіряють лічильники та виставляють прапори (просто додавши суфікс S до потрібних командам).
Отже, ми створили новий проект, виконали основні налаштування, створили та підключили до проекту файл, у якому хочемо написати на асемблері якусь простеньку програму.
Що далі? Далі, власне, можна писати програму, використовуючи набір команд thumb-2, що підтримується ядром Cortex-M3. Список та опис підтримуваних команд можна переглянути в документі під назвою Cortex-M3 Generic User Guide(глава The Cortex-M3 Instruction Set), який можна знайти на вкладці Books в менеджері проекту, в Keil uVision 5. Детально про команди thumb-2 буде написано в одній з наступних частин цієї статті, а поки що поговоримо про програми для STM32 загалом.
Як і будь-яка інша програма на асемблері, програма для STM32 складається з команд і псевдокоманд, які будуть трансльовані безпосередньо в машинні коди, а також різних директив, які в машинні коди не транслюються, а використовуються в службових цілях (розмітка програми, присвоєння константам символьних імен і т.д.)
Наприклад, розбити програму на окремі секції дозволяє спеціальна директива. AREA. Вона має наступний синтаксис: AREA Section_Name (, type) (, attr) …, де:
- Section_name- Ім'я секції.
- type- Тип секції. Для секції, що містить дані, потрібно вказувати тип DATA, а для секції, що містить команди, — тип CODE.
- attr- Додаткові атрибути. Наприклад, атрибути readonly або readwrite вказують в якій пам'яті повинна розміщуватися секція, атрибут align=0..31 вказує яким чином секція повинна бути вирівняна в пам'яті, атрибут noinit використовується для виділення областей пам'яті, які не потрібно ініціалізувати або ініціалізуються нулями (при використанні цього атрибуту можна не вказувати тип секції, оскільки він може використовуватися тільки для даних секцій).
Директива EQUнапевно всім добре знайома, оскільки зустрічається в будь-якому асемблері і призначена для присвоєння символьних імен різним константам, осередкам пам'яті і т.д. Вона має наступний синтаксис: Name EQU numberі повідомляє компілятору, що всі символьні позначення, що зустрічаються Nameпотрібно замінювати на число number. Скажімо, якщо як numberвикористовувати адресу комірки пам'яті, то надалі до цієї комірки можна буде звертатися не за адресою, а використовуючи еквівалентне символьне позначення ( Name).
Директива GET filenameвставляє у програму текст із файлу з ім'ям filename. Це аналог директиви include в асемблері для AVR. Її можна використовувати, наприклад, щоб винести в окремий файл директиви присвоєння символьних імен різним регістрам. Тобто ми виносимо всі присвоєння імен до окремого файлу, а потім, щоб у програмі можна було користуватися цими символьними іменами, просто включаємо цей файл до нашої програми директивою GET.
Зрозуміло, крім перерахованих вище є ще купа різних директив, повний список яких можна знайти в розділі Directives Referenceдокумента Assembler User Guide, який можна знайти в Keil uVision 5 наступним шляхом: вкладка Booksменеджера проектів -> Tools User’s Guide -> Complete User’s Guide Selection -> Assembler User Guide.
Більшість команд, псевдокоманд та директив у програмі мають наступний синтаксис:
(label) SYMBOL (expr) (, expr) (, expr) (; коментар)
(label) - мітка. Вона потрібна для того, щоб можна було визначити адресу наступної команди. Мітка є необов'язковим елементом і використовується лише коли необхідно дізнатися адресу команди (наприклад, щоб перейти на цю команду). Перед міткою не повинно бути пробілів (тобто вона повинна починатися з першої позиції рядка), крім того, ім'я мітки може починатися тільки з літери.
SYMBOL – команда, псевдокоманда чи директива. Команда, на відміну від мітки, навпаки, повинна мати певний відступ від початку рядка, навіть якщо перед нею немає мітки.
(expr) (, expr) (, expr) - операнди (регістри, константи ...)
; - Розділювач. Весь текст у рядку після цього роздільника сприймається як коментар.
Ну а тепер, як і обіцяв, найпростіша програма:
AREA START, CODE, READONLY dcd 0x20000400 dcd Program_start ENTRY Program_start b Program_start END |
AREA START, CODE, READONLY dcd 0x20000400 dcd Program_start ENTRY Program_start b Program_start END
У цій програмі у нас лише одна секція, яка називається START. Ця секція розміщується у flash-пам'яті (оскільки для неї використаний атрибут readonly).
Перші 4 байти цієї секції містять адресу вершини стека (у нашому випадку 0x20000400), а другі 4 байти - адреса точки входу (початок коду, що виконується). Далі слідує сам код. У найпростішому прикладі виконуваний код складається з однієї єдиної команди безумовного переходу на мітку Program_start, тобто знову виконання цієї команди.
Оскільки секція у флеші всього одна, то в scatter-файлі для нашої програми як First_Section_Name потрібно буде вказати саме її ім'я (тобто START).
В даному випадку у нас перемішані дані та команди. Адреса вершини стека та адреса точки входу (дані) записані за допомогою директив dcd прямо у секції коду. Так писати, звичайно, можна, але не дуже красиво. Особливо, якщо ми будемо описувати всю таблицю переривань і винятків (яка буде досить довгою), а не тільки вектор скидання. Набагато красивіше не захаращувати код зайвими даними, а помістити таблицю векторів переривань в окрему секцію, а ще краще - в окремий файл. Аналогічно в окремій секції або навіть файлі можна розмістити і ініціалізацію стека. Ми, наприклад, розмістимо все в окремих секціях:
AREA STACK, NOINIT, READWRITE SPACE 0x400; пропускаємо 400 байт Stack_top; та ставимо мітку AREA RESET, DATA, READONLY dcd Stack_top; адреса мітки Stack_top dcd Program_start; адреса теги Program_start AREA PROGRAM, CODE, READONLY ENTRY ; точка входу (початок виконуваного коду) Program_start; мітка початку програми b Program_start END
Ну ось, та сама програма (яка як і раніше не робить ніфіга корисного), але тепер виглядає набагато наочніше. У scatter-файлі для цієї програми потрібно вказати як First_Section_Name ім'я RESET, щоб ця секція розташовувалась у flash-пам'яті першою.
У цьому розділі описано набори інструкцій процесора ARM7TDMI.
4.1 Короткий опис формату
У цьому розділі наведено короткий опис наборів інструкцій ARM та Thumb.
Ключ до таблиць наборів інструкцій представлено таблиці 1.1.
Процесор ARM7TDMI виконаний з урахуванням архітектури ARMv4T. Більш повний опис обох наборів інструкцій наведено в "ARM Architecture Reference Manual".
Таблиця 1.1. Ключ до таблиць
Формати набору інструкцій ARM показані малюнку 1.5.
Більш детальна інформація щодо форматів набору інструкцій ARM наведена у "ARM Architectural Reference Manual".
Малюнок 1.5. Формати набору інструкцій ARM
Деякі коди інструкцій не визначені, але не викликають пошуку невизначених інструкцій, наприклад, інструкція множення з бітом 6 зміненим до 1. Забороняється використовувати такі інструкції, т.к. у майбутньому їхня дія може бути змінена. Результат виконання даних кодів інструкцій у складі процесора ARM7TDMI непередбачуваний.
4.2 Короткий опис інструкцій ARM
Набір інструкцій ARM наведено в таблиці 1.2.
Таблиця 1.2. Короткий опис інструкцій ARM
Операції | Синтаксис Асемблера | |
Пересилання | Пересилання | MOV (cond)(S) Rd, |
Пересилання NOT | MVN (cond)(S) Rd, |
|
Пересилання SPSR в регістр | MRS (cond) Rd, SPSR | |
Пересилання CPSR у регістр | MRS (cond) Rd, CPSR | |
Пересилання регістру SPSR | MSR (cond) SPSR (field), Rm | |
Пересилання CPSR | MSR (cond) CPSR (field), Rm | |
Пересилання константи у прапори SPSR | MSR (cond) SPSR_f, #32bit_Imm | |
Пересилання константи у прапори CPSR | MSR (cond) CPSR_f, #32bit_Imm | |
Арифметичні | Додавання | ADD (cond) (S) Rd, Rn, |
Додавання з перенесенням | ADC (cond)(S) Rd, Rn, |
|
Віднімання | SUB (cond)(S) Rd, Rn, |
|
Віднімання з перенесенням | SBC (cond)(S) Rd, Rn, |
|
Віднімання зворотного віднімання | RSB (cond)(S) Rd, Rn, |
|
Віднімання зворотного віднімання з перенесенням | RSC (cond)(S) Rd, Rn, |
|
множення | MUL (cond)(S) Rd, Rm, Rs | |
Множення-накопичення | MLA (cond)(S) Rd, Rm, Rs, Rn | |
Умноження довгих беззнакових чисел | UMULL | |
Множення - беззнакове накопичення довгих значень | UMLAL (cond)(S) RdLo, RdHi, Rm, Rs | |
Примноження знакових довгих | SMULL (cond)(S) RdLo, RdHi, Rm, Rs | |
Множення - знакове накопичення довгих значень | SMLAL (cond)(S) RdLo, RdHi, Rm, Rs | |
Порівняння | CMP (cond) Rd, |
|
Порівняння негативне | CMN (cond) Rd, |
|
Логічні | Перевірка | TST (cond) Rn, |
Перевірка на еквівалентність | TEQ (cond) Rn, |
|
Лог. І | AND (cond)(S) Rd, Rn, |
|
Викл. АБО | EOR (cond)(S) Rd, Rn, |
|
ORR | ORR (cond)(S) Rd, Rn, |
|
Скидання біта | BIC (cond)(S) Rd, Rn, |
|
Перехід | Перехід | (cond) label |
Перехід за посиланням | (cond) label | |
Перехід та зміна набору інструкцій | (cond) Rn | |
Читання | слова | LDR (cond) Rd, |
LDR (cond)T Rd, |
||
байта | LDR (cond)B Rd, |
|
LDR (cond)BT Rd, |
||
байта зі знаком | LDR (cond) SB Rd, |
|
півслова | LDR (cond) H Rd, |
|
півслова зі знаком | LDR (cond)SH Rd, |
|
операції з кількома блоками даних | - | |
LDM (cond) IB Rd (, !} |
||
LDM (cond) IA Rd (, !} |
||
LDM (cond) DB Rd (, !} |
||
LDM (cond)DA Rd(, !} |
||
LDM (cond) |
||
LDM (cond) |
||
операція над стеком з регістрами користувача | LDM (cond) |
|
Запис | слова | STR (cond) Rd, |
слова з перевагою режиму користувача | STR (cond)T Rd, |
|
байта | STR (cond) B Rd, |
|
байта з перевагою режиму користувача | STR (cond)BT Rd, |
|
півслова | STR (cond) H Rd, |
|
операції над кількома блоками даних | - | |
STM (cond) IB Rd (, !} |
||
STM (cond) IA Rd (, !} |
||
STM (cond) DB Rd (, !} |
||
o з наступним декрементом | STM (cond)DA Rd(, !} |
|
STM (cond) |
||
STM (cond) |
||
Обмін | слів | SWP (cond) Rd, Rm, |
байт | SWP (cond) B Rd, Rm, | |
Співпроцесор | Операція над даними | CDP (cond) p |
Пересилання в ARM-реєстр із співпроцесора | MRC (cond) p |
|
Пересилання в співпроцесор з ARM-реєстру | MCR (cond) p |
|
Читання | LDC (cond) p |
|
Запис | STC (cond) p |
|
Програмне переривання | SWI 24bit_Imm |
Докладно ознайомитися із системою команд у режимі ARM можна.
Режими адресації
Режими адресації - процедури, що використовуються різними інструкціями для створення значень, що використовуються інструкціями. Процесор ARM7TDMI підтримує 5 режимів адресації:
- Режим 1 – Зсувні операнди для інструкцій обробки даних.
- Режим 2 - Читання та запис слова або беззнакового байта.
- Режим 3 - Читання та запис напівслова або завантаження знакового байта.
- Режим 4 - Численні читання та запис.
- Режим 5 - Читання та запис співпроцесора.
Режими адресації із зазначенням їх типів та мнемонічних кодів представлені в таблиці 1.3.
Таблиця 1.3. Режими адресації
Режим адресації | Тип або режим адресації | Мнемонічний код або тип стеку |
Режим 2 |
Константа усунення | |
Регістр усунення | ||
Масштабний регістр усунення | ||
Попереднє індексоване зміщення | - | |
Константа | ! | |
Реєстр | ! | |
Масштабний регістр | ! | |
! | ||
! | ||
! | ||
! | ||
- | ||
Константа | , #+/-12bit_Offset | |
Реєстр | , +/-Rm | |
Масштабний регістр | ||
Режим 2, привілейований |
Константа усунення | |
Регістр усунення | ||
Масштабний регістр усунення | ||
Зміщення з наступним індексуванням | - | |
Константа | , #+/-12bit_Offset | |
Реєстр | , +/-Rm | |
Масштабний регістр | , +/-Rm, LSL #5bit_shift_imm | |
, +/-Rm, LSR #5bit_shift_imm | ||
, +/-Rm, ASR #5bit_shift_imm | ||
, +/-Rm, ROR #5bit_shift_imm | ||
Режим 3, |
Константа усунення | |
! | ||
Наступне індексування | , #+/-8bit_Offset | |
Реєстр | ||
Попереднє індексування | ! | |
Наступне індексування | , +/-Rm | |
Режим 4, читання |
IA, наступний інкремент | FD, full descending |
ED, empty descending | ||
DA, наступний декремент | FA, full ascending | |
DB попередній декремент | EA, empty ascending | |
Режим 4, запис |
IA, наступний інкремент | FD, full descending |
IB, попередній інкремент | ED, empty descending | |
DA, наступний декремент | FA, full ascending | |
DB попередній декремент | EA, empty ascending | |
Режим 5, передача даних співпроцесора |
Константа усунення | |
Попереднє індексування | ! | |
Наступне індексування | , #+/-(8bit_Offset*4) |
Операнд 2
Операнд є частиною інструкції, яка посилається на дані або периферійний пристрій. Операнди 2 представлені у таблиці 1.4.
Таблиця 1.4. Операнд 2
Поля представлені у таблиці 1.5.
Таблиця 1.5. Поля
Поля умов
Поля умов подано у таблиці 1.6.
Таблиця 1.6. Поля умов
Тип поля | Суфікс | Опис | Умова |
Умова (cond) | EQ | Рівно | Z=1 |
NE | Не дорівнює | Z=0 | |
CS | Беззнакове більше чи одно | C=1 | |
CC | Беззнакове менше | C=0 | |
MI | Негативне | N=1 | |
PL | Позитивне або нуль | N=0 | |
VS | Переповнення | V=1 | |
VC | Немає переповнення | V=0 | |
HI | Беззнакове більше | C=1, Z=0 | |
LS | Беззнакове менше чи одно | C=0, Z=1 | |
GE | Більше або дорівнює | N=V (N=V=1 або N=V=0) | |
LT | Менше | NV (N=1 та V=0) або (N=0 та V=1) | |
GT | Більше | Z=0, N=V (N=V=1 або N=V=0) | |
LE | Менше або дорівнює | Z=0 або NV (N=1 та V=0) або (N=0 та V=1) | |
AL | Завжди правдивий | прапори ігноруються |
4.3 Короткий опис набору інструкцій Thumb
Формати набору інструкцій Thumb показані малюнку 1.6. Докладніше про формати наборів інструкцій ARM наведено "ARM Architectural Reference Manual".
Малюнок 1.6. Формати набору інструкцій Thumb
Набір інструкцій Thumb наведено в таблиці 1.7.
Таблиця 1.7. Короткий опис набору інструкцій Thumb
Операція | Синтаксис Асемблера | |
Пересилання (копіювання) | константи | MOV Rd #8bit_Imm |
старшого до молодшого | MOV Rd, Hs | |
молодшого до старшого | MOV Hd, Rs | |
старшого до старшого | MOV Hd, Hs | |
Арифметичні | додавання | ADD Rd, Rs, #3bit_Imm |
додати молодший до молодшого | ADD Rd, Rs, Rn | |
додати старший до молодшого | ADD Rd, Hs | |
додати молодший до старшого | ADD Hd, Rs | |
додати старший до старшого | ADD Hd, Hs | |
додавання з константою | ADD Rd #8bit_Imm | |
додати значення до SP | ADD SP, #7bit_Imm ADD SP, #-7bit_Imm | |
додавання з урахуванням перенесення | ADC Rd, Rs | |
віднімання | SUB Rd, Rs, Rn SUB Rd, Rs #3bit_Imm | |
віднімання константи | SUB Rd #8bit_Imm | |
віднімання з перенесенням | SBC Rd, Rs | |
інверсія знаку | NEG Rd, Rs | |
множення | MUL Rd, Rs | |
порівняти молодший із молодшим | CMP Rd, Rs | |
порівняти молодший та старший | CMP Rd, Hs | |
порівняти старший та молодший | CMP Hd, Rs | |
порівняти старший та старший | CMP Hd, Hs | |
порівняти негативні | CMN Rd, Rs | |
порівняти з константою | CMP Rd #8bit_Imm | |
Логічні | І | AND Rd, Rs |
Викл. АБО | EOR Rd, Rs | |
АБО | ORR Rd, Rs | |
Скидання біта | BIC Rd, Rs | |
Пересилання NOT | MVN Rd, Rs | |
Тестування біт | TST Rd, Rs | |
Зсув/обертання | Логічний зсув вліво | LSL Rd, Rs, #5bit_shift_imm LSL Rd, Rs |
Логічний зсув праворуч | LSR Rd, Rs, #5bit_shift_imm LSR Rd, Rs | |
Арифметичний зсув праворуч | ASR Rd, Rs, #5bit_shift_imm ASR Rd, Rs | |
Обертання вправо | ROR Rd, Rs | |
Перехід | умовні переходи | - |
BEQ label | ||
BNE label | ||
BCS label | ||
BCC label | ||
BMI label | ||
BPL label | ||
BVS label | ||
BVC label | ||
BHI label | ||
BLS label | ||
BGE label | ||
BLT label | ||
BGT label | ||
BLE label | ||
Безумовний перехід | B label | |
Довгий перехід за посиланням | BL label | |
Опціональна зміна стану | - | |
BX Rs | ||
BX Hs | ||
Читання | з константою усунення | - |
LDR Rd, | ||
LDRH Rd, | ||
LDRB Rd, | ||
з регістром зміщення | - | |
LDR Rd, | ||
LDRH Rd, | ||
LDRSH Rd, | ||
LDRB Rd, | ||
LDRSB Rd, | ||
щодо лічильника програми PC | LDR Rd, | |
щодо покажчика стека SP | LDR Rd, | |
Адреса | - | |
ADD Rd, PC, #10bit_Offset | ||
ADD Rd, SP, #10bit_Offset | ||
Множинне читання | LDMIA Rb!, |
|
Запис | з константою усунення | - |
STR Rd, | ||
STRH Rd, | ||
STRB Rd, | ||
з регістром зміщення | - | |
STR Rd, | ||
STRH Rd, | ||
STRB Rd, | ||
щодо SP | STR Rd, | |
Множинний запис | STMIA Rb!, |
|
Приміщення/ витяг з стека | Помістити регістри у стек | PUSH |
Помістити LR та регістри у стек | PUSH |
|
Витягти регістри зі стека | POP |
|
Витягти регістри та PC зі стека | POP |
|
Програмне переривання | - | SWI 8bit_Imm |