div.main {margin-left: 20pt; margin-right: 20pt}
Создание
циклов.
Что такое
цикл? Допустим, нам нужно
выполнить некоторый код
программы несколько раз.
Возьмем, к примеру, вывод
строки функцией 09h прерывания
21h:
Пример
1.
mov ah,9
mov dx,offset Str
int 21h
mov ah,9
mov dx,offset Str
int 21h
mov ah,9
mov dx,offset Str
int 21h
Этот участок
кода выведет 3 раза на экран
некую строку Str. Код получается
громоздким, неудобно читать.
Размер программы
разрастается... Для выполнения
подобных примеров
используется оператор loop
(вспоминаем, как мы оформляем
новые операторы):
Команда
|
Перевод
(с англ.)
|
Назначение
|
Процессор
|
LOOP метка |
loop - петля |
Организация циклов |
8086 |
Количество
повторов задается в регистре CX
(счетчик). Вот как можно
использовать этот оператор на
практике (изменим Пример
1):
Пример
2:
(1) mov cx,3
(2) Label_1:
(3) mov ah,9
(4) mov dx,offset Str
(5) int 21h
(6) loop Label_1
(7) ...
В строке (1)
загружаем в CX количество
повторов (отсчет будет идти от 3
до 0). В строке (2) создаем метку
(Label - метка). Далее (строки (3)-(5))
выводим сообщение. И в строке (6)
оператор loop уменьшает на
единицу CX и, если он не равен
нулю, переходит на метку Label_1
(строка (2)). Итого строка будет
выведена на экран три раза.
Когда программа перейдет на
строку (7), регистр CX будет равен
нулю. В результате код
программы уменьшается почти в
три раза по сравнению с
Примером 1.
Удобно? Без
вопросов!
Тренироваться
будем на практике, а теперь
следующий оператор:
Команда
|
Перевод
(с англ.)
|
Назначение
|
Процессор
|
JMP метка |
jump - прыжок |
Безусловный переход |
8086 |
Команда jmp просто
переходит на указанную метку в
программе:
(1) mov ah,9
(2) mov dx,offset Str
(3) int 21h
(4) jmp Label_2
(5)
(6) add cx,12
(7) dec cx
(8) Label_2:
(9) int 20h
В результате строки (5) - (7)
работать не будут. Программа
выведет сообщение на экран, а
затем jmp перейдет к строке (8),
после чего программа
завершится.
Команда
|
Перевод
(с англ.)
|
Назначение
|
Процессор
|
DEC приемник |
decrement - декремент |
Уменьшение на 1 |
8086 |
Оператор dec уменьшает значение приемника на 1:
mov ah,12 ---> AH=12
dec ah ---> AH=11
С помощь данного оператора
можно также создавать циклы,
которые будут работать быстрее
оператора loop. Следующий пример
будет работать также, как Пример 2,
только чуть-чуть быстрее:
Пример 3:
(1) mov cx,3
(2) Label_1:
(3) mov ah,9
(4) mov dx,offset Str
(5) int 21h
(6) dec cx
(7) jnz Label_1
Не обращайте
внимание на строку (7). Мы ее
рассмотрим позже. Я привел этот
пример для того, чтобы
показать, что один и тот же
прием в Ассемблере можно
выполнить разными операторами.
И чем лучше программист
владеет ими, тем компактнее и
быстрее программа будет
работать. Поэтому и получается,
что разные программисты пишут
на одном языке, но скорость и
объем программы разные. В
процессе обучения, я буду также
учить вас оптимизировать
программы.
Программка для
практики.
Усовершенствуем
программу из предыдущей рассылки,
которая выводила в верхний
левый угол "рожицу" прямым
отображением в видеобуфер.
(1) CSEG segment
(2) org 100h
(3) Begin:
(4) mov ax,0B800h
(5) mov es,ax
(6) mov di,0
(7) mov al,1
(8) mov ah,31
(9) mov cx,2000
(10)
(11) Next_face:
(12) mov es:[di],ax
(13) add di,2
(14) loop Next_face
(15)
(16) mov ah,10h
(17) int 16h
(18) int 20h
(19) CSEG ends
(20) end Begin
Уфф! Длинная
получилась. Прежде чем читать
описание программы, попробуйте
сами разобраться, что в итоге
получится. Поверьте, это
принесет вам пользу. Все ведь
очень просто!
Теперь
описание программы.
Строки с (1) по
(10) и с (15) по (20) вы уже знаете.
Объясню только новое.
Строка (11) - это
метка, как бы "голова"
нашего цикла. Строка (14) -
"хвост" цикла. Все, что
находится в пределах строк (10) -
(14), является циклом. Сам цикл
будет повторяться 2000 раз, для
чего мы и заносим в CX число 2000
(строка (8)).
В строке (12)
записываем в видеобуфер (0B800:DI)
число в AX (это символ (строка (7)
и атрибут (строка (8)). Итак,
первый символ занесли. Что
делаем дальше?
Дальше
увеличиваем регистр DI на 2 для
того чтобы перейти к адресу
следующего символа. Почему на 2?
Дело в том, что в видеобуфере
один символ занимает 2 байта:
сам символ и его атрибут. Т.к.
символ у нас в AL, а атрибут в AH, и
мы загрузили уже эти два байта
в строке (12), то и увеличиваем DI
(смещение) на 2.
DI теперь
указывает на адрес для
следующего символа. Осталось
уменьшить счетчик (CX) на 1 и
повторить. Что мы, собственно, и
делаем в строке (14).
Все! Обратите
внимание на скорость вывода
символов при запуске
программы.
Еще раз напоминаю:
пожалуйста, печатайте все
программы сами! Так вы
быстрее освоите Ассемблер!
Автор: © oleg Источник:
http://asm.kalashnikoff.ru/
|