Типичный вторичный вывод ассемблера - показан исходный язык ассемблера (справа) для Motorola MC6800 и собранная форма | |
Paradigm | Imperative, неструктурированная |
---|---|
Впервые появилась | 1949; 71 год назад (1949 г.) |
В компьютерном программировании язык ассемблера (или язык ассемблера ), часто сокращенно asm, любой язык программирования низкого уровня, в котором существует очень строгое соответствие между инструкциями на языке и архитектурой машинным кодом инструкциями. Поскольку сборка зависит от инструкций машинного кода, каждый язык ассемблера предназначен только для одной конкретной компьютерной архитектуры. Язык ассемблера может также называться символьным машинным кодом.
Ассемблерный код преобразуется в исполняемый машинный код с помощью служебной программы, называемой ассемблером. Процесс преобразования называется сборкой, как и при сборке исходного кода . В языке ассемблера обычно есть один оператор на машинную команду (1: 1), но комментарии и операторы, которые являются ассемблерными директивами, макросами и символическими метками. программ и ячеек памяти также часто поддерживаются.
Термин «ассемблер» обычно приписывается Уилксу, Уиллеру и Гиллу в их книге 1951 года «Подготовка программ для электронного цифрового компьютер, который, однако, использовал этот термин для обозначения «программы, которая собирает другую программу, состоящую из нескольких разделов, в единую программу».
Каждый язык ассемблера специфичен для конкретной компьютерной архитектуры а иногда и в операционной системе. Однако некоторые языки ассемблера не предоставляют особый синтаксис для вызовов операционной системы, и большинство языков ассемблера могут использоваться универсально с любой операционной системой, поскольку язык обеспечивает доступ ко всем реальным возможностям процессора ., на котором в конечном итоге основываются все механизмы системного вызова. В отличие от языков ассемблера, большинство языков программирования высокого уровня обычно переносимы на несколько архитектур, но требуют интерпретации или компиляции, что значительно задача посложнее, чем сборка.
Вычислительный этап, когда ассемблер обрабатывает программу, называется временем сборки.
Язык ассемблера использует мнемонику для представления каждой низкоуровневой машинной инструкции или кода операции, обычно также каждый архитектурный регистр, флаг и т. Д. Многие операции требуют одного или нескольких операндов для формирования полной инструкции. Большинство ассемблеров разрешают именованные константы, регистры и метки для программ и ячеек памяти и могут вычислять выражения для операндов. Таким образом, программисты освобождаются от утомительных повторяющихся вычислений, а программы на ассемблере гораздо более читабельны, чем машинный код. В зависимости от архитектуры эти элементы также могут быть объединены для конкретных инструкций или режимов адресации с использованием смещений или других данных, а также фиксированных адресов. Многие ассемблеры предлагают дополнительные механизмы для облегчения разработки программ, управления процессом сборки и помощи отладки.
Программа на ассемблере создает объектный код путем перевода комбинаций мнемоники и синтаксиса для операций и режимов адресации в их числовых эквивалентах. Это представление обычно включает в себя код операции («opcode »), а также другие управляющие биты и данные. Ассемблер также вычисляет константные выражения и разрешает символические имена для ячеек памяти и других объектов. Использование символических ссылок - ключевая особенность ассемблеров, позволяющая избежать утомительных вычислений и обновления адресов вручную после модификации программы. Большинство ассемблеров также включают средства macro для выполнения текстовой подстановки - например, для генерации общих коротких последовательностей инструкций в виде inline вместо вызываемых подпрограмм.
Некоторые ассемблеры также могут быть может выполнять некоторые простые типы наборов инструкций -специфичных оптимизаций. Одним из конкретных примеров этого могут быть вездесущие ассемблеры x86 от различных поставщиков. Названные размер перехода, большинство из них могут выполнять замену инструкций перехода (длинные переходы заменяются короткими или относительными переходами) за любое количество проходов по запросу. Другие могут даже выполнять простую перестановку или вставку инструкций, например некоторые ассемблеры для RISC архитектур, которые могут помочь оптимизировать разумное планирование команд для использования ЦП максимально эффективно.
Как ранние языки программирования, такие как Fortran, Algol, Cobol и Lisp, ассемблеры были доступны с 1950-х годов и первых поколений текстовых компьютерных интерфейсов. Однако ассемблеры были первыми, поскольку их писать намного проще, чем компиляторы для языков высокого уровня. Это связано с тем, что каждая мнемоника, а также режимы адресации и операнды инструкции переводятся скорее непосредственно в числовые представления этой конкретной инструкции, без особого контекста или анализа. Также существовало несколько классов переводчиков и полуавтоматических генераторов кода со свойствами, аналогичными как ассемблерным, так и языкам высокого уровня, с Speedcode как, возможно, одним из более известные примеры.
Может быть несколько ассемблеров с различным синтаксисом для конкретного CPU или архитектуры набора команд. Например, инструкция по добавлению данных памяти в регистр в процессоре семейства x86 может быть add eax, [ebx]
в исходном синтаксисе Intel, тогда как это будет записано addl (% ebx),% eax
в синтаксисе ATT, используемом GNU Assembler. Несмотря на разный внешний вид, разные синтаксические формы обычно генерируют один и тот же числовой машинный код . Один ассемблер может также иметь разные режимы для поддержки вариаций синтаксических форм, а также их точных семантических интерпретаций (например, FASM -синтаксис, TASM -синтаксис, идеальный режим и т. Д.., в частном случае программирования ассемблера x86 ).
Существует два типа ассемблеров в зависимости от того, сколько проходов через исходный код необходимо (сколько раз ассемблер читает исходный код) для создания объектного файла.
В обоих случаях ассемблер должен уметь определять размер каждой инструкции на начальных проходах, чтобы вычислить адреса последующих символов. Это означает, что если размер операции, относящейся к операнду, определенному позже, зависит от типа или расстояния до операнда, ассемблер сделает пессимистическую оценку при первом столкновении с операцией и, если необходимо, дополнит ее одним или несколькими "no-operation "инструкции в более позднем проходе или исправлении. В ассемблере с оптимизацией глазка адреса могут быть пересчитаны между проходами, чтобы позволить заменить пессимистический код кодом, адаптированным к точному расстоянию от цели.
Первоначальной причиной использования однопроходных ассемблеров был размер памяти и скорость сборки - часто второй проход требовал сохранения таблицы символов в памяти (для обработки прямых ссылок ), перемотка и повторное чтение исходного кода программы на ленте или повторное считывание колоды из карточек или перфоленты. В более поздних компьютерах с гораздо большей памятью (особенно на дисках) было достаточно места для выполнения всей необходимой обработки без повторного чтения. Преимущество многопроходного ассемблера заключается в том, что отсутствие ошибок ускоряет процесс связывания (или загрузку программы, если ассемблер напрямую создает исполняемый код).
Пример: в следующем фрагменте кода однопроходный ассемблер сможет определить адрес обратной ссылки BKWDпри сборке оператора S2, но не сможет определить адрес прямой ссылки FWDпри сборке оператора ветвления S1; действительно, FWDможет быть неопределенным. Двухпроходный ассемблер определил бы оба адреса на проходе 1, поэтому они будут известны при генерации кода на проходе 2.
S1B FWD... FWDEQU *... BKWDEQU *... S2B BKWD
Более сложные ассемблеры высокого уровня предоставляют языковые абстракции, такие как:
Подробнее см. Language design ниже.
Программа, написанная на языке ассемблера, состоит из серии мнемонических инструкций процессора и мета-инструкций (известных как директивы, псевдо-инструкции и псевдо-инструкции). -опс), комментарии и данные. Инструкции на языке ассемблера обычно состоят из мнемоники кода операции , за которым следует список данных, аргументов или параметров. Они переводятся ассемблером в инструкции машинного языка, которые могут быть загружены в память и выполнены.
Например, инструкция ниже сообщает процессору x86 / IA-32 переместить немедленное 8-битное значение в зарегистрируйте. Двоичный код этой инструкции - 10110, за которым следует 3-битный идентификатор регистра, который следует использовать. Идентификатор регистра AL - 000, поэтому следующий машинный код загружает регистр AL данными 01100001.
10110000 01100001
Этот двоичный компьютерный код может быть создан более удобочитаемым, выражая его в шестнадцатеричном формате следующим образом.
B0 61
Здесь B0
означает «Переместить копию следующего значения в AL, а 61
- шестнадцатеричное представление значения. 01100001, что составляет 97 в десятичном формате. Язык ассемблера для семейства 8086 предоставляет мнемонику MOV (сокращение от move) для таких инструкций, поэтому приведенный выше машинный код может быть записан на языке ассемблера следующим образом в комплекте с пояснительный комментарий, если требуется, после точки с запятой. Это намного легче читать и запоминать.
MOV AL, 61ч; Загрузить AL с 97 десятичным (61 шестнадцатеричным)
В некоторых языках ассемблера (включая этот) одна и та же мнемоника, такая как MOV, может использоваться для семейства связанных инструкций для загрузки, копирования и перемещения данных, независимо от того, выполняются ли они немедленно. значения, значения в регистрах или ячейки памяти, на которые указывают значения в регистрах или непосредственные (a / k / a прямые) адреса. Другие ассемблеры могут использовать отдельные мнемоники кода операции, такие как L для «перемещения памяти в регистр», ST для «перемещения регистра в память», LR для «перемещения регистра в регистр», MVI для «немедленного перемещения операнда в память» и т. Д.
Если одна и та же мнемоника используется для разных инструкций, это означает, что мнемоника соответствует нескольким различным двоичным кодам инструкций, исключая данные (например, 61h
в этом примере), в зависимости от следующих за ними операндов. мнемоника. Например, для процессоров x86 / IA-32 синтаксис языка ассемблера Intel MOV AL, AH
представляет собой инструкцию, которая перемещает содержимое регистра AH в регистр AL. Шестнадцатеричная форма этой инструкции:
88 E0
Первый байт, 88h, идентифицирует перемещение между регистром байтового размера и другим регистром или памятью, а второй байт, E0h, кодируется (с тремя битовыми полями), чтобы указать, что оба операнда являются регистрами, источником является AH, а местом назначения - AL.
В таком случае, когда одна и та же мнемоника может представлять более одной двоичной инструкции, ассемблер определяет, какую команду генерировать, проверяя операнды. В первом примере операнд 61h
является допустимой шестнадцатеричной числовой константой и не является допустимым именем регистра, поэтому применима только инструкция B0
. Во втором примере операнд AH
является допустимым именем регистра, а не допустимой числовой константой (шестнадцатеричной, десятичной, восьмеричной или двоичной), поэтому может быть применима только инструкция 88
.
Языки ассемблера всегда разрабатываются таким образом, что такая однозначность повсеместно обеспечивается их синтаксисом. Например, в ассемблере Intel x86 шестнадцатеричная константа должна начинаться с цифровой цифры, чтобы шестнадцатеричное число 'A' (равное десятичной десятке) было записано как 0Ah
или 0AH
, а не AH
, в частности, чтобы он не мог быть именем регистра AH. (Это же правило также предотвращает двусмысленность имен регистров BH, CH и DH, а также любого определяемого пользователем символа, который заканчивается на букву H и в противном случае содержит только символы, являющиеся шестнадцатеричными цифрами, например слово "ПЛЯЖ" ".)
Возвращаясь к исходному примеру, в то время как код операции x86 10110000 (B0
) копирует 8-битное значение в регистр AL, 10110001 (B1
) перемещает его в CL, а 10110010 (B2
) делает это в DL. Примеры для них на языке ассемблера:
MOV AL, 1h; Загрузить AL немедленным значением 1 MOV CL, 2h; Загрузить CL немедленным значением 2 MOV DL, 3h; Загрузить DL с немедленным значением 3
Синтаксис MOV также может быть более сложным, как показывают следующие примеры:
MOV EAX, [EBX]; Переместите 4 байта из памяти по адресу, содержащемуся в EBX, в EAX MOV [ESI + EAX], CL; Переместить содержимое CL в байт по адресу ESI + EAX MOV DS, DX; Перемещение содержимого DX в сегментный регистр DS
В каждом случае мнемоника MOV транслируется ассемблером непосредственно в один из кодов операций 88-8C, 8E, A0-A3, B0-BF, C6 или C7, а программист обычно не должен знать или запоминать, какой.
Преобразование языка ассемблера в машинный код - это работа ассемблера, и обратное, по крайней мере, частично может быть достигнуто с помощью дизассемблера. В отличие от языков высокого уровня, существует однозначное соответствие между многими простыми операторами ассемблера и инструкциями машинного языка. Однако в некоторых случаях ассемблер может предоставлять псевдоинструкции (по сути, макросы), которые расширяются до нескольких инструкций машинного языка для обеспечения обычно необходимых функций. Например, для машины, в которой отсутствует команда «переходить, если больше или равно», ассемблер может предоставить псевдоинструкцию, которая расширяется до «установить, если меньше, чем» и «перейти, если будет ноль (по результату установки инструкции)». Большинство полнофункциональных ассемблеров также предоставляют богатый язык макросов (обсуждается ниже), который используется поставщиками и программистами для генерации более сложного кода и последовательностей данных. Поскольку информация о псевдоинструкциях и макросах, определенных в среде ассемблера, отсутствует в объектной программе, дизассемблер не может реконструировать вызовы макросов и псевдоинструкций, а может только дизассемблировать фактическиемашинные инструкции, ассемблер сгенерировал из этих абстрактных сущностей языка ассемблера. Точно так же, поскольку комментарии в исходном файле на языке ассемблера игнорируются ассемблером и не влияют на генерируемый объектный код, дизассемблер всегда полностью не может восстановить исходные комментарии.
Каждая компьютерная архитектура имеет собственный машинный язык. Компьютеры различаются и типом поддерживаемых ими операций, разным размером и регистров, а также представлением данных в хранилище. Хотя большинство компьютеров общего назначения могут выполнять в основном одни и те же функции, способы их выполнения различаются; соответствующие языки ассемблера отражают эти различия.
Для одного набора инструкций может существовать несколько наборов мнемоник или синтаксиса языка ассемблера, обычно экземпляры набора в разных программах на ассемблере. Этот наиболее распространенный, используемый в этой связи, использует в своих библиотеках.
Два примера процессоров с двумя наборами мнемоник - это семейство Intel 8080 и Intel 8086/8088. Intel заявила об авторских правах на языке ассемблера (по крайней мере, на каждой странице опубликованной в 1970-х и начале 1980-х годов), некоторые компании, которые самостоятельно производили процессоры, совместимые с наборами инструкций Intel, изобрели свои собственные мнемоники. ЦП Zilog Z80, усовершенствованный по сравнению с Intel 8080A, поддерживает все инструкции 8080A и многие другие; Zilog изобрел совершенно новый язык ассемблера не только для новых инструкций, но и для всех инструкций 8080A. Например, если Intel использует мнемонические символы MOV, MVI, LDA, STA, LXI, LDAX, STAX, LHLD и SHLD для различных инструкций по передаче данных, язык ассемблера Z80 использует мнемонические символы LD для всех из них. Похожим случаем являются процессоры NEC V20 и V30, улучшенные копии Intel 8086 и 8088 соответственно. Как и Zilog с Z80, NEC изобрела новую мнемонику для всех инструкций 8086 и 8088, чтобы избежать обвинений в нарушении авторских прав Intel. (Сомнительно, могут ли такие авторские права быть действительными, и более поздние процессоры, такие как AMD и Cyrix, переиздали мнемонику инструкций Intel x86 / IA-32 точно без разрешения или судебного решения.) Сомнительно, чтобы на практике многие программисты V20 и V30 писали на ассемблере NEC, а не на языке Intel; поскольку любые два языка ассемблера для одной и той же архитектуры набора инструкций изоморфны (наподобие и Pig Latin), нет необходимости использовать собственный опубликованный язык ассемблера с продуктами этого производителя.
Авторы ассемблеров сильно различаются в способах категоризации операторов и в используемой ими номенклатуре. В частности, некоторые описывают что-либо, кроме машинной мнемоники или расширенной мнемоники, как псевдооперацию (псевдооперацию). Типичный язык ассемблера состоит из 3 типов инструкций, которые используются для определения программных операций:
Инструкции (операторы) на языке ассемблера, как правило, очень просты, в отличие от языков высокого уровня. Обычно мнемоника - это символическое имя для одной исполняемой инструкции машинного языка (код операции ), и для каждой инструкции машинного языка определена по меньшей мере одна мнемоника кода операции. Каждая инструкция обычно состоит из операции или кода операции плюс ноль или более операндов . Большинство инструкций к одному значению или к паре значений. Операнды могут быть непосредственными (значение, закодированное в инструкции), регистрами, указанными в инструкции или подразумеваемыми, или адресами данных, расположенными в другом месте в хранилище. Это определенная архитектура процессора: ассемблер просто соответствует, как эта архитектура работает. Расширенный мнемоника используется для указаний комбинации операций с конкретным операндом, например, асслеры System / 360 используют B
в качестве расширенной мнемоники для BC
с маской 15 и NOP
(«NO OPeration» - ничего не делать за один шаг) для BC
с маской 0.
Расширенный мнемоника часто используется для поддержки специальных инструкций, часто для целей, не очевидных из названия инструкции. Например, многие процессоры не имеют явной инструкции NOP, но имеют инструкции, которые можно использовать для этой цели. В процессоре 8086 инструкция xchg ax, ax
используется для nop
, причем nop
является псевдо-кодом операции для кодирования инструкций xchg ax, ax
. Некоторые дизассемблеры распознают это и декодируют инструкцию xchg ax, ax
как nop
. Аналогично, ассемблеры IBM для System / 360 и System / 370 используют расширенную мнемонику NOP
и NOPR
для BC <142.>и
BCR
с нулевыми масками. В системе SPARC они известны как синтетические инструкции.
Некоторые ассемблеры также простые встроенные макрокоманды, которые генерируют две более машинные инструкции. Например, с некоторыми ассемблерами Z80 команда ld hl, bc
распознается для генерации ld l, c
, за которую следует ld h, b
. Иногда их называют псевдоопкодами.
Мнемоника - это произвольные символы; в 1985 г. IEEE опубликовал Стандарт 694 для унифицированного набора мнемоник, который будет всеобщим ассемблерами. С тех пор был стандарт отменен.
Существуют инструкции, используемые для определения элементов данных для хранения данных и числа. Они определяют тип данных, длину и выравнивание данных. Эти инструкции также могут определять внешние, будут ли данные доступны для программ (программ, собранных отдельно) или только для программ, в которых определен раздел данных. Некоторые ассемблеры классифицируют их как псевдооперации.
Директивы ассемблера, также называемые псевдоопкодами, псевдооперациями или псевдооперациями, предписывающими ему выполнять операции, отличные инструкции сборки ». Директивы на то, как работает ассемблер, и «могут влиять на объектный код, таблицу символов, файл листинга и внутренние параметры ассемблера». Иногда термин «псевдо-код операции» зарезервирован для директив, которые генерируют проектный код, например тех, которые генерируют данные.
Имена псевдоопераций часто начинаются с точки, чтобы отличать их от машинных инструкций. Псевдооперации могут сделать сборку зависимой от параметров, вводимых программистом, так что одна программа может быть собрана разными способами, возможно, для разных приложений. Илидо псевоперация местное руководство для управления представлением программы, чтобы ее было легче читать и поддерживать. Другое распространенное использование псевдоопераций - резервирование их области хранения данных времени выполнения и при необходимости инициализация содержимого содержимого известными значениями.
Символьные ассемблеры позволяют связывать произвольные имена (метки или символы) с ячейками памяти и различными константами. Обычно константе и внедряется имя, инструкции каждой ссылаться на эти местоположения по имени, тем самым продвигая самодокументирующийся код. В исполняемом коде необходимо использовать каждую подпрограмму с ее точкой входа, поэтому любые вызовы подпрограммы могут использовать ее имя. Внутри подпрограмм адресатам GOTO даются метки. Некоторые ассемблеры содержат локальные символы, которые часто лексически отличаются от обычных символов (например, использование «10 $» в качестве пункта назначения GOTO).
Некоторые ассемблеры, такие как NASM, обеспечивают гибкое управление символами, позволяя программистам управлять различными пространствами имен, автоматически вычислять размер в структурых данных и назначить метки, выполненными ассемблером. Метки также можно использовать для инициализации констант и объем с перемещаемыми адресами.
Языки ассемблера, как и большинство других компьютерных языков, позволяют комментарии к программному исходному коду, которые будут игнорироваться во время сборки. Разумное комментирование важно в программах на ассемблере, поскольку значение и установку двоичных машинных инструкций может быть трудно определить. «Необработанный» (раскомментированный) язык ассемблера, созданный компиляторами или дизассемблерами, довольно трудно читать, когда необходимо внести изменения.
Многие ассемблеры встроенные предопределенные макросы, другие макросы, стандартные программистом (и многократно переопределяемые), включающие набор текстовых строк, в которые включены переменные и константы. Определение макроса чаще всего представляет собой смесь операторов ассемблера, например директив, символьных машинных инструкций и шаблонов для операторов ассемблера. Эта последовательность текстовых строк может выполнить операции или директивы. После определенияа его имя может быть вместо мнемоники. Когда ассемблер обрабатывает такой оператор, он заменяет его текстовыми строками, связанными с этим макросом, а затем обрабатывает их, как если бы они существовали в файле исходного кода (включая в некоторых ассемблерах, раскрытие любых макросов, используя в тексте замены). Макросы в этом смысле к IBM автокодировщикам 1950-х годов.
На языке ассемблера термин «макрос» представляет собой более обширную концепцию, чем в некоторых других контекстах, таких как препроцессор на языке программирования C, где его директива #define обычно используется для создания коротких однострочных макросов. Макро-инструкции ассемблера, такие как макросы в PL / I и на некоторых других языках могут быть выполнены «программами», выполняемыми ассемблером путем интерпретации во время сборки.
. более высоких уровней языков. Их также можно использовать для добавления уровней структуры к программам сборки, опционально для введения встроенного кода с помощью параметров и других функций.
Макроассемблеры часто позволяют макросам принимать параметры. Некоторые ассемблеры включают в себя сложные сложные макроязыки, включающие такие элементы языка высокого уровня, как необязательные параметры, символьные переменные, условные выражения, манипуляции со строками и арифметические операции, которые можно использовать во время выполнения данного макроса и позволяющие макросам контекст или обмениваться информацией.. Таким образом, может вызвать набор инструментов на языке ассемблера или определений на основе инструкций аргументов макроса. Это может быть использовано, например, для генерации структур данных в виде записей или циклов «нутый » или может генерировать целые алгоритмы на основе сложных параметров. Например, макрос «сортировки» может принять спецификацию сложного ключа сортировки и сгенерировать код, созданный для этого конкретного ключа, не требуя тестов во время выполнения, которые потребуются для общей процедуры, интерпретирующей спецификацию. Организация, использующая язык ассемблера, который был сильно расширен с помощью такого набора макросов, может считаться работающей на языке более высокого уровня, поскольку такие программисты не работают с элементами нижнего уровня компьютера. Подчеркивая этот момент, макросы использовались для реализации ранней предлагаемой машины в SNOBOL4 (1967), которая была написана на языке реализации SNOBOL (SIL), языке асслера для данной машины.. Целевая машина переведет это в свой собственный код с помощью макроса-ассемблера . В то время это обеспечивало мощную портативность.
Макросы использовались для настройки крупномасштабных программных систем для конкретных клиентов в эпоху мэйнфреймов, а также использовались персоналом клиентов для удовлетворения потребностей своих работодателей путем создания конкретных версий операционных систем производителя. Это было сделано, например, системными программистами, работающими с системой / виртуальной машиной Conversational Monitor IBM (VM / CMS ) и с надстройками IBM для «обработки транзакций в реальном времени»., Система управления информацией о клиентах CICS и ACP / TPF, авиационная / финансовая система, которая началась в 1970-х и до сих пор управляет множеством крупных компьютерных резервных копий. системы (CRS) и системы кредитных карт сегодня.
Также возможно использовать только возможности обработки макросов ассемблера для генерации кода, написанного на совершенно разных языках, например, для генерации версии программы на COBOL с использованием чистого программа макроса ассемблера, содержащая строки кода COBOL внутри операторов времени сборки, инструктирующих ассемблер генерировать произвольный код. IBM OS / 360 использует макросы для выполнения генерации системы. Пользователь указывает параметры, кодируя серию макросов ассемблера. При сборке этих макросов создается поток заданий для построения системы, включая управляющие операторы языка управления заданиями и служебной программы.
Это связано с тем, что, как было реализовано в 1960-х годах, концепция «обработки макросов» не зависит от концепции «сборки», первая из которых, выражаясь современными терминами, представляет собой больше текстовую обработку, обработку текста, чем создание объекта. код. Концепция обработки макросов появилась и появляется в языке программирования C, который поддерживает «инструкции препроцессора» для установки переменных и выполнения условных тестов на их значениях. В отличие от некоторых предыдущих макропроцессоров внутри ассемблеров, препроцессор C не является завершенным по Тьюрингу, потому что ему не хватает возможности либо зацикливаться, либо «переходить к», последний позволяет программам зацикливаться.
Несмотря на мощь обработки макросов, она вышла из употребления во многих языках высокого уровня (основными исключениями являются C, C ++ и PL / I), оставаясь неизменной для ассемблеров.
Подстановка параметров макроса строго по имени: во время обработки макроса значение параметра заменяется в текстовом виде на его имя. Самым известным классом возникающих ошибок было использование параметра, который сам был выражением, а не простым именем, когда писатель макросов ожидал имя. В макросе:
foo: macro a load a * b
предполагается, что вызывающий объект предоставит имя переменной, и будет использоваться «глобальная» переменная или константа b умножить на "а". Если foo вызывается с параметром a-c
, происходит расширение макроса load a-c * b
. Чтобы избежать возможной двусмысленности, пользователи макропроцессоров могут заключать в скобки формальные параметры внутри определений макросов, или вызывающие могут заключать в скобки входные параметры.
Были написаны пакеты макросов, обеспечивающие элементы структурированного программирования для кодирования потока выполнения. Самый ранний пример этого подхода был первоначально предложен Харланом Миллсом (март 1970 г.) и реализован Марвином Кесслером из IBM Federal Systems Division, который предоставил IF / ELSE / ENDIF и аналогичные блоки потока управления для Программы ассемблера OS / 360. Это был способ уменьшить или исключить использование операций GOTO в коде ассемблера, что является одним из основных факторов, вызывающих спагетти-код на языке ассемблера. Этот подход был широко принят в начале 1980-х (последние дни широкомасштабного использования языка ассемблера).
Любопытным проектом был «поточно-ориентированный» ассемблер для 8080 / Z80, процессоры от Whitesmiths Ltd. (разработчики Unix -подобная операционная система Idris и первый коммерческий C компилятор ). Язык был классифицирован как ассемблер, потому что он работал с необработанными машинными элементами, такими как коды операций, регистры и ссылки на память; но он включал синтаксис выражения, чтобы указать порядок выполнения. Скобки и другие специальные символы, наряду с конструкциями блочно-ориентированного структурного программирования, управляют последовательностью генерируемых инструкций. A-natural был создан как объектный язык компилятора C, а не для ручного кодирования, но его логический синтаксис завоевал некоторых поклонников.
После упадка крупномасштабной разработки ассемблера очевидного спроса на более сложные ассемблеры не было. Несмотря на это, они все еще разрабатываются и применяются в тех случаях, когда ограничения ресурсов или особенности в системе управления системой препятствуют эффективному использованию языков более высокого уровня.
Ассемблеры с сильным макро-движком позволяют структурированное программирование через макросы, такие как макрос переключателя, поставляемый пакетом Masm32 (этот код является полной программой):
include \ masm32 \ include \ masm32rt.inc; используйте библиотеку Masm32.code demomain: REPEAT 20 switch rv (nrandom, 9); сгенерировать число от 0 до 8 mov ecx, 7 case 0 вывести «case 0» case ecx; в отличие от других языков программирования, выведите «case 7»; переключатель Masm32 разрешает "переменные случаи" case 1.. 3.if eax == 1 выведите "case 1".elseif eax == 2 выведите "case 2".else print "cases 1 to 3: other".endif case 4, 6, 8 выведите "варианты 4, 6 или 8" по умолчанию mov ebx, 19; напечатать 20 звезд. Повторить печать "*" dec ebx. До подписания? ; цикл до тех пор, пока не будет установлен флаг знака. endw print chr $ (13, 10) ENDM exit end demomain
Языки ассемблера не были доступны в то время, когда был представлен компьютер с хранимой программой. Кэтлин Бут «приписывают изобретение языка ассемблера» на основании теоретической работы, она начала в 1947 году, работая над ARC2 в Биркбек, Лондонский университет после консультации Эндрю Бут (позже ее муж) с математиком Джоном фон Нейманом и физиком Германом Голдстайном в Институте перспективных исследований.
В конце 1948 г., автоматический калькулятор с электронным запоминанием задержки (EDSAC) имел ассемблер (названный «начальные заказы»), интегрированный в его программу bootstrap. В нем использовалась однобуквенная мнемоника, разработанная Дэвидом Уилером, который признан компьютерным обществом IEEE создателем первого «ассемблера». В отчетах о EDSAC введен термин «сборка» для процесса объединения полей в командное слово. SOAP (Symbolic Optimal Assembly Program ) был ассемблером для компьютера IBM 650, написанным Стэном Поли в 1955 году.
Языки ассемблера устраняют большую часть подверженных ошибкам утомительное и трудоемкое программирование первого поколения, необходимое для самых первых компьютеров, освобождает программистов от утомительной работы, такой как запоминание числовых кодов и вычисление адресов.
Когда-то языки ассемблера широко использовались для всех видов программирования. Однако к 1980-м годам (1990-е годы на микрокомпьютерах ) их использование было в значительной степени вытеснено языками более высокого уровня в поисках улучшенной производительности программирования. Сегодня ассемблер по-прежнему используется для прямого управления оборудованием, доступа к специализированным инструкциям процессора или для решения критических проблем с производительностью. Обычно используются драйверы устройств, низкоуровневые встроенные системы и системы реального времени.
Исторически многие программы были написаны полностью на языке ассемблера. Burroughs MCP (1961) был первым компьютером, для которого операционная система не была полностью разработана на языке ассемблера; он был написан на проблемно-ориентированном языке исполнительных систем (ESPOL), диалекте Алгола. Многие коммерческие приложения также были написаны на языке ассемблера, включая большое количество программного обеспечения для мэйнфреймов IBM, написанного крупными корпорациями. COBOL, FORTRAN и некоторые PL / I в конечном итоге вытеснили большую часть этой работы, хотя ряд крупных организаций сохраняли инфраструктуры приложений на ассемблере и в 1990-х годах.
Большинство ранних микрокомпьютеров полагались на кодируемый вручную ассемблер, включая большинство операционных систем и крупных приложений. Это было связано с тем, что эти системы имели жесткие ограничения ресурсов, требовали особой архитектуры памяти и отображения и предоставляли ограниченные системные службы с ошибками. Возможно, более важным было отсутствие первоклассных компиляторов языков высокого уровня, подходящих для использования на микрокомпьютерах. Психологический фактор, возможно, также сыграл свою роль: первое поколение программистов микрокомпьютеров сохраняло увлеченность, «проволоку и плоскогубцы».
В более коммерческом контексте основными причинами использования языка ассемблера были минимальный раздут (размер), минимальные накладные расходы, большая скорость и надежность.
Типичными примерами программ на большом языке ассемблера того времени являются операционные системы IBM PC DOS, компилятор Turbo Pascal и ранние приложения, такие как электронная таблица программа Lotus 1-2-3. Язык ассемблера использовался для обеспечения максимальной производительности Sega Saturn, консоли, для которой, как известно, было сложно разрабатывать и программировать игры. Другой пример - аркадная игра NBA Jam 1993 года.
Язык ассемблера долгое время был основным языком разработки для многих популярных домашних компьютеров 1980-х и 1990-х годов (таких как MSX, Sinclair ZX Spectrum, Commodore 64, Commodore Amiga и Atari ST ). Во многом это было связано с тем, что интерпретируемые диалекты BASIC в этих системах предлагали недостаточную скорость выполнения, а также недостаточные возможности для полного использования доступного оборудования в этих системах. Некоторые системы даже имеют интегрированную среду разработки (IDE) с высокоразвитыми средствами отладки и макросов. Некоторые компиляторы, доступные для Radio Shack TRS-80 и его преемников, имели возможность комбинировать встроенный исходный код ассемблера с операторами программы высокого уровня. После компиляции встроенный ассемблер произвел встроенный машинный код.
Всегда велись споры о полезности и производительности языка ассемблера по сравнению с языками высокого уровня.
Хотя язык ассемблера имеет особую нишу, где это важно (см. Ниже), существуют и другие инструменты для оптимизации.
По состоянию на июль 2017 года индекс TIOBE из По популярности языка программирования ассемблер занимает 11 место, опережая, например, Visual Basic. Ассемблер можно использовать для оптимизации по скорости или для оптимизации по размеру. В случае оптимизации скорости современные оптимизирующие компиляторы, как утверждается, преобразуют языки высокого уровня в код, который может выполняться так же быстро, как рукописная сборка, несмотря на контрпримеры, которые можно найти. Сложность современных процессоров и подсистем памяти затрудняет эффективную оптимизацию как для компиляторов, так и для программистов на ассемблере. Более того, повышение производительности процессора привело к тому, что большинство процессоров большую часть времени простаивают с задержками, вызванными предсказуемыми узкими местами, такими как промахи кэша, операции ввода / вывода и подкачка. Это сделало скорость выполнения исходного кода не проблемой для многих программистов.
Есть некоторые ситуации, в которых разработчики могут выбрать использование ассемблера:
Язык ассемблера по-прежнему преподается в большинстве программ информатики и электронной инженерии. Хотя сегодня немногие программисты регулярно работают с языком ассемблера в качестве инструмента, основные концепции остаются важными. Такие фундаментальные темы, как двоичная арифметика, выделение памяти, обработка стека, набор символов кодирование, обработка прерываний, и компилятор было бы сложно изучить подробно, не имея представления о том, как компьютер работает на аппаратном уровне. Поскольку поведение компьютера в основном определяется его набором инструкций, логический способ изучить такие концепции - это изучение языка ассемблера. Большинство современных компьютеров имеют аналогичные наборы команд. Следовательно, изучения единственного языка ассемблера достаточно, чтобы усвоить: I) основные понятия; II) распознавать ситуации, когда использование ассемблера может быть уместным; и III), чтобы увидеть, насколько эффективный исполняемый код может быть создан из языков высокого уровня.