Команды LOOP, LOOPD, LOOPE, LOOPNE, LOOPNZ, LOOPZ
Команды LOOP, LOOPD, LOOPE, LOOPNE, LOOPNZ, LOOPZ используются для организации циклов. Счетчиком цикла служит регистр CX или ECX (в зависимости от разрядности). В командах циклов с условием (Z, NZ, E, NE) цикл выполняется пока заданное условие истинно.
Команды LOOP, LOOPD
Синтаксис: | LOOP op1 LOOPD op1 |
Операнды: | op1 — i8 |
Назначение: | Цикл |
Процессор: | 8086+ 80386+ |
Флаги: | Не изменяются |
Комментарий: | |
Ограничения: | Нет |
Примеры: |
mov cx,100 l1: add ax,[bx] inc bx loop l1 |
Синтаксис: | LOOPE op1 LOOPZ op1 |
Операнды: | op1 |
Назначение: | Цикл, пока равно (пока ноль) |
Процессор: | 8086+ |
Флаги: | Не изменяются |
Комментарий: | |
Ограничения: | Поскольку все модификации команды LOOP не изменяют регистр флагов, то флаг ZF должен изменяться внутри тела цикла. |
Примеры: |
mov cx,100 l1: add ax,[bx] dec bx loope l1 |
Синтаксис: | LOOPNE op1 LOOPNZ op1 |
Операнды: | op1 — i8 |
Назначение: | Цикл, пока не равно (пока не ноль) |
Процессор: | 8086+ |
Флаги: | Не изменяются |
Комментарий: | |
Ограничения: | Поскольку все модификации команды LOOP не изменяют регистр флагов, то флаг ZF должен изменяться внутри тела цикла. |
Примеры: |
mov cx,100 l1: add ax,[bx] dec bx loopne l1 |
. model tiny | |
.code | |
org 100h | |
pole equ 8 | |
begin: | |
mov ah,9h ;функция 9 Выдать строку на дисплей | |
lea dx,mess ;сообщение DS:DX | |
int 21h | |
mov ah,1 ;функция 1 Ввод с клавиатуры AL=вывод | |
int 21h ;в регистре AL — введеная перемнная | |
mov x,al ;x=AL | |
xor cx,cx ;CX=CX-CX | |
mov cl,al ;CL=AL | |
sub cl,30h ;CL=CL-30h в регистре CL число(преобразуем в число) | |
mov dl,cl ;DL=CL запомнили число(преобразованное число=DL) | |
mov ax,01h ;AX=1 подготовка к вычислению факториала | |
mov bl,01h ;BL=1 AX=1 Dl=Число Cl=Число X=символ числа | |
cloop: | |
mul bl ;AX=BL*AX | |
inc bl ;BL=BL+1 | |
loop cloop ;goto CLOOP when CX>0 | |
;цикл повторяется CX | |
;в CL находится число | |
;CL это младший регистр CX | |
;CX=00[CL] | |
;в регистре AX факториал | |
;преобразование числа в регистре AX в цепочку цифр | |
lea si,z+pole-1 ;SI=[8 dup(‘ ‘)]-1 | |
mov bx,0ah ;BX=Ah делить 2 байтовый | |
nach: | |
;xor dx,dx ;dx=0 очищаем регистр перед делением | |
div bx ;AX=DX:AX/BX DX=ОСТАТОК | |
;если dx=2030h ax=0040h DX:AX=20300040h | |
;DX:AX значение DWORD | |
;ПОЭТОМУ НАДО ОБНУЛЯТЬ DX перед делением | |
cmp ax,0 ;ax-0 | |
;проверка результата деления, если число | |
;однозначное то на выход | |
;если больше двух знаков продолжать деление | |
je kon ;if ax=0 goto kon; je=jz | |
add dl,30h ;dl=dl+30h dl становится символом | |
;dl это младший регистр dx | |
;dx=остаток от деления | |
;dl=остаток от деления в виде бита | |
mov [si],dl ;[si]=dl [si]=z+pole-1 | |
;пересылка символа dl В КОНЕЦ СТРОКИ | |
;si=адрес текущего конца строки | |
dec si ;si=si-1. | |
;Уменьшаем адрес конца строки на единицу | |
jmp nach ;GOTO nach | |
kon: | |
;В регистре SI колличество цифр в результата | |
add dl,30h ;код цифр старшего разряда | |
mov [si],dl ;переслали введеную цифру в поле ввода | |
;вывод на экран | |
mov ah,9 ;Функция 9 Выдать строку на дисплей | |
lea dx,ans ;Адрес начала строки. Выводится всё по порядку до знака $. | |
int 21h | |
ret | |
mess db «Введите число <=9:»,0dh,0ah,»$» | |
ans db 0dh,0ah, «Факториал « | |
x db ?,» равен « | |
z db Pole dup (‘ ‘) ;поле вывода | |
db 0dh,0ah ,’$’ | |
end begin |
linux — бесконечный цикл ассемблера NASM с регистром cx
спросил
Изменено 3 года, 5 месяцев назад
Просмотрено 272 раза
раздел . данные: msg1: db "Привет 10 раз!" msglen1: equ $-msg1 раздел .текст: глобальный _initial: глобальный _start: глобальный _end: _исходный: мов сх, 10 _Начало: дек сх мов ecx, msg1 мов edx, msglen1 мов акс,4 интервал 80ч cmp сх,0 jz _end jmp _start _конец мов акс,1 интервал 80ч
В приведенном выше коде должно было быть 10 раз выведено «Hello 10 times». Но это привело к бесконечному циклу, и я не мог понять, почему? я думаю, регистр cx не уменьшается или что-то еще?
- linux
- сборка
- x86
- nasm
- системные вызовы
1
У вас ряд проблем.
Точкой входа по умолчанию для программы Linux является
_start
. Ваша программа начинается с выполнения с метки_start
, а не сначального
, поэтому ваш счетчик циклов не инициализируется.Имена разделов не имеют
:
в имени, как и метки дляglobal
1Отсутствует параметр для системного вызова
SYS_Write
. 32-битные системные вызовы задокументированы в таблице:Необходимо установить EBX в дескриптор файла. СТАНДАРТНЫЙ ВХОД = 0, СТАНДАРТНЫЙ ВЫХОД = 1, СТАНДАРТНЫЙ ВЫВОД = 2. Вы хотите писать в консоль, поэтому вам нужно установить EBX в 1 перед вызовом
Int 80h
Вы затираете один из параметров ( ECX ) системного вызова
SYS_Write
. CX и ECX являются частью одного регистра. CX — это младшие 16 бит ECX . Изменение CX изменение ECX . Вам нужно использовать какой-то другой регистр для счетчика циклов. ESI , EDI и EBP в настоящее время не используются в вашем коде. Измените все вхождения CX на 32-битный регистр ESI .
Ваш код может выглядеть так:
раздел .data msg1: db "Привет 10 раз!", 10 ; Добавьте 10 в конец строки для перевода строки ; поэтому каждое сообщение печатается на отдельной строке msglen1 equ $-msg1 раздел .текст глобальный _initial глобальный _start глобальный _end _Начало: мов эси, 10 ; Инициализировать счетчик циклов _msgloop: дек эси ; Уменьшить счетчик циклов мов ebx, 1 ; Файловый дескриптор 1 = запись в стандартный вывод (STDOUT) mov ecx, msg1 ; Адрес сообщения для печати мов edx, msglen1 ; Длина сообщения для печати мов акс, 4 ; Системный вызов SYS_Write = 4 интервал 80ч cmp esi, 0 ; Счетчик циклов достиг 0? jz _end ; Если это так, то мы закончили jmp _msgloop ; в противном случае вернитесь и напечатайте сообщение снова _конец: дв акс,1 ; Системный вызов SYS_Exit интервал 80ч
Вы могли бы переписать свой цикл так:
section . data msg1: db "Привет 10 раз!", 10 ; Добавьте 10 в конец строки для перевода строки ; поэтому каждое сообщение печатается на отдельной строке msglen1 equ $-msg1 раздел .текст глобальный _start _Начало: мов эси, 10 ; Инициализировать счетчик циклов .msgloop: мов ebx, 1 ; Файловый дескриптор 1 = запись в стандартный вывод (STDOUT) mov ecx, msg1 ; Адрес сообщения для печати мов edx, msglen1 ; Длина сообщения для печати мов акс, 4 ; Системный вызов SYS_Write = 4 интервал 80ч дек эси ; Уменьшить счетчик циклов jnz .msgloop ; Если счетчик циклов не достиг нуля, снова напечатайте дв акс,1 ; Системный вызов SYS_Exit интервал 80ч
Сноски:
- 1 Вам не нужно делать
начальным
иконечным
глобальными, поскольку вы не связываетесь ни с какими другими объектными файлами. Этиглобальных
строки можно удалить.
0
Вы пытаетесь использовать регистр cx
для подсчета циклов, в то время как вам нужно использовать ecx
в качестве параметра для вашего вывода. Поскольку cx
— это младшие 16 бит из ecx
, вы сбиваете количество циклов.
Вам нужно либо использовать какой-то другой регистр (который не используется во время системного вызова) для счетчика циклов, либо сохранить счетчик в локальной переменной в стеке.
3
Зарегистрируйтесь или войдите в систему
Зарегистрируйтесь с помощью Google
Зарегистрироваться через Facebook
Зарегистрируйтесь, используя электронную почту и пароль
Опубликовать как гость
Электронная почта
Обязательно, но не отображается
Опубликовать как гость
Электронная почта
Требуется, но не отображается
Если, циклы и инструкция DBRA
Если, циклы и инструкция DBRAЕСЛИ
Простой IF
а)ГЛЛ б) 68К x:=x+1 ADDQ. W #1,X ЕСЛИ A=7, ТО CMPI.W #7,A Б:=3; БНЭ СЛЕДУЮЩИЙ С:=4; МОВЭК #3,Б КОНЕЦ, ЕСЛИ ДВИЖЕНИЕ #4,C х:=Х+2; СЛЕДУЮЩИЙ: ДОБАВИТЬQ.W #2,X б) на ГВУ ЕСЛИ Х=2, ТО КОД:=2; КОД:=1; изменить на ЕСЛИ X=2 ТО ДРУГОЙ КОД:=1; КОД:=2; КОНЕЦ ЕСЛИ КОНЕЦ ЕСЛИ Это упрощает код. в) ОБЩИЙ КОНТУР ХЛЛ 68К ЕСЛИ (I>GT> 0), ТО CMPI.W #0,I КОД(A) ДРУГОЕ ДРУГОЙ КОД(А) КОД(B) БЮСТГАЛЬТЕР NEXT КОНЕЦ, ЕСЛИ ЕЩЕ: КОД(В) СЛЕДУЮЩИЙ СЛЕДУЮЩИЙ: г) Я никогда не хочу видеть: ВЕЩИ БЮСТГАЛЬТЕР NEXT заменить на NEXT: СЛЕДУЮЩИЙ: Вы хотите уменьшить количество веток, не ставя лишние.
Петли
В мире существует два типа циклов — циклы ПОВТОР и циклы ПОКА. Повторяющиеся циклы всегда выполняются один раз, что является редкостью для циклов. Ты должен всегда проверяйте себя, чтобы увидеть, безопасно ли использовать повторяющийся цикл. Какое-то время цикл может быть выполнен ноль раз и всегда безопасен и очень распространен в программирование.ПОВТОР Циклы
а) Стандартный повторяющийся циклПОВТОР: Чтение с часовым JSR DECIN подходит для повторной петли. ЦМП.В '-1',d0 ПОВТОР БНЭ б) Общий цикл Петля: CODE(A) Так как CODE(A) всегда выполнял это CMPI.W #0,I — повторяющийся цикл. БЛТ СЛЕДУЮЩИЙ КОД(В) БЮСТГАЛЬТЕРПока цикл
а) ОК ВЕРСИЯ б) Лучшая версия ПРЕДЫДУЩИЙ КОД ПРЕДЫДУЩИЙ КОД Петля: БЮСТГАЛЬТЕР CHECK CMP --- ЦИКЛ: BEQ ENDLOOP CODE(C) ПРОВЕРКА КОД(С): БЮСТГАЛЬТЕР LOOP CMP --- КОНЕЦ ПЕТЛИ: BNE LOOP СЛЕДУЮЩИЙ КОД СЛЕДУЮЩИЙ КОД
Некоторые общие сведения о циклах
- Большинство циклов проходят «высшее тестирование» (например, циклы WHILE в Pascal или С). Это верно в большинстве случаев.
- Условие цикла должно быть проверено перед выполнением тела петли в первый раз, а не после. Это позволяет телу цикла выполняться 0 раз, если это уместно. В некоторых случаях цикл повторения до тех пор, пока цикл выбора.
- В языке ассемблера условный переход будет проверять условие цикла.
- Это не означает, что условная ветвь должна появляться в хотя начало цикла на ассемблере. Обычно это должно быть размещается внизу, и должна быть безусловная ветвь к нижней части цикла, чтобы начать. Иногда, если петля очень долго эта процедура не соблюдается.
- Какая разница?
- В первом внутри петли две ветви.
- Во втором внутри цикла всего одна ветвь. Другой один находится вне цикла, где он выполняется только один раз, а не на каждой итерации.
- Особенно в современных конвейерных процессорах очень важно чтобы свести к минимуму количество ветвей и упростить структуру петель.
Инструкция DBRA 68000k
- Синтаксис инструкции dbra (уменьшение и переход) следующий:
дБА Di,Addr
- Эта инструкция уменьшает Di. w и переходит к Addr, если результат не -1.
- Эквивалент следующих 3 инструкций
sub.w #1, Di cmp.w #-1, Di bne Адрес
- Это очень типичная инструкция CISC. В программе для RISC-машины вы бы использовали три отдельные инструкции.
- Его цель — упростить «циклы, контролируемые счетом». (Который то есть, если вы заранее точно знаете, сколько раз должен выполняться цикл, воспользуйтесь этой инструкцией.)
- Чтобы выполнить цикл ровно N раз, используйте этот код:
move.w N,D2 ;Поместите N в регистр D, если его еще нет bra EndL ;Начните с инструкции dbra внизу Петля: ... ... EndL: dbra D2, Loop
- Это «правильный» тип петли, только с одной ветвью в оно, внизу. Цикл вводится путем перехода к «dbra» внизу.
- Это также объясняет, почему инструкция dbra проверяет -1, а не 0.
- Если вы хотите использовать значения в регистре, они Н-1,Н-2,…1,0.
- Вот два неверных способа сделать это:
ход.w N,D1 Петля: ... ... петля dbra
- Приведенный выше цикл выполняется N+1 раз, а не N раз.
- Итак, как насчет того, чтобы сначала уменьшить N, чтобы решить эту проблему?
ход.w N,D1 sub.w #1,D1 Петля: ... ... петля dbra
- Приведенный выше цикл содержит огромную ошибку . Если N=0, цикл не будет быть выполнено 0 раз (что правильно). Вместо этого будет выполнено 65536 раз!! (В первый раз «dbra» будет уменьшаться на -1 и получите -2. Это не -1, поэтому он будет продолжаться. Значение в D1 будет нужно считать до -32768, переполниться до +32767 и продолжить вниз, пока он, наконец, не достигнет -1 трудным путем.