Ассемблер — это низкоуровневый язык программирования, который раскрывает инструкции, используемые компьютером для управления своим аппаратным обеспечением. В этой статье мы рассмотрим ассемблер как введение в программирование с примерами кода и пояснениями. Если вы когда-нибудь задумывались, что такое «указатель» или «стек» и стоит ли вам изучать ассемблер, чтобы стать программистом, читайте дальше. В этой статье объясняется, что такое язык ассемблера, его преимущества и недостатки, приводятся примеры типичных случаев использования языков ассемблера, а также некоторые ресурсы, если вы хотите узнать больше об этой теме. Начнем!
Определение языка ассемблера
Ассемблерный язык — это низкоуровневый язык программирования, специфичный для определенного типа процессора. Обычно он записывается в мнемонической форме, которая представляет собой символическое представление машинного кода, генерируемого ассемблером. В языке ассемблера коды операций машинных инструкций заменяются мнемоническими кодами, а адреса инструкций или операндов заменяются адресными символами или метками. В разных устройствах язык ассемблера соответствует разным наборам машинных инструкций и преобразуется в машинные инструкции в процессе ассемблирования. Между конкретным языком ассемблера и конкретным набором машинных инструкций существует однозначное соответствие, и он не является непосредственно переносимым между разными платформами.
История языка ассемблера
История языка ассемблера уходит корнями в самые ранние дни развития вычислительной техники. Один из первых языков ассемблера был создан в 1950-х годах для компьютера IBM 701. Этот язык, названный Autocode, был разработан Аликом Гленни и основывался на существующем коде, использовавшемся для предшественника 701, IBM 650.
Вскоре за Autocode последовал ряд других языков ассемблера, в том числе FORTRAN Assembly Language (FAL) и COBOL Assembly Language (CAL). Эти языки были разработаны для упрощения программирования на соответствующих языках высокого уровня и имели большой успех.
Язык программирования
С момента появления первого в мире электронного компьютера в 1946 году способ и язык общения между людьми и машинами стали основным направлением исследований инженеров-программистов и специалистов в области компьютерных технологий. Более эффективные и простые языки программирования стали новым фаворитом инженеров-программистов. С быстрым развитием компьютеров скорость обновления аппаратного обеспечения компьютеров становится все быстрее, а требования к языкам программирования становятся все более строгими. За последние несколько десятилетий языки программирования прошли долгий путь, и на сегодняшний день существует уже 3 поколения языков. Чтобы удовлетворить требования к программированию и функции программного обеспечения в различных областях, большое количество языков программирования прошло процесс модификации, замены и развития, и в конечном итоге превратилось в разнообразие современных языков программирования. Несмотря на многочисленные попытки найти универсальный язык, который мог бы адаптироваться ко всем средам программирования, ни одна из них не увенчалась успехом. Язык программирования стремительно развивается вместе с современными технологиями, и мудрость человечества проявляется все больше и больше.

1 поколение: машинный язык
Машинный язык — это язык программирования первого поколения. В начале изобретения компьютера, чтобы управлять компьютером для выполнения своих задач или проектов, люди могли только писать двоичные строки цифр, такие как «0» и «1», чтобы управлять компьютером. Этот язык и есть машинный язык. Интуитивно машинный язык очень непонятен и сложен для понимания, и его значение часто можно понять, заглянув в таблицы или руководства. Использовать его очень сложно, особенно когда нужно изменить готовую программу. Такой беспорядочный машинный язык не дает вам начать работу, и в нем трудно найти ошибки программы. Кроме того, операционная среда разных компьютеров различна, а инструкции и методы работы также отличаются. Поэтому, когда у вас есть специфика в этом машинном языке, вы можете выполнять его только на определенном компьютере. И как только вы меняете машину, вам нужно перепрограммировать, что значительно снижает эффективность использования и продвижения программы. Однако из-за специфики машинного языка он идеально адаптирован к определенному типу компьютера, поэтому его операционная эффективность намного выше, чем у других языков.

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

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

Состав языка ассемблера
Из-за огромной системы ассемблерных инструкций необходимо создать систему инструкций, которая имеет большое количество инструкций, сложные форматы и плохую запоминаемость. Самая сложная часть инструкции — это режим адресации, поддерживаемый инструкцией, и его суть заключается в том, как получить операнд в инструкции. Для процессора это вопрос того, как найти нужные ему данные. Однако для ассемблерного языка, лежащего в основе компьютера, этот метод адресации будет включать в себя большое количество форматов хранения вычислений и тесно связан со сложным методом управления хранением, поэтому его трудно понять. Наконец, ассемблерные инструкции также связаны с тем, как влиять на флаги, но флаги процессора очень сложны, поэтому понять этот механизм еще труднее.
отправить команду
Команды отправки включают:
- общая инструкция передачи данных: MOV;
- команда условной передачи: CMOVcc;
- команды операций со стеком: PUSH, PUSHA, PUSHAD, POP, POPA, POPAD;
- команда обмена: XCHG, XLAT, BSWAP;
- команда передачи селектора адреса или дескриптора сегмента: LEA, LDS, LES, LFS, LGS, LSS и т. д.
логическая операция
Эта часть инструкций используется для выполнения арифметических и логических операций, в том числе:
- инструкция сложения: ADD, ADC;
- команда вычитания: SUB, SBB;
- команда «плюс один»: INC;
- инструкции десятичной корректировки: AAA, AAS, DAA, DAS;
- инструкция минус один: DEC;
- команда сравнения: CMP;
- команды расширения знака: CBW, CWDE, CDQE;
- команды умножения: MUL, IMUL;
- инструкции деления: DIV, IDIV;
- инструкции логических операций: AND, NOT, OR, XOR, TEST.
команда сдвига
Эта часть инструкции используется для перемещения регистра или операнда памяти указанное количество раз.
- команда логического сдвига влево: SHL;
- команда логического сдвига вправо: SHR;
- инструкция арифметического сдвига влево: SAL;
- инструкция арифметического сдвига вправо: SAR;
- команда циклического сдвига влево: ROL ;
- команда циклического сдвига вправо: ROR и т. д.
манипуляция битами
Эта часть инструкций включает:
- инструкцию проверки бита: BT;
- инструкцию проверки и установки бита: BTS;
- инструкцию проверки и сброса бита: BTR;
- инструкцию проверки и инвертирования бита: BTC;
- инструкцию по сдвигу битов вперед: BSF;
- команда обратного сканирования битов: BSR.
передача контроля
Эта часть включает:
- команду безусловного перехода: JMP;
- условную инструкцию перехода: JCC, JCXZ;
- команду цикла: LOOP, LOOPE, LOOPNE;
- команду вызова процедуры: CALL;
- команду возврата из подпроцедуры: RET;
- команда прерывания: INTn, INT3, INTO, IRET и т. д.
Манипуляции со строками
Эта часть инструкций используется для работы со строкой данных, включая:
- команду переноса строки: MOVS;
- инструкцию сравнения строк: CMPS;
- инструкцию сканирования строки: SCANS;
- инструкцию сохранения строки: STOS;
- инструкцию загрузки строки: LODS.
вход Выход
Эта часть инструкций используется для обмена данными с периферийными устройствами, включая инструкции ввода портов IN/INS и инструкции вывода портов OUT/OUTS.
Пример на языке ассемблера
Вот простая программа на языке ассемблера, которая складывает два числа:
; add.asm
;
; This program adds two numbers together
;
section .data
; These are the two numbers we will be adding
; We must store them in memory so the CPU can access them
num1: dw 1234
num2: dw 5678
section .text
; This is the code section of the program
; The code is a set of instructions for the CPU to execute
global _start
_start:
; Load the two numbers into registers
mov eax, [num1]
mov ebx, [num2]
; Add the numbers together
add eax, ebx
; Store the result in the num1 memory location
mov [num1], eax
; Exit the program
mov eax, 1
int 0x80
Особенности языка ассемблера
1. Зависимость от машины
Это низкоуровневый язык, ориентированный на машину, обычно разработанный для конкретного компьютера или семейства компьютеров. Поскольку он представляет собой символьное представление машинных инструкций, разные машины имеют разные языки ассемблера.
2. Высокая скорость и высокая эффективность
Ассемблер сохраняет преимущества машинного языка и обладает такими характеристиками, как прямота и простота. Он может эффективно обращаться к различным аппаратным устройствам компьютера, таким как диски, память, ЦП, порты ввода-вывода и т. д., и занимает меньше памяти, а также имеет высокую скорость выполнения. Это эффективный язык программирования.
3. Сложность написания и отладки
Поскольку аппаратное обеспечение управляется напрямую, а простые задачи также требуют большого количества операторов языка ассемблера, при разработке программ необходимо учитывать все аспекты. Необходимо учитывать все возможные проблемы, а также разумно распределять и использовать различные программные и аппаратные ресурсы. Таким образом, это неизбежно увеличит нагрузку на программиста. Точно так же при отладке программы, если возникает проблема с ее работой, ее трудно обнаружить.
Преимущества
Как язык программирования второго поколения, стоящий выше машинного языка, ассемблер также имеет много преимуществ:
- Может легко считывать состояние памяти и состояние аппаратного интерфейса ввода-вывода.
- Написанный код может быть точно выполнен, поскольку количество этапов компиляции значительно сокращается.
- Как низкоуровневый язык, он обладает высокой расширяемостью.
Недостатки
- Поскольку код очень монотонен и в нем мало специальных символов-инструкций, он становится многословным и сложным для написания.
- Поскольку ассемблер все равно должен самостоятельно вызывать память для хранения данных, легко возникнуть ошибки, которые нелегко отладить.
- Даже если программа будет завершена, ее последующее обслуживание займет много времени.
- Из-за особенности машины возникает проблема низкой совместимости кода.
Язык ассемблера и машинный язык
Основное различие между языком ассемблера и машинным языком заключается в том, что язык ассемблера является низкоуровневым языком программирования, который требует отдельного ассемблера для преобразования в машинный код, в то время как машинный язык является прямым представлением базового машинного кода.
Ассемблерный язык более понятен для человека, чем машинный язык, но все же довольно сложен для чтения и написания. Он разработан так, чтобы быть близким к нативному машинному коду, что позволяет программисту легко понять, что делает код. Однако это также затрудняет написание переносимого кода, поскольку код тесно связан с конкретной архитектурой машины.
Машинный язык, с другой стороны, является родным кодом для машины и не читаем человеком. Он непосредственно выполняется процессором и поэтому более эффективен, чем язык ассемблера. Однако программировать на нем гораздо сложнее, поскольку программисту приходится иметь дело с базовыми инструкциями машинного кода.
Ресурсы для изучения языка ассемблера
Если вы хотите узнать больше об ассемблере, вам доступно множество различных ресурсов. Книги по ассемблеру можно найти на Amazon и в местных книжных магазинах. Вы также можете найти онлайн-курсы на таких сайтах, как Coursera, edX и Udemy. Если вы хотите узнать больше об архитектуре компьютеров, по этой теме также есть много книг. Эти ресурсы помогут вам понять, что такое ассемблер и какое влияние он оказывает на проектирование компьютерного оборудования и программного обеспечения.


