14 июл. 2010 г.

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

Assembler

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

Архитектура x86

Ассемблеры для DOS

Наиболее известными ассемблерами для операционной системы DOS являлись Borland Turbo Assembler (TASM) и Microsoft Macro Assembler (MASM). Также в своё время был популярен простой ассемблер A86.
Изначально они поддерживали лишь 16-битные команды (до появления процессора Intel 80386). Более поздние версии TASM и MASM поддерживают и 32-битные команды, а также все команды, введённые в более современных процессорах, и системы команд, специфических для конкретной архитектуры (такие как, например, MMX, SSE, 3DNow! и т. д.). 

Microsoft Windows

При появлении операционной системы Microsoft Windows появилось расширение TASM, именуемое TASM32, позволившее создавать программы для выполнения в среде Windows. Последняя известная версия Tasm — 5.3, поддерживающая инструкциии MMX, на данный момент включена в Turbo C++ Explorer. Но официально развитие программы полностью остановлено.
Microsoft поддерживает свой продукт под названием Microsoft Macro Assembler. Она продолжает развиваться и по сей день, последние версии включены в наборы DDK. Но версия программы, направленная на создание программ для DOS, не развивается. Кроме того, Стивен Хатчессон создал пакет для программирования на MASM под названием «MASM32». 

GNU и GNU/Linux

В состав операционной системы GNU входит компилятор gcc, включающий в себя ассемблер gas (GNU Assembler), использующий AT&T синтаксис, в отличие от большинства других популярных ассемблеров, которые используют Intel-синтаксис. 

Переносимые ассемблеры

Также существует открытый проект ассемблера, версии которого доступны под различные операционные системы, и который позволяет получать объектные файлы для этих систем. Называется этот ассемблер NASM (Netwide Assembler).
YASM это переписанная с нуля версия NASM под лицензией BSD (с некоторыми исключенями).
FASM (Flat Assembler) — молодой ассемблер под модифицированной для запрета перелицензирования (включая под GNU GPL) BSD?лицензией. Есть версии для KolibriOS, GNU/Linux, MS-DOS и Microsoft Windows, использует Intel-синтаксис и поддерживает инструкции AMD64. 

Архитектуры RISC


MCS-51
AVR
На данный момент существуют 2 компилятора производства Atmel (AVRStudio 3 и AVRStudio4). Вторая версия — попытка исправить не очень удачную первую. Так же ассемблер есть в составе WinAVR.
ARM
AVR32
MSP430
PowerPC
 

Ассемблирование и компилирование

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

Язык ассемблера

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

Содержание языка

Команды языка ассемблера один в один соответствуют командам процессора и, фактически, представляют собой удобную символьную форму записи (мнемокод) команд и их аргументов. Также язык ассемблера обеспечивает базовые программные абстракции: связывание частей программы и данныx через метки с символьными именами (при ассемблировании[1] для каждой метки высчитывается адрес, после чего каждое вхождение метки заменяется на этот адрес) и директивы[2].
Директивы ассемблера позволяют включать в программу блоки данных (описанные явно или считанные из файла); повторить определённый фрагмент указанное число раз; компилировать фрагмент по условию; задавать адрес исполнения фрагмента, отличный от адреса расположения в памяти[уточнить!]; менять значения меток в процессе компиляции; использовать макроопределения с параметрами и др.
Каждая модель процессора, в принципе, имеет свой набор команд и соответствующий ему язык (или диалект) ассемблера. 

Достоинства и недостатки


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

Минимальное количество избыточного кода, то есть использование меньшего количества команд и обращений в память, позволяет увеличить скорость и уменьшить размер программы.
Обеспечение полной совместимости и максимального использования возможностей нужной платформы: использование специальных инструкций и технических особенностей данной платформы.
При программировании на ассемблере становятся доступными специальные возможности: непосредственный доступ к аппаратуре, портам ввода-вывода и особым регистрам процессора, а также возможность написания самомодифицирующегося кода (то есть метапрограммирование, причём без необходимости программного интерпретатора).
Последние технологии безопасности, внедряемые в операционные системы, не позволяют делать самомодифицирующегося кода, так как исключают одновременную возможность исполнения инструкций и запись в одном и том же участке памяти (технология W^X в BSD-системах, DEP в Windows). 

Недостатки языка ассемблера

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

Применение

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

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

Поскольку на ассемблере чаще всего пишут лишь фрагменты программы, их необходимо связывать с остальными частями на других языках. Это достигается 2 основными способами:
На этапе компиляции — вставка в программу ассемблерных фрагментов (англ. inline assembler) специальными директивами языка, в том числе написание процедур на языке ассемблера. Способ удобен для несложных преобразований данных, но полноценного ассемблерного кода, с данными и подпрограммами, включая подпрограммы с множеством входов и выходов, не поддерживаемых высокоуровневыми языками, с помощью него сделать нельзя.
На этапе компоновки, или раздельная компиляция. Для взаимодействия скомпонованных модулей достаточно, чтобы связующие функции[3] поддерживали нужные соглашения о вызовах (англ. calling conventions) и типы данных. Написаны же отдельные модули могут быть на любых языках, в том числе и на ассемблере. 

Синтаксис

Общепринятого стандарта для синтаксиса языков ассемблера не существует. Однако, существуют стандарты, которых придерживаются большинство разработчиков языков ассемблера. Основными такими стандартами являются Intel-синтаксис и AT&T-синтаксис.

Инструкции

Общий формат записи инструкций одинаков для обоих стандартов:
[метка:] опкод [операнды] [;комментарий]
где опкод — непосредственно мнемоника инструкции процессору. К ней могут быть добавлены префиксы (повторения, изменения типа адресации и пр.).
В качестве операндов могут выступать константы, названия регистров, адреса в оперативной памяти и пр.. Различия между стандартами Intel и AT&T касаются, в основном, порядка перечисления операндов и их синтаксиса при различных методах адресации.
Используемые мнемоники обычно одинаковы для всех процессоров одной архитектуры или семейства архитектур (среди широко известных - мнемоники процессоров и контроллеров Motorola, ARM, x86). Они описываются в спецификации процессоров. Возможные исключения:
Если ассемблер использует кроссплатформенный AT&T-синтаксис (оригинальные мнемоники приводятся к синтаксису AT&T)
Если изначально существовало два стандарта записи мнемоник (система команд была наследована от процессора другого производителя).
Например, процессор Zilog Z80 наследовал систему команд Intel i8080, расширил ее и поменял мнемоники (и обозначения регистров) на свой лад. Например сменил интеловские mov[4] на ld. Процессоры Motorola Fireball наследовали систему команд Z80, несколько её урезав. Вместе с тем, Motorola официально вернулась к мнемоникам Intel. И в данный момент половина ассемблеров для Fireball работает с интеловскими мнемониками, а половина с мнемониками Zilog. 

Директивы

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

Пример программы

Пример программы Hello world для MS-DOS для архитертуры x86 на диалекте TASM:
.MODEL TINY
CODE SEGMENT
ASSUME CS:CODE, DS:CODE
ORG 100h
START:
        mov ah,9
        mov dx,OFFSET Msg
        int 21h
        int 20h
        Msg DB 'Hello World',13,10,'$'
CODE ENDS
END START 
 

Происхождение и критика термина «язык ассемблера»

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