Red4u.ru

SEO Сервисы и программы
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Код на ассемблере

Программирование на Ассемблере для начинающих с примерами программ

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

IDE для Assembler

Первый вопрос: в какой среде разработки программировать на Ассемблере? Ответ однозначный – MASM32. Это стандартная программа, которую используют для данного ЯП. Скачать её можно на официальном сайте masm32.com в виде архива, который нужно будет распаковать и после запустить инсталлятор install.exe. Как альтернативу можно использовать FASM, однако для него код будет значительно отличаться.

Перед работой главное не забыть дописать в системную переменную PATH строчку:

Программа «Hello world» на ассемблере

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

Для начала запускаем редактор qeditor.exe в папке с установленной MASM32, и в нём пишем код программы. После сохраняем его в виде файла с расширением «.asm», и билдим программу с помощью пункта меню «Project» → «Build all». Если в коде нет ошибок, программа успешно скомпилируется, и на выходе мы получим готовый exe-файл, который покажет окно Windows с надписью «Hello world».

Сложение двух чисел на assembler

В этом случае мы смотрим, равна ли сумма чисел нулю, или же нет. Если да, то на экране появляется соответствующее сообщение об этом, и, если же нет – появляется иное уведомление.

Здесь мы используем так называемые метки и специальные команды с их использованием (jz, jmp, test). Разберём подробнее:

  • test – используется для логического сравнения переменных (операндов) в виде байтов, слов, или двойных слов. Для сравнения команда использует логическое умножение, и смотрит на биты: если они равны 1, то и бит результата будет равен 1, в противном случае – 0. Если мы получили 0, ставятся флаги совместно с ZF (zero flag), которые будут равны 1. Далее результаты анализируются на основе ZF.
  • jnz – в случае, если флаг ZF нигде не был поставлен, производится переход по данной метке. Зачастую эта команда применяется, если в программе есть операции сравнения, которые как-либо влияют на результат ZF. К таким как раз и относятся test и cmp.
  • jz – если флаг ZF всё же был установлен, выполняется переход по метке.
  • jmp – независимо от того, есть ZF, или же нет, производится переход по метке.

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

Примитивная программа, которая показывает процесс суммирования двух переменных:

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

Получение значения из командной строки на ассемблере

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

Также можно воспользоваться альтернативным методом:

Здесь используется invoke – специальный макрос, с помощью которого упрощается код программы. Во время компиляции макрос-команды преобразовываются в команды Ассемблера. Так или иначе, мы пользуемся стеком – примитивным способом хранения данных, но в тоже время очень удобным. По соглашению stdcall, во всех WinAPI-функциях переменные передаются через стек, только в обратном порядке, и помещаются в соответствующий регистр eax.

Циклы в ассемблере

Для создания цикла используется команда repeat. Далее с помощью inc увеличивается значение переменной на 1, независимо от того, находится она в оперативной памяти, или же в самом процессоре. Для того, чтобы прервать работу цикла, используется директива «.BREAK». Она может как останавливать цикл, так и продолжать его действие после «паузы». Также можно прервать выполнение кода программы и проверить условие repeat и while с помощью директивы «.CONTINUE».

Сумма элементов массива на assembler

Здесь мы суммируем значения переменных в массиве, используя цикл «for»:

Команда dec, как и inc, меняет значение операнда на единицу, только в противоположную сторону, на -1. А вот cmp сравнивает переменные методом вычитания: отнимает одно значение из второго, и, в зависимости от результата ставит соответствующие флаги.

С помощью команды jne выполняется переход по метке, основываясь на результате сравнения переменных. Если он отрицательный – происходит переход, а если операнды не равняются друг другу, переход не осуществляется.

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

Почему Ассемблер — это круто, но сложно

Есть высо­ко­уров­не­вые язы­ки — это те, где вы гово­ри­те if — else, print, echo, function и так далее. «Высо­кий уро­вень» озна­ча­ет, что вы гово­ри­те с ком­пью­те­ром более-менее чело­ве­че­ским язы­ком. Дру­гой чело­век может не понять, что имен­но у вас напи­са­но в коде, но он хотя бы смо­жет про­чи­тать сло­ва.

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

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

Вари­ан­тов Ассем­бле­ра доволь­но мно­го. Но так как все они рабо­та­ют по оди­на­ко­во­му прин­ци­пу и исполь­зу­ют (в основ­ном) оди­на­ко­вый син­так­сис, мы будем все подоб­ные язы­ки назы­вать общим сло­вом «Ассем­блер».

Как мыслит процессор

Что­бы понять, как рабо­та­ет Ассем­блер и поче­му он рабо­та­ет имен­но так, нам нуж­но немно­го разо­брать­ся с внут­рен­ним устрой­ством про­цес­со­ра.

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

Читайте так же:
Функция плт в excel

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

Обще­го назна­че­ния. Это 8 реги­стров, каж­дый из кото­рых может хра­нить все­го 4 бай­та инфор­ма­ции. Такой регистр мож­но раз­де­лить на 2 или 4 части и рабо­тать с ними как с отдель­ны­ми ячей­ка­ми.

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

Регистр фла­гов. Флаг — какое-то свой­ство про­цес­со­ра. Напри­мер, если уста­нов­лен флаг пере­пол­не­ния, зна­чит про­цес­сор полу­чил в ито­ге такое чис­ло, кото­рое не поме­ща­ет­ся в нуж­ную ячей­ку памя­ти. Он туда кла­дёт то, что поме­ща­ет­ся, и ста­вит в этот флаг циф­ру 1. Она — сиг­нал про­грам­ми­сту, что что-то пошло не так.

Фла­гов в про­цес­со­ре мно­го, какие-то мож­но менять вруч­ную, и они будут вли­ять на вычис­ле­ния, а какие-то мож­но про­сто смот­реть и делать выво­ды. Фла­ги — как сиг­наль­ные лам­пы на пане­ли при­бо­ров в само­лё­те. Они что-то озна­ча­ют, но толь­ко само­лёт и пилот зна­ют, что имен­но.

Сег­мент­ные реги­стры. Нуж­ны были для того, что­бы рабо­тать с опе­ра­тив­ной памя­тью и полу­чать доступ к любой ячей­ке. Сей­час такие реги­стры име­ют по 32 бита, и это­го доста­точ­но, что­бы полу­чить 4 гига­бай­та опе­ра­тив­ки. Для про­грам­мы на Ассем­бле­ре это­го обыч­но хва­та­ет.

Так вот: всё, с чем рабо­та­ет Ассем­блер, — это коман­ды про­цес­со­ра, пере­мен­ные и реги­стры.

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

Команды Ассемблера

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

Любая коман­да на этом язы­ке выгля­дит так:

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

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

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

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

mov eax, ebx ; Пересылаем значение регистра EBX в регистр EAX

mov x, 0 ; Записываем в переменную x значение 0

add eax, х ; Складываем значение регистра ЕАХ и переменной х, результат отправится в регистр ЕАХ

Здесь нет меток, пер­вы­ми идут коман­ды (mov или add), а за ними — опе­ран­ды и ком­мен­та­рии.

Пример: возвести число в куб

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

Пер­вый вари­ант

mov al, x ; Пересылаем x в регистр AL

imul al ; Умножаем регистр AL на себя, AX = x * x

movsx bx, x ; Пересылаем x в регистр BX со знаковым расширением

imul bx ; Умножаем AX на BX. Результат разместится в DX:AX

Вто­рой вари­ант

mov al, x ; Пересылаем x в регистр AL

imul al ; Умножаем регистр AL на себя, AX = x * x

cwde ; Расширяем AX до EAX

movsx ebx, x ; Пересылаем x в регистр EBX со знаковым расширением

imul ebx ; Умножаем EAX на EBX. Поскольку x – 1-байтовая переменная, результат благополучно помещается в EAX

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

x = Math.pow(x,3);
x := exp(ln(x) * 3);
на худой конец x = x*x*x.

Хит­рость в том, что когда каж­дая из этих строк будет све­де­на к машин­но­му коду, это­го кода может быть и 5 команд, и 10, и 50, и даже 100. Чего сто­ит вызов объ­ек­та Math и его мето­да pow: толь­ко на эту слу­жеб­ную опе­ра­цию (ещё до само­го воз­ве­де­ния в куб) может уйти несколь­ко сотен и даже тысяч машин­ных команд.

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

Почему это круто

Ассем­блер поз­во­ля­ет рабо­тать с про­цес­со­ром и памя­тью напря­мую — и делать это очень быст­ро. Дело в том, что в Ассем­бле­ре почти не тра­тит­ся зря про­цес­сор­ное вре­мя. Если про­цес­сор рабо­та­ет на часто­те 3 гига­гер­ца — а это при­мер­но 3 мил­ли­ар­да про­цес­сор­ных команд в секун­ду, — то очень хоро­ший код на Ассем­бле­ре будет выпол­нять при­мер­но 2,5 мил­ли­ар­да команд в секун­ду. Для срав­не­ния, JavaScript или Python выпол­нят в тыся­чу раз мень­ше команд за то же вре­мя.

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

Спра­вед­ли­во­сти ради отме­тим, что совре­мен­ные ком­пи­ля­то­ры С++ дают машин­ный код, близ­кий по быст­ро­дей­ствию к Ассем­бле­ру, но всё рав­но немно­го усту­па­ют ему.

Почему это сложно

Для того, что­бы писать про­грам­мы на Ассем­бле­ре, нуж­но очень любить крем­ний:

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

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

Для чего всё это

Ассем­блер неза­ме­ним в таких вещах:

  • драй­ве­ры;
  • про­грам­ми­ро­ва­ние мик­ро­кон­трол­ле­ров и встра­и­ва­е­мых про­цес­со­ров;
  • кус­ки опе­ра­ци­он­ных систем, где важ­но обес­пе­чить ско­рость рабо­ты;
  • анти­ви­ру­сы (и виру­сы).
Читайте так же:
Вход в безопасном режиме на ноутбуке

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

Примеры на Ассемблере для микроконтроллеров Atmel AVR

Здесь представлены примеры различных программ на языке Ассемблера для микроконтроллеров Atmel AVR. Примеры выложены в виде проектов для AVR Studio под микроконтроллер ATmega16, поэтому при переносе на другие МК семейства AVR это нужно учитывать. Тактовая частота микроконтроллера во всех примерах 8 МГц (используется тактирование от внутреннего генератора). Код примеров разбит на блоки и снабжен комментариями. При обнаружении ошибок просьба сообщить на почту.

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

Подробное описание каждой команды представлено в AVR 8bit Instruction Set.

Для более глубокого изучения ассемблера AVR советую к прочтению книгу Юрия Ревича «Практическое программирование микроконтроллеров Atmel AVR на языке ассемблера» . С беcплатным фрагментом книги можно ознакомиться здесь. Также можно изучить различные методические пособия ВУЗов, например, вот.

Start – Заготовка стартовой инициализации микроконтроллера, в которую входят инициализация стека, очистка ОЗУ (SRAM) и регистров общего назначения (R0 — R31), а также глобальный запрет прерываний. Пример кода приведён ниже. При использовании следует помнить, что регистры ввода-вывода (порты, периферия и т.д.) не очищаются, поэтому их НЕОБХОДИМО инициализировать отдельно.

Delays – Библиотека с подпрограммами задержки. Все задержки рассчитаны на тактовую частоту МК 8 МГц. Библиотека включает следующие процедуры:

  • Delay1us – Задержка повышенной точности в 1 мкс c учетом длительности RCALL и RET.
  • Delay5us – Задержка повышенной точности в 5 мкс c учетом длительности RCALL и RET.
  • Delay10us – Задержка повышенной точности в 10 мкс c учетом длительности RCALL и RET.
  • Delayus – Задержка высокой точности в несколько десятков микросекунд.
  • Delayms – Задержка высокой точности в несколько миллисекунд.

Math – Библиотека с подпрограммами математических операций, включает следующие процедуры:

  • SUB16X16 – Вычитание 16-разрядных чисел.
  • ADD16X16 – Сложение 16-разрядных чисел.
  • MUL16X16s – Знаковое умножение 16-разрядных чисел.
  • MUL16X16u – Беззнаковое умножение 16-разрядных чисел.
  • DIV16X16s – Знаковое деление 16-разрядных чисел.
  • DIV16X16u – Беззнаковое деление 16-разрядных чисел.
  • DIV16POWER2s – Знаковое деление 16-разрядного числа на степень 2.
  • DIV16POWER2u – Беззнаковое деление 16-разрядного числа на степень 2.
  • SIGN16 – Смена знака 16-разрядного числа.
  • DEC2BCD – Перевол 8-разрядного десятичного числа в двоично-десятичное (BCD).
  • BCD2DEC – Перевол 8-разрядного двоично-десятичного (BCD) числа в десятичное.
  • CP16X16 – Сравнение 16-разрядных чисел.
  • DIGITS8 – Вычисление цифр 8-разрядного числа.
  • DIGITS16 – Вычисление цифр 16-разрядного числа.

IO Ports – В данном примере рассматривается работа с портами ввода-вывода. К порту А подключены 8 светодиодов (линии 0-7). К линии 0 порта С подключена кнопка, с подтяжкой на землю. При нажатии кнопка выдает на линию 0 порта С уровень логической единицы. Цикл программы организован следующим образом: при запуске включается бегущий огонь, сначала загорается светодиод на линии 0 порта А, затем на линии 1 и т.д. По достижении линии 7 направление бегущего огня меняется (от 7 к 0). При нажатии на кнопку бегущий огонь останавливается и загораются одновременно все светодиоды. После повторного нажатия на кнопку бегущий огонь продолжает перемещаться с места остановки.

Dynamic Indication – В данном примере рассматривается работа с 7-сегментным индикатором. В моём случае он имеет 4 разряда (цифры). Поскольку у меня на плате установлены транзисторы для управления разрядами, то управление осуществляется выводом логической единицы и на разряды и на сегменты. Схема подключения следующая: к линиям 0-7 порта C подключены сегменты индикатора, а к линиям 0-3 порта В разряды индикатора. При запуске на индикатор выводятся цифры 1 2 3 4.

UART – В данном примере рассматривается периферийного модуля UART (универсальный асинхронный приёмопередатчик). Модуль UART можно настроить как на работу с прерываниями, так и без них (вручную, путём работы с флагами). Пример работает следующим образом: при получении байта, МК переходит в обработчик прерывания (используется только прерывание по приёму данных) и разбирает численное значение байта (0-255) на цифры, которые и выводятся на 7-сегментный индикатор. Схема подключения аналогична предыдущему примеру. Передача осуществляется по двум линиям UART (порт D линии 0-1), к которым необходимо подключить линии RX и TX преобразователя USB-UART. Для настройкки без прерываний необходимо обнулить бит RXCIE в регистре UCSRB и вручную опрашивать интерфейс в основном цикле программы.

Clock – В данном примере рассматривается реализация простых часов с 7-сегментым индикатором и парой кнопок. Только здесь уже требуется 6 разрядов, хотя секунды можно опустить. Кнопки с подтяжкой на землю. При нажатии кнопка выдает на линию высокий логический уровень. Индикатор подключается как и в предыдущих примерах (сегменты к порту C, разряды к порту B), а кнопки к линиям 2-3 порта D. Кнопка PD2 используется для установки минут, а PD3 для установки часов. По нажатию каждой из кнопок увеличивается значение соответствующего разряда (минуты или часы).

DS18B20 – В данном примере рассматривается работа с цифровым датчиком температуры DS18B20. Показания температуры выводятся на 7-сегментый индикатор. Вывод DQ датчика поключен к линии PC7. Линия должна быть подтянута к плюсу питания резистором на 4.7-10 кОм (согласно документации). Датчик опрашивается каждую секунду. Температура выводится на 4-разрядный индикатор: знак, два разряда на целуюю часть и один на вещественную. Документация к датчику здесь.

ADC Indication – Данный пример аналогичен примеру с UART. Отличие в том, что байт берется с линии 0 порта А (линия 0 АЦП, ADC0). Микроконтроллер по таймеру производит аналого-цифровое преобразование напряжения на линии 0 порта А, (младшие 2 бита отбрасываются как шум). При измерении используется внутренняя опора 5 В. К линии PD2 порта D подключена кнопка, которая определяет режим вывода показаний. При нажатии на кнопку выводится результат измерений в виде числа от 0 до 255. Если кнопка не нажата, то результат измерений переводится в вольты и выводится на индикатор (с точностью до десятых).

Fast PWM – В данном примере показана настройка аппаратного ШИМ (широтно-импульсная модуляция, англ. PWM). К линиям 4 и 5 порта D подключены светодиоды, а к линиям 0-3 порта С – кнопки. Кнопки с подтяжкой на землю (при нажатии кнопка выдает на линию порта уровень логической единицы) подключены к линиям 2-5 порта C. Кнопки на линях 2 и 3 соответственно увеличивают и уменьшают коэффициент заполнения ШИМ (меняется яркость светодиода) канала А. Кнопки на линях 4 и 5 соответственно увеличивают и уменьшают коэффициент заполнения ШИМ канала B. Число сравнения для каждого из каналов меняется в диапазоне от 0 до 255. Для канала А шаг изменения равен 10, для канала В шаг равен 5.

HCSR04 – В данном примере рассматривается работа с ультразвуковым датчиком расстояния HCSR04. К линии PD6 подключен вывод Trigger датчика, а к линии PD7 вывод Echo. Поключение 7-сегментного индикатора аналогично предыдущим примерам. По таймеру микроконтроллер раз в секунду опрашивает датчик и определяет расстояние до препятсвия в миллиметрах. После этого число разбивается на цифры и выводится на дисплей. Документация к датчику здесь.

Читайте так же:
Вход в безопасном режиме на ноутбуке

Matrix Keyboard – В данном примере показана работа с матричной клавиатурой. Микроконтроллер динамически опрашивает клавиатуру, а затем определяет номер нажатой клавиатуры. Размер поля 3 на 3 – получаем 9 кнопок. Нажатие первых 8-ми приводит к зажиганию светодиода на соответствующей линии порта А, нажатие 9-ой кнопки зажигает все светодиоды порта А. Матричная клавиатура подключается к линиям 0-5 порта С (три столбца и три строки). В архиве схема и печатная плата матричной клавиатуры (Diptrace).

Shift Register – В данном примере рассматривается работа с модулем SPI на примере сдвигового регистра 74HC595. К регистру подключены светодиоды, в качестве линии CS используется линия 4 порта B (вывод not SS). Линия DS (14 нога) регистра идет к MOSI (PB5), линия SHCP (11 нога) к линии SCK (PB7), линия STCP (12 нога) к линии SS (PB4). Линии MR (10 нога) и OE (13 нога) должны быть подтянуты к высокому и низкому логическим уровням соответственно. По таймеру микроконтроллер меняет состояние светодиодов: поочерёдно горят то чётные светодиоды, то нечётные. Если при этом передать байт по UART’у, то он будет выведен в порт на светодиоды. Чтобы обратно переключиться в режим мигания необходимо послать по UART’у 0x00 (ноль). Документация к микросхеме 74HC595 здесь.

SG-90 Servo – В данном примере рассматривается работа с сервоприводом SG-90. Используется аппаратный ШИМ. Линия ШИМ сервпопривода подключена к каналу А аппаратного ШИМ. Кнопки поворота подключены к линиям PD2 и PD3. Кнопка на линии PD2 увеличивает длительность импульса, кнопка на линии PD3 уменьшает длительность импульса. Длительность импульса меняется от 1 до 2 мс. Описание сервомотора здесь.

RGB Lamp – В данном примере рассматривается работа с трехцветным RGB-светодиодом. Реализовано плавное переливание цветов с использованием программного ШИМ. Линии красного, зеленого и синего цветов подключаются соответственно к линиям 2, 3 и 4 порта D.

Ассемблер. Сегменты памяти и регистры

Обновл. 29 Сен 2019 |

Мы уже рассматривали 3 секции из которых состоят программы на ассемблере. Эти секции также представляют различные сегменты памяти. Что интересно, если вы замените ключевое слово section на segment , то получите тот же результат. Например:

Результат выполнения программы выше:

Сегменты памяти

Модель сегментированной памяти разбивает системную память на группы независимых сегментов, на которые указывают указатели, расположенные в регистрах сегментов. Каждый сегмент используется для хранения данных определённого типа. Один сегмент используется для хранения кода инструкций, второй — для хранения элементов данных, а третий — для программного стека.

Сегмент данных (data segment) — представлен секциями .data и .bss. Секция .data используется для объявления области памяти, где хранятся элементы данных для программы. Эта секция не может быть расширена после объявления элементов данных, и она остаётся статической во всей программе. Секция .bss также является секцией статической памяти, которая содержит буферы для данных, которые будут объявлены в программе позже. Эта буферная память заполнена нулями.

Сегмент кода (code segment) — представлен секцией .text. Он определяет область в памяти, в которой хранятся коды инструкций. Это также фиксированная область.

Стек (stack) — это сегмент, который содержит значения данных, передаваемые в функции и процедуры в программе.

Регистры

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

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

Регистры процессора

В архитектуре IA-32 есть десять 32-битных и шесть 16-битных процессорных регистра. Регистры делятся на три категории:

Общие регистры (General Registers)

Регистры управления (Control Registers)

Сегментные регистры (Segment Registers)

В свою очередь, общие регистры делятся на следующие:

Регистры данных (Data Registers)

Регистры-указатели (Pointer Registers)

Индексные регистры (Index Registers)

Регистры данных

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

Как полные 32-битные регистры данных: EAX, EBX, ECX, EDX.

Нижние половины 32-битных регистров могут использоваться как четыре 16-битных регистра данных: AX, BX, CX и DX.

Нижняя и верхняя половины вышеупомянутых четырёх 16-битных регистров могут использоваться как восемь 8-битных регистров данных: AH, AL, BH, BL, CH, CL, DH и DL.

Некоторые из этих регистров данных имеют специфическое применение в арифметических операциях.

AX (primary accumulator) используется для ввода/вывода и в большинстве арифметических операций. Например, в операции умножения один операнд сохраняется в регистре EAX или AX или AL в соответствии с размером операнда.

BX (base register) используется при индексированной адресации.

CX (count register) хранит количество циклов в повторяющихся операциях (также, как и регистры ECX и CX).

DX (data register) используется в операциях ввода/вывода, а также с регистрами AX и DX для выполнения операций умножения и деления, связанных с большими значениями.

Регистры-указатели

Регистрами-указателями являются 32-битные регистры EIP, ESP и EBP и соответствующие им 16-битные регистры IP, SP и BP. Есть три категории регистров-указателей:

Указатель на инструкцию или команду (Instruction Pointer или IP) — 16-битный регистр IP хранит смещение адреса следующей команды, которая должна быть выполнена. IP в сочетании с регистром CS (как CS:IP) предоставляет полный адрес текущей инструкции в сегменте кода.

Указатель на стек (Stack Pointer или SP) — 16-битный регистр SP обеспечивает значение смещения в программном стеке. SP в сочетании с регистром SS (SS:SP) относится к текущей позиции данных или адреса в программном стеке.

Базовый указатель (Base Pointer или BP) — 16-битный регистр BP используется в основном при передаче параметров в подпрограммы. Адрес в регистре SS объединяется со смещением в BP, чтобы получить местоположение параметра. BP также можно комбинировать с DI и SI в качестве базового регистра для специальной адресации.

Индексные регистры

32-битные индексные регистры ESI и EDI и их 16-битные версии: SI и DI, которые используются в индексированной адресации, и, иногда, в операциях сложения/вычитания. Есть два типа индексных указателей:

Читайте так же:
Функция плт в excel

Исходный индекс (Source Index или SI) — используется в качестве исходного индекса в строковых операциях.

Индекс назначения (Destination Index или DI) — используется в качестве индекса назначения в строковых операциях.

Регистры управления

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

Распространённые битовые флаги:

Флаг переполнения (Overflow Flag или OF) — указывает на переполнение старшего бита данных (крайнего левого бита) после signed арифметической операции.

Флаг направления (Direction Flag или DF) — определяет направление влево или вправо для перемещения или сравнения строковых данных. Если DF = 0 , то строковая операция принимает направление слева направо, а когда DF = 1 , то строковая операция принимает направление справа налево.

Флаг прерывания (Interrupt Flag или IF) — определяет, будут ли игнорироваться или обрабатываться внешние прерывания (например, ввод с клавиатуры и т.д.). Он отключает внешнее прерывание, когда значение равно 0, и разрешает прерывание, когда установлено значение 1.

Флаг ловушка (Trap Flag или TF) — позволяет настроить работу процессора в одношаговом режиме.

Флаг знака (Sign Flag или SF) — показывает знак результата арифметической операции. Этот флаг устанавливается в соответствии со знаком элемента данных после выполнения арифметической операции. Знак определяется по старшему левому биту. Положительный результат сбрасывает значение SF до 0, а отрицательный результат устанавливает его равным 1.

Нулевой флаг (Zero Flag или ZF) — указывает результат арифметической операции или операции сравнения. Ненулевой результат сбрасывает нулевой флаг до 0, а нулевой результат устанавливает его равным 1.

Вспомогательный флаг переноса (Auxiliary Carry Flag или AF) — после выполнения арифметической операции содержит перенос с бита 3 на бит 4. Используется для специализированной арифметики. AF устанавливается, когда 1-байтовая арифметическая операция вызывает перенос из бита 3 в бит 4.

Флаг равенства (Parity Flag или PF) — указывает общее количество 1-бит в результате, полученном после выполнения арифметической операции. Чётное число 1-бит сбрасывает PF до 0, а нечётное число 1-бит устанавливает PF равным 1.

Флаг переноса (Carry Flag или CF) — после выполнения арифметической операции содержит перенос 0 или 1 из старшего бита (крайнего слева). Кроме того, хранит содержимое последнего бита операции сдвига или поворота.

В таблице ниже указано положение битовых флагов в 16-битном регистре флагов:

Код на ассемблере

Системное программирование

Главная страница

Младший специалист

Бакалавр

О сайте

Дополнительные материалы

Пройти тест

Лекция 8. Логические команды языка Ассемблер.

Методики условных вычислений на самом нижнем (двоичном) уровне основаны на четырех основных операциях двоичной алгебры: И, ИЛИ, ИСКЛЮЧАЮЩЕЕ ИЛИ и НЕ. Эти операции положены в основу работы логических схем компьютера, а также его программного обеспечения.

В системе команд процессоров семейства x 86 предусмотрены команды AND, OR, XOR, NOT, TEST и ВT, выполняющие перечисленные выше булевы операции между байтами, словами и двойными словами (табл.1).

Таблица 1. Логические команды процессора.

Выполняет операцию логического И между двумя операндами

Выполняет операцию логического ИЛИ между двумя операндами

Выполняет операцию исключающего ИЛИ между двумя операндами

Выполняет операцию логического отрицание (НЕ) единственного операнда

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

Копирует бит операнда получателя, номер n которого задан в исходном операнде, во флаг переноса ( CF ), а затем, в зависимости от команды, тестирует, инвертирует, сбрасывает или устанавливает этот же бит операнда получателя

8.2. Флаги состояния процессора.

Каждая команда, рассмотренная на этой лекции, влияет на состояние флагов процессора. Ранее вы узнали что, что после выполнения арифметических и логических команд процессор устанавливает соответствующее значение флагов нуля (ZF), переноса (CF), знака (ZF) и др., как описано ниже.

· Флаг нуля (Zero flag, или ZF) устанавливается, если при выполнении арифметической или логической операции получается число, равное нулю (т.е. все биты результата равны 0).

· Флаг переноса (Carry flag, или CF) устанавливается в случае, если при выполнении беззнаковой арифметической операции получается число, разрядность которого превышает разрядность выделенного для него поля результата.

· Флаг знака (Sign flag, или SF) устанавливается, если при выполнении арифметической или логической операции получается отрицательное число (т.е. старший бит результата равен 1).

· Флаг переполнения (Overflow flag, или OF) устанавливается в случае, если при выполнении арифметической операции со знаком получается число, разрядность которого превышает разрядность выделенного для него поля результата.

· Флаг четности (Parity flag, или PF) устанавливается в случае, если в результате выполнения арифметической или логической операции получается число, содержащее четное количество единичных битов.

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

AND получатель, источник

Существуют следующие варианты команды AND:

Команда AND может работать с 8-, 16- или 32-разрядными операндами, причем длина у обоих операндов должна быть одинаковой. При выполнении операции поразрядного логического И значение результата будет равно 1 только в том случае, если оба бита пары равны 1. В табл. 2 приведена таблица истинности для операции логического И.

Таблица 2. Таблица истинности для операции логического И.

Команда AND обычно используется для сброса отдельных битов двоичного числа (например, флагов состояния процессора) по заданной маске. Если бит маски равен 1, значение соответствующего разряда числа не изменяется (в этом случае говорят, что разряд замаскирован), а если равен 0 — то сбрасывается. В качестве примера на рис. 8.1 показано, как можно сбросить четыре старших бита 8 -разрядного двоичного числа.

Для выполнения этой операции можно воспользоваться двумя командами:

mov al, 00111011b

and al, 00001111b

Рис. 8.1. Сброс битов по маске с помощью команды AND .

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

Флаги. Команда AND всегда сбрасывает флаги переполнения (OF) и переноса (CF). Кроме того, она устанавливает значения флагов знака (SF), нуля (ZF) и четности (PF) в соответствии со значением результата.

Команда OR выполняет операцию логического ИЛИ между соответствующими парами битов операндов команды и помещает результат на место операнда получателя данных:

Читайте так же:
Функция плт в excel

OR получатель, источник

В команде OR используются аналогичные команде AND типы операндов:

Команда OR может работать с 8-, 16- или 32-разрядными операндами, причем длина у обоих операндов должна быть одинаковой. При выполнении операции поразрядного логического ИЛИ значение результата будет равно 1, если хотя бы один из битов пары операндов равен 1. В табл. 3 приведена таблица истинности для операции логического ИЛИ.

Таблица 3. Таблица истинности для операции логического ИЛИ.

Команда OR обычно используется для установки в единицу отдельных битов двоичного числа (например, флагов состояния процессора) по заданной маске. Если бит маски равен 0, значение соответствующего разряда числа не изменяется, а если равен 1 — то устанавливается в 1. В качестве примера на рис. 8.2 показано, как можно установить четыре младших бита 8-разрядного двоичного числа, выбрав в качестве маски число 0Fh. Значение старших битов числа при это не меняется.

Рисунок 8.2. Установка битов по маске с помощью команды OR .

С помощью команды OR можно преобразовать двоичное число, значение которого находится в диапазоне от 0 до 9 в ASCII-строке. Для этого нужно установить в единицу биты 4 и 5. Например, если в регистре AL находится число 05h, то чтобы преобразовать его в соответствующий ASCII-код, нужно выполнить операцию OR регистра AL с числом 30h. В результате получится число 35h, которое соответствует ASCII-коду цифры 5 (рис. 8.3).

Рис. 8.3. Преобразование двоичного числа в ASCII-код

с помощью команды OR.

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

mov dl , 5 ; Двоичное число

or dl , 30 h ; Преобразуем в ASCII-код

Флаги. Команда OR всегда сбрасывает флаги переполнения (OF) и переноса (CF). Кроме того, она устанавливает значения флагов знака (SF), нуля (ZF) и четности (PF) в соответствии со значением результата. Например, с помощью команды OR можно определить, какое значение находится в регистре (отрицательное, положительное или нуль). Для этого вначале нужно выполнить команду OR, указав в качестве операндов один и тот же регистр, например:

а затем – проанализировать значение флагов, как показано в табл. 4.

Таблица 4. Определение значения числа по флагам состояния процессора.

Команда XOR выполняет операцию ИСКЛЮЧАЮЩЕГО ИЛИ между соответствующими парами битов операндов команды и помещает результат на место операнда получателя данных:

XOR получатель, источник

В команде XOR используются аналогичные командам AND и OR типы операндов:

Команда XOR может работать с 8-, 16- или 32-разрядными операндами, причем длина у обоих операндов должна быть одинаковой. При выполнении операции поразрядного ИСКЛЮЧАЮЩЕГО ИЛИ значение результата будет равно 1, если значения битов пары операндов различны, и 0 — если значения битов равны. В табл. 5 приведена таблица истинности для операции логического ИСКЛЮЧАЮЩЕГО ИЛИ.

Таблица 6.5. Таблица истинности для операции ИСКЛЮЧАЮЩЕГО ИЛИ.

Как следует из таблицы, при выполнении операции ИСКЛЮЧАЮЩЕГО ИЛИ с нулевым битом получается исходное значение бита, а с единичным битом — значение исходного бита инвертируется.

Операция ИСКЛЮЧАЮЩЕГО ИЛИ обладает свойством реверсивности — если ее выполнить дважды с одним и тем же операндом, то значение результата инвертируется. Как показано в табл. 6, если два раза подряд выполнить операцию ИСКЛЮЧАЮЩЕГО ИЛИ между битами X и Y , то в результате получится исходное значение бита X .

Таблица 6. Демонстрация свойства реверсивности операции ИСКЛЮЧАЮЩЕГО ИЛИ.

Флаги. Команда XOR всегда сбрасывает флаги переполнения (OF) и переноса (CF). Кроме того, она устанавливает значения флагов знака (SF), нуля (ZF) и четности (PF) в соответствии со значением результата.

Проверка флага четности (PF). Флаг четности позволяет узнать, какое количество единичных битов (четное или нечетное) содержится в младшем байте результата выполнения логической или арифметической команды. Если этот флаг установлен, значит, в результате получилось четное количество единичных битов, а если сброшен, то нечетное. Количество единичных битов можно проверить, не меняя значения результата. Для этого сначала нужно выполнить команду XOR с нулевым значением (т.е. с числом, все биты которого равны нулю), а затем проверить флаг четности:

mov al , 10110101 b ; Число содержит нечетное (5) количество единиц битов

xor al , 0 ; Поэтому флаг четности ( PF ) не устанавливается ( PO )

mov al , 11001100 b ; Число содержит четное (4) количество единиц битов

xor al , 0 ; Поэтому флаг четности ( PF ) устанавливается ( PE )

В отладчиках часто для обозначения четного количества единиц в полученном результате используется аббревиатура РЕ (т.е. Parity Even), а для нечетного — РО (т.е. Parity Odd).

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

mov ax , 64 C 1 h ; 0110 0100 1100 0001

xor ah , al ; Флаг четности ( PF ) устанавливается ( PF )

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

А если нам нужно оценить четность 32-разрядного числа? Тогда, пронумеровав его байты, соответственно, В, В1, В2 и В3, четность можно определить по следующей формуле: В XOR В1, XOR B 2 XOR В3.

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

Например, обратный код числа F 0 h равен 0 Fh :

mov al, 11110000b

not al ; AL = 00001111b

Флаги. Команда NOT не изменяет флаги процессора.

8.7. Команда TEST.

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

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

test al,00001001b ; Тестируем биты 0 и 3

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

голоса
Рейтинг статьи
Ссылка на основную публикацию
Adblock
detector