Как сделать цикл в ассемблере

Добавил пользователь Владимир З.
Обновлено: 04.10.2024

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

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

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

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

.
sub %rax, %rbx
jz l2
.
l2:
.

В данном фрагменте происходит переход на метку l2 , при условии, что в результате выполненной операции вычитания в rbx оказалось число 0 . Если использовать команду jnz , то переход будет осуществляться если результат не 0 . Если мы не хотим реально менять содержимое регистров, то вместо sub используется команда cmp , которая действие не выполняет, но меняет содержимое регистра флагов так же как sub:

.
cmp %rax, %rbx
jz l2
.
l2:
.

Есть набор команд, позволяющий сравнивать операнды на предмет "больше - меньше":

.
cmp %rax, %rbx
jge l2
.
l2:
.

В данном фрагмент переход на метку l2 происходит только в том случае. если содержимое rbx больше или равно содержимого rax .

До сих пор мы приводили примеры так сказать не полных условных конструкций. Но реализовать полную условную конструкцию совсем не сложно:

Как мы видим, для этого нам понадобилась команда безусловного перехода jmp .

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

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

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

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

В конце выполнения тела цикла проверяется значение счетчика, и если счетчик еще не равен нулю, то цикл продолжается. Важно понимать, что если мы храним счетчик в регистре (у нас это rdx ), то в результате выполнения тела цикла он может не сохранится. Для его сохранности мы используем стек. Надо также помнить, что если используете системные вызовы то как минимум содержимое rcx и r11 у вас не сохраняется. Я уж не говорю о использовании функций сторонних библиотек. Ну и более коротки вариант цикла "ДО" представлен ниже

А вот типичный цикл "ПОКА".

Как видим, для организации цикла "ПОКА" понадобилась более сложная программная структура.

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

Как видим, loop делает цикл более компактным.

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

Ассемблер на нашем канале Old Programmer продолжается. Подписывайтесь на мой канал, ставьте лайки и комментируйте.

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

Задача: необходимо вывести на экран цифры от ‘0’ до ‘9’, используя цикл со счетчиком.

Цикл со счетчиком можно организовать, используя те же условные команды, взяв в качестве счетчика любой свободный регистр. Но в микропроцессоре предусмотрены специальные команды для организации таких циклов: loop / loope / loopne . Эти команды используют в качестве счетчика регистр cx, они сами уменьшают его значение после каждой итерации цикла и сравнивают после уменьшения с нулём.

Команда loop выполняет следующие действия:

  • уменьшает регистр cx ;
  • сравнивает cx с нулем, если cx >0, то управление передается на метку перехода (продолжаем цикл).

Команды loope/loopne позволяют выйти из цикла по дополнительному условию.

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

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

IDE для Assembler

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

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

Сложение двух чисел на 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.

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

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

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

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

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

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

Задача: необходимо вывести на экран цифры от ‘0’ до ‘9’, используя цикл со счетчиком.

Цикл со счетчиком можно организовать, используя те же условные команды, взяв в качестве счетчика любой свободный регистр. Но в микропроцессоре предусмотрены специальные команды для организации таких циклов: loop / loope / loopne . Эти команды используют в качестве счетчика регистр cx, они сами уменьшают его значение после каждой итерации цикла и сравнивают после уменьшения с нулём.

Команда loop выполняет следующие действия:

  • уменьшает регистр cx ;
  • сравнивает cx с нулем, если cx >0, то управление передается на метку перехода (продолжаем цикл).

Команды loope/loopne позволяют выйти из цикла по дополнительному условию.

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

Читайте также: