Форум программистов, компьютерный форум, киберфорум
Assembler, MASM, TASM
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.63/2256: Рейтинг темы: голосов - 2256, средняя оценка - 4.63
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16381 / 7693 / 1080
Регистрация: 11.11.2010
Сообщений: 13,779
15.11.2013, 04:52  [ТС]
ГЛАВА 13
ПЕРЕХОДЫ И ЦИКЛЫ
(часть 4/5)

Команды управления циклом
С помощью условных операторов и логических и арифметических команд теоретически можно организовать сколь угодно сложный алгоритм. Однако на практике при необходимости можно организовать обработку тысяч элементов массива (например, присвоить каждому элементу начальное значение), вручную набирать тысячу операторов присваивания крайне тяжело.
Поэтому в языках программирования имеются средства для организации повторных вычислений, называемые операторами цикла. С помощью команд перехода на языке ассемблера можно реализовать любые разветвления и циклы. Например, за следующими операторами на языках Паскаль, BASIC и Си.
Рис. 13.3.1
Рисунок |Паскаль| BASIC |Си, Си++
(a)|
if X>0 then S1
else S2;|IF X>0 THEN S1
ELSE S2
ENDIF|
if (X>0) S1
else S2;
(b) |while X>0 do S;| DO WHILE X>0 S
LOOP
|while (X>0) S;
(c) |repeat S until X<0|| do S until (X<0);
Где S, S1 и S2 какие-то операторы, а X – знаковая переменная, скрываются следующие ассемблерные команды
 CMP X,0 BEG: CMP X,0 BEG: S
 JLE L2 JLE FIN CMP X,0
 S1 S JG BEG
 JMP FIN JMP BEG  
L2: S2 FIN:   
FIN:     
 (a) (b) (c)
Если Вам необходимо по каким-то причинам скрыть условный переход, то помните, что конструкция b*x + NOT(b)*y эквивалентна условному выражению if (b==TRUE) then x else y. Вместо умножения и сложения можно использовать логическое умножение (AND) и сложение (OR).
Циклы
Допустим в вашей программе необходимо решить уравнение (A+B)*C=D. Хорошо, если известно, что A=1, B=5, C=7. Посчитать D – нет никаких проблем. А если эти числа получаются после других восьмиэтажных уравнений? Подойдем к решению задачи творчески (для простоты считаем что размерность A, B и C – байт):
Assembler
1
2
3
4
mov al,A
add al,B ;al=A+B
mul C ;ax=(A+B)*C
mov D,ax ;D=(A+B)*C
Допустим, надо пересчитать этот фрагмент 10 раз с разными значениями A, B и C. Как? Десять раз переписать? Нет – нам нужно каким-то образом заставить компьютер выполнять повторяющиеся вычисления. Вообще говоря, именно эта потребность является одной из главных причин использования машин для выполнения арифметических вычислений. Цикл – это набор операций, который регулярно повторяется в одной и той же последовательности.
Существует три основных типа цикла: циклы с условием в начале, циклы с условием в конце и циклы с условием в середине. Кроме того, существуют комбинированные циклы, которые имеют несколько условий в разных местах, например в начале и конце одновременно. Циклы с условием в начале – это циклы с предусловием, while-циклы. Цикл с условием в конце – это цикл с пост-условием, do /while или repeat / until – циклы. Блок статического предсказания направлений ветвлений процессоров Pentium оптимизирован под переходы, направленные в область младших адресов (назад). Поэтому циклы с постусловием должны выполняться быстрее циклов с предусловием. Циклы со счетчиком представляют собой синтаксическую разновидность циклов с предусловием. Замена цикла с приращением на цикл с убыванием счетчика делает ненужной команду CMP. Цикл с условием в середине называется break-циклы.
Существует целых два основных варианта оператора повторения, причем каждый для своих целей:
1) повторение, управляемое вычисляемым условием, и
2) повторение, управляемое явным счетчиком или списком значений.
В первую группу входят два оператора:
while b do S
и
repeat S until not b
Среди циклов на практике часто встречаются циклы с заранее известным числом повторений (for-циклы), поэтому в систему команд многих ЭВМ обычно включают дополнительные команды, упрощающие реализацию подобных циклов.
Команда LOOP (LOOPW, LOOPD)
Синтаксис команды:
LOOP <TARGET>
Пусть некоторую группу команд (тело цикла) надо повторить N раз (N>0). Когда-то на языке ассемблера этот цикл реализовали по такой схеме:
Assembler
1
2
3
4
5
MOV CX,N ;CX – счетчик цикла
LOOP: . . . ;тело цикла
DEC CX ;CX-1->CX
CMP CX,0 ;CX=0?
JNE LOOP ;если CX не равен 0 идти к метке LOOP
Создатели языка ассемблера обратили внимание, что в конце таких циклов всегда применяется одна и та же тройка команд (dec cx/cmp cx,0/jne loop). Учитывая это, в систему команд была введена специальная команда LOOP («петля»), которая объединяет в себе действие всех этих команд.
Действие команды LOOP немного сходно с циклом FOR-NEXT в Бейсике или соответствует паре инструкций REAPET-UNTIL, где инструкция REAPET означает начало блока инструкций цикла. Как и для цикла FORNEXT в Бэйсике, мы должны сообщить LOOP о том, сколько раз запускать цикл. Мы делаем это, помещая счетчик повторений в регистр RCX/ECX/CX. В каждом цикле микропроцессор вычитает единицу из RCX/ECX/CX, и когда RCX/ECX/CX становится равным нулю, LOOP заканчивает цикл. С помощью команды LOOP наш цикл запишется следующим образом:
Assembler
1
2
3
4
5
6
7
8
9
 mov cx,N;cx – счетчик цикла
;-- тело цикла-------------
L: mov al,A
add al,B;al=A+B
mul C;ax=(A+B)*C
mov D,ax;D=(A+B)*C
. . .
;----------------------------
LOOP L
Получилось короче, и работает команда LOOP быстрее трех команд.
Необходимо учитывать ряд особенностей команды LOOP:
  1. команда LOOP требует, чтобы в качестве счетчика цикла обязательно использовался регистр CX/ECX;
  2. начальное значение регистру CX/ECX должно быть присвоено до начала цикла;
  3. так как команда LOOP ставится в конце цикла, тело цикла хотя бы раз должно выполниться;
  4. если по какой-либо причине перед началом цикла регистр CX/ECX равен 0, то тело цикла все равно будет выполнено, из содержимого регистра CX/ECX будет отнята 1 и уже после этого регистр CX/ECX будет проверен на равенство 0. Что, по вашему, окажется в регистре CX/ECX? Правильно, содержимое регистра CX/ECX равно –1 (0FFFFh/0FFFFFFFFh) и цикл, вместо того чтобы остановиться, будет запущен еще 65535 раз, если мы используем регистр CX, или 4 миллиона раз (!), если использовали регистр ECX, – это один из источников ошибок.
    Поэтому, если возможен вариант, что число повторений может быть и нулевым, то при CX /ECX =0 надо делать обход цикла:
    Assembler
    1
    2
    3
    4
    5
    
    MOV ECX,N ;N>=0
    JECXZ L1 ;если ECX=0 перейти на L1
    L: … ;
    LOOP L
    L1: . . .
    Именно ради осуществления таких обходов и были введены команды условного перехода JCXZ/JECXZ;
  5. команда LOOP реализует только короткий переход, поэтому расстояние от LOOP до начала цикла не должно превышать 127 байтов вниз или 128 байтов вверх.
Процедуры возвращают какое-либо значение в регистре EAX, которые на равенство/неравенство нулю проверяют последовательностью команд:
Команда Код Длина (байт)
Самый неоптимальный вариант
cmp eax,0 83F800 или 3D00000000 3 байта или 5 байтов
jz TARGET 74XX или 0F84XXXXXXXX 2 короткий или 6 длинный
Итого: от 5 до 11 байт
Более короткий вариант – TEST ничего не пишет в регистр (OR и AND пишут),
поэтому эта инструкция лучше спаривается, значит и код будет более быстрым
test eax,eax/85C0 или2 байта
and eax,eax/23C0 или 
or eax,eax0BC0 
jz TARGET 74XX или 0F84XXXXXXXX 2 или 6 байтов
Итого: от 4 до 8 байт
Самый короткий вариант – Если вам не важно содержимое регистра ECX
или неважно, где будет находится содержимое EAX
xchg eax,ecx 91 1 байт
jecxz TARGET E3XX 2 байта
Итого: 3 байта
Иногда по смыслу требуется команда jecxnz TARGET, которую можно сэмулировать последовательностью inc ecx/loop TARGET команда inc ecx необходима потому, что loop вычтет 1 из содержимого ecx и если там окажется 0, перехода к TARGET не произойдет.
Если вам необходим бесконечный цикл – воспользуйтесь командой JMP, а если не знаете сколько раз придется запускать цикл, используйте команды условного перехода.
Если программа транслируется в 32-разрядном режиме, команда LOOP автоматически использует регистр ECX в качестве счетчика циклов.
Команда LOOPW использует регистр CX в качестве счетчика циклов в 32-разрядном режиме. Фактически это та же самая LOOP, предваряемая префиксом 67h. Если программа транслируется в 16-разрядном режиме, команда LOOPD использует регистр ECX в качестве счетчика циклов. Фактически это та же LOOP, предваряемая префиксом 67h.
Команды LOOPE/LOOPZ и LOOPNE/LOOPNZ
(Цикл по счетчику пока равно/ = “LOOP if Equal”
Цикл по счетчику пока ноль = “LOOP if Zero”
Цикл по счетчику пока не равно = “LOOP if Not Equal”
Цикл по счетчику пока не ноль = “LOOP if Not Zero”)
Синтаксис команд:
LOOPE <TARGET>
LOOPZ <TARGET>
LOOPNE <TARGET>
LOONZ <TARGET>
Эти команды похожи на команду LOOP, то есть заставляют цикл повториться столько раз, сколько указано в регистре CX/ECX, однако они допускают и досрочный выход из цикла.
Команда LOOPE используется для организации цикла с известным числом повторений, из которого возможен досрочный выход. До начала цикла в регистр CX/ECX записывается число повторений. Команда LOOPE ставится в конец цикла, а перед ней помещается команда, меняющая флаг ZF – обычно это команда сравнения CMP. Команда LOOPE заставляет цикл повторяться CX/ECX раз, но только если команда CMP фиксирует неравенство сравниваемых величин – происходит выход из цикла. По какой именно причине произошел выход из цикла (по ZF=0 или CX/ECX=0), надо проверять после цикла. Чаще всего команда LOOPE используется для поиска первого элемента некоторой последовательности, отличного от заданной величины. Команда LOOPZ – синоним команды LOOPE.
Команда LOOPNE аналогична команде LOOPE, но выход из цикла осуществляется при ZF=1, если предыдущая команде LOOPNE команда CMP зафиксировала равенство или по CX/ECX=0. Команда LOOPNE обычно используется для поиска в некоторой последовательности первого элемента, имеющего заданную величину. Команда LOOPNZ – синоним команды LOOPNE.
Пусть ASCII_STR является переменной, ассоциируемой с началом цепочки из L символов, и в цепочке необходимо найти символ пробела. Цикл заканчивается, когда обнаружен пробел или когда просмотрена вся цепочка. Если пробел не найден, осуществляется переход к метке NOT_FOUND:
Assembler
1
2
3
4
5
6
 MOV ECX,L ;передать размер массива в ECX
MOV EDI,offset ASCII_STR ;инициализировать индекс
MOV AL,’ ’ ;загрузить в AL код пробела
NEXT: SCASB ;проверить на символ пробела
LOOPNE NEXT ; зациклить, если не пробел и ECX не равен 0
JNZ NOT_FOUND ; перейти к NOT_FOUND,если пробел не найден
0
Закрытая тема Создать тему
Новые блоги и статьи
GitHub Copilot в JetBrains: модель по умолчанию, Fix и контроль MCP
and_y87 12.10.2026
10 октября 2026 года GitHub обновил Copilot для JetBrains: добавил настройки стартовой модели, исправления из диагностики и управление автозапуском MCP. Поддержка IDE 2025. 1 прекращена — требуется. . .
День 5 из 28: Codex подсказывает следующий запрос
and_y87 12.10.2026
9 октября 2026 года OpenAI запустила бету Composer predictions — подсказок следующего сообщения в текущем диалоге Codex. Это помощь с формулировкой запроса, а не автоматическое продолжение задачи. . . .
Запустил конкурс "тем и промптов для текстовых квестов созданных почти чисто ИИ"
Adler 06.10.2026
Всем привет! За последние три-четыре дня я создал более 16 текстовых квестовых игр используя преимущественно по одному запросу к ИИ на игру. Мне так понравилось смотреть все ветки/ сцены во всех. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru