Язык ассемблера
Язык ассемблера или просто ассемблер - понятное человеку представление машинного языка, используемого определенной архитектурой компьютера. Машинный язык, шаблон битов кодирующих машинные операции, делается читабельным заменой сырых значения так называемой "мнемоникой".
Например, компьютер с соответствующим процессором поймет эту машинную инструкцию x86/IA-32: ::10110000 01100001 Однако програмистам легче запомнить эквивалентное представление на ассемблере: ::mov al, 0x61 что означает переместить шестадцатеричное значение 61 (97 - десятичное) в процессорный регистр с именем 'al'. Мнемоническое "mov" сокращение для "move," (переместить), и за ним следует разделяемый запятой список аргументов или параметров; это типичное выражение на языке ассемблера.
В отличии от языков высокого уровня, обычно имеется точное соответствие между простыми выражениями ассемблера и инструкциями на машинном языке. Превращение ассемблера в машинный язык делается ассемблером и обратное диассемблером.
У каждой компьютерной архитектуры есть сообственный машинный язык, а значит и язык ассемблера. Компьютерные архитектуры отличаются количеством и тпипом операций, поддерживаемых ими. Они также могут иметь различный размер, количество регисторов и различные представления типов данных в памяти. Тогда как все компьютеры для общих целей имеют в целом одну и ту же функциональность, способ её реализации отличается, и все эти отличия неизбежно отражаются на языке ассемблера.
В дополнение, могут существовать различные варианты мнемоники или синтаксиса для одного набора инструкций. В таких случаях, обычно в документации производителем указывается самый популярный вариант.
Машинные инструкции
Инструкции в языке ассемблера обычно очень простые, в отличии от языков высокого уровня. Любая инструкция, обращающаяся к памяти (за данными или за целью прыжка) будет также иметь режим адресации для определения того, как высчитать требуемый адрес памяти. Более сложные операции должны быть построены из этих простых. Некоторые из операций, доступные в большинстве наборе инструкций:
- перемещения
- задают регистр (временная "сверхоперативная память" в самом CPU) в определённое постоянное значение
- переместить данные из памяти в регистр или наоборот. Это делается для того, чтобы получить данные и позже выполнить по ним вычисление, или сохранить результат вычисления.
- считывание и запись данных с оборудования
- вычисление
- прибавить, вычесть, умножить, или разделить значения двух регистров, помещая результат в регистр
- выполнить побитовые операции, taking the conjunction/disjunction (and/or) of corresponding bits in a pair of registers, or the negation (not) of each bit in a register
- сравнить два значения в регистрах (например, чтобы проверить, меньше ли одно другого, или они равны)
- повлиять на поток программы
- прыгнуть на другое местоположение в программе и исполнить инструкции там
- прыгнуть на другое местоположение при определённом условии
- прыгнуть на другое местоположение, но сохранить местоположение следующей инструкции как точку возврата (вызов)
Определённые наборы инструкций часто будут иметь одну или несколько инструкций для обычных операций, для чего иначе потребуется множество инструкций. Примеры:
- сохранить много регисторов в стеке за раз
- переместить большие блоки памяти
- сложная арифметика и/или операции с плавающей точкой (синус, косинус, квадратный корень и т. д.)
- применить простую операцию (к примеру, прибавление) к вектору значений
Директивы языка ассемблера
В дополнение к кодам машинных инструкций, у языков ассемблера есть дополнительные директивы для сборки блоков данных и присвоения адресов инструкциям или коду.
У них есть обычно простая возможность определять значения символьными выражениями, которые превращаются в машинные во время ассемблирования, делая возможным написание кода, более лёгкого для чтения и понимания.
Как и в большинстве компьютерных языков, возможно добавление комментариев, которые игнорируются ассемблером.
Также обычно используется встроенный макро-язык для облегчения генерациии сложных кусков кода или данных.
На практике, отсутствие комментариев и замена символов настоящими цифрами делает понимание человеком диассамблированного кода определённо более сложным, чем был бы оригинальный исходник.
Usage of assembly language
There is some debate over the usefulness of assembly language. It is often said that modern compilers can render higher-level languages into codes that run as fast as hand-written assembly, but counter-examples can be made, and there is no clear consensus on this topic. It is reasonably certain that, given the increase in complexity of modern processors, effective hand-optimization is increasingly difficult and requires a great deal of knowledge.
However, some discrete calculations can still be rendered into faster running code with assembly, and some low-level programming is simply easier to do with assembly. Some system-dependent tasks performed by operating systems simply cannot be expressed in high-level languages. In particular, assembly is often used in writing the low level interaction between the operating system and the hardware, for instance in device drivers. Many compilers also render high-level languages into assembly first before fully compiling, allowing the assembly code to be viewed for debugging and optimization purposes.
It's also common, especially in relatively low-level languages such as C, to be able to embed assembly language into the source code with special syntax. Programs using such facilities, such as the Linux kernel, often construct abstractions where different assembly is used on each platform the program supports, but it is called by portable code through a uniform interface.
Many embedded systems are also programmed in assembly to obtain the absolute maximum functionality out of what is often very limited computational resources, though this is gradually changing in some areas as more powerful chips become available for the same minimal cost.
Another common area of assembly language use is in the system BIOS of a computer. This low-level code is used to initialize and test the system hardware prior to booting the OS and is stored in ROM. Once a certain level of hardware initialization has taken place, code written in higher level languages can be used, but almost always the code running immediately after power is applied is written in assembly language. This is usually due to the fact system RAM may not yet be initialized at power-up and assembly language can execute without explicit use of memory, especially in the form of a stack.
Assembly language is also valuable in reverse engineering, since many programs are distributed only in machine code form, and machine code is usually easy to translate into assembly language and carefully examine in this form, but very difficult to translate into a higher-level language. Tools such as the Interactive Disassembler make extensive use of disassembly for such a purpose.