Язык ассемблера — всё по этой теме для программистов


Оглавление (нажмите, чтобы открыть):

Программирование: язык Ассемблера. Основы языка Ассемблера

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

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

Все языки программирования разделяются по уровням: низкий и высокий. Любой из синтаксической системы «семейки» Ассемблера отличается тем, что объединяет сразу некоторые достоинства наиболее распространенных и современных языков. С другими их роднит и то, что в полной мере можно использовать систему компьютера.

Отличительной особенностью компилятора является простота в использовании. Этим он отличается от тех, которые работают лишь с высокими уровнями. Если взять во внимание любой такой язык программирования, Ассемблер функционирует вдвое быстрее и лучше. Для того чтобы написать в нем легкую программу, не понадобится слишком много времени.

Кратко о структуре языка

Если говорить в общем о работе и структуре функционирования языка, можно точно сказать, что его команды полностью соответствуют командам процессора. То есть Ассемблер использует мнемокоды, наиболее удобные человеку для записи.

В отличие от других языков программирования, Ассемблер использует вместо адресов для записи ячеек памяти определенные метки. Они с процессом выполнения кода переводятся в так называемые директивы. Это относительные адреса, которые не влияют на работу процессора (не переводятся в машинный язык), а необходимы для распознавания самой средой программирования.

Для каждой линейки процессора существует своя система команд. При таком раскладе правильным будет любой процесс, в том числе и переведенный машинный код.

Язык Ассемблера имеет несколько синтаксисов, которые будут рассмотрены в статье.

Плюсы языка

Наиболее важным и удобным приспособлением языка Ассемблера станет то, что на нем можно написать любую программу для процессора, которая будет весьма компактной. Если код оказывается огромным, то среда программирования некоторые процессы перенаправляет в оперативную память. При этом они все выполняют достаточно быстро и без сбоев, если конечно, ими управляет квалифицированный программист.

Драйвера, операционные системы, BIOS, компиляторы, интерпретаторы и т. д. – это все программа на языке Ассемблера.

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

Минусы языка

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

Для того чтобы написать даже самую простую программу, программист должен быть квалифицированным, а его уровень знаний достаточно высоким. Средний специалист, к сожалению, зачастую пишет плохие коды.

Если платформа, для которой создается программа, обновляется, то все команды необходимо переписывать вручную – этого требует сам язык. Ассемблер не поддерживает функцию автоматического регулирования работоспособности процессов и замену каких-либо элементов.

Команды языка

Как уже было сказано выше, для каждого процессора имеется свой набор команд. Простейшими элементами, которые распознаются любыми типами, являются следующие коды:

  • Пересылка данных осуществляется при помощи mov и т. д.
  • Команды, связанные с арифметикой: sub, imul и др.
  • Побитовые и логические функции можно реализовать при помощи or, and и т. п. Именно эти основы языка Ассемблера позволяют ему быть схожим с другими.
  • Для того чтобы осуществить переход от одной команды к другой, следует прописать такие операторы: djnz, cfsneq, cjne. Неопытному программисту может показаться, что это просто набор букв, однако это неверно.
  • In и out применяются в том случае, если возникла необходимость ввода в порт (или вывода из него).
  • К управляющим командам относят int. Благодаря ему можно прекратить выполнение каких-либо процессов в пользу основного действия.

Использование директив

Программирование микроконтроллеров на языке (Ассемблер это позволяет и прекрасно справляется с функционированием) самого низкого уровня в большинстве случаев заканчивается удачно. Лучше всего использовать процессоры с ограниченным ресурсом. Для 32-разрядной техники данный язык подходит отлично. Часто в кодах можно заметить директивы. Что же это? И для чего используется?

Для начала необходимо сделать акцент на том, что директивы не переводятся в машинный язык. Они регулируют выполнение работы компилятором. В отличие от команд, эти параметры, имея различные функции, отличаются не благодаря разным процессорам, а за счет другого транслятора. Среди основных директив можно выделить следующие:

  • Всем известные макросы.
  • Имеются также директивы, которыми обладает высший язык. Ассемблер отлично «считывает» их и выполняет.
  • Функции контролирования и управления режимами компилятора.
  • Распознавание констант и переменных.
  • Регулирование работы программ, которые находятся в оперативной памяти.

Происхождение названия

Благодаря чему получил название язык – «Ассемблер»? Речь идет о трансляторе и компиляторе, которые и производят зашифровку данных. С английского Assembler означает не что иное, как сборщик. Программа не была собрана вручную, была использована автоматическая структура. Более того, на данный момент уже у пользователей и специалистов стерлась разница между терминами. Часто Ассемблером называют языки программирования, хотя это всего лишь утилита.

Из-за общепринятого собирательного названия у некоторых возникает ошибочное решение, что существует единый язык низкого уровня (или же стандартные нормы для него). Чтобы программист понял, о какой структуре идет речь, необходимо уточнять, для какой платформы используется тот или иной язык Ассемблера.

Макросредства

Языки Ассемблера, которые созданы относительно недавно, имеют макросредства. Они облегчают как написание, так и выполнение программы. Благодаря их наличию, транслятор выполняет написанный код в разы быстрее. При создании условного выбора можно написать огромный блок команд, а проще воспользоваться макросредствами. Они позволят быстро переключаться между действиями, в случае выполнения условия или невыполнения.

При использовании директив макроязыка программист получает макросы Ассемблера. Иногда он может широко использоваться, а иногда его функциональные особенности снижаются до одной команды. Их наличие в коде облегчает работу с ним, делает его более понятным и наглядным. Однако следует все равно быть внимательным – в некоторых случаях макросы, наоборот, ухудшают ситуацию.

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

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

1.«Загрузить» содержимое первой ячейки памяти в регистр общего назначения, используемый в качестве накопителя. По команде загрузки производится выборка содержимого первой ячейки памяти (оставляя содержимое самой ячейки неизменным) и запоминание его в нашем регистре-накопителе, при этом предыдущее содержимое этого регистра теряется.

2.«Сложить» содержимое второй ячейки памяти с содержимым регистра-накопителя. Как мы знаем, содержимое второй ячейки остается неизменным, а результат сложения по окончании выполнения команды оказывается в регистре-накопителе.

3.«Запомнить» результат, находящийся пока в регистре, в некоторой ячейке памяти. Таким образом, третья ячейка памяти, как и регистр, содержит результат сложения содержимых первых двух ячеек.

Предположим, что в рассмотренном примере первое число хранилось в ячейке 256, второе — в ячейке 260, и результат требовалось поместить в ячейку 249- Регистр 6 был выбран в качестве накопителя. Соответствующие команды машинного языка IBM 360 таковы:

В большинстве программ команд значительно больше. Однако и в рассмотренном выше примере для задания необходимых команд на машинном языке потребовалось задать 96 (3×32) нулей и единиц. Ошибка хотя бы в одном бите привела бы к неверному конечному результату. Кроме того, программы на машинном языке очень трудны в составлении и для понимания из-за отсутствия соответствия между языком машины и обычным «человеческим» языком.

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

Это значительное улучшение по сравнению с двоичной формой машинного языка. Для программирования сложения двух чисел вместо 96 двоичных цифр теперь требуется написать всего лишь 3×8=24 шестнадцатеричных. В самом деле, наиболее удобной формой представления двоичной информации является шестнадцатеричная форма. Но все же команды машинного языка независимо от формы их представления лишь очень отдаленно напоминают о действиях, которые в соответствии с, ними выполняются.

Нужно попытаться представить операции машинного языка и адреса в форме более понятной программисту. Этой цели служит язык ассемблера. Символические формы представления, или мнемоники кодов операций, позволяют нам, например, писать Lвместо кода операции «Загрузка» (LOAD), А вместо кода операции «Сложение» (ADD),

STвместо кода операции «Запись в память» (STORE). Применение мнемоник исключает необходимость использования двоичных кодов операций машинного языка.

Но гораздо более важным является то, что в языке ассемблера можно использовать символические имена для указания адресов памяти, регистров и констант. Результат применения символа в программе будет тот же, как если бы мы указали его двоичный эквивалент. Но поскольку выбор символа для представления некоторого числа или ячейки памяти зависит только от нас, то мы можем выбрать для этой цели символ, характеризующий данное число. Например, если программа вычисляет среднее значение нескольких чисел, то для обозначения адреса ячейки, в которой будет находиться результат, можно выбрать символ AVERAGE(СРЕДНЕЕ). Если мы не используем символов для обозначения ячеек памяти, то необходимо знать физические адреса тех ячеек, в которых хранится обрабатываемая информация. Использование символов облегчает программирование, поскольку оно устраняет необходимость иметь дело с действительными машинными адресами.

Возвращаясь к нашему примеру, назовем три ячейки памяти NUM- BER1, NUMBER2 и NUMсоответственно и регистр 6 — REGSIX. Теперь мы можем переписать команды так:

Несмотря на то что кое-что в нашем примере еще не совсем ясно, очевидно, что программу на ассемблере гораздо легче и писать и воспринимать, чем программу в машинных кодах.

Язык ассемблера — это символическое представление машинного языка. Каждой правильной команде языка ассемблера соответствует команда машинного языка, и каждой команде, записанной на языке ассемблера в некоторой программе, соответствует команда окончательной программы на машинном языке.

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

Существуют языки, такие, как, например, ФОРТРАН, программы на которых гораздо больше походят на математическое или словесное описание необходимых вычислений. На ФОРТРАНе рассмотренная выше программа будет выглядить так:

Рис. 3.1. Иерархия языков.

Мы знаем, что конечной формой любой программы является программа на языке машины. Как же программа, написанная на символическом языке ассемблера, преобразуется в машинные коды? Эту задачу выполняет программа-ассемблер. Входной информацией для программы-ассемблер является программа, написанная на языке ассемблера. На выходе получается эквивалент исходной программы на машинном языке.

ФОРТРАН, КОБОЛ, АЛГОЛ, ПЛ/1 и многие другие символические языки принадлежат классу языков, называемых компилируемыми языками. Вообще говоря, одно предложение или команда такого языка после обработки программой, называемой компилятором, преобразуется в несколько команд машинного языка. Мы уже видели, что оператор языка ФОРТРАН, задающий сложение двух чисел, эквивалентен трем машинным командам. В этом принципиальная разница между компилируемыми языками и языком ассемблера. Выражаясь математическим языком, между предложениями языка ассемблера и командами машинного языка существует взаимно однозначное соответствие, тогда как между предложениями компилируемых языков и командами машинного языка такого соответствия нет.

Компилируемые языки вообще являются языками более высокого уровня, чем машинный язык или язык ассемблера. Существует понятие иерархии языков, уровень некоторого языка в которой определяется «степенью отличия» его от машинного языка, языка «низшего» уровня. Рис. 3.1 дает представление об интересующей нас части иерархической лестницы языков.

Использование программы, написанной на одном из символических языков, состоит из двух этапов: 1) программа, написанная на символическом языке, должна быть переведена на язык машины, т. е. исходный код должен быть преобразован в объектный код; 2) под управлением полученного объектного кода работает ЭВМ, иначе говоря, объектный код является выполняемой программой. На этапе трансляции ЭВМ выполняет программу-компилятор или ассемблер, при этом исходный код рассматривается как входные данные. На этапе выполнения выполняется программа в объектном коде, в качестве входных используются данные, определенные программистом.

Зачем изучать язык ассемблера?

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

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

Во многих случаях программу для решения конкретной задачи удобнее писать на языке высокого уровня, чем на языке ассемблера, так как большинство языков высокого уровня специально ориентировано на выполнение некоторых видов работ. ФОРТРАН (FORmulaTRANslator) создан главным образом учеными; он соединяет вычислительные возможности с относительной простотой программирования алгебраических выражений, что сделало его наиболее употребимым языком для программирования научных вычислений. КОБОЛ (COmmonBusinessOrientedLanguage) был создан для программирования экономических задач; обработка больших объемов информации, например результатов инвентаризации товаров или списков сотрудников, наиболее легко программируется на КОБОЛе. АЛГОЛ (ALGOrithmicLanguage) является языком для описания алгоритмов, или правил вычислений.

Каждый из перечисленных языков служит вполне определенной цели. Но попытки выйти за пределы области ориентации языка приводят к значительным осложнениям. Обработку экономических данных трудно запрограммировать на ФОРТРАНе; чем больше требуется действий невычислительного типа для решения данной задачи, тем менее пригоден ФОРТРАН. С другой стороны, программирование на КОБОЛе сложных алгебраических выражений по меньшей мере утомительно. Кроме того, в КОБОЛе отсутствуют некоторые существенные особенности языков для научных вычислений, например библиотека стандартных подпрограмм для вычисления математических функций, и он слишком «словесен» для подобных применений. АЛГОЛ — международный язык описания алгоритмов; алгоритмы, написанные на АЛГОЛе, можно найти, например, в таком журнале, как САСМ. Однако большинство программистов согласится, что существует много задач, для которых этот язык неудобен. Язык сам по себе не содержит специальных операторов ввода-вывода, кроме того, синтаксис или структура языка, удобная для описания алгоритмов, затрудняет организацию вычислений. Таким образом, каждый из рассмотренных специализированных языков служит определенной цели, но чем он удобнее для программирования в конкретной области, тем больше теряет он в общности.

ПЛ/I(ProgrammingLanguageI) претендует на общность, которой так не хватает перечисленным выше языкам. Синтаксически этот язык является комбинацией АЛГОЛа, ФОРТРАНа и КОБОЛа и вдобавок обладает некоторыми чертами, которые делают возможным написание на нем полной операционной системы. Платой за эту гибкость является чрезвычайная сложность языка для начинающего программиста. Именно эта сложность не позволила и, вероятно, не позволит этому языку стать универсальным языком программирования для машин фирмы IBM. Для начинающего изучать вычислительную науку студента тот факт, что ПЛ/Iявляется языком высокого уровня, означает, что изучение его не может дать того, что дает изучение ассемблера — знания структуры и функций вычислительной системы.

В распоряжении программиста на ассемблере все возможности ЭВМ. Язык ассемблера по существу является машинным языком. Специализированные языки высокого уровня обычно используют только часть возможностей машины. Это вовсе не означает, что, изучив язык ассемблера, следует все программы писать только на нем. Это было бы все равно, что сказать, что, если кто-нибудь научился водить машину, ему следует перестать ходить пешком. Но можно применять ассемблер для использования тех возможностей, которые отсутствуют в других языках программирования. В действительности большинство языков высокого уровня позволяет пользователю писать подпрограммы на языке ассемблера для использования возможностей, не предусмотренных или трудно программируемых в данном языке.

Программисту, пишущему на языке высокого уровня, знание языка ассемблера дает дополнительные возможности. Чем ближе он знаком с ЭВМ и ее возможностями, тем большую выгоду он может извлечь для себя независимо от того, на каком языке он программирует. Например, человеку, знакомому с ассемблером, понятны форматы, используемые в ФОРТРАНе, и предложения определения файлов в КОБОЛе. Ему известно, для чего употребляется знак = в ФОРТРАНе и какова разница между целыми и вещественными переменными. Встретившись с вопросом: «Можно ли это сделать на ФОРТРАНе?», он сможет использовать свои знания возможностей машины для определения, имеет ли смысл этот вопрос с машинной точки зрения. Этого обычно достаточно для получения по крайней мере частичного ответа на вопрос.

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

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

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

Системные программы служат цели освобождения пользователя от выполнения машинно- и системно-ориентированных задач, не представляющих непосредственного интереса для него. Например, программист использует компилятор или ассемблер для трансляции исходной программы на машинный язык; пользователь, который был бы вынужден прежде, чем написать программу на ФОРТРАНе, написать компилятор, вероятно, откажется от такого способа решения своей задачи. Такие необходимые действия, как преобразование исходных данных из символьного кода в двоичное представление, удобное для выполнения арифметических операций, управление периферийными устройствами и управление вычислениями в целом непосредственно не связаны с проблемной программой и выполняются системными программами. Короче говоря, системные программы служат связующим звеном между программистом и аппаратурой вычислительной машины.

Поскольку системные программы расположены как бы между пользователем и машиной, то в своем большинстве они пишутся на языке ассемблера. Системные программы имеют дело с деталями конкретных машинных операций, обращение к которым невозможно при использовании языков высокого уровня. Например, программист на ФОРТРАНе обычно не управляет конкретным расположением программ в памяти машины, и ему все равно, какое конкретно устройство ввода с перфокарт будет использовано для ввода данных его программы. Действительно, поскольку он программирует на языке высокого уровня, ему нет необходимости заботиться об этих деталях. Но очевидно, что некоторые системные программы вынуждены работать с такого рода детальной информацией для обеспечения правильности работы программ пользователя и получения этими программами нужных данных. Язык ассемблера — это язык, дающий возможность выполнить эти задачи.

Теперь, как и было обещано, возвратимся к вопросу эффективности. Машинное время, т. е. время, в течение которого центральный процессор находится в распоряжении пользователя, стоит очень дорого, обычная цена составляет от 250 до 500 долларов в час. В некоторых случаях дополнительная плата взимается за использование памяти и загрузку периферийных устройств. Решение некоторых задач требует использования огромного количества памяти и машинного времени, в этих случаях возможность и экономичность их решения определяется эффективностью используемой программы.

Именно на ассемблере можно составить наиболее эффективную программу. Кроме того, программисту на ассемблере значительно •облегчается проблема использования всех возможностей ЭВМ. Так, он может увеличить скорость вычислений, используя большое количество памяти, и наоборот.

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

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

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

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

Основные сведения о языке Ассемблер. По дисциплине «Системное программирование»

Курсоваяработа

По дисциплине «Системное программирование»


Тема №4: «Решение задач на процедуры»

Вариант 2

Выполнил(а): студент(ка) гр. К751
___________ Главинская А. А
Руководитель: Доцент кафедры СИ
___________ Дамбаева С.В.
Оценка: ___________
Дата защиты: ___________

ВОСТОЧНО-СИБИРСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

ТЕХНОЛОГИЙ И УПРАВЛЕНИЯ

ЗАДАНИЕ

на курсовую работу

Дисциплина:
Тема: Решение задач на процедуры
Исполнитель(и):Главинская Арина Александровна
Руководитель:ДамбаеваСэсэгма Викторовна
Краткое содержание работы:изучение подпрограмм на языке Ассемблера,
решение задач с использованием подпрограмм
1. Теоретическая часть:Основные сведения о языке Ассемблер (набор
команд и т.д.), Организация подпрограмм, Способы передачи впараметров
в подпрограммах
2. Практическая часть:Разработать две подпрограммы, одна из которых преобразует любую заданную букву в заглавную (в том числе для русских букв), а другая преобразует букву в строчную.
преобразует любую заданную букву в заглавную, а другая преобразует букву в строчную.
преобразует букву в строчную.
Сроки выполнения проекта по графику:
1. Теоретическая часть — 30 % к 7 неделе.
2. Практическая часть — 70 % к 11 неделе.
3. Защита — 100% к 14 неделе.
Требования к оформлению:
1. Расчетно-пояснительная записка курсового проекта должна быть представлена в
электронной и твердой копиях.
2. Объем отчета должен быть не менее 20 машинописных страниц без учета приложений.
3. РПЗ оформляется по ГОСТу 7.32-91 и подписывается у руководителя.

Руководитель работы __________________

Дата выдачи «26» сентября 2017 г.

1.1 Основные сведения о языке Ассемблер. 3

1.1.1 Набор команд. 4

1.2 Организация подпрограмм в языке Ассемблер. 4

1.3 Способы передачи параметров в подпрограммах. 6

1.3.1 Передача параметров через регистры.. 6

1.3.2 Передача параметров через стек. 7

2 ПРАКТИЧЕСКИЙ РАЗДЕЛ.. 9

2.1 Постановка задачи. 9

2.2 Описание решения задачи. 9

2.3 Тестирование программы.. 7

Список литературы.. 9

Введение

Общеизвестно, что программировать на Ассемблере трудно. Как Вы знаете, сейчас существует много различных языков высокого уровня, которые позволяют затрачивать намного меньше усилий при написании программ. Естественно, возникает вопрос, когда у программиста может появиться необходимость использовать Ассемблер при написании программ. В настоящее время можно указать две области, в которых использование языка Ассемблера оправдано, а зачастую и необходимо.

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

Вторая область применения Ассемблера связана с оптимизацией выполнения программ. Очень часто программы-переводчики (компиляторы) с языков высокого уровня дают весьма неэффективную программу на машинном языке. Обычно это касается программ вычислительного характера, в которых большую часть времени выполняется очень небольшой (порядка 3-5%) участок программы (главный цикл). Для решения этой проблемы могут использоваться так называемые многоязыковые системы программирования, которые позволяют записывать части программы на различных языках. Обычно основная часть программы записывается на языке программирования высокого уровня (Фортране, Паскале, С и т.д.), а критические по времени выполнения участки программы – на Ассемблере. Скорость работы всей программы при этом может значительно увеличиться. Часто это единственный способ заставить программу дать результат за приемлемое время[1].

Целью данной курсовой работы является получение практических навыков работы программирования на языке ассемблера.

1. Изучить основные сведения о языке Ассемблер (структура и компоненты программы на Ассемблере, формат команд, организация подпрограмм и др.);

2. Изучить виды битовых операций, формат и логику работы логических команд Ассемблера;

3. Решить индивидуальную задачу на применение подпрограмм в Ассемблере;

4.. Сформулировать вывод о проделанной работе.

1 ТЕОРЕТИЧЕСКИЙ РАЗДЕЛ

Основные сведения о языке Ассемблер

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

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

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

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

· минимальное количество избыточного кода (использование меньшего количества команд и обращений в память). Как следствие — большая скорость и меньший размер программы;

· большие объемы кода, большое число дополнительных мелких задач;

· плохая читабельность кода, трудность поддержки (отладка, добавление возможностей);

· трудность реализации парадигм программирования и любых других сколько-нибудь сложных конвенций, сложность совместной разработки;

· меньшее количество доступных библиотек, их малая совместимость;

· непосредственный доступ к аппаратуре: портам ввода-вывода, особым регистрам процессора;

· максимальная «подгонка» для нужной платформы (использование специальных инструкций, технических особенностей «железа»);

· непереносимость на другие платформы (кроме двоично совместимых)[4].

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

· определение данных (констант и переменных);

· управление организацией программы в памяти и параметрами выходного файла;

· задание режима работы компилятора;

· всевозможные абстракции (т.е. элементы языков высокого уровня) — от оформления процедур и функций (для упрощения реализации парадигмы процедурного программирования) до условных конструкций и циклов (для парадигмы структурного программирования);

Набор команд

Типичными командами языка ассемблера являются:

· Команды пересылки данных (mov и др.)

· Арифметические команды (add, sub, imul и др.)

· Логические и побитовые операции (or, and, xor, shr и др.)

· Команды управления ходом выполнения программы (jmp, loop, ret и др.)

· Команды вызова прерываний (иногда относят к командам управления): int

· Команды ввода-вывода в порты (in, out)

Для микроконтроллеров и микрокомпьютеров характерны также команды, выполняющие проверку и переход по условию, например:

· jne — перейти, если не равно;

· jge — перейти, если больше или равнo [3].

Не нашли то, что искали? Воспользуйтесь поиском:

Лучшие изречения: Для студента самое главное не сдать экзамен, а вовремя вспомнить про него. 10029 — | 7497 — или читать все.

188.64.174.135 © studopedia.ru Не является автором материалов, которые размещены. Но предоставляет возможность бесплатного использования. Есть нарушение авторского права? Напишите нам | Обратная связь.

Отключите adBlock!
и обновите страницу (F5)

очень нужно

Начинаем программировать на языке ассемблера

Оригинал: Get started in assembly language. Part 1
Автор: Mike Saunders
Дата публикации: 30 октября 2015 г.
Перевод: А.Панин
Дата перевода: 10 ноября 2015 г.

Часть 1: Преодолеваем ограничения высокоуровневых языков программирования и разбираемся, как на самом деле работает центральный процессор.

Для чего это нужно?

  • Для понимания принципов работы компиляторов.
  • Для понимания инструкций центрального процессора.
  • Для оптимизации вашего кода в плане производительности.

Большинство людей считает, что язык ассемблера мало чем отличается от черной магии и является частью темного и страшного мира, в который рискует войти лишь 0.01% лучших разработчиков программного обеспечения. Но на самом деле это красивый и очень доступный язык программирования. Вам стоит изучить его основы хотя бы для того, чтобы лучше понимать механизм генерации кода компиляторами, принцип работы центральных процессоров, а также лучше представлять принцип работы компьютеров. Язык ассемблера по своей сути является текстовым представлением инструкций, которые исполняет центральный процессор, с некоторыми дополнительными возможностями, упрощающими процесс программирования.

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

В статьях данной серии мы будем подробно исследовать мир языка ассемблера. В данной статье мы рассмотрим лишь базовые приемы программирования, в статье из следующего номера журнала разберемся с более сложными вопросами, после чего закончим рассмотрение языка ассемблера написанием простой загружающейся операционной системы — она не сможет выполнять какой-либо полезной работы, но будет основываться на вашем коде и работать непосредственно с аппаратным обеспечением без необходимости загрузки каких-либо сторонних ОС. Звучит неплохо, не правда ли? Давайте начнем

Ваша первая программа на языке ассемблера

Многие руководства по программированию на языке ассемблера начинаются с длинных, запутанных и утомительных разделов, в которых осуществляется бесконечное рассмотрение вопросов бинарной арифметики и теории проектирования центральных процессоров, причем эти разделы не содержат какого-либо реального кода. Я считаю, что подобные материалы сводят на нет интерес читателя, поэтому мы начнем непосредственно с рассмотрения кода реальной программы. После этого мы рассмотрим каждую из строк кода этой программы для того, чтобы вы поняли принцип работы ассемблера на основе практического примера.


Некоторые текстовые редакторы, такие, как Vim, осуществляют подсветку синтаксиса языка ассемблера (попробуйте использовать команду set syn=nasm )

Скопируйте следующий код в в текстовое поле любого текстового редактора и сохраните его в файле с именем myfirst.asm в вашей домашней директории:

(Примечание: для отступов в коде вы можете использовать как как символы пробелов, так и символы табуляции — это не имеет значения.) Данная программа просто выводит строку «Assembly rules!» на экран и завершает работу.

Инструмент, который мы будем использовать для преобразования данного кода языка ассемблера в исполняемый бинарный файл носит довольно забавное название «ассемблер». Существует много различных ассемблеров, но моим любимым ассемблером является NASM; он находится в репозитории пакетов программного обеспечения практически любого дистрибутива, поэтому вы можете установить его с помощью менеджера пакетов программного обеспечения с графическим интерфейсом, команды yum install nasm , apt-get install nasm или любой другой команды, актуальной для вашего дистрибутива.

Теперь откройте окно эмулятора терминала и введите следующие команды:

Первая команда предназначена для генерации с помощью NASM (исполняемого) файла объектного кода с именем myfirst.o формата ELF (формат исполняемых файлов, используемый в Linux). Вы можете спросить: «Для чего генерируется файл объектного кода, ведь логичнее сгенерировать файл с инструкциями центрального процессора, которые он должен исполнять?» Ну, вы могли бы использовать исполняемый файл с инструкциями центрального процессора в операционных системах 80-х годов, но современные операционные системы предъявляют больше требований к исполняемым файлам. Бинарные файлы формата ELF включают информацию для отладки, они позволяют разделить код и данные благодаря наличию отдельных секций, что позволяет предотвратить переписывание данных в этих секциях.

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

Взгляд в прошлое

На данный момент в нашем распоряжении имеется файл myfirst.o с исполняемым кодом нашей программы. При этом процесс сборки программы еще не завершен; с помощью линковщика ld мы должны связать код из этого файла со специальным системным кодом запуска программ (т.е., шаблонным кодом, который исполняется при запуске каждой программы) для генерации исполняемого файла с именем myfirst . (Параметр elf_i386 описывает тип бинарного формата — в данном случае это означает, что вы можете использовать 32-битный ассемблерный код даже если вы используете 64-битный дистрибутив.)

Если процесс сборки программы пройдет успешно, вы сможете выполнить вашу программу с помощью следующей команды:

В результате вы должны увидеть вывод: «Assembly rules!». Это означает, что вы добились своего — создали полноценную независимую программу для Linux, код которой написан полностью на языке ассемблера. Разумеется, данная программа не выполняет каких-либо полезных действий, но при этом она является отличным примером, демонстрирующим структуру программы на языке ассемблера и позволяющим проследить процесс преобразования исходного кода в бинарный файл.

Благодаря данным системным вызовам вы можете сообщить ядру ОС о необходимости выполнения различных задач, связанных с обработкой файлов и текстовым вводом/выводом.

Перед тем, как мы перейдем к углубленному изучению кода, было бы неплохо узнать размер бинарного файла нашей программы. После выполнения команды ls -l myfirst вы увидите, что размер бинарного файла равен примерно 670 байтам. Теперь оценим размер эквивалентной программы на языке C:

Если вы сохраните этот код в файле с именем test.c , скомпилируете его ( gcc -o test test.c ) и рассмотрите параметры результирующего бинарного файла с именем test , вы обнаружите, что этот файл имеет гораздо больший размер — 8.4k. Вы можете удалить из этого файла отладочную информацию ( strip -s test ), но и после этого его размер сократится незначительно, лишь до 6 k. Это объясняется тем, что компилятор GCC добавляет большой объем упомянутого выше кода для запуска и завершения работы приложения, а также связывает приложение с библиотекой языка программирования C большого размера. Благодаря данному примеру несложно сделать вывод о том, что язык ассемблера является лучшим языком программирования для разработки приложений, предназначенных для эксплуатации в условиях жесткого ограничения объема носителя данных.

Следует упомянуть о том, что многие разработчики, использующие язык ассемблера, получают отличные зарплаты за разработку кода для ограниченных в плане ресурсов встраиваемых устройств и именно поэтому язык ассемблера является единственным реальным вариантом для разработки игр для старых 8-битных консолей и домашних компьютеров.

Дизассемблирование кода

Разработка нового кода является увлекательным занятием, но еще более интересным занятием может оказаться исследования чужой работы. Благодаря инструменту под названием objdump (из пакета Binutils) вы можете «дизассемблировать» исполняемый файл, а именно, преобразовать инструкции центрального процессора в их текстовые эквиваленты. Попытайтесь использовать данный инструмент по отношению к бинарному файлу myfirst, над которым мы работали в данном руководстве, следующим образом:

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

В процессе ассемблирования NASM заменил метку строки «message» на числовое значение, соответствующее расположению этой строки в секции данных бинарного файла. Таким образом, результаты дизассемблирования бинарных файлов менее полезны, чем их оригинальный код, ведь в них отсутствуют такие вещи, как комментарии и строки, но они все же могут оказаться полезными для ознакомления с реализациями критичных к времени исполнения функций или взлома систем защиты приложений. Например, в 80-х и 90-х годах многие разработчики использовали инструменты для дизассемблирования программ с целью идентификации и нейтрализации систем защиты от копирования игр.

Вы также можете дизассемблировать программы, разработанные с использованием других языков программирования, но полученные при этом результаты дизассемблирования могут быть значительно усложнены. Например, вы можете выполнить приведенную выше команду objdump по отношению к бинарному файлу /bin/ls и самостоятельно оценить тысячи строк из секции кода, сгенерированные компилятором на основе оригинального исходного кода утилиты на языке C.

Это результат дизассемблирования нашей программы, в котором представлены шестнадцатеричные коды и инструкции.

Анализ кода

А теперь давайте обсудим назначение каждой из строк кода нашей программы. Начнем с этих двух строк:

Это не инструкции центрального процессора, а директивы ассемблера NASM; первая директива сообщает о том, что приведенный ниже код должен быть расположен в секции кода «text» финального исполняемого файла. Немного неочевидным является тот факт, что секция с названием «text» содержит не обычные текстовые данные (такие, как наша строка «Assembly rules!» ), а исполняемый код, т.е., инструкции центрального процессора. Далее расположена директива global _start , сообщающая линковщику ld о том, с какой точки должно начаться исполнение кода из нашего файла. Эта директива может оказаться особенно полезной в том случае, если мы захотим начинать исполнение кода не с самого начала секции кода, а из какой-либо заданной точки. Параметр global позволяет читать данную директиву не только ассемблеру, но и другим инструментам, поэтому она обрабатывается линковщиком ld .

Как было сказано выше, исполнение кода должно начинаться с позиции _start . Ввиду этого мы явно указываем соответствующую позицию в нашем коде:

Отдельные слова с символами двоеточия в конце называются метками и предназначены для указания позиций в коде, к которым мы можем перейти (подробнее об этом в следующей статье серии). Таким образом, исполнение программы начинается с этой строки! Кроме того, мы наконец достигли первой реальной инструкции центрального процессора:

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

А это демонстрация того, что нас ждет: код для взаимодействия с аппаратным обеспечением, исполняющийся в эмуляторе ПК! Мы также покажем вам, как загрузить его на реальной машине.

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

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

Это 32-х битный регистр (следовательно, он может хранить числа из диапазона от 0 до 4,294,967,295). При рассмотрении следующих строк кода вы увидите, что мы также работаем с регистрами edx , ebx и eax — это регистры общего назначения, которые могут использоваться для выполнения любых задач, в отличие от специализированных регистров, с которыми мы познакомимся в следующем месяце. А это небольшое пояснение для тех, кому не терпится узнать о происхождении имен регистров: регистр ecx носил имя c во время выпуска 8-ми битных процессоров, после чего был переименован в сх для хранения 16-и битных значений и в ecx для хранения 32-х битных значений. Таким образом, несмотря на то, что имена регистров в настоящее время выглядят немного странно, во времена выпуска старых центральных процессоров разработчики использовали регистры общего назначения с отличными именами a , b , c и d .

После того, как вы начнете работу, вы не сможете остановиться

Одним из вопросов, которые мы будем рассматривать в следующем месяце, является вопрос использования стека, поэтому мы подготовим вас к его рассмотрению прямо сейчас. Стек является областью памяти, в которой могут храниться временные значения тогда, когда необходимо освободить регистры для других целей. Но наиболее важной возможностью стека является способ хранения данных в нем: вы будете «помещать» («push») значения в стек и «извлекать» («pop») их из него. В стеке используется принцип LIFO (last in, first out — первый вошел, последний вышел), следовательно, последнее добавленное в стек значение будет первым извлечено из него.

Представьте, что у вас есть, к примеру, пустая упаковка от чипсов Pringles и вы помещаете в нее вещи в следующей последовательности: двухслойный крекер, фишка с персонажем «Альф» и диск от приставки GameCube. Если вы начнете извлекать эти вещи, вы извлечете диск от приставки GameCube первым, затем фишку с персонажем «Альф» и так далее. При работе с языком ассембера стек используется следующим образом:

После исполнения этих шести инструкций регистр eax будет содержать значение 10, регистр ebx — значение 5 и регистр ecx — значение 2. Таким образом, использование стека является отличным способом временного освобождения регистров; если, к примеру, в регистрах eax и ebx имеются важные значения, но вам необходимо выполнить текущую работу перед их обработкой, вы можете поместить эти значения в стек, выполнить текущую работу и извлечь их из стека, вернувшись к предыдущему состоянию регистров.

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

Двигаемся дальше

Вернемся к коду: инструкция mov перемещает (на самом деле, копирует) число из одного места в другое, справа налево. Таким образом, в данном случае мы говорим: «следует поместить message в регистр ecx «. Но что такое «message»? Это не другой регистр, это указатель на расположение данных. Ближе концу кода в секции данных «data» вы можете обнаружить метку message , после которой следует параметр db , указывающий на то, что вместо метки message в коде должно быть размещено несколько байт. Это очень удобно, так как нам не придется выяснять точное расположение строки «Assembly rules!» в секции данных — мы можем просто сослаться на нее с помощью метки message . (Число 10 после нашей строки является всего лишь символом перехода на новую строку, аналогичным символу \n , добавляемому к строкам при работе с языком программирования C).

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

Поэтому мы попросим ядро ОС сделать это для нас. Ядро Linux предоставляет в распоряжение низкоуровневых приложений большое количество системных вызовов, с помощью которых приложения могут инициировать выполнение различных операций на уровне ядра. Один из этих системных вызовов предназначен для вывода текстовой строки. После использования этого системного вызова ядро ОС выполняет всю необходимую работу — и, разумеется, оно предоставляет даже более глубокий уровень абстракции, на котором строка может быть выведена с помощью обычного текстового терминала, эмулятора терминала оконной системы X или даже записана в открытый ранее файл.

Однако, перед тем, как сообщить ядру ОС о необходимости вывода текстовой строки, нам придется передать ему дополнительную информацию, помимо информации о расположении строки, уже находящейся в регистре ecx . Также нам придется сообщить ему о том, сколько символов нужно вывести для того, чтобы вывод строки не продолжался после ее окончания. Именно для этого используется строка из секции данных ближе к концу кода приложения:

В данной строке используется другая метка length , но вместо параметра db для связывания этой метки с какими-либо данными, мы используем параметр equ для того, чтобы сообщить, что данная метка является эквивалентом чего-либо (это немного похоже на директиву препроцессора #define в языке программирования C). Символ доллара соответствует текущей позиции в коде, поэтому в данном случае мы говорим: «метка length должна быть эквивалентна текущей позиции в коде за вычетом расположения строки с меткой «message»».

Вернемся к секции кода приложения, в которой мы размещаем данное значение в регистре edx :

Все идет отлично: два регистра заполнены информацией о расположении строки и количестве символов строки для вывода. Но перед тем, как мы сообщим ядру ОС о необходимости выполнения его части работы, нам придется предоставить ему еще немного информации. Во-первых, мы должны сообщить ядру ОС о том, какой «дескриптор файла» следует использовать — другими словами, куда должен быть направлен вывод. Данная тема выходит за границы руководства по использованию языка ассемблера, поэтому скажем лишь, что нам нужно использовать стандартный поток вывода stdout , что означает: выводить строку на экран. Стандартный поток вывода использует фиксированный дескриптор 1, который мы помещаем в регистр ebx .

Теперь мы крайне близки к осуществлению системного вызова, но остался еще один регистр, который должен быть заполнен. Ядро ОС может выполнять большое количество различных операций, таких, как монтирование файловых систем, чтение данных из файлов, удаление файлов и других. Соответствующие механизмы активируются с помощью упомянутых системных вызовов и перед тем, как мы передадим управление ядру ОС, нам придется сообщить ему, какой из системных вызовов следует использовать. На странице http://asm.sourceforge.net/syscall.html вы можете ознакомиться с информацией о некоторых системных вызовах, доступных программам — в нашем случае необходим системный вызов sys_write («запись данных в дескриптор файла») с номером 4. Поэтому мы разместим его номер в регистре eax :

И это все! Мы выполнили все необходимые приготовления для осуществления системного вызова, поэтому сейчас мы просто передадим управление ядру ОС следующим образом:

Инструкция int расшифровывается как «interrrupt» («прерывание») и буквально прерывает поток исполнения данной программы, переходя в пространство ядра ОС. (В данном случае используется шестнадцатеричное значение 0x80 — пока вам не следует беспокоиться о нем.) Ядро ОС осуществит вывод строки, на которую указывает значение в регистре ecx , после чего вернет управление нашей программе.

Для завершения исполнения программы следует осуществить системный вызов sys_exit , который имеет номер 1. Поэтому мы размещаем данный номер в регистре eax , снова прерываем исполнение нашей программы, после чего ядро ОС аккуратно завершает исполнение нашей программы и мы возвращаемся к приветствию командной оболочки. Можно сказать, что вы выполнили поставленную задачу: реализовали завершенную (хотя и очень простую) программу на языке ассемблера, код которой разработан вручную без использования каких-либо объемных библиотек.

Мы рассмотрели достаточно много аспектов использования языка ассемблера в данном руководстве и, как упоминалось ранее, вместо этого мы могли бы сфокусироваться лишь на теоретической информации. Но я все же надеюсь, что реальный пример программы оказался полезным для вас, а в следующем номере журнала мы потратим больше времени на рассмотрение некоторых концепций, которые были затронуты в данном руководстве. Кроме того, мы усовершенствуем нашу программу, добавив в нее логику и подпрограммы — версии операторов if и goto языка ассемблера.

В процессе ознакомления с кодом данной программы вы можете попытаться самостоятельно модифицировать его для выполнения следующих операций:

  • Вывода отличной, более длинной строки.
  • Вывода двух строк, одна после другой.
  • Возврата измененного кода завершения работы приложения командной оболочке (для этого придется воспользоваться поисковой системой Google!).

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

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

Содержание

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

Команды языка ассемблера один к одному соответствуют командам процессора, фактически, они представляют собой более удобную для человека символьную форму записи (мнемокод) команд и их аргументов.

Кроме того, язык ассемблера обеспечивает использование символических меток вместо адресов ячеек памяти, которые при ассемблировании заменяются на автоматически рассчитываемые абсолютные или относительные адреса, а также так называемых директив (команд, не переводящихся в процессорные инструкции, а выполняемых самим ассемблером).

Директивы ассемблера позволяют, в частности, включать блоки данных, задать ассемблирование фрагмента программы по условию, задать значения меток, использовать макроопределения с параметрами.

Каждая модель (или семейство) процессоров имеет свой набор команд и соответствующий ему язык ассемблера. Наиболее популярные синтаксисы: Intel-синтаксис и AT&T-синтаксис.

Существуют компьютеры, реализующие в качестве машинного язык программирования высокого уровня (Forth, Lisp, Эль-76), фактически в них он является «ассемблером».

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

Достоинства

  • Файл:Symbol support vote.svg Искусный программист, как правило, способен написать более эффективную программу на ассемблере, чем те, что генерируются трансляторами с языков программирования высокого уровня, то есть для программ на ассемблере характерно использование меньшего количества команд и обращений в память, что позволяет увеличить скорость и уменьшить размер программы.Шаблон:Нет АИ
  • Файл:Symbol support vote.svg Обеспечение максимального использования специфических возможностей конкретной платформы, что также позволяет создавать более эффективные программы с меньшими затратами ресурсов.
  • Файл:Symbol support vote.svg При программировании на ассемблере возможен непосредственный доступ к аппаратуре, в том числе портам ввода-вывода, регистрам процессора, и др.
  • Файл:Symbol support vote.svg Язык ассемблера применяется для создания драйверов оборудования и ядра операционной системы
  • Файл:Symbol support vote.svg Язык ассемблера используется для создания «прошивок» BIOS.
  • Файл:Symbol support vote.svg С помощью языка ассемблера создаются компиляторы и интерпретаторыязыков высокого уровня, а также реализуется совместимость платформ.
  • Файл:Symbol support vote.svg Существует возможность исследования других программ с отсутствующим исходным кодом с помощью дизассемблера.

Недостатки


  • Файл:Symbol oppose vote.svg Главное преимущество ассемблера практически полностью нивелируется хорошей оптимизацией в современных компиляторахязыков высокого уровня.Шаблон:Нет АИ
  • Файл:Symbol oppose vote.svg В силу своей машинной ориентации («низкого» уровня) человеку по сравнению с языками программирования высокого уровня сложнее читать и понимать программу, она состоит из слишком «мелких» элементов — машинных команд, соответственно усложняются программирование и отладка, растет трудоемкость, велика вероятность внесения ошибок. В значительной степени возрастает сложность совместной разработки.
  • Файл:Symbol oppose vote.svg Как правило, меньшее количество доступных библиотек по сравнению с современными индустриальными языками программирования.
  • Файл:Symbol oppose vote.svg Отсутствует переносимость программ на компьютеры с другой архитектурой и системой команд (кроме двоично-совместимых).

Применение

Исторически можно рассматривать ассемблер как второе поколение языков программирования ЭВМ (если первым считать машинный код). Недостатки ассемблера, сложность разработки на нем больших программных комплексов привели к появлению языков третьего поколения — языков программирования высокого уровня (Фортран, Лисп, Кобол, Паскаль, Си и др.). Именно языки программирования высокого уровня и их наследники в основном используются в настоящее время в индустрии информационных технологий. Однако, языки ассемблера сохраняют свою нишу, обуславливаемую их уникальными преимуществами в части эффективности и возможности полного использования специфических средств конкретной платформы.

На ассемблере пишутся программы или фрагменты программ, для которых критически важны:

  • быстродействие (драйверы, игры);
  • объем используемой памяти (загрузочные сектора, встраиваемое (привет) программное обеспечение, программы для микроконтроллеров и процессоров с ограниченными ресурсами, вирусы, программные защиты).

С использованием программирования на ассемблере производятся:

  • Оптимизация критичных к скорости участков программ написанных на языке высокого уровня, таком как C++. Это особенно актуально для игровых приставок, у которых фиксированная производительность, и для мультимедийныхкодеков, которые стремятся делать менее ресурсоемкими и более популярными.
  • Создание операционных систем (ОС). ОС часто пишут на Си, языке, который специально был создан для написания одной из первых версий Unix. Аппаратно зависимые участки кода, такие, как загрузчик ОС, уровень абстрагирования от аппаратного обеспечения — HAL и ядро, часто пишутся на ассемблере. Ассемблерного кода в ядрах Windows или Linux совсем немного, поскольку авторы стремятся к переносимости и надёжность, но тем не менее он присутствует. Некоторые любительские ОС, такие, как MenuetOS, целиком написаны на ассемблере. При этом MenuetOS помещается на дискету и содержит графический многооконный интерфейс.
  • Программирование микроконтроллеров (МК) и других встраиваемых процессоров. По мнению профессора Танненбаума, развитие МК повторяет историческое развитие компьютеров новейшего времени. [1] На сегодняшний день для программирования МК весьма часто применяют ассемблер. В МК приходится перемещать отдельные байты и биты между различными ячейками памяти. Программирование МК весьма важно, так как, по мнению Танненбаума, в автомобиле и квартире современного цивилизованного человека в среднем содержится 50 микроконтроллеров. [2]
  • Создание драйверов. Некоторые участки драйверов, взаимодействующие с аппаратным обеспечением, программируют на ассемблере. Хотя в целом в настоящее время драйверы стараются писать на языках высокого уровня в связи с повышенными требованиями к надёжности. Надёжность для драйверов играет особую роль, поскольку в Windows NT и Linux драйверы работают в режиме ядра. Одна ошибка может привести к краху системы.
  • Создание антивирусов и других защитных программ.
  • Написание трансляторов языков программирования.

Нелегальная сфера деятельности

Программирование на языке ассемблера характерно также для нелегальных сфер деятельности в ИТ, в частности, с использованием ассемблера производятся:

  • Взлом программ. «Оригинал» ПО, копии которого продаются незаконно, если в нём использовались технические средства защиты авторских прав, вероятно, был взломан с помощью отладчика и знаний языка ассемблера. Это позволяет при помощи отладчика или дизассемблера найти внутри кода программы функцию, ответственную за ввод кода активации или прекращение работы демонстрационной версии программы. Взломщик может изменить исходный код программы при помощи специального редактора, либо создать генератор ключа. Первый способ более прост для конечного пользователя. Второй менее наказуем (УК РФ, ст. 272: до 2 лет). [3]
  • Создание вирусов и других вредоносных программ (УК РФ, ст. 273: до 3 лет, при тяжких последствиях до 7 лет).

Связывание программ на разных языках

Поскольку на ассемблере часто разрабатываются только фрагменты программ, их необходимо связывать с остальными частями программной системы, написанными на других языках программирования.

Это достигается 2 основными способами:

  • На этапе компиляции — вставка в программу ассемблерных фрагментов (привет) с помощью специальных директив языка (в частности, данный способ поддерживается языком программирования Си), в том числе написание функций на языке ассемблера. Способ удобен для несложных преобразований данных, но полноценного ассемблерного кода, с данными и подпрограммами, включая подпрограммы с множеством входов и выходов, не поддерживаемых высокоуровневыми языками, с помощью него сделать нельзя.
  • На этапе компоновки, или раздельной компиляции. Для взаимодействия скомпонованных модулей достаточно, чтобы связующие функции (определённые в одних модулях и использующиеся в других) поддерживали нужные соглашения вызова (привет) и типы данных. Написаны же отдельные модули могут быть на любых языках, в том числе и на ассемблере.

Синтаксис

Синтаксис языка ассемблера определяется системой команд конкретного процессора.

Набор команд

Типичными командами языка ассемблера являются (большинство примеров даны для Intel-синтаксиса архитектуры x86):

  • Команды пересылки данных (mov, lea и т. д.)
  • Арифметичекие команды (add, sub, imul и т. д.)
  • Логические и побитовые операции (or, and, xor, shr и т. д.)
  • Команды управления ходом выполнения программы (jmp, loop, ret и т. д.)
  • Команды вызова прерываний (иногда относят к командам управления): int, into
  • Команды ввода/вывода в порты (in, out)
  • Для микроконтроллеров и микрокомпьютеров характерны также команды, выполняющие проверку и переход по условию, например:
  • cbne — перейти, если не равно
  • dbnz — декрементировать, и если результат ненулевой, то перейти
  • cfsneq — сравнить, и если не равно, пропустить следующую команду

Инструкции

Типичный формат записи команд: [метка:] опкод [операнды] [;комментарий]

где опкод (код операции) — непосредственно мнемоника инструкции процессору. К ней могут быть добавлены префиксы (повторения, изменения типа адресации и пр.).

В качестве операндов могут выступать константы, адреса регистров, адреса в оперативной памяти и пр.. Различия между стандартами Intel и AT&T касаются, в основном, порядка перечисления операндов и их синтаксиса при различных методах адресации.

Используемые мнемоники обычно одинаковы для всех процессоров одной архитектуры или семейства архитектур (среди широко известных — мнемоники процессоров и контроллеров x86, ARM, SPARC, PowerPC, M68k). Они описываются в спецификации процессоров. Возможные исключения:

  • Если ассемблер использует кроссплатформенный AT&T-синтаксис (оригинальные мнемоники приводятся к синтаксису AT&T)
  • Если изначально существовало два стандарта записи мнемоник (система команд была наследована от процессора другого производителя).

Например, процессор Zilog Z80 наследовал систему команд Intel i8080, расширил ее и поменял мнемоники (и обозначения регистров) на свой лад. Процессоры Motorola Fireball наследовали систему команд Z80, несколько её урезав. Вместе с тем, Motorola официально вернулась к мнемоникам Intel. И в данный момент половина ассемблеров для Fireball работает с интеловскими мнемониками, а половина с мнемониками Zilog.

Директивы

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

  • определение данных (констант и переменных)
  • управление организацией программы в памяти и параметрами выходного файла
  • задание режима работы компилятора
  • всевозможные абстракции (то есть элементы языков высокого уровня) — от оформления процедур и функций (для упрощения реализации парадигмы процедурного программирования) до условных конструкций и циклов (для парадигмы структурного программирования)
  • макросы

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

Примеры программы Hello, world! для разных платформ и разных диалектов: Шаблон:Hider hiding

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

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

Использование термина «язык ассемблера» также может вызвать ошибочное мнение о существовании некоего единого языка низкого уровня, или хотя бы стандартов на такие языки. При именовании языка ассемблера желательно уточнять, ассемблер для какой архитектуры имеется в виду.

В СССР язык ассемблера ранее называли «автокод».

Язык ассемблера — всё по этой теме для программистов

Команды языка ассемблера один к одному соответствуют командам процессора. Фактически, они и представляют собой более удобную для человека символьную форму записи — мнемокоды — команд и их аргументов. При этом одной команде языка ассемблера может соответствовать несколько вариантов команд процессора. [4]

Кроме того, язык ассемблера позволяет использовать символические метки вместо адресов ячеек памяти, которые при ассемблировании заменяются на вычисляемые ассемблером или компоновщиком абсолютные или относительные адреса, а также так называемые директивы (команды ассемблера, не переводимые в машинные команды процессора, а выполняемые самим ассемблером).

Директивы ассемблера позволяют, в частности, включать блоки данных, задать ассемблирование фрагмента программы по условию, задать значения меток, использовать макрокоманды с параметрами.

Каждая модель (или семейство) процессоров имеет свой набор — систему — команд и соответствующий ему язык ассемблера. Наиболее популярные синтаксисы языков ассемблера — Intel-синтаксис и AT&T-синтаксис.

Существуют компьютеры, реализующие в качестве машинного язык программирования высокого уровня (Форт, Лисп, Эль-76). Фактически, в таких компьютерах они выполняют роль языков ассемблера.

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

Достоинства

  • Язык ассемблера позволяет писать самый быстрый и компактный код, какой вообще возможен для данного процессора.
  • Если код программы достаточно большой, — данные, которыми он оперирует, не помещаются целиком в регистрах процессора, то есть частично или полностью находятся в оперативной памяти, — то искусный программист, как правило, способен значительно оптимизировать программу по сравнению с транслятором с языка высокого уровня по одному или нескольким параметрам:
    • скорость работы — за счёт оптимизации вычислительного алгоритма и/или более рационального обращения к ОП, перераспределения данных;
    • объём кода (в том числе за счёт эффективного использования промежуточных результатов). (Сокращение объёма кода также нередко повышает скорость выполнения программы.)
  • Обеспечение максимального использования специфических возможностей конкретной платформы, что также позволяет создавать более эффективные программы, в том числе менее ресурсоёмкие.
  • При программировании на языке ассемблера возможен непосредственный доступ к аппаратуре, и, в частности, портам ввода-вывода, регистрам процессора и др.
  • Язык ассемблера часто применяется для создания драйверов оборудования и ядра операционной системы (или машиннозависимых подсистем ядра ОС).
  • Язык ассемблера используется для создания «прошивок» BIOS.
  • С помощью языка ассемблера часто создаются машиннозависимые подпрограммы компиляторов и интерпретаторыязыков высокого уровня, а также реализуется совместимость платформ.
  • С помощью дизассемблера возможно исследовать существующие программы при отсутствии исходного кода.

Недостатки

  • В силу машинной ориентации («низкого» уровня) языка ассемблера человеку сложнее читать и понимать программу на нём по сравнению с языками программирования высокого уровня; программа состоит из слишком «мелких» элементов — машинных команд, соответственно, усложняются программирование и отладка, растут трудоёмкость и вероятность внесения ошибок.
  • Требуется повышенная квалификация программиста для получения качественного кода: код, написанный средним программистом на языке ассемблера, обыкновенно оказывается не лучше или даже хуже кода, порождаемого оптимизирующим компилятором для сравнимых программ, написанных на языке высокого уровня. [5]
  • Программа на языке высокого уровня может быть перекомпилирована с автоматической оптимизацией под особенности новой целевой платформы [6] , программа же на языке ассемблера на новой платформе может потерять своё преимущество в скорости без ручного переписывания кода. [7][8]
  • Как правило, меньшее количество доступных библиотек по сравнению с современными индустриальными языками программирования.
  • Отсутствует переносимость программ на компьютеры с другой архитектурой и системой команд.


Применение

Исторически, если первым поколением языков программирования считать машинные коды, то язык ассемблера можно рассматривать как второе поколение языков программирования. Недостатки языка ассемблера, сложность разработки на нём больших программных комплексов привели к появлению языков третьего поколения — языков программирования высокого уровня (таких как Фортран, Лисп, Кобол, Паскаль, Си и др.). Именно языки программирования высокого уровня и их наследники в основном используются в настоящее время в индустрии информационных технологий. Однако языки ассемблера сохраняют свою нишу, обусловленную их уникальными преимуществами в части эффективности и возможности полного использования специфических средств конкретной платформы.

На языке ассемблера пишут программы или их фрагменты в тех случаях, когда критически важны:

  • быстродействие (драйверы, игры);
  • объём используемой памяти (загрузочные секторы, встраиваемое (англ.embedded ) программное обеспечение, программы для микроконтроллеров и процессоров с ограниченными ресурсами, вирусы, программные защиты).

С использованием программирования на языке ассемблера производятся:

  • Оптимизация критичных к скорости участков программ в программах на языках высокого уровня, таких как C++ или Pascal. Это особенно актуально для игровых приставок, имеющих фиксированную производительность, и для мультимедийныхкодеков, которые стремятся делать менее ресурсоёмкими и более быстрыми.
  • Создание операционных систем (ОС) или их компонентов. В настоящее время подавляющее большинство ОС пишут на более высокоуровневых языках (в основном на Си — языке высокого уровня, который специально был создан для написания одной из первых версий UNIX). Аппаратно зависимые участки кода, такие как загрузчик ОС, уровень абстрагирования от аппаратного обеспечения (hardware abstraction layer) и ядро, часто пишутся на языке ассемблера. Фактически, ассемблерного кода в ядрах Windows или Linux совсем немного, поскольку авторы стремятся обеспечить переносимость и надёжность, но, тем не менее, он там присутствует. Некоторые любительские ОС, такие как MenuetOS, целиком написаны на языке ассемблера. При этом MenuetOS помещается на дискету и содержит графический многооконный интерфейс.
  • Программирование микроконтроллеров (МК) и других встраиваемых процессоров. По мнению профессора Таненбаума, развитие МК повторяет историческое развитие компьютеров новейшего времени. [9] На сегодняшний день для программирования МК весьма часто применяют язык ассемблера (хотя и в этой области широкое распространение получают языки вроде Си). В МК приходится перемещать отдельные байты и биты между различными ячейками памяти. Программирование МК весьма важно, так как, по мнению Таненбаума, в автомобиле и квартире современного цивилизованного человека в среднем содержится 50 микроконтроллеров. [10]
  • Создание драйверов. Некоторые части драйверов программируют на языке ассемблера. Хотя в целом в настоящее время драйверы также стараются писать на языках высокого уровня в связи с повышенными требованиями к надёжности и достаточной производительностью современных процессоров и достаточным совершенством компиляторов с языков высокого уровня. Надёжность для драйверов играет особую роль, поскольку в Windows NT и UNIX (в том числе в Linux) драйверы работают в режиме ядра. Одна ошибка в драйвере может привести к краху всей системы.
  • Создание антивирусов и других защитных программ.
  • Написание трансляторов языков программирования.

Связывание программ на разных языках

Поскольку уже давно на языке ассемблера часто кодируют только фрагменты программ, их необходимо связывать с остальными частями программной системы, написанными на других языках программирования. Это достигается двумя основными способами:

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

Синтаксис

Синтаксис языка ассемблера определяется системой команд конкретного процессора.

Набор команд

Типичными командами языка ассемблера являются (большинство примеров даны для Intel-синтаксиса архитектуры x86):

  • Команды пересылки данных ( mov и др.)
  • Арифметические команды ( add , sub , imul и др.)
  • Логические и побитовые операции ( or , and , xor , shr и др.)
  • Команды управления ходом выполнения программы ( jmp , loop , ret и др.)
  • Команды вызова прерываний (иногда относят к командам управления): int
  • Команды ввода/вывода в порты ( in , out )
  • Для микроконтроллеров и микрокомпьютеров характерны также команды, выполняющие проверку и переход по условию, например:
  • cjne — перейти, если не равно
  • djnz — декрементировать, и если результат ненулевой, то перейти
  • cfsneq — сравнить, и если не равно, пропустить следующую команду

Инструкции

Типичный формат записи команд:

где мнемокод — непосредственно мнемоника инструкции процессору. К ней могут быть добавлены префиксы (повторения, изменения типа адресации и пр.).

В качестве операндов могут выступать константы, адреса регистров, адреса в оперативной памяти и пр. Различия между синтаксисом Intel и AT&T касаются в основном порядка перечисления операндов и указания различных методов адресации.

Используемые мнемоники обычно одинаковы для всех процессоров одной архитектуры или семейства архитектур (среди широко известных — мнемоники процессоров и контроллеров x86, ARM, SPARC, PowerPC, M68k). Они описываются в спецификации процессоров. Возможные исключения:

  • если ассемблер использует кроссплатформенный AT&T-синтаксис (оригинальные мнемоники приводятся к синтаксису AT&T);
  • если изначально существовало два стандарта записи мнемоник (система команд была наследована от процессора другого производителя).

Например, процессор Zilog Z80 наследовал систему команд Intel 8080, расширил её и поменял мнемоники (и обозначения регистров) на свой лад. Процессоры Motorola Fireball наследовали систему команд Z80, несколько её урезав. Вместе с тем, Motorola официально вернулась к мнемоникам Intel и в данный момент половина ассемблеров для Fireball работает с мнемониками Intel, а половина — с мнемониками Zilog.

Директивы

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

  • определение данных (констант и переменных),
  • управление организацией программы в памяти и параметрами выходного файла,
  • задание режима работы компилятора,
  • всевозможные абстракции (то есть элементы языков высокого уровня) — от оформления процедур и функций (для упрощения реализации парадигмы процедурного программирования) до условных конструкций и циклов (для парадигмы структурного программирования),
  • макросы.

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

Примеры программы Hello, world! для разных платформ и разных диалектов:

Программирование на языке Ассемблер

Сегодня повсюду используются такие языки высокого уровня как Бейсик, Фортран и Паскаль. Возможно, вы уже знакомы, по крайней мере, с одним языком высокого уровня. Если вы постоянно пользуетесь персональным компьютером IBM, то вы знаете, что интерпретатор Бейсика является частью системы. Зачем же возиться еще с одним языком программирования, тем более с таким, который сулит определенные трудности? Очевидно, даже располагая современными могучими языками, вы все еще нуждаетесь в Ассемблере из-за его эффективности и точности.

Ассемблерные программы могут быть очень эффективными. Из программистов, с равными навыками и способностями, работающий на языке Ассемблера создаст программу более компактную и быстродействующую, чем такая же программа, написанная на языке высокого уровня. Это так практически для всех небольших или средних программ. К сожалению, по мере возрастания размеров, программы на языке Ассемблера теряют часть своих преимуществ. Это происходит из-за необходимого в ассемблерной программе внимания к деталям. Как вы видите, язык Ассемблера требует от вас планирования каждого действия компьютера. В небольших программах это позволяет оптимизировать работу программы с аппаратными средствами. В больших же программах огромное количество деталей может помешать вам эффективно работать над самой программой, даже если отдельные компоненты программы окажутся очень неплохими. Безусловно, программирование на языке Ассемблера отвечает потребностям не каждой программы.

Программы на языке Ассемблера очень точны. Поскольку этот язык позволяет программисту непосредственно работать со всем аппаратным обеспечением, ассемблерная программа может делать то, что недоступно никакой другой программе. Несомненно, что в программировании устройств ввода-вывода, где требуется контроль над отдельными разрядами регистров устройства, программирование на языке Ассемблера — единственный подходящий выбор [6].

Ясно, что эффективность и точность языка Ассемблера дают определенные преимущества. Но его детализированность создает и некоторые проблемы. Когда же стоит избирать для программирования язык Ассемблера?

Конечно, вы должны пользоваться программами на языке Ассемблера, когда нет другого способа написать программу. Например, программисты фирмы IBM писали с использованием процедур Ассемблера все программы управления устройствами ввода-вывода для IBM PC. Для управления устройствами ввода-вывода и системой прерываний, потребовалась та точность языка Ассемблера, которую не может обеспечить ни один другой язык программирования. Аналогично, на языке Ассемблера в фирме IBM писались процедуры диагностики, которые должны проверять каждую деталь аппаратуры.

Язык Ассемблера необходим также и в тех случаях, когда главными являются рабочие характеристики программы. Это может быть время исполнения или конечный размер программы. Библиотека математических процедур Фортрана — пример программы, требующей хороших характеристик, как в отношении времени, так и размера. Математические процедуры являются частью любой программы на Фортране, поэтому они должны занимать как можно меньше места. Кроме того, эти процедуры управляют всеми математическими функциями в фортрановской программе и часто используются. Следовательно, они должны исполняться быстро.

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

Очевидно далее, что вы нуждаетесь в соединении программ языка Ассемблера с программами языков высокого уровня. Здесь мы ограничимся программированием на языке Ассемблера для тех задач, для которых он хорошо подходит, таких как управление вводом-выводом.

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

Самыми популярными на сегодняшний день являются пакеты Turbo Assembler (TASM) фирмы Borland, Macro Assembler for Windows32 (MASM32), созданный Hutch (на основе старого MASM фирмы Microsoft), а также Flat Assembler (FASM), и Netwide Assembler (NASM), развиваемый командой NASM Development Team. Все эти ассемблеры предоставляют весьма широкие возможности для программиста. На них можно писать программы как под DOS, так и под Windows [7].

Существует также множество других видов ассемблера, число которых растет. Например: CodeX Assembler, Gema Assembler, Light Macro Assembler (LASM), Lazy Assembler (LZASM), Table Driven Assembler (TDASM), NewBasic++ Assembler (NBASM), TMA Macro Assembler и др.

Ассемблер

Народ требует хлеба и зрелищ!
Народ требует иллюстраций к статье!
В конце концов, если бы мы хотели почитать, мы бы пошли в библиотеку.
«

Нам не дано предугадать, как слово наше отзовется.

»
— Ф. И. Тютчев программирует на Ассемблере

Ассемблер или asm (англ. assembler — сборщик) — утилита, транслирующая исходники программы на языке ассемблера собственно в машинный код, то есть на язык бездушной машины. Для обратного превращения существует дизассемблер (англ. disassembler — разборщик, ломалка), широко используемый хакерами. Но это для шибко грамотных, а все остальные словом «ассемблер» называют сам язык ассемблера — простейший способ записи машинного кода с помощью расовых английских сокращений, называемых мнемониками (см. в конце статьи — если осилите). Поскольку язык ассемблера привязан к устройству процессора, программы на нем не являются переносимыми на иную компьютерную архитектуру, так как тип процессора жёстко определяет набор доступных команд машинного языка. Но это не так уж и важно, так как нет процессора кроме x86 и фон Нейман пророк его, а всякие Жабы и дотнеты вообще используют собственные виртуальные машины с собственным асмом — похуй, что в тысячу раз медленнее, зато переносимость и безопасность, блджад! Также, используя макросы, юный друг сможет убедиться в отсутствии принципиальной разницы между асмом и всеми остальными «православными» языками.

Содержание

[править] История

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

Простейший транслятор (ассемблер первого поколения) позволял фактически просто писать машинные команды на «человеческом» языке, что позволило программерам немного расслабиться. Достаточно быстро туда добавились многочисленные свистелки и перделки, нарушающие Истинную Красоту Программирования На Ассемблере, но еще больше экономящие время и силы (а значит и деньги), как то макросы, библиотеки и прочее. Затем появились языки высокого уровня и компиляторы (более интеллектуальные генераторы кода с более понятного человеку языка) и интерпретаторы (исполнители человекописанной программы на лету). Которые совершенствовались, совершенствовались, совершенствовались, и досовершенствовались до «программирования мышкой», а компиляторы научились генерировать код, которому по скорости написанный человеком начал сливать [1] . Такие дела.

В целом история языков программирования протекает в направлении от программирования на языке компьютера до манипуляции абстракциями вроде Послать.Лесом(всех(кому не нравится эта статья)) Ну или (послать (лесом (всех (кому-не-нравится «эта-статья»)))) или

На языке ассемблера этот код занял бы более 9000 строк. И потребовал бы долгой и кропотливой работы по своему созданию. Зато, в отличие от Шindoшs, оно бы работало, а не только свистело, пердело, выдавало красивые иконки и жрало 640K памяти.

[править] Асмофаги и студенты из Политеха

Среди программистов с длинной бородой, видевших еще те компьютеры-динозавры, есть такие, которые очень трепетно относятся к напейсанию кода, и считают, что ассемблер — это труЪ, а языки высокого уровня — шлак и унылое говно, предназначенные исключительно для быдлокодерства. При упоминании факта, что современные компиляторы генерируют гораздо более быстрый код, начинают срать кирпичами и обзываться нехорошими словами, ибо сама мысль, что машина может быть умнее человека, считается недопустимой и кощунственной. При этом упускается тот факт, что компиляторы, вообще-то, пишут люди. Степень владения языком ассемблера считается мерилом отличия программиста от быдлокодера. Наиболее тяжелый клинический случай представляет молочный брат Линуса расовый чухонец Вилле Турьянмаа, написавший целую ОС — MenuetOS на чистом ассемблере. На ассемблере же была написана первая версия UNIX, коя им и не является по нынешним понятиям, так как позже была переписана чуть более, чем наполовину на C и помещена в анналы истории.

Тем не менее, программирование на языке ассемблера очень часто используется при написании программ, использующих возможности процессора, не реализуемые языками высокого уровня, а также программировании всевозможных нестандартных программистских хитростей. Драйвера для новых устройств тоже содержат в себе чуть менее, чем половину ассемблерного кода, коий при доведении их до ума ушастыми мартышками заменяют на стандартный C/C++. Часто компиляторы (чуть менее, чем все) предоставляют возможность вставлять ассемблерный код в текст программы.

Алсо, ассемблер используется для программирования всевозможных микроконтроллеров и сигнальных процессоров, параллельно с С-компиляторами. В этом случае зачастую важна компактность кода и скорость работы, которых компилятор может не обеспечить.

Понятно, что писать что-либо крупное на ассемблере в современном мире не будет даже самый отъявленный маньяк, даже большинство фирмваря и прочих глюкалок для микроконтроллеров сейчас пишется на чем-то более продвинутом. Но когда стоимость каждого лишнего не то что мегабайта, а полубайта в ОЗУ становится критичной (ибо их просто нет, например на каком-нибудь запущенном много лет назад спутнике, до которого можно достучаться по радио, но никак физически), выливаются в миллиарды и даже жизни (встроенный софт бортовых систем в авиации и космонавтике) — ассемблер strikes back! К примеру, софт для кораблей «Союз ТМА-М», «Прогресс М-М» написан на Си (по крайней мере, российский сегмент МКС). ПО ЦВМов «Бурана» писалось на ПРОЛ-2. «Союз ТМА» — не на Си, у него БЦВК «Аргон-16», сначала программировали его на ассемблере, в поздних машинах транслировали на высокоуровневый язык. Спутники «Ямал» и «Белка» написаны на Си.

[править] Ассемблер и Линукс

Хотя программирование на ассемблере в Unix-мире считается моветоном, поскольку нарушает один из основополагающих принципов unix way «жертвуй производительностью ради переносимости», даже тут находится место разногласиям, способным вылиться в настоящую драму.

Дело в том, что встроенный ассемблер большинства Unix-ов (и Linux в том числе) использует синтаксис (AT&T-синтаксис), в корне отличающийся от оригинального Intel-синтаксиса. А поскольку Intel-синтаксис у большинства труЪ-линуксоидов прочно ассоциируется с маздаем, он объявляется ересью, и всем, кто пользуется nasm, yasm или fasm пророчатся вечные муки, страшный суд, ад и погибель, хотя gas тоже владеет интеловским синтаксисом. Правда все ELF-инфекторы всё же приходится писать на старом добром асме.

Алсо какой либо годной документации по ассемблеру AT&T в интернете хрен найдешь. По этой причине программистов на AT&T крайне мало. А по этой причине некому писать документацию.

[править] Ассемблер в кино

Как минимум один раз снялся в кино, но зато в каком! В первой части Терминатора можно видеть как Киборгъ предпочитает MOS Technology 6510/8500 ассемблер.

Необходимое пояснение. На скриншоте видна программка из чередующихся инструкций LDA-STA-LDA-STA…(load-store-load-store). В семействе 6502 программы состоят чуть менее, чем полностью из LDA/LDY/LDX/STA/STX/STY вследствие наличия всего 3х 8-битных регистров. Наличие почти полного отсутствия регистров компенсируется нулевой страницей памяти, откуда-куда постоянно приходится перекладывать байтики, так как хранить их более негде. В данном ЦПУ 13 режимов адресации на всего 53 команды. Алсо порты ввода-вывода замаплены в адреса памяти, так что чтение-запись из-в порты осуществляется также этими командами. Короче: программы в Терминаторе имеют вполне осмысленный вид и не являются кучей команд от балды. Подробнее: Этот удивительный Терминатор /48076 .

Во второй части интерфейс Терминатора более очеловечен. Впрочем, это можно объяснить тем, что ПО для первого терминатора делал Скайнет, а для второго набыдлокодил Джон Коннор.

Алсо, ассемблер семейства 8080 (у нас серия КР580) засветился в давнем аниме «Megazone 23».


Алсо, в фильме «Элизиум» (2013) пафосный топ-манагер кодит на асме.

[править] Область применения

  • Популярен для допиливания зацикливаемых кусков программы… в роли напильника. Перепиливание критических участков кода может принести PROFIT, а может и не принести. В любом случае, заниматься таким перепиливанием стоит только тогда, когда у вас на руках уже полностью работающий алгоритм, который можно было бы ускорить, а не наоборот.
  • Для использования в софтине новых команд, доступных в новых процессорах. Компилятор хоть и оптимизирует код высокого уровня при компиляции, но… практически никогда не способен генерировать инструкции из расширенных наборов команд типа AVX, СTV, XOP и т. п. Потому что команды в процессоры добавляют быстрее, чем в компиляторах появляется логика для генерации этих команд.
  • Используется для написания кода, создание которого невозможно (или затруднено) на языках высокого уровня, например, получение дампа памяти/стека. Даже когда аналог на языке высокого уровня возможен, профит от языка ассемблера может быть значительным в разы. Например, реализация подсчета среднего арифметического двух чисел с учётом переполнения для x86 процессоров занимает всего 2 команды (сложение с выставлением флага переноса и сдвиг с заёмом этого флага). Аналог на языке высокого уровня ((long) x + y) >> 1
    1. может не работать в принципе, ведь sizeof(long) == sizeof(int). Ну или может бомбануть, как это было на ракете Ариан-5 (64-ичные переменные с плавающей запятой float преобразовывались в 16 бит фиксированных, когда программа дошла до 32767+1, у ракеты включилась система «самоуничтожение на случай попадания в мир КГБ» прямо на космодроме.
    2. при компиляции конвертируется в чуть менее чем дохрена команд процессора.

Ещё пример: перемножение чисел с фиксированной запятой в языке, в который не вшита поддержка такого формата чисел. На ассемблере это умножение eax на edx с сохранением 64-битного результата в edx:eax с последующим сдвигом вправо (shrd). На ЯВУ это выглядит либо как (x*y)>>16, что не даёт верный результат при выходе результата умножения за 32 бита, либо как (int64(x)*y)>>16, что конвертируется в дохрена команд. Впрочем, в данных примерах современные компиляторы умеют правильно понимать намерения программиста и генерировать оптимальный код.

  • Также может использоваться задротами для написания очень компактных программ. Пример: клиент для аськи, занимающий в скомпилированном виде 34 КБ. Другой пример: тоже IM-клиент Faim 0.21 — 12.61 КБ. Отдельная дисциплина этой специальной олимпиады — демки.
  • В вузах изучается для вливания в МНУ студентоты познаний об устройстве и работе компьютера на уровне повыше кликанья мышкой по окошкам. Отличное лекарство от быдлокодерства (ну, а для оказавшихся неспособными осилить, соответственно — окончательное решение).
  • Является единственным языком программирования для создания достойных инфекторов — Cih, Sality, Sinowal.
  • Чуть более чем половина программ для GPU пишутся на ассемблере, наряду с использованием языков высокого уровня HLSL или GLSL . Хороший, годный шейдер обязательно содержит асму, ибо GPU — это принципиально узкое место для выделения графической составляющей в монитор (Есть, конечно и более современные методы типа использования OpenCL, но всё же ASM [править] Пример программирования

Нижеследующий кусок кода переключает процессор из реального (16-битного) режима в длинный (64-битный). Эпичность заключается в «прыжке через десятилетия», поскольку переключает ЦП x86 из режима совместимости со своим предком 1979 года рождения, в коем он находится сразу после подачи питания, в современный (2003 года разработки), где доступны все инструкции и всё адресное пространство памяти.

Данный пример написан для ассемблера nasm:

Вообще, в среде ASM программистов существует целая война ассемблеров. Кто-то предпочитает nasm, кто-то yasm, tasm, masm или даже fasm. На самом деле вроде бы и ерундовая вещь, но каждый ассемблер имеет свои особенности и целый ряд анальных ограничений. Поэтому не удивляйтесь, если кто-то будет с пеной у рта отстаивать свой ассемблер, или тому, что код, написанный под одну разновидность, на другой попросту не оттранслируется. Ассемблер имеет множество диалектов, некоторые из них довольно продвинуты, например, никому не нужные макросы в fasm чем-то напоминают язык высокого уровня, поэтому он вызывает в среде ассемблерщиков некоторый баттхёрт, да ещё и ассоциации с хорошо оптимизированным и разбитым на отдельные операции с прямыми указателями Си делают контрольный выстрел.

[править] Мнение изучающей студентоты

На самом деле, соль Ассемблера в том, что он делает ВСЁ, кроме того, что нужно. Термин «Научиться программировать на Ассемблере» (см. ассемблировать, «оседлать», «обуздать», «поработить» Ассемблер) подразумевает вывод на протяжении долгого и попабольного обучения хитроумного плана, который позволит наебать Ассемблер незаметно для него, тем самым заставив его работать на вас. Судя по всему, успеха добиваются единицы. Остальные же, которые, не понимая того, становятся рабами Вышеназванного, теряют волю, сходят с ума и, в лучшем случае, попадают в армию.

[править] Мнение изучившей студентоты

Будучи изученным в течение нескольких лет в вузе и применяемым на практике, добавляет стопицот к ЧСВ, что мешает при необходимости программировать на не-ТруЪ-языках при смене работы (а также пола, возраста, профессии и т.п). Например, писать на javascript после ассемблера походит на процесс создания красных сердечек на торте из сахара паяльником: дорого, долго и болезненно. Зато чем-то похоже на ТруЪ

Язык ассемблера. Особенности, макросы, формат операторов, псевдокоманды. Часть 1.

Язык, на котором написана исходная программа, называется вход­ным языком, а язык, на который она переводится для исполнения процессо­ром, — выходным языком. Процесс преобразования входного языка в выходной язык называется трансляцией. Так как процессоры способны выполнять програм­мы на машинном языке двоичных кодов, который не используется для програм­мирования, то необходима трансляция всех исходных программ. Известны два способа трансляции: компиляция и интерпретация.

При компиляции исходная программа сначала полностью переводится в экви­валентную программу на выходном языке, называемую объектной программой, а затем выполняется. Этот процесс реализуется с помощью специальной про­граммы, называемой компилятором. Компилятор, для которого входной язык яв­ляется символической формой представления машинного (выходного) языка дво­ичных кодов, называется ассемблером.

При интерпретации каждая строка текста исходной программы анализируется (интерпретируется) и указанная в ней команда сразу выполняется. Реализация такого способа возлагается на программу–интерпретатор. Интерпретация занима­ет длительное время. Для повышения ее эффективности вместо обработки каждой строки интерпретатор предварительно осуществляет преобразование всех ко­мандных строк в символы (

). Сформированная последовательность симво­лов используется для выполнения возложенных на исходную программу функций.

Рассматриваемый ниже язык ассемблера реализуется с помощью компиляции.

Основные особенности ассемблера:

● вместо двоичных кодов в языке используются символьные имена — мнемо­ника. Например, для команды сложения (

и т. д. Символьные имена используются и для адресации ячеек памяти. Для программирования на языке ассемблера вместо двоичных кодов и адре­сов нужно знать только символические названия, которые ассемблер транс­лирует в двоичные коды;

● каждое высказывание соответствует одной машинной команде (коду), т. е. су­ществует взаимно однозначное соответствие между машинными командами и операторами в программе на языке ассемблера;

● язык обеспечивает доступ ко всем объектам и командам. Языки высокого уровня такой способностью не обладают. Например, язык ассемблера позво­ляет выполнить проверку бита регистра флагов, а язык высокого уровня (на­пример,

) такой способностью не обладает. Отметим, что языки для сис­темного программирования (например, С) часто занимают промежуточное положение. С точки зрения возможностей доступа они ближе к языку ассемб­лера, однако обладают синтаксисом языка высокого уровня;

● язык ассемблера не является универсальным языком. Для каждой определен­ной группы микропроцессоров имеется свой ассемблер. Языки высокого уровня лишены этого недостатка.

В отличие от языков высокого уровня написание и отладка программы на язы­ке ассемблера занимает много времени. Несмотря на это, язык ассемблера по­лучил широкое распространение в силу следующих обстоятельств:

● составленная на языке ассемблера программа имеет значительно меньшие размеры и работает гораздо быстрее, чем программа, написанная на языке высокого уровня. Для некоторых приложений эти показатели играют перво­степенную роль, например, многие системные программы (в том числе ком­пиляторы), программы в кредитных карточках, сотовых телефонах, драйверах устройств и др.;

● некоторым процедурам требуется полный доступ к аппаратному обеспече­нию, что обычно невозможно осуществить на языке высокого уровня. К этому случаю относятся прерывания и обработчики прерываний в операционных системах, а также контроллеры устройств во встроенных системах, работа­ющих в режиме реального времени.

В большинстве программ лишь небольшой процент всего кода отвечает за большой процент времени выполнения программы. Обычно 1% программы отве­чает за 50% времени выполнения, а 10% программы отвечает за 90% времени выполнения. Поэтому для написания конкретной программы в реальных ус­ловиях используется как ассемблер, так и один из языков высокого уровня.

Формат оператора в языке ассемблера.

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

Для поля метки отводится колонка 1. Метка является символи­ческим именем, или идентификатором, адреса памяти. Она необходима для того, чтобы можно было:

● совершить условный или безусловный переход к команде;

● получить доступ к месту, где хранятся данные.

Такие высказывания снабжаются меткой. Для обозначения имени используют­ся (прописные) буквы английского алфавита и цифры. В начале имени должна стоять буква, в конце — разделитель в виде двоеточия. Метку с двоеточием мож­но писать на отдельной строке, а код операции — на следующей строке в колонке 2, что упрощает работу компилятора. Отсутствие двоеточия не поз­воляет отличить метку от кода операции, если они расположены на отдельных строках.

В некоторых версиях языка ассемблера двоеточия ставятся только после ме­ток команд, но не после меток данных, а длина метки может быть ограничена 6 или 8 символами.

В поле метки не должно встречаться одинаковых имен, так как метка ассоции­руется с адресами команд. Если в процессе выполнения программы отсутствует необходимость вызова команды или данных из памяти, то поле метки остается пустым.

Поле кода операции.

Это поле содержит мнемокод команды или псевдо­команды (см. ниже). Мнемокод команд выбирается разработчиками языка. В язы­ке ассемблера

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

), а для сохранения содержимого регистра в памяти — мнемоника

). В языках ассемблера

для обеих операций можно ис­пользовать одно имя, соответственно

. Если выбор названий мнемо­ник может быть произвольным, то необходимость использования двух машинных команд обусловлено архитектурой процессоров

Мнемоника регистров также зависит от версии ассемблера (табл. 5.2.1).

Здесь располагается дополнительная информация, необ­ходимая для выполнения операции. В поле операндов для команд перехода ука­зан адрес, куда нужно совершить переход, а также заданы адреса и регистры, ко­торые являются операндами для машинной команды. В качестве примера приве­дем операнды, которые могут быть использованы для 8–разрядных процессоров

представленные в различных системах счисления. Для обозначения используемой системы счисления за константой следует одна из латинских букв: В,

— соответственно двоичная, восьмеричная, шестнадцатеричная, десятичная системы счисления (

можно не записывать). Если первой цифрой шестнадцатеричного числа являются А, В, С,

, то впереди добавляется незначащий 0 (нуль);

● коды внутренних регистров микропроцессора и ячейки памяти

М (источников или приемников информации) в виде букв А, В, С,

, М или их адреса в любой системе счисления (например, 10В — адрес регистра

в двоичной системе);

для регистровых пар ВС,

, Н; для пары аккумулятора и регистра признаков —

; для счетчика команд —

;для указателя стека —

● метки, указывающие адреса операндов или следующих команд в условных

(при выполнении условия) и безусловных переходах. Например, операнд М1 в команде

означает необходимость безусловного перехода к коман­де, адрес которой в поле метки отмечен идентификатором М1;

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

— определить слово), а позднее ввели альтернативный вариант .

который с самого начала был в языке для процессоров

Процессоры обрабатывают операнды разной длины. Для ее определения раз­работчики ассемблера приняли разные решения, например:

II регистры разной длины имеют разные названия: ЕАХ — для раз­мещения 32–битных операндов (тип

); АХ — для 16–битных (тип

и АН — для 8–битных (тип

к каждому коду операции прибавляются суффиксы: суффикс

; суффикс «.В» для типа


для операндов разной длины используются разные коды операций, например, для загрузки байта, полуслова (

) и слова в 64–битный ре­гистр используются коды операций

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

● точка с запятой (;) в языках для процессоров фирмы

● восклицательный знак (!) в языках для

Каждая отдельная строка, отведенная под комментарий, предваряется начальным символом.

В языке ассемблера можно выделить два ос­новных вида команд:

базовые команды, являющиеся эквивалентом машинного кода процессора. Эти команды выполняют всю предусмотренную программой обработку;

псевдокоманды, или директивы, предназначенные для обслуживания процес­са трансляции программы на язык кодовых комбинаций. В качестве примера в табл. 5.2.2 приведены некоторые псевдокоманды из ас­семблера

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

● писать нужную последовательность команд всякий раз, когда она встречается. Такой подход приводит к увеличению объема программы;

● оформить эту последовательность в процедуру (подпрограмму) и вызывать ее при необходимости. Такой выход имеет свои недостатки: каждый раз при­дется выполнять специальную команду вызова процедуры и команду возврата, что при короткой и часто используемой последовательности может сильно снизить скорость работы программы.

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

Макрос, или макрокоманда, характеризуется тремя аспектами: макроопреде­лением, макрообращением и макрорасширением.

— это обозначение многократно повторяемой последова­тельности команд программы, используемое для ссылок в тексте программы.

Макроопределение имеет следующую структуру:

Список выражений ; Макроопределение

В приведенной структуре макроопределения можно выделить три части:

макроса, включающий в себя имя

и набор параметров;

● отмеченное точками тело макроса;

Набор параметров макроопределения содержит перечень всех параметров, приведенных в поле операнда для выбранной группы команд. Если эти парамет­ры приведены в программе ранее, то их в заголовке макроопределения можно не указывать.

Для повторного ассемблирования выбранной группы команд используется об­ращение, состоящее из имени

макрокоманды и перечня параметров с дру­гими значениями.

Когда в процессе компиляции ассемблер встречает макроопределение, он со­храняет его в таблице макроопределений. При последующих появлениях в про­грамме имени (

) макроса ассемблер замещает его телом макроса.

Использование имени макроса в качестве кода операции называется макро–обращением (макровызовом), а его замещение телом макроса — макрорасши­рением.

Если программу представить как последовательность символов (букв, цифр, пробелов, знаков пунктуации и возврата каретки для перехода на новую строку), то макрорасширение состоит в замене одних цепочек из этой последовательно­сти другими цепочками.

Макрорасширение происходит во время процесса ассемблирования, а не во время выполнения программы. Способы манипулирования цепочками символов возлагается на макросредства.

Процесс ассемблирования осуществляется в два прохода:

● на первом проходе сохраняются все макроопределения, а макровызовы расширяются. При этом исходная программа считывается и преобразуется в программу, в которой удалены все макроопределения, а каждый макро­вызов замещен телом макроса;

● на втором проходе обрабатывается полученная программа без макросов.

Макросы с параметрами.

Для работы с повторяющимися последовательно­стями команд, параметры которых могут принимать различные значения, преду­смотрены макроопределения:

● с фактическими параметрами, которые помещаются в поле операндов макро-обращения;

● с формальными параметрами. В процессе расширения макроса каждый фор­мальный параметр, появляющийся в теле макроса, замещается соответству­ющим фактическим параметром.

использования макросов с параметрами.

В программе 1 приведено две похожих последовательности команд, отличающихся тем, что пер­вая из них меняет местами Р и

В программе 2 включен макрос с двумя формальными параметрами Р1 и Р2. Во время расшире­ния макроса каждый символ Р1 внутри тела макроса замещается первым фактическим параметром (Р,

), а символ Р2 замещается вторым фактическим параметром (

) из программы № 1. В макровызо­ве

программы 2 обозначено: Р,

— первый фактический параметр,

— второй фактический параметр.

MOV EBX,Q MOV EAX,Pl

MOV Q,EAX MOV EBX,P2

MOV P,EBX MOV P2,EAX

Рассмотрим некоторые расширенные возмож­ности языка

Если макрос, содержащий команду условного перехода и метку, к которой со­вершается переход, вызывается два и более раз, то метка будет дублироваться (проблема дублирование меток), что вызовет ошибку. Поэтому при каждом вызове в качестве параметра приписывается (программистом) отдельная метка. В языке

метка объявляется локальной (

) и благодаря расширенным возмож­ностям ассемблер автоматически порождает другую метку при каждом расширении макроса.

позволяет определять макросы внутри других макросов. Такая расширенная возможность весьма полезна в сочетании с условной компоновкой программы. Рассмотрим

IF WORDSIZE GT 16 M2 MACRO

Макрос М2 может быть определен в обеих частях оператора

. Однако опре­деление зависит от того, на каком процессоре ассемблируется программа: на 16–битном или на 32–битном. Если М1 не вызывается, то макрос М2 вообще не будет определен.

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

Об использовании макросредств в ассемблере.

При использовании мак­росов ассемблер должен уметь выполнять две функции: сохранять макроопреде­ления и расширять макровызовы.

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

При встрече с макросом в процессе ассемблирования создается:

новый элемент таблицы с именем макроса, числом параметров и указателем на другую таблицу макроопределений, где будет храниться тело макроса;

● список формальных параметров.

Затем считывается и сохраняется в таблице макроопределений тело макроса, представляющее собой просто цепочку символов. Формальные параметры, встречающиеся в теле цикла, помечаются специальным символом.

Внутреннее представление макроса

из приведенного выше примера для программы 2 (стр. 244) имеет вид:

MOV EAX,&P1; MOV EBX,&P2; MOV &P2EAX;MOV &

где в качестве символа возврата каретки используется точка с запятой, а в каче­стве символа формального параметра — амперсант &.

Всякий раз, когда при ассемблировании встречается макроопределение, оно сохраняется в таблице макросов. При вызове макроса ассемблер временно приостанавливает чтение входных данных из вход­ного устройства и начинает считывать сохраненное тело макроса. Извлеченные из тела макроса формальные параметры замещаются фактическими параметра­ми и предоставляются вызовом. Амперсант & перед параметрами позволяет ас­семблеру распознать их.

Несмотря на то, что существует много версий ассемблера, процессы ассемб­лирования имеют общие черты и во многом сходны. Ниже рассматривается рабо­та двухпроходного ассемблера.

Программа состоит из ряда операторов. Поэто­му, казалось бы, что при ассемблировании можно использовать следующую по­следовательность действий:

● транслировать его на машинный язык;

● перенести полученный машинный код в файл, а соответствующую часть лис­тинга — в другой файл;

● повторять перечисленные процедуры до тех пор, пока вся программа не бу­дет оттранслирована.

Однако такой подход не является эффективным. Примером может служить так называемая проблема опережающей ссылки. Если первым оператором является переход к оператору Р, расположенному в самом конце программы, то ассемблер не может транслировать его. Он сначала должен определить адрес оператора Р, а для этого необходимо прочитать всю программу. Каждое полное прочтение исходной программы называется проходом. Покажем, как можно решить пробле­му опережающей ссылки с использованием двух проходов:

● на первом проходе следует собрать и сохранить в таблице все определения символов (в том числе меток), а на втором проходе — выполнить чтение и ас­семблирование каждого оператора. Такой способ относительно прост, однако второй проход по исходной программе требует дополнительных временных затрат на операции ввода–вывода;

● на первом проходе следует преобразовать программу в промежуточную фор­му и сохранить ее в таблице, а второй проход выполнить не по исходной про­грамме, а по таблице. Такой способ ассемблирования позволяет сэкономить время, так как на втором проходе не выполняются операции ввода–вывода.

Для вывода листинга нужно сохранить полностью исходное выражение, вклю­чая комментарии. Если листинг не нужен, то промежуточную форму можно сократить, оставив только одни команды.

Ассемблер, способный читать исходную программу дважды, называется двух –проходным ассемблером.

Цель первого прохода — построить таблицу символов. Как отмечалось выше, еще одной задачей первого прохода является сохранение всех макроопределений и расширение вызовов по мере их появления. Следовательно, в одном проходе происходит и определение символов, и расширение макросов. Символом может быть либо метка, либо значение, которому с помощью директи­вы приписывается определенное имя:

;Значение — размер буфера

Придавая значения символьным именам в поле метки команд, ассемблер по сути дела задает адреса, которые будет иметь каждая команда во время выпол­нения программы. Для этого ассемблер во время процесса ассемблирования со­храняет счетчик адреса команд (

) как специаль­ную переменную. В начале первого прохода значение специальной переменной устанавливается на 0 и увеличивается после каждой обработанной команды на длину этой команды. В качестве примера в табл. 5.2.3 приведен фрагмент про­граммы с указанием длины команд и значений счетчика. При первом проходе формируются таблицы символьных имен, директив и кодов операций, а при необ­ходимости литеральная таблица. Литерал — это константа, для которой ассемб­лер автоматически резервирует память. Сразу же отметим, что современные процессоры содержат команды с непосредственными адресами, поэтому их ас­семблеры не поддерживают литералы.

Таблица символьных имен

содержит один элемент для каждого имени (табл. 5.2.4). В каждом элементе таблицы символьных имен содержится само имя (или указатель на него), его численное значение и иногда некоторая дополни­тельная информация, которая может включать:

● длину поля данных, связанного с символом;

● биты перераспределения памяти (которые показывают, изменяется ли зна­чение символа, если программа загружается не в том адресе, в котором предполагал ассемблер);

● сведения о том, можно ли получить доступ к символу извне процедуры.

Символьные имена являются метками. Они могут быть заданы с помощью операторов (например,

В этой таблице приводятся все директивы, или псевдо­команды, которые встречаются при ассемблировании программы.

Таблица кодов операций.

Для каждого кода операции в таблице преду­смотрены отдельные графы: обозначение кода операции, операнд 1, операнд 2, 16–ричное значение кода операции, длина команды и тип команды (табл. 5.2.5). Коды операций делятся на группы в зависимости от числа и вида операндов. Тип команды определяет номер группы и задает процедуру, которая вызывается для обработки всех команд данной группы.

Цель второго прохода — создание объектной программы и распечатка при необходимости протокола ассемблирования; вывод информации, необходимой компоновщику для связывания процедур, которые ассемблирова­лись в разное время, в один выполняемый файл.

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

Исходная программа может содержать ошибки, например:

● приведенный символ не определен или определен более одного раза;

● код операции представлен недопустимым именем (из–за опечатки), не снабжен достаточным количеством операндов или имеет слишком много операндов;

Некоторые ассемблеры могут выявить неопределенный символ и заменить его. Однако в большинстве случаев при обнаружении оператора с ошибкой ассемблер выводит сообщение об ошибке на экран и пытается продолжить процесс ассемб­лирования.

Вторая частьстатьи посвященной языку ассемблер.

Ассемблер

Несмотря на развитие визуальных средств разработки для языков высокого уровня, программирование на ассемблере по прежнему актуально. Конечно, вряд ли вам пригодится ассемблер, если вы программист баз данных или веб-программист. Но если вы пишите драйверы, игры, работаете с разным железом и т.п., то язык ассемблера приходится применять довольно часто.

В этом разделе вы найдёте книги, статьи и документацию, где описан язык ассемблера для процессоров Intel (и совместимых). Это основной раздел сайта, поэтому он будет постоянно пополняться новыми материалами.

Ассемблер для чайников
Бесплатная книга для начинающих. Очень подробно и в доступной форме описаны различные способы создания программ на языке Ассемблера. Возможно, что книга будет полезна и для опытных прораммистов.

Примеры на Ассемблере (исходники на Ассемблере)
Примеры на Ассемблере позволяют, во-первых, потренироваться в программировании на этом языке, а во-вторых, более глубоко вникнуть в ассемблер и более полно в нём разобраться. Поэтому я решил наконец создать раздел, где буду приводить примеры относительно простых программ.

Ассемблер и языки высокого уровня
Может быть не очень часто, но довольно регулярно (во всяком случае профессиональным программистам), в программы на языках высокого уровня приходится вставлять куски кода на ассемблере. Для чего они это делают? Хочется экзотики? Или чтобы жизнь мёдом не казалась? На самом деле причины есть, и это не капризы программистов, а осознанная необходимость.

Документация для Emu8086
Перевод справочной информации для Emu8086. Программа Emu8086 — это очень полезный инструмент для начинающих изучать язык Ассемблера. Рекомендую.

Мастер Йода рекомендует:  СУБД Oracle подвержена новому классу уязвимостей
Добавить комментарий