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, чтобы осуществить переход). Результаты можете выкладывать в комментариях.

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

•Промоделируем на Ассемблере простейшую задачу для 16-разрядных знаковых и беззнаковых данных:

–unsigned int c, d;

–int a, b;

•if (a=b) then Fsign = 0;

•if (a<b) then Fsign = -1;

•if (a>b) then Fsign = 1;

•if (c=d) then Fusign = 0;

•if (c<d) then Fusign = -1;

•if (c>d) then Fusign = 1;

Команды циклов LOOPx

Группа команд условного перехода LOOPх служит для организации циклов в программах. Все команды цикла используют регистр CX в качестве счетчика цикла. Простейшая из них – команда LOOP. Она уменьшает содержимое CX на 1 и передает управление на указанную метку, если содержимое CX не равно 0. Если вычитание 1 из CX привело к нулевому результату, выполняется команда, следующая за LOOP.

Синтаксис команды: LOOP короткая метка

Логика работы команды:

СХ = Counter short_label: Выполнение тела цикла СХ = СХ — 1

if (СХ != 0) goto short_label

Аналог реализации команды LOOP на Ассемблере:

MOV СХ, Counter short_label:

;Выполнение тела цикла ;

;Проверка условия ПРОДОЛЖЕНИЯ цикла DEC СХ

СМР СХ, 0 JNE short_label

Команда LOOP уменьшает содержимое регистра СХ на 1, затем передает управление метке shorMabel, если содержимое СХ не равно 0. Передача управления на метку shortjabel для базовых процессоров — только КОРОТКАЯ

[-128,0]. Поскольку условие выхода из цикла проверяется в

КОНЦЕ, при значении Counter=0 цикл все равно выполнится. Этого мало, мы еще и зациклимся. Чтобы этого избежать,

обычно ДО НАЧАЛА ЦИКЛА проверяют содержимое регистра СХ на ноль. Таким образом, стандартная последовательность команд для организации цикла СО СЧЕТЧИКОМ имеет следующий вид:

MOV CX, Counter

JCXZ ExitCicle ; если CX = 0, цикл ОБОЙТИ short_label:

; Выполнение тела цикла LOOP short_label

ExitCicle:

ПРИМЕР

Вычислить значение факториала р = n! = 1*2*3*…*n

Известно, что 0! = 1. Отрицательным значение n быть НЕ может

.Model Large,С

 

; определение префикса для локальных меток

 

locals @@

 

 

.code

 

 

Extrn С n: Word

 

Extrn С p: Word

 

Public proizv1

 

Proizv1 Proc far ; Вариант 1

 

mov cx,n

; количество повторений

 

mov si,1

 

 

mov ax,si

 

 

jcxz @@Exit ;if cx=0 then Exit

 

@@begin: ;

= НАЧАЛО цикла

=

mul si

; <dx:ax> = <ax>*si

 

inc si

 

 

; ==== Выход из цикла ==================

loop @@beg±n @@Exit: mov p,ax

ret

proizv1 endp

Public proizv2

Proizv2 Proc far ; Вариант 2

mov cx ,n ; количество повторений mov ax,1

jcxz @@Exit ;if cx=0 then Exit @@begin: ;— = НАЧАЛО цикла =====

mul cx ; <dx:ax> = <ax>*cx

; ==== Выход из цикла =========

loop @@begin @@Exit:

mov p,ax ret

proizv2 endp end

Директива locals

Директива locals позволяет нам не думать о дублировании имен меток в разных подпрограммах. Метки с префиксом @@ считаются локальными. Если компилятор встретит метку с таким же именем, он просто при компиляции присвоит ей другое имя (обычно эти метки получают в конце имени номер, который увеличивается на единицу — все очень просто!).

Команда LOOPE (LOOPZ)

•Команда LOOPE (LOOPZ) переход по счетчику и если равно

Данная команда имеет два равнозначных мнемонических имени (if Equal — если Равно или if Zero — если Ноль).

Синтаксис команды: LOOPE короткая_метка

LOOPZ короткая_метка

Логика работы команды:

СХ = Counter Short_Label: Выполнение тела цикла СХ = СХ — 1

if (СХ != 0 && ZF = 1) goto Short_Label

Все то, что говорилось для команды LOOP, справедливо и для команды LOOPE (LOOPZ), добавляется еще проверка флага ZF. Применяется данная команда в случае, если нужно досрочно выйти из цикла, как только находится ПЕРВЫЙ элемент, ОТЛИЧНЫЙ от заданной величины.

Команда LOOPNE (LOOPNZ)

•Команда LOOPNE (LOOPNZ) переход по счетчику и если НЕ равно

Данная команда тоже имеет два равнозначных мнемонических имени (if Not Equal — если НЕ равно или if Not Zero — если НЕ ноль). В отличие от предыдущей команды проверяется, сброшен ли флаг нуля ZF=0.

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

спросил

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

Просмотрено 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

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

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

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

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

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

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

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

Как сделать цикл на ассемблере x86?

спросил

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

Просмотрено 136 тысяч раз

Я написал код до сих пор:

 . code
главный
 Clrscr
  мов дч,10 ;строка 10
  mov dl,20 ;столбец 20
  вызвать Gotoxy ;найти курсор
  Промптфоринтегерс
    WriteString ; отображать строку
    ReadInt ;введите целое число
  Сумма массива
    WriteString ; отображать строку
    WriteInt ;отобразить целое число
DisplaySum ENDP
КОНЕЦ основной
 

Как заставить его повторять одни и те же шаги три раза, используя цикл, очищая экран после каждой итерации цикла?

  • петли
  • сборка
  • x86

4

 мов сх,3
циклический запуск:
   делать вещи
   dec cx ;Примечание: уменьшение cx и переход к результату
   jnz loopstart намного быстрее на Intel (и, возможно, на AMD, поскольку я не
                   ; проверено, может быть, 12 лет) вместо использования цикла loopstart
 

6

Еще один метод использует инструкцию LOOP:

 mov cx, 3
моя петля:
    ; Содержимое вашего цикла
    петля
 

Инструкция цикла автоматически уменьшает значение cx и выполняет переход только в том случае, если cx != 0. Существуют также варианты LOOPE и LOOPNE, если вы хотите сделать дополнительную проверку на преждевременное завершение цикла.

Если вы хотите изменить cx во время цикла, обязательно поместите его в стек перед содержимым цикла и извлеките его после:

 мов сх, 3
моя петля:
    нажать сх
    ; Содержимое вашего цикла
    поп сх
    петля
 

1

Использовать регистр CX для подсчета петель

мов сх, 3
стартовый цикл:
   см сх, 0
   jz эндофлоп
   нажать сх
зацикленный:
   Вызов ClrScr
   поп сх
   дек сх
   джмп стартлуп
эндофлоп:
   ; Цикл завершен
   ; Делайте то, что когда-либо приходилось делать здесь
 

Это просто повторяется 3 раза, вызывая ClrScr , помещая регистр CX в стек, сравнивая с 0, переходя, если установлен ZeroFlag, затем переходя к эндофлоп . Обратите внимание, как содержимое CX помещается/извлекается из стека/извлекается из стека для поддержания потока в цикле.

3

 .модель маленькая
.стек 100ч
.код
Основной процесс
Мов сх, 30; //это число управляет циклом 30 означает, что цикл будет
;возбудить 30 раз
Ioopfront:
Мов ах, 1
Интервал 21ч
лицевая петля;
 

этот код займет 30 символов

1

Вам нужно использовать условные команды jmp. Это не тот синтаксис, который вы используете; выглядит как MASM, но с использованием GAS вот пример кода, который я написал для вычисления gcd:

 gcd_alg:
    subl %ecx, %eax /* а = а - с */
    cmpl $0, %eax /* если a == 0 */
    je gcd_done /* перейти в конец */
    cmpl %ecx, %eax /* если a < c */
    jl gcd_preswap /* поменять местами и начать заново */
    jmp gcd_alg /* продолжайте вычитание */
 

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

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

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

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