Garry's Mod

Garry's Mod

Not enough ratings
Программируем на Ассемблере ARM
By Dr. L
В этом руководстве будет описанно все о процесе написание кода на ассемблере на архитектуре ARM, с набором инструкций Thumb (урезанный набор ARM, под встраиваемые системы).
   
Award
Favorite
Favorited
Unfavorite
Введение
Данное руководство не свзяанно с игрой Garry's mod!

Для примера мы будем использовать процессор микроконтроллера RP2040 от Raspberry Pi Foundation.

Написание программ на ассемблере для ARM Cortex-M0+ может показаться сложным на первый взгляд, но с пошаговым руководством это становится более понятным. В этом руководстве мы рассмотрим основные концепции, необходимые для создания полноценной программы на ассемблере, включая структуру программы, объявление данных, использование секций, инструкции и процесс компиляции и запуска программы.
Основы архитектуры ARM Cortex-M0+
Прежде чем приступить к написанию программ на ассемблере, важно понимать основные характеристики и возможности архитектуры ARM Cortex-M0+:
  • Регистры:
    • R0 - R12: Регистры общего назначения.
    • R13 (SP): Указатель стека.
    • R14 (LR): Регистр возврата (Link Register).
    • R15 (PC): Счётчик команд (Program Counter).

  • Набор инструкций:
    • Thumb: Cortex-M0+ использует набор инструкций Thumb, который является упрощённой версией полной архитектуры ARM, оптимизированной для встроенных систем.

  • Память:
    • Flash: Для хранения программы.
    • RAM: Для хранения данных и переменных во время выполнения.

  • Прерывания и исключения: Механизмы для обработки событий вне основного потока выполнения.
Структура программы на ассемблере
Программа на ассемблере для ARM Cortex-M0+ обычно состоит из нескольких секций:
  • .text: Содержит исполняемый код.
  • .data: Содержит инициализированные данные.
  • .bss: Содержит неинициализированные данные (переменные, которые будут
    инициализированы во время выполнения
    ).
  • rodata: Содержит только для чтения данные, такие как строки.

Кроме того, могут использоваться различные директивы для определения глобальных символов, указания точек входа и других параметров.
Секции в ассемблере
.text
Секция .text содержит исполняемый код программы. Здесь размещаются инструкции, которые процессор будет выполнять.
.text .global _start _start: ; Ваши инструкции здесь
  • .global _start: Делает символ _start глобальным, чтобы компоновщик знал точку входа программы.

.data
Секция .data содержит инициализированные данные. Это переменные, которые имеют начальные значения.
.data my_var: .word 0x12345678 ; Инициализация 32-битного слова

.bss
Секция .bss содержит неинициализированные данные. Переменные в этой секции будут инициализированы нулями во время выполнения.
.bss res_var: .space 4 ; Выделение 4 байт памяти

.rodata
Секция .rodata содержит только для чтения данные, такие как строки.
.rodata my_string: .ascii "Hello, ARM Cortex-M0+!\n"
Основные инструкции и синтаксис
Директивы ассемблера
  • .syntax unified: Использует унифицированный синтаксис (Thumb/ARM).
  • .thumb: Указывает на использование набора инструкций Thumb.
  • .global: Делает символ глобальным.
  • .extern: Объявляет внешний символ (например, функцию из другого файла).

Основные инструкции
  • MOV: Перемещение данных.
    MOV R0, #10 ; Поместить число 10 в регистр R0 MOV R1, R0 ; Копировать значение из R0 в R1
  • ADD: Сложение.
    ADD R2, R0, R1 ; R2 = R0 + R1
  • SUB: Вычитание.
    SUB R3, R2, #5 ; R3 = R2 - 5
  • MUL: Умножение.
    MUL R4, R0, R1 ; R4 = R0 * R1
  • LDR: Загрузка из памяти.
    LDR R5, =variable ; Загрузить адрес переменной в R5 LDR R6, [R5] ; Загрузить значение по адресу R5 в R6
  • STR: Сохранение в память.
    STR R6, [R5] ; Сохранить значение R6 по адресу R5
  • B: Безусловный переход.
    B loop_start ; Переход к метке loop_start
  • BL: Вызов подпрограммы (branch with link).
    BL function_name ; Вызов функции function_name
  • BX: Переход по адресу в регистре (обычно для возврата из функции).
    BX LR ; Возврат из функции
  • CMP: Сравнение.
    CMP R0, R1 ; Сравнить R0 и R1
  • BEQ, BNE, BGT и т.д.: Условные переходы.
    BEQ equal_label ; Переход, если равны (Zero флаг установлен) BNE not_equal ; Переход, если не равны (Zero флаг сброшен)

Комментарии
Комментарии начинаются с точки с запятой ; или двойного косого слэша //.
MOV R0, #10 ; Поместить 10 в R0
Пишем простую программу
Давайте напишем простую программу, которая выполняет следующие действия:
  • Инициализирует два числа.
  • Умножает их.
  • Сохраняет результат.
  • Завершает выполнение программы

Пример программы: Умножение двух чисел
.syntax unified .thumb .global _start .section .text _start: MOV R0, #6 ; Первое число: 6 MOV R1, #7 ; Второе число: 7 MUL R2, R0, R1 ; R2 = R0 * R1 = 42 B . ; Бесконечный цикл, чтобы программа не завершилась .section .data ; Здесь можно объявить инициализированные данные .section .bss ; Здесь можно объявить неинициализированные данные .section .rodata ; Здесь можно объявить константы, например, строки

Объяснение программы:

  • Директивы:
    • .syntax unified: Использует унифицированный синтаксис.
    • .thumb: Указывает на использование Thumb-инструкций.
    • .global _start: Делает символ _start точкой входа программы.
  • Секция .text и точка входа _start:
    • MOV R0, #6: Помещает число 6 в регистр R0.
    • MOV R1, #7: Помещает число 7 в регистр R1.
    • MUL R2, R0, R1: Умножает значения в R0 и R1, результат помещается в R2.
    • B .: Безусловный переход на текущую метку (бесконечный цикл), чтобы программа не завершилась неожиданно.
  • Секции .data, .bss, .rodata:
    • Используются для размещения данных, но в этом примере они пусты.
Компиляция и связывание программы
Чтобы превратить написанный ассемблерный код в исполняемый файл, необходимо выполнить несколько шагов: компиляция, связывание и создание файла ELF.

Шаг 1: Создание файлов
Создайте файл multiply.s с содержимым программы с кодом из прошлой части.

Шаг 2: Создание linker script
Создайте файл linker_script.ld с простым содержимым, определяющим расположение секций в памяти:
/* linker_script.ld */ ENTRY(_start) SECTIONS { /* Начальный адрес памяти */ . = 0x00000000; .text : { *(.text*) } .data : { *(.data*) } .bss : { *(.bss*) } .rodata : { *(.rodata*) } }

Шаг 3: Компиляция и связывание
Используйте инструменты GNU для ARM (arm-none-eabi) для компиляции и связывания программы.
Они доступны на Unix и Unix-подобных системах.
  • Ассемблирование:
  • arm-none-eabi-as -mcpu=cortex-m0plus -mthumb -o multiply.o multiply.s
    • -mcpu=cortex-m0plus: Указывает целевой процессор.
    • -mthumb: Использует Thumb-инструкции.
    • -o multiply.o: Указывает имя выходного объектного файла.
  • Связывание:
  • arm-none-eabi-ld -T linker_script.ld -o multiply.elf multiply.o
    • -T linker_script.ld: Указывает linker script.
    • -o multiply.elf: Имя выходного ELF-файла.
    • multiply.o: Объектный файл для связывания.
  • Проверка ELF-файла:
    • Вы можете использовать arm-none-eabi-objdump для просмотра содержимого ELF-файла.
  • arm-none-eabi-objdump -d multiply.elf
Запуск программы в QEMU
QEMU — это эмулятор, который позволяет запускать и тестировать программы для различных архитектур, включая ARM Cortex-M0+.

Шаг 1: Установка QEMU
Если QEMU ещё не установлен, установите его. Для Ubuntu/Debian:
sudo apt-get update sudo apt-get install qemu-system-arm

Шаг 2: Запуск программы
Запустите вашу программу с помощью QEMU. Для Cortex-M0+ подойдёт, например, машина lm3s6965evb, которая поддерживает Cortex-M3, но для Cortex-M0+ можно использовать lm4f120xl или другую подходящую модель. Однако обратите внимание, что не все модели могут поддерживать Cortex-M0+ напрямую.
qemu-system-arm -machine lm4f120xl -cpu cortex-m0plus -nographic -kernel multiply.elf
  • -machine lm4f120xl: Выбор модели машины.
  • -cpu cortex-m0plus: Указание процессора.
  • -nographic: Отключение графического интерфейса.
  • -kernel multiply.elf: Указание ELF-файла для загрузки.

Примечание:
Некоторые модели машин в QEMU могут не поддерживать Cortex-M0+ напрямую. В таком случае, используйте ближайшую поддерживаемую модель или проверьте актуальные версии QEMU на наличие поддержки.

Просмотр результата:
В текущей программе результат умножения хранится в регистре R2. Чтобы просмотреть значение регистра, используйте отладчик, например, GDB.
Полезные советы и рекомендации
  • Изучите ARM Reference Manual:
    • Официальная документация ARM предоставляет подробную информацию о наборах инструкций, регистрах и архитектуре.
  • Используйте комментарии:
    • Комментируйте свой код, чтобы легче было понимать логику программы.
  • Разбивайте код на функции:
    • Это облегчает отладку и повторное использование кода.
  • Практикуйтесь:
    • Напишите несколько простых программ, постепенно увеличивая их сложность.
  • Используйте макросы и константы:
    • Это сделает ваш код более читаемым и управляемым.
  • Отлаживайте регулярно:
    • Используйте GDB для проверки значений регистров и памяти на различных этапах выполнения программы.
  • Изучите работу со стеком:
    • Понимание использования стека (PUSH, POP) важно для написания функций и работы с подпрограммами.
  • Обращайтесь к примерам:
    • Изучение существующих примеров программ на ассемблере поможет лучше понять практическое применение инструкций.
  • Оптимизируйте использование регистров:
    • Эффективное использование регистров повышает производительность и снижает энергопотребление.
  • Будьте внимательны с адресацией памяти:
    • Ошибки в адресации могут привести к сбоям программы или непредсказуемому поведению.
Заключение
Написание программ на ассемблере для ARM Cortex-M0+ требует внимательного подхода и понимания архитектуры процессора. Следуя этому руководству, вы сможете создавать простые программы, выполнять их компиляцию, связывание и запуск в эмуляторе QEMU. Практика и изучение более сложных примеров помогут вам углубить знания и создать более функциональные и оптимизированные программы.

Если у вас возникнут дополнительные вопросы или потребуется помощь с конкретными аспектами программирования на ассемблере для ARM Cortex-M0+, можете задать вопрос в комментариях к руководству.

Другие мои руководства:
https://steamoss.com/sharedfiles/filedetails/?id=3389441554
https://steamoss.com/sharedfiles/filedetails/?id=3386163984
https://steamoss.com/sharedfiles/filedetails/?id=3197737260
https://steamoss.com/sharedfiles/filedetails/?id=3390154519
8 Comments
ip.nagorniy.da 28 Dec, 2024 @ 2:00am 
━━━━╮ Это Мемопёс. Помогите
╰┃ ┣▇━▇ Мемопсу прокатиться
 ┃ ┃  ╰━▅╮ через гайды в стим вставляя
 ╰┳╯ ╰━━┳╯ его в другие
  ╰╮ ┳━━╯ гайды иначе он
 ▕▔▋ ╰╮╭━╮ не станет псом-мемом
╱▔╲▋╰━┻┻╮╲╱▔▔▔╲
▏  ▔▔▔▔▔▔▔  O O┃
╲╱▔╲▂▂▂▂╱▔╲▂▂▂╱
 ▏╳▕▇▇▕ ▏╳▕▇▇▕
 ╲▂╱╲▂╱ ╲▂╱╲▂╱
Alex 22 Dec, 2024 @ 9:07pm 
про распберри к тому, что первое, что на ум пришло, связанное с gpio - это распберри пай
Alex 22 Dec, 2024 @ 9:06pm 
просто помню, мы с кентом змейку пытались написать для ардуино, подключали экранчик от нокиа к ардуино вроде нано, не помню
Alex 22 Dec, 2024 @ 9:05pm 
чесслово, связывался только с ардуино, дома валется 4 распберри. С micropython я даже не связывался. Вообще, делай на какой захочешь
Dr. L  [author] 22 Dec, 2024 @ 12:09pm 
вот такой вопросик, на ардуино или micropython? Или вообще на Pico-SDK?
Alex 22 Dec, 2024 @ 1:27am 
вот-вот, и об этом напишешь
Dr. L  [author] 22 Dec, 2024 @ 12:54am 
Зависит от того, какой дисплей, какие у него пины подключения и тд, типо там есть и TFT, SPI, I2C и тд, может быть через дня 2-3 сделаю подробный гайд
Alex 21 Dec, 2024 @ 11:38pm 
теперь давай гайд как на дисплей, подключенный через gpio выводить картинку