Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка1.doc
Скачиваний:
8
Добавлен:
05.12.2018
Размер:
407.55 Кб
Скачать

10.3 Циклы

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

Циклы используются для обработки массивов, проверки статуса портов ввода-вывода, пока не будет достигнуто определенное состояние, очистки блоков памяти, чтения строк с клавиатуры, вывода строк на экран и т.п. Циклы - это основа для обработки чего-либо, что требует повторяющихся действий. Поэтому они часто используются, и Ассемблер предоставляет несколько специальных инструкций для циклов: LOOP, LOOPE, LOOPNE и JCXZ. В начале посмотрим LOOP. Предположим, что Вы хотите распечатать 17 символов строки TestString. Вы можете сделать это

...

.DATA

TestString DB 'this is a test...'

...

.CODE

...

MOV CX,17

MOV bx,OFFSET TestString

PrINTStringLOOP:

MOV dl,[bx] ;взять следующий символ

INC bx ;указать следующий символ

MOV AH,2 ;# функции DOS для вывода

INT 21h ;вызов DOS для печати символа

DEC CX ;уменьшить счетчик

JNZ PrINTStringLOOP ;делать следующий символ

...

Однако есть лучший способ. Мы отмечали, как Вы помните, что CX особенно полезен в циклах. Здесь

LOOP PrINTStringLOOP

делает то же, что и

DEC CX

JNZ PrINTStringLOOP

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

Если необходим цикл с более сложным условием окончания, чем простое уменьшение счетчика то можно воспользоваться LOOPE и LOOPNE.

LOOPE делает то же самое, что и LOOP за исключением того, что LOOPE будет заканчивать цикл либо, если счетчик в CX достиг 0, либо если флаг нуля установлен в 1. (Вспомним, что флаг нуля устанавливается в 1, если последний арифметический результат был 0, или если 2 операнда последнего сравнения были неравны.) Подобно, LOOPNE будет заканчивать цикл, если счетчик в CX достиг 0, либо если флаг нуля установлен в 0.

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

...

.DATA

KeyBuffer DB 128 DUP (?)

...

.CODE

...

MOV CX,128

MOV bx,OFFSET KeyBuffer

KeyLOOP:

MOV AH,1 ; функции DOS для ввода

INT 21h ;вызов DOS для чтения символа

MOV [bx],AL ;сохранить символ

INC bx ;указать на следующий символ

CMP AL,0dh ;нажат ВВОД?

LOOPne KeyLOOP ;если нет, взять следующий символ,

;если не считано 128 символов

...

LOOPE так же известна как LOOPZ, и LOOPNE так же известна, как LOOPNZ (аналогично JE/JZ).

С циклами связана еще одна инструкция - JCXZ. JCXZ осуществляет переход, только если CX=0; это полезный способ, проверять CX в начала цикла. Например, следующий код в котором BX указывает на блок байт, устанавливаемых в 0, а CX содержит длину блока, использует JCXZ для пропуска цикла, если CX - 0:

...

jCXz SkipLOOP ;если CX=0, ничего не делать

CLearLOOP:

MOV BYTE PTR [SI],0 ;установить следующий байт в 0

INC SI ;указать на следующий байт

LOOP CLearLOOP ;обнулить следующий символ, если он есть

SkipLOOP:

...

Почему нужно пропустить цикл, если CX - 0? Если Вы выполните LOOP с CX=0, то CX уменьшится до 0FFFFh и инструкция LOOP выполнит переход на метку назначения. Тогда цикл выполнится более 65,535 раз. Когда Вы устанавливали CX в 0, Вы подразумевали, что нет байтов для обнуления, а получилось 65,536 байт. JCXZ позволяет Вам проверить этот случай быстро и эффективно.

Несколько интересных примечаний к инструкциям цикла. Во-первых, запомните, что инструкция цикла как и условный переход, может выполнять переход на метку только в диапазоне 128 байт, до или после инструкции цикла. Циклы, больше 128 байт, требуют использования техники "Условный переход перед безусловным переходом", описанный в разделе "Условные переходы". Во-вторых, важно представлять, что ни одна инструкция цикла не действует на флаги. Это означает, что LOOP LOOPTop не точно такая же, как

DEC CX

JNZ LOOPTop

поскольку DEC изменяет флаги переполнения, знака, нуля, дополнительного переноса и четности в то время, как LOOP не изменяет флаги вообще. По той же причине инструкция DEC не точно такая же, как

SUB CX,1

JNZ LOOPTop

поскольку SUB изменяет флаг переноса, а инструкция DEC нет.

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