Loop в ассемблере: Инструкция LOOP

Инструкция LOOP


Лучшие книги по Ассемблеру

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

Инструкция LOOP в Ассемблере уменьшает значение в регистре СХ в реальном режиме или ECX в защищённом. Если после этого значение в СХ не равно нулю, то команда LOOP выполняет переход на МЕТКУ. Синтаксис:

LOOP МЕТКА

Состояние флагов не изменяется.

МЕТКА — это допустимый в Ассемблере идентификатор.

О метках в Ассемблере я рассказывал здесь.

Алгоритм работы команды LOOP:

  • CX = CX — 1
  • Если CX не равен 0, то выполнить переход (продолжить цикл)
  • Иначе не выполнять переход (прервать цикл и продолжить выполнение программы)

То есть команда LOOP выполняется в два этапа. Сначала из регистра СХ вычитается единица и его значение сравнивается с нулём. Если регистр не равен нулю, то выполняется переход к указанной МЕТКЕ. Иначе переход не выполняется и управление передаётся команде, которая следует сразу после команды LOOP.

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

Выполнение цикла в Ассемблере можно организовать с помощью нескольких команд. Одна из таких команд — это команда LOOP. Команда цикла в Ассемблере всегда уменьшает значение счётчика на единицу. Это значение находится в регистре СХ (или ECX). Отличия между командами цикла заключаются только в условиях, при которых выполняется переход к метке или цикл завершается.

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

Более понятно это будет в примере программы (см. ниже).

ПРИМЕЧАНИЕ
В качестве счётчика команда LOOP использует регистр CX в реальном режиме, и регистр ECX в защищённом режиме. Это не всегда удобно, если программу (или её часть) планируется использовать в обоих режимах. Поэтому в системе команд процессоров Интел предусмотрены две специальные команды — LOOPD и LOOPW, которые независимо от режима работы процессора в качестве счётчика используют регистры ECX и CX соответственно.

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


  . model	tiny
  .code
  ORG 	100h
	
start:	
  MOV CX, 26        ; Цикл будет выполнен 26 раз
  MOV DL, 'A'       ; CL = 41h (ASCII-код) - первая буква
  MOV BX, 676h      ; Позиция первой буквы на экране
  MOV AX, 0B800h    ; Установить AX = B800h (память VGA)
  MOV DS, AX        ; Копировать значение из AX в DS	
  MOV DH, 01001110b ; CH = атрибуты цвета

abcde:
  MOV [BX], DX      ; Записать символ в видеопамять
  INC DL            ; Увеличить ASCII-код (для следующего символа)
  ADD BX, 2         ; Сместить координаты
  
LOOP
abcde ; Повторить цикл END start

Возможные ошибки

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

При обнулении счётчика перед циклом при первой итерации цикла значение в регистре CX будет равно FFFFh (потому что команда LOOP отнимет от СХ единицу, а в СХ у нас был 0), и цикл в программе будет выполняться, соответственно 65536 раз.

Ещё один момент: диапазон адресов для передачи управления в команде LOOP ограничен в пределах -128…+127 байтов относительно адреса следующей команды. Если учесть, что в реальном режиме процессора средняя длина машинной команды равна 3 байта, то получается, что блок команд, выполняющихся в цикле, может состоять примерно из 42 команд. Если же в вашем цикле будет больше команд, то, например, MASM, выдаст сообщение об ошибке, которое будет выглядеть примерно так:

error A2075: jump destination too far : by 10 byte(s)

Здесь говорится, что местоположение перехода слишком далеко (примерно на 10 байт больше допустимого).

Ещё одна ошибка — это изменение значения регистра CX в теле цикла. В итоге команда LOOP будет работать неправильно. К тому же при этом можно попасть в бесконечный цикл. Пример:


  MOV CX, 2      ; Устанавливаем счётчик
top:
  INC CX
  LOOP top

Здесь в теле цикла увеличивается значение регистра СХ, поэтому он никогда не будет равен нулю, и, следовательно, цикл никогда не завершится.

А теперь о происхождении мнемоники LOOP. В общем то это не мнемоника, а слово. В переводе с английского оно означает “петля”, “виток”, “цикл”.


Подписаться на Дзен-канал

Вступить в группу «Основы программирования»

Подписаться на рассылки по программированию


Первые шаги в программирование

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


Учебный курс.

Часть 13. Циклы и команда LOOP

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

Синтаксис объявления меток

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

m1: mov ax,4C00h
    int 21h

Теперь вместо имени

m1 компилятор везде будет подставлять адрес комады mov ax,4C00h. Можно объявлять метку на пустой строке перед командой:

exit_app:
    mov ax,4C00h
    int 21h

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

no_error:
exit_app:
m1: mov ax,4C00h

Подробнее о синтаксисе объявления меток рассказывается в части 27.

Команда LOOP

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

Содержимое CX интерпретируется командой как число без знака. В CX нужно помещать число, равное требуемому количеству повторений цикла. Понятно, что максимально может быть 65535 повторений. Ещё одно ограничение связано с дальность перехода. Метка должна находиться в диапазоне -127…+128 байт от команды LOOP (если это не так, FASM сообщит об ошибке).

Пример цикла

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

02h (выводимый байт должен находиться в регистре DL).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use16                 ;Генерировать 16-битный код
org 100h              ;Программа начинается с адреса 100h
 
    mov ah,02h        ;Для вызова функции DOS 02h - вывод символа
    mov dl,'A'        ;Первый выводимый символ
    mov cx,26         ;Счётчик повторений цикла
metka:
    int 21h           ;Обращение к функции DOS
    inc dl            ;Следующий символ
    loop metka        ;Команда цикла
 
    mov ah,09h        ;Функция DOS 09h - вывод строки
    mov dx,press      ;В DX адрес строки
    int 21h           ;Обращение к функции DOS
 
    mov ah,08h        ;Функция DOS 08h - ввод символа без эха
    int 21h           ;Обращение к функции DOS
 
    mov ax,4C00h      ;\
    int 21h           ;/ Завершение программы
;-------------------------------------------------------
press:
    db 13,10,'Press any key.
..$'

Команды «int 21h» и «inc dl» (строки 8 и 9) будут выполняться в цикле 26 раз. Для того, чтобы программа не закрылась сразу, используется функция DOS 08h — ввод символа с клавиатуры без эха, то есть вводимый символ не отображается. Перед этим выводится предложение нажать любую кнопку (но Reset лучше не нажимать). Для примера адрес строки объявлен с помощью метки. Символы с кодами 13 и 10 обозначают переход на следующую строку (символ 13(0Dh) называется CR — Carriage Return — возврат каретки, а символ 10(0Ah) LF — Line Feed — перевод строки . Эти символы унаследованы со времён древних телетайпов, когда текст печатался, как на печатной машинке). Так выглядит результат работы программы:

Вложенные циклы

Иногда требуется организовать вложенный цикл, то есть цикл внутри другого цикла. В этом случае необходимо сохранить значение CX перед началом вложенного цикла и восстановить после его завершения (перед командой LOOP внешнего цикла). Сохранить значение можно в другой регистр, во временную переменную или в стек. Следующая программа выводит все доступные ASCII-символы в виде таблицы 16×16. Значение счётчика внешнего цикла сохраняется в регистре BX.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
use16                 ;Генерировать 16-битный код
org 100h              ;Программа начинается с адреса 100h
 
    mov ah,02h        ;Для вызова функции DOS 02h - вывод символа
    sub dl,dl         ;Первый выводимый символ
    mov cx,16         ;Счётчик внешнего цикла (по строкам)
lp1:
    mov bx,cx         ;Сохраняем счётчик в BX
    mov cx,16         ;Счётчик внутреннего цикла (по столбцам)
lp2:
    int 21h           ;Обращение к функции DOS
    inc dl            ;Следующий символ
    loop lp2          ;Команда внутреннего цикла
 
    mov dh,dl         ;Сохраняем значение DL в DH
    mov dl,13         ;\
    int 21h           ; \
    mov dl,10         ; / Переход на следующую строку
    int 21h           ;/
    mov dl,dh         ;Восстанавливаем значение DL
 
    mov cx,bx         ;Восстанавливаем значение счётчика
    loop lp1          ;Команда внешнего цикла
 
    mov ah,09h        ;Функция DOS 09h - вывод строки
    mov dx,press      ;В DX адрес строки
    int 21h           ;Обращение к функции DOS
 
    mov ah,08h        ;Функция DOS 08h - ввод символа без эха
    int 21h           ;Обращение к функции DOS
 
    mov ax,4C00h      ;\
    int 21h           ;/ Завершение программы
;-------------------------------------------------------
press db 13,10,'Press any key. ..$'

Как видите, всё довольно просто 🙂 Результат работы программы выглядит вот так:

Упражнение

Напишите программу для вычисления степени числа 3 по формуле a = 3n. Число a — 16-битное целое без знака, число n — 8-битное целое без знака (используйте n<11, чтобы избежать переполнения). Проверьте работу программы в отладчике (нажимайте F7 на команде LOOP, чтобы осуществить переход). Результаты можете выкладывать в комментариях.

Следующая часть »

linux — Создание цикла for в сборке x86

спросил

Изменено 5 лет, 6 месяцев назад

Просмотрено 6к раз

Я новичок в ассемблере и мне интересно, как написать цикл for, который будет выполняться 15 раз. В цикле for мне нужно иметь условие if, которое проверяет, является ли целое число k больше 7. Как это будет записано в коде?

  • линукс
  • сборка

3

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

 MOV ECX, 15 ; количество итераций
.петля:
    ...
    CMP EAX, 8 ; сравните к с 7
    ДЕК ЕСХ ; после этого необходимо слияние с частичным флагом, медленное на некоторых процессорах
    JA .loop ; проверить условие выхода из цикла
Б: ; код после цикла
    ...
    ;; проверьте CF здесь, чтобы увидеть, если k >= 8 или нет, то есть k > 7
 

РЕДАКТИРОВАТЬ: Чтобы k хранилось в памяти как 32-разрядное целое число, инструкция CMP выглядит следующим образом:

 CMP DWORD [k], 8
 

EDIT2: Сохранить один условный переход. В OP не упоминалось, что нужно уйти или остаться, когда k больше 7. Приведенный выше код выходит из цикла, когда он выполняется 15 раз или k не превышает 7.

Обратите внимание, что этот трюк с объединением сравнений только вероятно, будет хорош на процессорах, таких как семейство Intel Sandybridge или Silvermont, которые не имеют остановок с частичным флагом для чтения CF после дек . На Core2/Nehalem это остановится примерно на 7 циклов для слияния флагов, как если бы вы попали в цикл adc BigInteger с использованием dec . См. PDF-файл микроарха Агнера Фога.

Если вы не уверены, что он подходит для всех процессоров, на которых будет работать ваш код, используйте cmp/ja или jae отдельно от dec/jnz .

EDIT3: OP запрашивает увеличение/уменьшение целого числа (здесь edx), когда eax больше/меньше 7:

 .цикл:
    ...
    ЦМП ЕАХ, 7
    ДЕКАБРЬ EDX
    JB .end
    INC EDX
    JE .end
    INC EDX
.конец:
    ДЕК ЕСХ
    JZ . петля
 

(возможно, кто-то будет оптимизировать это дальше)

9

Я предлагаю научиться использовать скомпилированный код, чтобы научить вас таким вещам. Напишите нужный код на C, затем скомпилируйте его с флагами для вывода сборки. Например, в gcc это делается с помощью -S флаг. Затем вы можете прочитать сгенерированный машиной ассемблерный код, чтобы увидеть, как это было сделано.

Общая стратегия для цикла for может быть примерно такой:

 MOV ECX, 0 ; очистить ЕСХ
   А:
       ЦМП [к], 7 ; сравните к с 7
       JGT Б; перейти к B, если k больше 7
       CMP ECX, 15 ; проверить условие выхода из цикла
       JE C ; перейти к C, если мы повторили 15 раз
       INC ECX
       СПМ А
   Б: ; это происходит, если K больше 7
   ...
   С: ; это происходит, если мы достигаем конца цикла
 

1

Зарегистрируйтесь или войдите в систему

Зарегистрируйтесь с помощью Google

Зарегистрироваться через Facebook

Зарегистрируйтесь, используя адрес электронной почты и пароль

Опубликовать как гость

Электронная почта

Требуется, но никогда не отображается

Опубликовать как гость

Электронная почта

Требуется, но не отображается

Assembly/loop.

asm на мастере · fredimachado/Assembly · GitHub

Постоянная ссылка

владелец

Тег с указанным именем ветви уже существует. Многие команды Git принимают имена как тегов, так и веток, поэтому создание этой ветки может привести к неожиданному поведению. Вы уверены, что хотите создать эту ветку?

Перейти к файлу

 

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

Этот файл содержит двунаправленный текст Unicode, который может быть интерпретирован или скомпилирован не так, как показано ниже. Для просмотра откройте файл в редакторе, который показывает скрытые символы Unicode. Узнайте больше о двунаправленных символах Unicode

Показать скрытые символы

Оставить комментарий

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

© 2019 Штирлиц Сеть печатных салонов в Перми

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