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

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

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

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

Такая программа сборки будет состоять из многих, многих инструкций, которые вместе делают что-то, что кажется человеку очень простым и базовым. Это затрудняет человеку чтение ассемблерной программы. В отличие от этого, язык программирования высокого уровня может иметь одну единственную инструкцию, такую как PRINT "Hello, world!", которая подскажет компьютеру выполнить все маленькие задачи за вас.

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

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

Программирование в машинном коде

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

05 2A 00

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

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

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

Например, код машины в предыдущей главе (05 2A 00) можно записать на ассемблере как:

 добавить ось,42

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

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

Демонтаж и отладка

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

Организация вычислительной техники

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

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

ОЗУ

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

Несмотря на то, что адрес относится к определенному байту в памяти, процессоры позволяют использовать несколько байт памяти подряд. Наиболее распространенное использование этой возможности - использовать 2 или 4 байта подряд для представления числа, обычно целого. Одиночные байты иногда также используются для представления целых чисел, но поскольку они имеют длину всего 8 бит, они могут содержать только 28 или 256 различных возможных значений. Использование 2 или 4 байт в строке увеличивает количество различных возможных значений до 216, 65536 или 232, 4294967296, соответственно.

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

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

Процессор

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

Инструкции по чтению или записи в память

Следующая инструкция ассемблера x86 считывает (загружает) 2-байтовый объект из байта по адресу 4096 (0x1000 в шестнадцатеричной системе) в 16-битный регистр, называемый 'ax':

        движущийся топор, [1000 часов]

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

        фильм bx, 20

Из-за того, что не было использовано индентификации, в регистр было занесено само фактическое значение.

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

        Движение [1000 часов], топор

Здесь память по адресу 1000h получает значение оси. Если этот пример выполнить сразу после предыдущего, то 2 байта по 1000h и 1001h будут 2-байтовым целым числом со значением 20.

Инструкции, выполняющие математические или логические операции.

Некоторые инструкции делают такие вещи, как вычитание или логические операции, как нет:

Пример машинного кода ранее в этой статье был бы на языке ассемблера:

        добавить ось, 42

Здесь 42 и ось складываются вместе, и результат сохраняется обратно в ось. В сборке x86 также можно комбинировать доступ к памяти и математическую операцию:

        добавить топор, [1000 часов]

Эта инструкция добавляет значение 2-байтового целого, сохраненного при 1000ч, к оси и сохраняет ответ по оси.

        или ось, bx

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

Инструкции, которые решают, какой будет следующая инструкция.

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

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

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

  • вычесть 100 из длины стороны
  • если ответ меньше нуля, установите длину стороны равной 100.
  • умножить длину стороны сама по себе

Этот алгоритм можно выразить следующим кодом, где ось - это длина стороны.

        перемещение bx, ось    подуровень, 100        джем-прежнему Двигай топор, 100 продолжается: многоосевая модель

В этом примере представлено несколько новых вещей, но первые две инструкции знакомы. Они копируют значение оси в bx, а затем вычитают 100 из bx.

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

Другая новая концепция - это флаги. На процессорах x86 многие инструкции устанавливают в процессоре 'флаги', которые могут быть использованы следующей инструкцией для принятия решения о том, что делать. В этом случае, если bx был меньше 100, sub установит флаг, который говорит, что результат был меньше нуля.

Следующая инструкция - "Прыгать, если больше или равно". Это инструкция ветви. Если флаги в процессоре указывают, что результат больше или равен нулю, вместо того, чтобы просто перейти к следующей команде, процессор перейдет к команде по метке continue, которая является mul ax.

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

        сжимающая ось, 100     джем-прежнему Двигай топор, 100 продолжается:         многоосевая модель

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

Вход и выход

В то время как ввод и вывод являются фундаментальной частью вычислений, нет ни одного способа сделать это на языке ассемблера. Это связано с тем, что способ ввода/вывода зависит от настроек компьютера и операционной системы, на которой он работает, а не только от того, какой у него процессор. В разделе примеров пример Hello World использует вызовы операционной системы MS-DOS и пример после использования вызовов BIOS.

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

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

Языки сборки и портативность

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

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

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

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

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

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

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

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

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

Мировая программа "Привет", написанная на Ассамблее x86:

adosseg .model small .stack 100h .data hello_message db 'Hello, World! ',0dh,0ah, '$' .код основной proc mov ax,@данные mov ds,ax mov ah,9 mov dx,смещение hello_message int 21h mov ax,4C00h int 21h main endp end main

Функция, которая печатает номер на экран, используя прерывания BIOS, записанные в NASM x86 сборке. Модульный код можно написать в ассемблере, но это требует дополнительных усилий. Обратите внимание, что всё, что приходит после точки с запятой в строке, является комментарием и игнорируется ассемблером. Помещение комментариев в код на ассемблере очень важно, так как большие программы на ассемблере так трудно понять.

; void printn(int number, int base); printn: нажать         фильм   лёгкий удар         нажать на      ось     нажать на              нажать на              нажать на               нажать на      si      фильм   си, 0   фильм   ось, [bp + 4]  номер         фильм   [bp + 6]       ...базовый щуп..:      inc     si             длина строки         фильм   декс, 0        нулевой декс   div                    разделение по основам        cmp     декс, 10               Это Ge 10?     jge     номер   добавить        dx, '0'        добавить ноль к dx     прыжок  анумовое число: добавить       dx, ('A'- 10)      ; шестнадцатеричное значение, добавьте "А" к dx - 10. анум:  нажать                 Поставь Дкс на стопку.         cmp     ось, 0         Может, продолжим?         jne     глоп    фильм   час пикселя,         для прерывания петли:  поп-музыка        ось            получить его стоимость         фильм   ах, 0eh        для перерыва         int     10h            письменный характер    декабрь si             избавляться от характера           шлюз    популярный сайт si      популярный сайт        популярный сайт           популярный сайт        популярный сайт ось     популярный сайт         повторный просмотр     4

Вопросы и ответы

В: Что такое язык ассемблера?


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

В: Как компьютер понимает программу на ассемблере?


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

В: Что такое инструкции в языке ассемблера?


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

В: К какому типу языков программирования относится ассемблер?


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

В: Чем это отличается от языков высокого уровня?


О: Языки высокого уровня могут иметь отдельные команды, такие как PRINT "Hello, world!", которые скажут компьютеру выполнить все эти небольшие задачи автоматически, без необходимости указывать их по отдельности, как это нужно делать в программе на ассемблере. Это делает языки высокого уровня более легкими для чтения и понимания человеком, чем программы на ассемблере, состоящие из множества отдельных команд.

В: Почему человеку может быть трудно читать программу на ассемблере?


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

AlegsaOnline.com - 2020 / 2023 - License CC3