- •Команды языка ассемблера
- •3.2.1 Программные сегменты. Директива assume
- •6. Арифметические команды ……………………………………………….
- •Архитектура пк.
- •Регистры.
- •Регистры общего назначения.
- •1.1.2. Сегментные регистры
- •1.1.3 Регистр флагов
- •Организация памяти.
- •1.3. Представление данных
- •1.3.1 Типы данных
- •1.3.2 Представление символов и строк
- •Операторы программы на ассемблере
- •2.1 Команды языка ассемблера
- •2.2. Режимы адресации и форматы машинных команд
- •3. Псевдооператоры
- •3.1 Директивы определения данных
- •3.2 Структура программы на ассемблере
- •3.2.1 Программные сегменты. Директива assume
- •Начальная загрузка сегментных регистров
- •Упрощенная директива сегментации
- •4. Ассемблирование и компоновка программы.
- •5. Команды пересылки данных
- •5.1 Команды общего назначения
- •5.2 Команды работы со стеком
- •4.3 Команды ввода-вывода
- •5.4 Команды пересылки адреса
- •5.5 Команды пересылки флагов
- •6. Арифметические команды
- •Арифметические операции над целыми двоичными числами.
- •6.1.1 Сложение и вычитание.
- •6.1.2 Команды приращения и уменьшения приемника на единицу
- •6.2 Умножение и деление.
- •6.3 Изменение знака.
- •7. Логические операции
- •8. Сдвиги и циклические сдвиги
- •9. Строковые операции
- •10. Логика и организация программ
- •10.1 Безусловные переходы
- •10.2 Условные переходы
- •10.3 Циклы
- •10.4 Процедуры в языке ассемблера
- •10.5 Прерывания int
- •10.6 Системное программное обеспечение
- •10.6.1.1 Чтение клавиатуры.
- •10.6.1.2 Вывод символов на экран.
- •10.6.1.3 Завершение программ.
- •10.6.2.1 Выбор режимов дисплея.
- •11. Дисковая память
- •11.1 Оглавление диска (каталог)
- •11.2 Таблица распределения файлов
- •11.3 Операции ввода-вывода на диск
- •11.3.1 Запись файла на диск
- •11.3.1.1 Данные в формате asciiz
- •11.3.1.2 Файловый номер
- •11.3.1.3 Создание дискового файла
- •Чтение дискового файла
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 нет.
Конечно, это небольшие отличия, но они важны для понимания набора инструкций при программировании на ассемблере.