Изучение системы команд процессора ARM. Школа ассемблера: язык ассемблера для центральных процессоров архитектуры ARM Подпрограммы, циклы и условные инструкции

В данном разделе приводится описание наборов инструкций процессора 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} Rd{!},
  • операция над стеком и восстановление CPSR
  • LDM {cond} Rd{!}, ^
    операция над стеком с регистрами пользователя LDM {cond} Rd{!}, ^
    Запись слова 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} Rd{!},
  • операция над стеком с регистрами пользователя
  • STM {cond} Rd{!}, ^
    Обмен слов SWP {cond} Rd, Rm,
    байт SWP {cond}B Rd, Rm,
    Сопроцессор Операция над данными CDP {cond} p, , CRd, CRn, CRm,
    Пересылка в ARM-регистр из сопроцессора MRC {cond} p, , Rd, CRn, CRm,
    Пересылка в сопроцессор из ARM-регистра MCR {cond} p, , Rd, CRn, CRm,
    Чтение LDC {cond} p, CRd,
    Запись STC {cond} p, CRd,
    Программное прерывание 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
  • C=1, Z=0
  • BHI label
  • C=0, Z=1
  • BLS label
  • N=1, V=1 или N=0, V=0
  • BGE label
  • N=1, V=0 или N=0, V=1
  • BLT label
  • Z=0 и ((N или V=1) или (N или V=0))
  • BGT label
  • Z=1 или ((N=1 или V=0) или (N=0 и V=1))
  • 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,
    Адрес -
  • с помощью PC
  • ADD Rd, PC, #10bit_Offset
  • с помощью SP
  • 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

    GBA ASM - День 2: Немного информации об ассемблере ARM — Архив WASM.RU

    ARM - это компания, которая делает процессор GBA. Процессоры ARM являются RISC-процессорами (в отличии от процессоров INTEL). RISC расшифровывается как Reduced Instruction Set Computers (CISC - Complex ...). Хотя в этих процессорах не так много инструкций (что хорошо), инструкции ARM (а может и других RISC-процессоров, я не знаю) имеют много различных назначений и комбинаций, что делает RISC-процессорами таким могущественными, каким они являются.

    Регистры

    Я не знаю о других процессорах ARM, но у того, который используется в GBA, 16 регистров и в отличии от процессоров Intel (и других), все регистры можно спокойно использовать (как правило). Регистры следующие:

    r0,r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15

    Вау! Много! Объясняю по-порядку.

    ro: Делайте, что хотите!

    r2 to r12: то же самое

    r13: На некоторых ARM-системах r13 - это указатель на стек (SP в процессорах INTEL). Я не уверен, играет ли r13 ту же роль в GBA, я могу только предупредить, чтобы вы были осторожны с ним, когда работаете со стеком.

    r14: Содержит адрес возврата для вызываемых процедур. Если вы их не используете, тогда можете делать с ним всё, что хотите.

    r15: Program Counter и флаги, так же как IP (Instruction Pointer в Intel). Он отличается от Intel"овского регистра IP тем, что у вас есть свободный доступ к нему, так же как и к любому другому регистру, но учтите, что его изменение приведет к тому, что управление будет передано на другой участок кода, а флаги изменятся.

    Давайте проделаем небольшое математическое упражнение... 16 минус 3 (как правило) даёт нам 13 регистров. Не правда ли, это круто? Успокойтесь .

    Сейчас вы можете спросить, чем же на самом деле являются регистры. Регистры - это специальные участки памяти, которые являются частью процессора и не имеют действительного адреса, а известны только по своим именам. Регистры 32-х битны. Почти всё в любом ассемблерном языке использует регистры, поэтому вы должны знать их также хорошо, как своих родственников.

    Инструкции ассемблера ARM

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

    Во-вторых, я хочу представить моего хорошего друга CMP. Скажите ему привет, и возможно, только возможно, он станет и вашим другом. CMP расшифровывается как CoMPare (сравнить). Эта инструкция может сравнить регистр и число, регистр и регистр или регистр и ячейку памяти. Затем, после сравнивания CMP устанавливает флаги статуса, которые сообщают вам результат сравнения. Как вы можете вспомнить, регистр r15 содержит флаги. Они сообщают результат сравнения. Инструкция CMP была специально создана для установки значения этих флагов и ни для чего более.

    Флаги могут содержать следующие состояния:

      EQ EQual / Равно

      NE Not Equal / Не равно

      VS oVerflow Set / Установка переполнения

      VC oVerflow Clear / Очищение переполнения

      HI HIgher / Выше

      LS Lower or the Same / Ниже или то же самое

      PL PLus / Плюс

      MI MInus / Минус

      CS Carry Set / Установка переноса

      CC Carry Clear / Очищение переноса

      GE Greater than or Equal / Больше или равно

      GT Greater Than / Больше

      LE Less than or Equal / Меньше или равно

      LT Less Than / Меньше

      Z is Zero / Ноль

      NZ is not Zero / Не ноль

    Эти состояния играют очень важную роль в ассемблере ARM.

    ПРИМЕЧАНИЕ: флаги только сохраняют условия (Равно, Меньше и так далее). Больше они ничем не важны.

    Суффиксы условий

    Вы уже видели инструкцию B (Branch). Инструкция B делает то, что называется безусловным переходом (как GoTo в Basic или JMP в ассемблере INTEL). Но она может иметь суффикс (один из тех, что были перечислены выше), тогда она проверяет, соответствует ли ему состояние флагов. Если нет, то инструкция перехода просто не выполняется. Поэтому, если вы хотите проверить, равен ли регистр r0 регистру r4, а затем перейти к метке под названием label34, то вам необходимо написать следующий код:

      CMP r0, r4 ; Комментарии в ассемблере идут после точки с запятой (;)

      BEQ label34 ; B - инструкция перехода, а EQ - суффикс, означающий

      ; "Если Равно"

    ПРИМЕЧАНИЕ: В Goldroad Assembler метки не нужно сопровождать (, и кроме имени метки в строке ничего не должно быть.

    ПРИМЕЧАНИЕ II: Писать CMP и BEQ заглавными буквами не обязательно, это просто для того, чтобы вам было более понятно.

    Теперь вы знаете как делать переход в зависимости от состояния флагов, но что вы не знаете, так это то, что вы можете делать в зависимости от состояния флагов всё, что угодно, просто добавьте к любой инструкции нужный суффикс!

    Также вам не обязательно использовать CMP для установления состояния флагов. Если вы хотите, чтобы, например, инструкция SUB (Subtract) устанавливала флаги, добавьте суффикс "S" к инструкции (расшифровывается как "Set flags"). Это полезно, если вы не хотите устанавливать состояние флагов лишней инструкцией CMP, поэтому сделать это и совершить переход, если результат был равен нулю, можно следующим образом:

      SUBS r0,r1,0x0FF ; Устанавливает флаги согласно результату выполнения

      ; инструкции

      ldrZ r0,=0x0FFFF ; Загрузит в регистр r0 0x0FFFF только, если состояние

      флагов равно Нулю.

    Обзор

    Сегодня мы изучили (ещё немного) о регистрах. Мы также узнали о гибкости инструкций ARM, которые могут выполняться (или не выполняться) в зависимости от состояния флагов. Мы узнали много сегодня.

    Завтра иы используем приобретённое сегодня знание ассемблера ARM для того, чтобы вывести картинку на экран GBA.

    Нечто невозможное является таковым только до той поры, пока оно не становится возможным /Jean-Luc Picard, Capt. , USS Enterprise/. Mike H, пер. Aquila

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

    Однако при изучении архитектуры процессоров и ее особенностей целесообразно использовать языки Ассемблера, так как только такой подход может обеспечить выявление особенностей изучаемой архитектуры. По этой причине дальнейшее изложение ведется с использованием языка Ассемблера.

    Прежде чем приступить к рассмотрению команд ARM7, необходимо отметить следующие ее особенности:

      Поддержку двух наборов команд: ARM с 32-битными командами и THUMB с 16-битными командами. Далее рассматривается 32-битный набор команд, слово ARM будет означать команды, принадлежащие к этому формату, а слово ARM7 - собственно ЦПУ.

      Поддержку двух форматов 32-х разрядного адреса: с обратным порядком бит (big-endian processor и с прямым порядком бит (little-endian processor)). В первом случае старший бит (Most Significant Bit - MSB) располагается в младшем бите слова, а во втором случае - в старшем. Это обеспечивает совместимость с другими семействами 32-х разрядных процессоров при использовании языков высокого уровня. Однако в ряде семейств процессоров с ядром ARMиспользуется только прямой порядок байтов (т.е. MSB является самым старшим битом адреса), что значительно облегчает работу с процессором. Поскольку компилятор, используемый для ARМ7, работает с код в обоих форматах, необходимо удостовериться, что формат слов задан правильно, в противном случае полученный код будет «вывернут наизнанку».

      Возможность выполнения различных типов сдвига одного из операндов «на проходе» перед использованием в АЛУ

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

      Возможность запрета изменения флагов результатов выполнения операции.

        1. Условное выполнение команд

    Одна из важных особенностей набора команд ARM заключается в том, что поддерживается условное выполнение любой команды. В традиционных микроконтроллерах единственными условными командами являются команды условных переходов, и, быть может, ряд других, таких как команды проверки либо изменения состояния отдельных битов. В наборе команд ARM старшие 4 бита кода команды всегда сравниваются с флагами условий в регистре CPSR. Если их значения не совпадают, команда на стадии дешифрации заменяется команда NOP (нет операции).

    Это существенно сокращает время выполнения участков программы с «короткими» переходами. Так, например, при решении квадратных уравнений с вещественными коэффициентами и произвольными корнями при отрицательном дискриминанте необходимо перед вычислением квадратного корня сменить знак дискриминанта, а результат присвоить мнимой части ответа.

    При традиционном решении этой задачи необходимо ввести команду условного перехода. Выполнение этой команды занимает как минимум 2 такта – дешифрация и загрузка нового значения адреса в счетчик команд и дополнительное число тактов на загрузку конвейера команд. При использовании условного выполнения команды при положительном дискриминанте команда смены знака заменяется пустой операцией. При этом не происходит очистка конвейера команд, и потери составляют не более одного такта. Порог, при котором замена условных команд командой NOP оказывается эффективнее выполнения традиционных команд условного перехода и связанного с этим повторным заполнением конвейера равен его глубине, т.е. трем.

    Для реализации этой возможности к базовым мнемоническим обозначениям команд ассемблера (и С то же), нужно добавить любой из шестнадцати префиксов, определяющих тестируемые состояния флагов условий. Эти префиксы приведены в табл. 3. Соответственно существует 16 вариантов каждой команды. Например, следующая команда:

    MOVEQ R1, #0x008

    означает, что загрузка числа 0x00800000 в регистр R1 будет произведена только том случае, если результат выполнения последней команды обработки данных был «равно» или получен 0 результат и соответственно установлен флаг (Z) регистра CPSR.

    Таблица 3

    Префиксы команд

    Значение

    Z установлен

    Z сброшен

    С установлен

    Выше или равно (беззнаковое)

    C сброшен

    Ниже (беззнаковое)

    N установлен

    Отрицательный результат

    N сброшен

    Положительный результат или 0

    V установлен

    Переполнение

    V сброшен

    Нет переполнения

    С установлен,

    Z сброшен

    Выше (беззнаковое)

    С сброшен,

    Z установлен

    Ниже или равно (беззнаковое)

    Больше или равно (знаковое)

    N не равен V

    Меньше (знаковое)

    Z сброшен И

    (N равен V)

    Больше (знаковое)

    Z установлен ИЛИ

    (N не равен V)

    Меньше или равно (знаковое)

    (игнорируются)

    Безусловное выполнение

    1. Счетчик часов реального времени должен быть включен (1); бит выбора источника тактирования сброшен (2), если тактирование не осуществляется от основного тактового генератора.

    2. Один или оба бита выбора прерывающего события (3) должны быть установлены. И выбрано, какие именно события будут вызывать запрос прерывания (5).

    3. Должны быть заданы маски прерывающих событий (4, 7).

    2.5 О программировании ARM7 на ассемблере

    Система команд ARM7 (раздел 1.4) включает всего 45 инструкций, которые довольно сложны из-за многообразия методов адресации, условных полей и модификаторов. Программа на ассемблере получается громоздкой и

    с трудом читается. Поэтому ассемблер редко применяется в программировании для архитектуры ARM7.

    Вместе с тем, язык высокого уровня Си скрывает от программиста многие особенности архитектуры. Программист практически не касается таких процедур, как выбор режима ядра, выделение памяти под стек и обработка прерываний. Для изучения этих процедур полезно составить хотя бы одну простую программу на ассемблере.

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

    1. Следует контролировать Си-компилятор, отслеживая, не исключил ли он в ходе оптимизации важные команды, посчитав их ненужными. Не генерирует ли компилятор исключительно неэффективный код для сравнительно простой операции, из-за недостаточной оптимизации. Чтобы убедиться, что компилятор действительно задействует те аппаратные ресурсы, которые, призваны повысить эффективность конкретного алгоритма.

    2. В ходе поиска ошибок или причин возникновения исключительных ситуаций (раздел 2.4.1).

    3. Для получения кода, абсолютно оптимального по быстродействию или расходу памяти (разделы 2.2.20, 3.1.5).

    Рассмотрим основные приемы составления программы на ассемблере

    с целью продемонстрировать весь код исполняемый микроконтроллером, как есть, и без посредничества Си-компилятора.

    Порядок создания проекта на основе ассемблера почти тот же, что и для Си-программ (разделы 2.3.1–2.3.3). Исключения лишь два:

    а) файлу исходного текста присваивается расширение *.S;

    б) здесь предполагается, что файл STARTUP.S к программе не подключается.

    2.5.1 Основные правила записи программ на ассемблере

    Текст программы на ассемблере принято оформлять в четыре колонки. Можно сказать, что каждая строка состоит из четырех полей, а именно: поля меток, операций, операндов, комментариев. Поля отделяются друг от друга символом «табуляция» или пробелами.

    Основными являются поля операций и операндов. Допустимые операции и их синтаксис приведены в таблице (1.4.2)

    Метка - это символьное обозначение адреса команды. Везде вместо метки будет выполняться подстановка адреса команды, которой предшествует метка. Чаще всего метки используются в командах передачи управления. Каждая метка должна быть уникальной и при этом является не обязательной. В отличие от многих других версий, в ассемблере RealView метки не заканчиваются двоеточием («: »).

    Комментарии по желанию помещаются в конце строки и отделяются точкой с запятой («; »).

    Приведем простой пример.

    2.5.2 Псевдокоманды

    Ассемблер RealView поддерживает так называемые псевдокоманды. Псевдокоманда - это мнемоническое обозначение, которое на самом деле не соответствует системе команд процессора, а заменяется одной или (реже) несколькими командами. Псевдокоманды являются своего рода макросами и служат для упрощения синтаксиса. Перечень поддерживаемых псевдокоманд приведен в таблице (2.5.1).

    2.5.3 Директивы ассемблера

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

    Рассмотрим часто используемые директивы ассемблера RealView 4.

    Имя EQU Константа

    Назначает Константе символьное обозначение Имя , которое становится синонимом константы. Основное назначение - введение имен управляющих регистров,

    AREA Имя, Параметры

    Определяет область памяти с заданным Именем . С помощью параметров указывается назначение области памяти, например, DATA (данные) или CODE (код). От выбранного назначения зависят адреса определяемой области. Область CODE размещается, начиная с адреса 0x00000000, область DATA - с адреса 0x40000000. В программе обязательно должна существовать область CODE c именем RESET . Константы, размещаемые в памяти программ, следует объявлять в секции с парой параметров CODE, READONLY .

    Обозначает точку входа в программу, показывает ее «начало». Одна такая директива всегда должна присутствовать в программе. Обычно помещается непосредственно после директивы AREA RESET, CODE .

    Таблица 2.5.1 – Псевдокоманды, поддерживаемые ассемблером RealView 4

    Мнемоническое обозначение

    Операция

    Фактическая реализация

    и синтаксис

    ADR{Усл }

    в регистр

    Сложение или вычитание константы из PC ко-

    мандами ADD или SUB

    ADRL{Усл }

    в регистр

    Дважды ADD или SUB с участием PC

    (расширенный диапазон адресов)

    ASR{Усл }{S}

    Арифметический сдвиг вправо

    ASR{Усл }{S}

    нием сдвигового операнда

    LDR{Усл }

    в регистр

    адресацией (PC + непосредственное смещение)

    Размещение константы

    в памяти программ

    LDR{с индексной адреса-

    цией. Смещением служит PC.

    LSL{Усл }{S}

    Логический сдвиг влево

    LSL{Усл }{S}

    нием сдвигового операнда

    LSR{Усл }{S}

    Логический сдвиг вправо

    LSR{Усл }{S}

    нием сдвигового операнда

    POP{Усл }

    Восстановить регистры из стека

    Восстановление

    регистров

    командой

    LDMIA R13!,{...}

    PUSH{Усл }

    Сохранение

    регистров

    командой

    STMDB R13!,{...}

    ROR{Усл }{S}

    Циклический сдвиг вправо

    ROR{Усл }{S}

    нием сдвигового операнда

    RRX{Усл }{S}

    Циклический сдвиг вправо через

    перенос на 1 разряд

    нием сдвигового операнда

    Имя SPACE Размер

    Резервирует память для хранения данных заданного Размера . Имя становится синонимом адреса зарезервированного пространства. Единство адресного пространства позволяет применять эту директиву, как для постоянной, так и для оперативной памяти. Основное назначение - создание глобальных переменных в оперативной памяти (в области DATA ).

    Метка DCB/DCW/DCD Константа

    «Прошивают» данные (числовые Константы ) в памяти программ. Метка становиться синонимом адреса, по которому будут записаны данные. Разные директивы (DCB , DCW и DCD ) служат для данных разного размера: байт, 16-разрядное слово, 32-разрядное слово (соответственно).

    Служит признаком конца файла. Весь текст после этой директивы игнорируется ассемблером.

    2.5.4 Макросы

    Макрос представляет собой предопределенный фрагмент программы, выполняющий какую-либо распространенную операцию. В отличие от подпрограмм, вызываемых с помощью команд передачи управления, использование макросов не снижает быстродействия, но не снижает и расход памяти программ. Потому что при каждом вызове макроса ассемблер внедряет в программу полностью его текст.

    Для объявления макроса служит следующая конструкция

    $ Параметр1, $ Параметр2, ...

    Параметры позволяют модифицировать текст макроса при каждом обращении к нему. Внутри (в теле) макроса параметры используются также с предшествующим знаком «$ ». Вместо параметров в теле макроса подставляются параметры, указанные при вызове.

    Вызов макроса осуществляется так:

    Имя Параметр1, Параметр2, ...

    Имеется возможность организовать проверку условия и ветвление.

    IF "$ Параметр" == " Значение"

    Обращаем внимание на то, такая конструкция не приводит к программной проверке условия микроконтроллером. Проверку условия осуществляет ассемблер в ходе формирования исполнимого кода.

    Привет всем!
    По роду деятельности я программист на 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 сопроцессоры
    • Декодер инструкций - занимается преобразованием инструкции в последовательность микроопераций
    Это не все составляющие 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 and SL (r10)

    6.fp and FP (r11)

    7.ip and IP (r12)

    8.sp and SP (r13)

    9.lr and 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 и др.

    Итак, для эмуляции arm понадобится 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 R07=00000000 R08=00000000 R09=00000000 R10=00000000 R11=00000000 R12=00000000 R13=00000000 R14=00000000 R15=00000010 PSR=400001d3 -Z-- A svc32

    P.S. В этой статье я постарался описать основы программирования на ARM ассемблер. Надеюсь вам понравилось! Этого хватит для того, чтобы далее углубляться в дебри этого языка и писать на нем программы. Если все получится, буду писать дальше о том, что узнаю сам. Если есть ошибки, прошу не пинать, так как я новичок в ассемблере.



    Просмотров