Почему Ассемблер — это круто, но сложно
Есть высокоуровневые языки — это те, где вы говорите if — else, print, echo, function и так далее. «Высокий уровень» означает, что вы говорите с компьютером более-менее человеческим языком. Другой человек может не понять, что именно у вас написано в коде, но он хотя бы сможет прочитать слова.
Но сам компьютер не понимает человеческий язык. Компьютер — это регистры памяти, простые логические операции, единицы и нули. Поэтому прежде чем ваша программа будет исполнена процессором, ей нужен переводчик — программа, которая превратит высокоуровневый язык программирования в низкоуровневый машинный код.
Ассемблер — это собирательное название языков низкого уровня: код всё ещё пишет человек, но он уже гораздо ближе к принципам работы компьютера, чем к принципам мышления человека.
Вариантов Ассемблера довольно много. Но так как все они работают по одинаковому принципу и используют (в основном) одинаковый синтаксис, мы будем все подобные языки называть общим словом «Ассемблер».
Как мыслит процессор
Чтобы понять, как работает Ассемблер и почему он работает именно так, нам нужно немного разобраться с внутренним устройством процессора.
Кроме того, что процессор умеет выполнять математические операции, ему нужно где-то хранить промежуточные данные и служебную информацию. Для этого в самом процессоре есть специальные ячейки памяти — их называют регистрами.
Регистры бывают разного вида и назначения: одни служат, чтобы хранить информацию; другие сообщают о состоянии процессора; третьи используются как навигаторы, чтобы процессор знал, куда идти дальше, и так далее. Подробнее — в расхлопе ↓
Общего назначения. Это 8 регистров, каждый из которых может хранить всего 4 байта информации. Такой регистр можно разделить на 2 или 4 части и работать с ними как с отдельными ячейками.
Указатель команд. В этом регистре хранится только адрес следующей команды, которую должен выполнить процессор. Вручную его изменить нельзя, но можно на него повлиять различными командами переходов и процедур.
Регистр флагов. Флаг — какое-то свойство процессора. Например, если установлен флаг переполнения, значит процессор получил в итоге такое число, которое не помещается в нужную ячейку памяти. Он туда кладёт то, что помещается, и ставит в этот флаг цифру 1. Она — сигнал программисту, что что-то пошло не так.
Флагов в процессоре много, какие-то можно менять вручную, и они будут влиять на вычисления, а какие-то можно просто смотреть и делать выводы. Флаги — как сигнальные лампы на панели приборов в самолёте. Они что-то означают, но только самолёт и пилот знают, что именно.
Сегментные регистры. Нужны были для того, чтобы работать с оперативной памятью и получать доступ к любой ячейке. Сейчас такие регистры имеют по 32 бита, и этого достаточно, чтобы получить 4 гигабайта оперативки. Для программы на Ассемблере этого обычно хватает.
Так вот: всё, с чем работает Ассемблер, — это команды процессора, переменные и регистры.
Здесь нет привычных типов данных — у нас есть только байты памяти, в которых можно хранить что угодно. Даже если вы поместите в ячейку какой-то символ, а потом захотите работать с ним как с числом — у вас получится. А вместо привычных циклов можно просто прыгнуть в нужное место кода.
Команды Ассемблера
Каждая команда Ассемблера — это команда для процессора. Не операционной системе, не файловой системе, а именно процессору — то есть в самый низкий уровень, до которого может дотянуться программист.
Любая команда на этом языке выглядит так:
Метка — это имя для фрагмента кода. Например, вы хотите отдельно пометить место, где начинается работа с жёстким диском, чтобы было легче читать код. Ещё метка нужна, чтобы в другом участке программы можно было написать её имя и сразу перепрыгнуть к нужному куску кода.
Команда — служебное слово для процессора, которое он должен выполнить. Специальные компиляторы переводят такие команды в машинный код. Это сделано для того, чтобы не запоминать сами машинные команды, а использовать вместо них какие-то буквенные обозначения, которые проще запомнить. В этом, собственно, и выражается человечность Ассемблера: команды в нём хотя бы отдалённо напоминают человеческие слова.
Операнды отвечают за то, что именно будут делать команды: какие ячейки брать для вычислений, куда помещать результат и что сделать с ним дополнительно. Операндом могут быть названия регистров, ячейки памяти или служебные части команд.
Комментарий — это просто пояснение к коду. Его можно писать на любом языке, и на выполнение программы он не влияет. Примеры команд:
mov eax, ebx ; Пересылаем значение регистра EBX в регистр EAX mov x, 0 ; Записываем в переменную x значение 0 add eax, х ; Складываем значение регистра ЕАХ и переменной х, результат отправится в регистр ЕАХ
Здесь нет меток, первыми идут команды (mov или add), а за ними — операнды и комментарии.
Пример: возвести число в куб
Если нам понадобится вычислить х³, где х занимает ровно один байт, то на Ассемблере это будет выглядеть так.
Первый вариант
mov al, x ; Пересылаем x в регистр AL imul al ; Умножаем регистр AL на себя, AX = x * x movsx bx, x ; Пересылаем x в регистр BX со знаковым расширением imul bx ; Умножаем AX на BX. Результат разместится в DX:AX
Второй вариант
mov al, x ; Пересылаем x в регистр AL imul al ; Умножаем регистр AL на себя, AX = x * x cwde ; Расширяем AX до EAX movsx ebx, x ; Пересылаем x в регистр EBX со знаковым расширением imul ebx ; Умножаем EAX на EBX. Поскольку x – 1-байтовая переменная, результат благополучно помещается в EAX
На любом высокоуровневом языке возвести число в куб можно одной строкой. Например:
Хитрость в том, что когда каждая из этих строк будет сведена к машинному коду, этого кода может быть и 5 команд, и 10, и 50, и даже 100. Чего стоит вызов объекта Math и его метода pow: только на эту служебную операцию (ещё до самого возведения в куб) может уйти несколько сотен и даже тысяч машинных команд.
А на Ассемблере это гарантированно пять команд. Ну, или как реализуете.
Почему это круто
Ассемблер позволяет работать с процессором и памятью напрямую — и делать это очень быстро. Дело в том, что в Ассемблере почти не тратится зря процессорное время. Если процессор работает на частоте 3 гигагерца — а это примерно 3 миллиарда процессорных команд в секунду, — то очень хороший код на Ассемблере будет выполнять примерно 2,5 миллиарда команд в секунду. Для сравнения, JavaScript или Python выполнят в тысячу раз меньше команд за то же время.
Ещё программы на Ассемблере занимают очень мало места в памяти. Именно поэтому на этом языке пишут драйверы, которые встраивают прямо в устройства, или управляющие программы, которые занимают несколько килобайт. Например, программа, которая находится в брелоке сигнализации и управляет безопасностью всей машины, занимает всего пару десятков килобайт. А всё потому, что она написана для конкретного процессора и использует его возможности на сто процентов.
Справедливости ради отметим, что современные компиляторы С++ дают машинный код, близкий по быстродействию к Ассемблеру, но всё равно немного уступают ему.
Почему это сложно
Для того, чтобы писать программы на Ассемблере, нужно очень любить кремний:
- понимать архитектуру процессора;
- знать устройство железа, которое работает с этим процессором;
- знать все команды, которые относятся именно к этому типу процессоров;
- уметь работать с данными в побайтовом режиме (забудьте о строках и массивах, ведь ваш максимум — это одна буква);
- понимать, как в ограниченных условиях реализовать нужную функциональность.
Теперь добавьте к этому отсутствие большинства привычных библиотек для работы с чем угодно, сложность чтения текста программы, медленную скорость разработки — и вы получите полное представление о программировании на Ассемблере.
Для чего всё это
Ассемблер незаменим в таких вещах:
- драйверы;
- программирование микроконтроллеров и встраиваемых процессоров;
- куски операционных систем, где важно обеспечить скорость работы;
- антивирусы (и вирусы).
На самом деле на Ассемблере можно даже запилить свой сайт с форумом, если у программиста хватает квалификации. Но чаще всего Ассемблер используют там, где даже скорости и возможностей C++ недостаточно.
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.