Разное

Ассемблер язык программирования: Ассемблер. Вступление | Уроки Ассемблера

Содержание

Ассемблер. Вступление | Уроки Ассемблера

  Обновл. 29 Сен 2019  | 

Каждый персональный компьютер имеет микропроцессор, который управляет арифметической, логической и управляющей деятельностью компьютера.

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

Процессор понимает только инструкции машинного языка, которые являются последовательностью бит: 1 или 0. Однако машинный язык слишком сложен и непонятен для использования в разработке программного обеспечения. Поэтому для определённого семейства процессоров был разработан низкоуровневый язык, который представляет собой набор инструкций для написания программ в более понятной форме. Этот язык назвали языком ассемблера (или просто «ассемблер»). Ассемблер – это транслятор (переводчик), который переводит код, написанный на языке ассемблера, в машинный язык.

Преимущества языка ассемблера

Использование языка ассемблера позволяет понять:

   как программы взаимодействуют с операционной системой, процессором и BIOS-ом;

   как данные представлены в памяти и других устройствах;

   как процессор получает доступ к инструкциям и как он их выполняет;

   как инструкции получают доступ к данным и обрабатывают эти данные;

   как программа получает доступ к внешним устройствам.

Другие преимущества использования языка ассемблера:

   требует меньше памяти;

   быстрее выполняется;

   упрощает сложные аппаратные задачи.

Основные характеристики аппаратного обеспечения ПК

Основное аппаратное обеспечение ПК состоит из процессора, памяти и регистров. Регистры – это компоненты процессора, содержащие данные и их адреса в памяти. Чтобы выполнить программу, система копирует её с внешнего устройства во внутреннюю память. Затем процессор выполняет инструкции программы.

Данные в компьютере хранятся в битах: 1 (ВКЛ) или 0 (ВЫКЛ).

Процессор поддерживает следующие размеры данных:

   word: 2-байтовый элемент данных;

   doubleword: 4-байтовый (32-битный) элемент данных;

   quadword: 8-байтовый (64-битный) элемент данных;

   paragraph: 16-байтовая (128-битная) область;

   kilobyte: 1024 байт;

   megabyte: 1 048 576 байт.

Двоичная система счисления

Каждая система счисления использует позиционные обозначения. Т.е. позиции в которых записаны биты, имеют разные позиционные значения. Каждое следующее позиционное значение состоит из предыдущего позиционного значения, умноженного на 2 (именно на 2, так как это бинарная система, которая состоит из 2-ух чисел). Если битом является 1, то позиционное значение умножается на 2, а если 0, то позиционное значение остаётся 0. В бинарной системе счисления отсчёт ведётся справа налево, а не слева направо (как в десятичной системе).

Например, в следующей таблице показаны позиционные значения 8-битного двоичного числа 11111101:

Бит 1 1 1 1 1 1 0 1
Позиционное значение 128 64 32 16 8 4 0 1
Номер бита 7 6 5 4 3 2 1 0

Значение бинарного числа равно сумме позиционных значений всех битов:

1 + 4 + 8 + 16 + 32 + 64 + 128 = 253

Двоичное 11111101 = десятичное 253. Детальнее о конвертации чисел из двоичной системы в десятичную и наоборот, а также о сложении двоичных чисел читайте в уроке №44.

Шестнадцатеричная система счисления

Шестнадцатеричная система счисления состоит из 16 символов: 0-9 и A-F. Символы A-F используются для представления шестнадцатеричных цифр, соответствующих десятичным значениям с 10 по 15.

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

Десятичное представление Двоичное представление Шестнадцатеричное представление
0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F

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

Пример: Бинарное число 1000 (8) 1100 (C) 1101 (D) 0001 (1) эквивалентно шестнадцатеричному 8CD1.

Чтобы конвертировать шестнадцатеричное число в двоичное, просто запишите каждую шестнадцатеричную цифру в её 4-значный двоичный эквивалент.

Пример: Шестнадцатеричное число FAD8 эквивалентно двоичному 1111 (F) 1010 (A) 1101 (D) 1000 (8).

Адресация данных в памяти

Процесс, посредством которого процессор управляет выполнением инструкций, называется циклом выполнения, который состоит из трёх последовательных шагов:

   извлечение инструкции из памяти;

   расшифровка или идентификация инструкции;

   выполнение инструкции.

Процессор может одновременно обращаться к одному или нескольким байтам памяти. Например, рассмотрим шестнадцатеричное значение 0824H, которое занимает 2 байта памяти. Байт старшего разряда или старший значащий байт – 08, младший байт – 24.

Процессор хранит данные в обратной последовательности байтов, т.е. байт младшего разряда сохраняется в нижнем адресе памяти (слева), а байт старшего разряда – в верхнем адресе памяти (справа). Таким образом, если процессор перенесёт значение 0824H из регистра в память, то 24 будет в начале строки, а 08 – в конце, но читать данные процессор будет справа налево, а не слева направо (помним, что процессор работает в бинарной системе счисления):

Когда процессор переносит данные из памяти в регистр, то он опять меняет местами байты (т.е. 08 опять будет слева, а 24 – справа).

Есть 2 вида адресов памяти:

   абсолютный адрес – прямая ссылка на конкретное местоположение.

   сегментный адрес (или ещё «смещение») – адрес сегмента памяти со значением смещения.

В следующем уроке мы рассмотрим установку среды разработки для языка ассемблера.

Оценить статью:

Загрузка…

Поделиться в социальных сетях:

Архивы Ассемблер с нуля — Ассемблерный код

Прощай ассемблер MS-DOS и здравствуй Windows!

Мы закончили изучать 16 битный ассемблер MS-DOS и приступаем к изучению программирования на 32 битном ассемблере для Windows.

Нужно ли было копаться в коде мёртвой операционной системы, вместо того, чтобы сразу перейти к основам современного программирования? Для последующего успеха в изучении программирования — это необходимо. Практическое применение знаниям 16 битного ассемблера вы вряд ли найдёте в наше время. Пройденный нами этап — это основа теоретических знаний и практического понимания сути программирования через его основополагающее начало. Подробнее «MS-DOS и TASM 2.0. Часть 19. Прощай ассемблер MS-DOS!»

Указатель в программировании.

В статье MS-DOS и TASM 2.0. Часть 9. Указатель просто и понятно было рассмотрено, что такое указатель в программировании (pointer). Сейчас мы перейдём к вопросу практического использования указателя. Ещё раз напомним, что указатель в ассемблере — более широкое понятие, чем в Си и С++, где указатель определён как переменная, значением которой является адрес ячейки памяти. Указатель — не только переменная. Указатель в программировании на ассемблере — адрес определённой ячейки памяти. Жёсткой привязки к понятию «переменной» нет.

Преимущество указателя — простая возможность обращаться к определённой части исполняемого кода либо данных, избегая их дублирования. Например, один раз написав код функции, мы можем обращаться к нему неоднократно, осуществляя вызов указанной функции. Кстати, вызов функции — это переход исполнения кода по указателю, который для удобства «обозвали» понятным для человека названием (ну, например, «MyBestFunc»). Подробнее «MS-DOS и TASM 2.0. Часть 18. Ещё раз об указателе.»

Организация данных в ассемблере.

Прежде, чем переходить к рассмотрению вопроса что такое константа, массив, структура в ассемблере, поговорим о понятии абстракции.

Для упрощения написания кода необходимо преобразовать его в понятный для человека вид, желательно не в ущерб для машины. Для этого используют условности и обобщения — определённую степень абстракции. Один из простейших способов абстракции — разбивка кода и данных на части — блоки по определённым правилам и с определёнными особенностями. Затем эти блоки обзывают понятным для человека языком. Подробнее «MS-DOS и TASM 2.0. Часть 17. Константы, массивы, структуры и т.д.»

Ядро операционной системы — набор системных функций.

Основу операционной системы Windows 95 — Windows XP составляет набор системных функций, содержащихся в файлах с расширением *.DLL, которые располагаются в системных дирректориях … Windows\System,  System32, SysWOW64 (для 64 битных версий операционок) — так называемый Win API. К слову, в Windows 10 поддержка Win API реализована практически в полном объёме, хотя ядро системы несколько изменилось). В MS-DOS ядро также состоит из системных функций, которые называются прерывания DOS. Ну, если точно, то есть прерывания DOS, а есть прерывания BIOS (связаны с управлением компьютерным железом, определённые прерывания DOS  можно реализовать через прерывания BIOS), да и понятие ядра системы можно расширить… Для наших целей и на данный момент отбросим усложнения в сторону! Далее мы будем использовать общее понятие для всех прерываний: прерывания DOS. Подробнее «MS-DOS и TASM 2.0. Часть 16. Прерывания DOS.»

Макрос — макрокоманда, макроопределение.

У большинства популярных ассемблеров (TASM, MASM, FASM), имеется определённая «вкусность», которая помогает писать более читабельный и понятный код, а также уменьшает вероятность ошибок. Мы имеем ввиду макросы. Макрос — миникод, который определяет алгоритм действий основных команд ассемблера. Этот код либо уже создан и входит в комплект ассемблера, либо пишется пользователем самостоятельно. В данной статье мы выясним, как использовать макрос функции (процедуры), встроенный в TASM. Подробнее «MS-DOS и TASM 2.0. Часть 15. Упрощаем вызов функции в TASM.»

Параметры функции.

Функции в ассемблере — это часть кода, которая решает конкретную задачу или несколько, объединённых одной целью задач. Функция может вызываться без дополнительного дублирования кода. Человек способен помнить, воспринимать  и использовать ограниченное число информации. Для облегчения понимания и создания кода его структурируют — дробят на определенные части. Подробнее «MS-DOS и TASM 2.0. Часть 14. Конвенции вызова функции.»

Стек в ассемблере.

Работа процедур тесно связана со стеком. Стеком называется область программы для временного хранения данных. Стек в ассемблере работает по правилу «Первым зашёл — последним вышел, последним зашёл — первым вышел». В любой период времени в стеке доступен только первый элемент, то есть элемент, загруженный в стек последним. Выгрузка из стека верхнего элемента делает доступным следующий элемент. Это напоминает ящик, в который поочерёдно ложатся книги. Чтобы получить доступ к книге, которую положили первой, необходимо достать поочерёдно все книги, лежащие сверху. Подробнее «MS-DOS и TASM 2.0. Часть 13. Стек.»

язык Ассемблера. Основы языка Ассемблера

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

Краткое описание языков Ассемблера

Все языки программирования разделяются по уровням: низкий и высокий. Любой из синтаксической системы «семейки» Ассемблера отличается тем, что объединяет сразу некоторые достоинства наиболее распространенных и современных языков. С другими их роднит и то, что в полной мере можно использовать систему компьютера.

Отличительной особенностью компилятора является простота в использовании. Этим он отличается от тех, которые работают лишь с высокими уровнями. Если взять во внимание любой такой язык программирования, Ассемблер функционирует вдвое быстрее и лучше. Для того чтобы написать в нем легкую программу, не понадобится слишком много времени.

Кратко о структуре языка

Если говорить в общем о работе и структуре функционирования языка, можно точно сказать, что его команды полностью соответствуют командам процессора. То есть Ассемблер использует мнемокоды, наиболее удобные человеку для записи.

В отличие от других языков программирования, Ассемблер использует вместо адресов для записи ячеек памяти определенные метки. Они с процессом выполнения кода переводятся в так называемые директивы. Это относительные адреса, которые не влияют на работу процессора (не переводятся в машинный язык), а необходимы для распознавания самой средой программирования.

Для каждой линейки процессора существует своя система команд. При таком раскладе правильным будет любой процесс, в том числе и переведенный машинный код.

Язык Ассемблера имеет несколько синтаксисов, которые будут рассмотрены в статье.

Плюсы языка

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

Драйвера, операционные системы, BIOS, компиляторы, интерпретаторы и т. д. – это все программа на языке Ассемблера.

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

Минусы языка

К сожалению, начинающим программистам (и зачастую профессионалам) трудно разобрать язык. Ассемблер требует подробного описания необходимой команды. Из-за того, что нужно использовать машинные команды, растет вероятность ошибочных действий и сложность выполнения.

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

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

Команды языка

Как уже было сказано выше, для каждого процессора имеется свой набор команд. Простейшими элементами, которые распознаются любыми типами, являются следующие коды:

  • Пересылка данных осуществляется при помощи mov и т. д.
  • Команды, связанные с арифметикой: sub, imul и др.
  • Побитовые и логические функции можно реализовать при помощи or, and и т. п. Именно эти основы языка Ассемблера позволяют ему быть схожим с другими.
  • Для того чтобы осуществить переход от одной команды к другой, следует прописать такие операторы: djnz, cfsneq, cjne. Неопытному программисту может показаться, что это просто набор букв, однако это неверно.
  • In и out применяются в том случае, если возникла необходимость ввода в порт (или вывода из него).
  • К управляющим командам относят int. Благодаря ему можно прекратить выполнение каких-либо процессов в пользу основного действия.

Использование директив

Программирование микроконтроллеров на языке (Ассемблер это позволяет и прекрасно справляется с функционированием) самого низкого уровня в большинстве случаев заканчивается удачно. Лучше всего использовать процессоры с ограниченным ресурсом. Для 32-разрядной техники данный язык подходит отлично. Часто в кодах можно заметить директивы. Что же это? И для чего используется?

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

  • Всем известные макросы.
  • Имеются также директивы, которыми обладает высший язык. Ассемблер отлично «считывает» их и выполняет.
  • Функции контролирования и управления режимами компилятора.
  • Распознавание констант и переменных.
  • Регулирование работы программ, которые находятся в оперативной памяти.

Происхождение названия

Благодаря чему получил название язык – «Ассемблер»? Речь идет о трансляторе и компиляторе, которые и производят зашифровку данных. С английского Assembler означает не что иное, как сборщик. Программа не была собрана вручную, была использована автоматическая структура. Более того, на данный момент уже у пользователей и специалистов стерлась разница между терминами. Часто Ассемблером называют языки программирования, хотя это всего лишь утилита.

Из-за общепринятого собирательного названия у некоторых возникает ошибочное решение, что существует единый язык низкого уровня (или же стандартные нормы для него). Чтобы программист понял, о какой структуре идет речь, необходимо уточнять, для какой платформы используется тот или иной язык Ассемблера.

Макросредства

Языки Ассемблера, которые созданы относительно недавно, имеют макросредства. Они облегчают как написание, так и выполнение программы. Благодаря их наличию, транслятор выполняет написанный код в разы быстрее. При создании условного выбора можно написать огромный блок команд, а проще воспользоваться макросредствами. Они позволят быстро переключаться между действиями, в случае выполнения условия или невыполнения.

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

Assembler – Язык программирования низкого уровня / Хабр

Месяц назад я попытался сосчитать, сколько разных инструкций поддерживается современными процессорами, и насчитал 945 в Ice Lake. Комментаторы затронули интересный вопрос: какая часть всего этого разнообразия реально используется компиляторами? Например, некто Pepijn de Vos в 2016 подсчитал, сколько разных инструкций задействовано в бинарниках у него в /usr/bin, и насчитал 411 — т.е. примерно треть всех инструкций x86_64, существовавших на тот момент, не использовались ни в одной из стандартных программ в его ОС. Другая любопытная его находка — что код для x86_64 на треть состоит из инструкций mov. (В общем-то известно, что одних инструкций mov достаточно, чтобы написать любую программу.)

Я решил развить исследование de Vos, взяв в качестве «эталонного кода» компилятор LLVM/Clang. У него сразу несколько преимуществ перед содержимым /usr/bin неназванной версии неназванной ОС:

  1. С ним удобно работать: это один огромный бинарник, по размеру сопоставимый со всем содержимым /usr/bin среднестатистического линукса;
  2. Он позволяет сравнить разные ISA: на releases.llvm.org/download.html доступны официальные бинарники для x86, ARM, SPARC, MIPS и PowerPC;
  3. Он позволяет отследить исторические тренды: официальные бинарники доступны для всех релизов начиная с 2003;
  4. Наконец, в исследовании компиляторов логично использовать компилятор и в качестве подопытного объекта 🙂

Начну со статистики по мартовскому релизу LLVM 10.0:
В прошлом топике комментаторы упомянули, что самый компактный код у них получается для SPARC. Здесь же видим, что бинарник для AArch64 оказывается на треть меньше что по размеру, что по общему числу инструкций.

А вот распределение по числу инструкций:

Assembler – Язык программирования низкого уровня / Хабр

Месяц назад я попытался сосчитать, сколько разных инструкций поддерживается современными процессорами, и насчитал 945 в Ice Lake. Комментаторы затронули интересный вопрос: какая часть всего этого разнообразия реально используется компиляторами? Например, некто Pepijn de Vos в 2016 подсчитал, сколько разных инструкций задействовано в бинарниках у него в /usr/bin, и насчитал 411 — т.е. примерно треть всех инструкций x86_64, существовавших на тот момент, не использовались ни в одной из стандартных программ в его ОС. Другая любопытная его находка — что код для x86_64 на треть состоит из инструкций mov. (В общем-то известно, что одних инструкций mov достаточно, чтобы написать любую программу.)

Я решил развить исследование de Vos, взяв в качестве «эталонного кода» компилятор LLVM/Clang. У него сразу несколько преимуществ перед содержимым /usr/bin неназванной версии неназванной ОС:

  1. С ним удобно работать: это один огромный бинарник, по размеру сопоставимый со всем содержимым /usr/bin среднестатистического линукса;
  2. Он позволяет сравнить разные ISA: на releases.llvm.org/download.html доступны официальные бинарники для x86, ARM, SPARC, MIPS и PowerPC;
  3. Он позволяет отследить исторические тренды: официальные бинарники доступны для всех релизов начиная с 2003;
  4. Наконец, в исследовании компиляторов логично использовать компилятор и в качестве подопытного объекта 🙂

Начну со статистики по мартовскому релизу LLVM 10.0:
В прошлом топике комментаторы упомянули, что самый компактный код у них получается для SPARC. Здесь же видим, что бинарник для AArch64 оказывается на треть меньше что по размеру, что по общему числу инструкций.

А вот распределение по числу инструкций:

ARM аccемблер / Хабр

Привет всем!
По роду деятельности я программист на 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

2.a1-a4

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 ассемблера

В данной таблице я собрал основные инструкции, которая потребуется для дальнейшей разработки (на самом базовом этапе:):












Название Синтаксис Применение
ADD (добавление) ADD r0, r1, r2 r0 = r1 + r2
SUB (вычитание) SUB r0, r1, r2 r0 = r1 — r2
RSB (обратное вычитание) RSB r0, r1, #10 r0 = 10 — r1
MUL (умножение) MUL r0, r1, r2 r0 = r1 * r2
MOV MOV r0, r1 r0 = r1
ORR( логическая операция) ORR r0, r1, r2 r0 = r1 | r2
TEQ TEQ r0, r1 r0 == r1
LDR (загрузка) LDR r4, [r5] r4 = *r5
STR STR r4, [r5] *r5 = r4
ADR ADR r3, a a — переменная. r3 = &a

Чтобы закрепить использование основных инструкций давайте напишем несколько простых примеров, но сначала нам понадобится 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

На выходе вы получите что-то вроде этого:

[[email protected] ~]$ 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 ассемблер. Надеюсь вам понравилось! Этого хватит для того, чтобы далее углубляться в дебри этого языка и писать на нем программы. Если все получится, буду писать дальше о том, что узнаю сам. Если есть ошибки, прошу не пинать, так как я новичок в ассемблере.

Список ресурсов для изучения Ассемблера / Хабр

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

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

   Каждая модель процессора, в принципе, имеет свой набор команд и соответствующий ему язык (или диалект) ассемблера.

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

Связывание ассемблерного кода с другими языками

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

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

   Вставка фрагментов на языке ассемблера в текст программы (специальными директивами языка) или написание процедур на языке ассемблера. Способ хороший для несложных преобразований данных, но полноценного ассемблерного кода — с данными и подпрограммами, включая подпрограммы с множеством входов и выходов, не поддерживаемых высокоуровневыми языками, с помощью него сделать нельзя.
   Модульная компиляция. Большинство современных компиляторов работают в два этапа. На первом этапе каждый файл программы компилируется в объектный модуль. А на втором объектные модули линкуются (связываются) в готовую программу. Прелесть модульной компиляции состоит в том что каждый объектный модуль будущей программы может быть полноценно написан на своем языке программирования и скомпилирован своим компилятором (ассемблером).

Синтаксис

   Единого стандарта для синтаксиса языков ассемблера не существует, конкретный разработчик волен установить свои собственные синтаксические правила. Однако существуют традиционные подходы, которых придерживаются языки ассемблера для наиболее распространённых процессорных архитектур, своего рода стандарт de facto. Так основными стандартами являются стандарты — Intel и AT&T.

Каждая инструкция записывается в отдельной строке.

Полный формат каждой строки инструкций следующий:

label: code ; comment

где label — название метки; code — собственно, инструкция языка ассемблера; comment — комментарий.

   При этом один или два компонента строки могут отсутствовать, то есть строка может состоять, к примеру, только из комментария, или содержать только метку или инструкцию.

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

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

jmp — продолжать выполнение с нового адреса памяти (от англ. jump — прыжок)
mov — переместить данные (от англ.

404 Not Found

move — передвинуть)
sub — получить разность двух значений (от англ. subtract — вычесть)
xchg — обменять значения в регистрах/ячейках памяти (от англ. exchange — обмен)

   От ассемблера к ассемблеру меняется синтаксис аргументов, но мнемоники, обычно, остаются одинаковыми (такими какие используются в оригинальной спецификации процессора), за исключением двух случаев: Если ассемблер использует кроссплатформенный AT&T-синтаксис, то оригинальные мнемоники приводятся к синтаксису AT&T.

   Если изначально существовало два стандарта записи мнемоник (система команд была наследована от процессора другого производителя).

   Например процессор Zilog Z80 наследовал систему команд Intel i8080, расширил ее и поменял мнемоники (и обозначения регистров) на свой лад. Например сменил интеловские «mov» на «ld» (команда перемещения данных). Процессоры Motorola Fireball наследовали систему команд Z80, несколько её урезав. Вместе с тем, Motorola официально вернулась к мнемоникам Intel. И в данный момент половина ассемблеров для Fireball работает с интеловскими мнемониками, а половина с мнемониками Zilog.

   Текст программ может быть дополнен директивами ассемблера (параметры, влияющие на процесс ассемблирования и свойства выходного файла).

Каждый ассемблер имеет собственные директивы.

Для упрощения и ускорения написания программ на языке ассемблера служат макросы.

Достоинства языка ассемблера

   Максимально оптимальное использование средств процессора, использование меньшего количества команд и обращений в память, и как следствие — большая скорость и меньший размер программы
   Использование расширенных наборов инструкций процессора (MMX, SSE, SSE2, SSE3)
   Доступ к портам ввода-вывода и особым регистрам процессора (в большинстве ОС эта возможность доступна только на уровне модулей ядра и драйверов)
   Возможность использования самомодифицирующегося (в том числе перемещаемого) кода (под многими платформами эта возможность недоступна, так как запись в страницы кода запрещена, в том числе и аппаратно, однако в большинстве общедоступных систем из-за их врожденных недостатков имеется возможность исполнения кода содержащегося в сегменте (секции) данных, куда запись разрешена)
   Максимальная «подгонка» для нужной платформы

   NB: Последние технологии безопасности, внедряемые в операционные системы и компиляторы, не позволяют делать самомодифицирующего кода, так как исключают одновременную возможность исполнения программы и запись в одном и том же участке памяти (технология W^X).

   Технология W^X используется в OpenBSD (где и появилась), в других BSD-системах, в Linux; в Microsoft Windows (начиная с Windows XP SP2) используется схожая технология DEP.

Недостатки

   Большие объемы кода, большое число дополнительных мелких задач, меньшее количество доступных для использования библиотек, по сравнению с языками высокого уровня
   Трудоёмкость чтения и поиска ошибок (хотя здесь многое зависит от комментариев и стиля программирования)
   Зачастую компилятор языка высокого уровня, благодаря современным алгоритмам оптимизации, даёт более эффективную программу (по соотношению качество/время разработки).
   Непереносимость на другие платформы (кроме совместимых).
   Ассемблер более сложен для совместных проектов.

Пример программы на языке ассемблера

   Пример программы для операционной системы DOS на процессоре семейства Intel x86, выдающей на экран приветствие (написан на TASM):

mov bx,1 ; указание направления вывода (на экран)
mov cx,13 ; указание количества символов строки
mov dx,offset msg ; поместить в регистр DX смещение строки
mov ah,40h ; выбор функции вывода строки
int 21h ; вызов прерывания DOS «Набор процедур» для вывода строки
int 20h ; вызов прерывания DOS (завершение программы)

msg DB ‘Hello, World!$’

msg — метка (идентификатор), упрощающая доступ к данным.
Происхождение и критика термина «язык ассемблера»

   Данный тип языков получил свое название от названия транслятора (компилятора) с этих языков — ассемблера (англ. assembler — сборщик). Название последнего обусловлено тем, что на древних компьютерах не существовало языков более высокого уровня, и единственной альтернативой созданию программ с помощью ассемблера было программирование непосредственно в кодах.

   Язык ассемблера в русском языке часто называют «ассемблером» (а что-то связанное с ним — «ассемблерный»), что, согласно английскому переводу слова, неправильно, но вписывается в правила русского языка. Однако, сам ассемблер (программу) тоже называют просто «ассемблером», а не «компилятором языка ассемблера» и т.п.

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

Язык ассемблера — Simple English Wikipedia, бесплатная энциклопедия

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

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

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

Такая ассемблерная программа могла бы состоять из множества, многих инструкций, которые вместе делают что-то, что кажется человеку очень простым и базовым. Это затрудняет чтение программы сборки для людей.Напротив, высокоуровневый язык программирования может иметь единственную инструкцию, например PRINT «Hello, world!» , который скажет компьютеру выполнить все небольшие задачи за вас.

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

Программирование в машинном коде [изменить | изменить источник]

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

05 2A 00

Этот шестнадцатеричный машинный код сообщает процессору компьютера x86 добавить 42 к сумматору.Человеку очень сложно его прочитать и понять, даже если он знает машинный код.

Использование языка ассемблера вместо [изменить | изменить источник]

На языке ассемблера каждая инструкция может быть записана в виде короткого слова, называемого мнемоникой , за которым следуют другие вещи, такие как числа или другие короткие слова. Мнемоника используется для того, чтобы программисту не приходилось запоминать точные числа в машинном коде, необходимые для того, чтобы компьютер что-то сделал.Примеры мнемоники на языке ассемблера включают add , который добавляет данные, и mov , который перемещает данные из одного места в другое. Поскольку слово «мнемоника» встречается редко, вместо него иногда используется фраза типа инструкции или просто инструкция , часто неправильно. Слова и числа после первого слова дают больше информации о том, что делать. Например, вещи, следующие за , добавляют , могут быть двумя вещами, которые нужно сложить вместе, а вещи, следующие за mov , говорят, что переместить и куда это положить.

Например, машинный код из предыдущего раздела (05 2A 00) может быть записан на ассемблере как:

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

Язык ассемблера обеспечивает так называемую абстракцию машинного кода.При использовании ассемблера программистам не нужно знать детали того, что числа означают для компьютера, ассемблер выясняет это вместо этого. На самом деле язык ассемблера по-прежнему позволяет программисту использовать все возможности процессора, которые они могли бы использовать с машинным кодом. В этом смысле язык ассемблера имеет очень хорошую и редкую черту: он обладает той же способностью выражать вещи, что и абстрагируемый объект (машинный код), но при этом гораздо проще в использовании. Из-за этого машинный код почти никогда не используется в качестве языка программирования.

Разборка и отладка [изменить | изменить источник]

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

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

  1. основная память или ОЗУ, в которой хранятся данные и инструкции,
  2. процессор, который обрабатывает данные, выполняя инструкции, а
  3. ввода и вывода (иногда сокращенно до ввода-вывода), которые позволяют компьютеру связываться с внешним миром и хранить данные вне основной памяти, чтобы он мог получить данные позже.

Основная память [изменить | изменить источник]

В большинстве компьютеров память d

.

Simple English Wikipedia, бесплатная энциклопедия

В информатике ассемблер — это программа, которая превращает язык ассемблера в машинный код. [1]
Ассемблер — это программа, которая берет базовые компьютерные инструкции и преобразует их в набор битов, которые процессор компьютера может использовать для выполнения своих основных операций. Некоторые люди называют эти инструкции языком ассемблера, а другие используют термин язык ассемблера.

Большинство компьютеров поставляются с определенным набором очень простых инструкций, которые соответствуют основным операциям, которые компьютер может выполнять.Например, команда «Загрузить» заставляет процессор перемещать строку битов из места в памяти процессора в специальное место хранения, называемое регистром. Предполагая, что процессор имеет не менее восьми регистров, каждый из которых пронумерован, следующая инструкция переместит значение (строку бит определенной длины) из ячейки памяти 3000 в место хранения, называемое регистром 8:

 л 8,3000
 

Программист может написать программу, используя последовательность этих инструкций ассемблера.Эта последовательность инструкций ассемблера, известная как исходный код или исходная программа, затем указывается программе ассемблера при запуске этой программы.
Программа на ассемблере берет каждый программный оператор в исходной программе и генерирует соответствующий битовый поток или шаблон (последовательность нулей и единиц заданной длины).
Выходные данные программы на ассемблере называются объектным кодом или объектной программой относительно исходной программы ввода. Последовательность нулей и единиц, которые составляют объектную программу, иногда называют машинным кодом.После этого объектную программу можно запустить (или выполнить) в любое время.
На самых ранних компьютерах программисты фактически писали программы в машинном коде, но вскоре были разработаны языки ассемблера или наборы инструкций для ускорения программирования. Сегодня программирование на ассемблере используется только там, где требуется очень эффективный контроль над операциями процессора. Однако для этого требуется знание набора команд конкретного компьютера. Исторически сложилось так, что большинство программ было написано на языках «более высокого уровня», таких как COBOL, FORTRAN, PL / I и C.Эти языки легче изучать и быстрее писать программы, чем язык ассемблера. Программа, обрабатывающая исходный код, написанный на этих языках, называется компилятором. Как и ассемблер, компилятор принимает операторы языка более высокого уровня и сводит их к машинному коду.

Более новая идея в подготовке и переносимости программ — это концепция виртуальной машины. Например, при использовании языка программирования Java операторы языка компилируются в общую форму машинного языка, известную как байт-код, которая может выполняться виртуальной машиной, своего рода теоретической машиной, которая приближает большинство компьютерных операций.Затем байт-код можно отправить на любую компьютерную платформу, которая ранее была загружена или встроена в виртуальную машину Java. Виртуальная машина знает об определенной длине инструкций и других особенностях платформы и гарантирует, что байт-код Java может работать.

  1. ↑ Peter Calingaert, Ассемблеры, компиляторы и перевод программ (Лондон: Pitman, 1979), стр. 9

.

x86 Assembly — Викиучебники, открытые книги для открытого мира

В этой книге рассматривается программирование на ассемблере для семейства микропроцессоров x86. Цель — научить программировать на ассемблере x86, а также познакомить с историей и базовой архитектурой семейства процессоров x86.

Говоря о x86, мы имеем в виду весь спектр процессоров на базе x86 (начиная с оригинального Intel 8086 в 1978 году). Это включает в себя:

  • Сборка IA-32, также обычно называемая сборкой x86-32 (32-разрядная архитектура Intel, начиная с Intel 80386), 32-разрядное расширение исходной 16-разрядной архитектуры процессора Intel x86 (используется в Intel 8086 — 80286 процессоров).IA-32 имеет полную обратную совместимость с 16-битной x86.
  • x86-64, также называемый расширением AMD64 или AMD 64-бит, обратно совместим с 32-битным кодом без потери производительности.
  • Intel 64, ранее называвшаяся IA-32e или EM64T, почти идентична x86-64.

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

Содержание

Введение
Основные вопросы и ответы

Основы x86

Семейство x86
Архитектура x86 и описание регистров
Комментарии
16, 32 и 64 бит
Внутренние типы данных

Набор команд x86

x86 Инструкции
Инструкции по передаче данных
Инструкции по потоку управления
Арифметические инструкции
Логические инструкции
Инструкции по сдвигу и повороту
Прочие инструкции

.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

2021 © Все права защищены. Карта сайта