Інформаційний портал msevm
Цикли, що дозволяють виконати деяку ділянку програми багаторазово, в будь-якій мові є однією з найбільш уживаних конструкцій. В системі команд МП 86 цикли реалізуються, головним чином, за допомогою команди loop (петля), хоча є й інші способи організації циклів. У всіх випадках число кроків в циклі визначається вмістом регістра СХ, тому максимальне число кроків складає 64 К.
Розглянемо простий приклад організації циклу. Нехай в програмі зарезервовано місце для масиву розміром 10000 слів, і цей масив треба заповнити натуральним рядом чисел від 0 до 9999. Ці числа, що заповнюють послідовні елементи масиву, іноді називають числами-заповнювачами. Відповідний фрагмент програми буде виглядати наступним чином:
; У сегменті даних
array dw 10000 dup (0)
; В програмному сегменті
mov AX, 0; Початкове значення заповнювача
mov CX, 10000; Лічильник циклу
fill: mov [BX] [SI], AX; Заповнювач пошлемо в масив
inc AX; Інкремент заповнювач
add SI, 2; модифікація індексу
loop fill; команда циклу
; Організація довгого циклу
mov CX, 10000; Лічильник циклу
fill:; Мітка початку циклу
; Тіло довгого циклу
dec CX; Декремент лічильника циклу
cmp CX, 0; Відпрацьовано задане число кроків?
je finish; Так, на мітку продовження програми
jmp fill; Ні, на початок циклу
finish:; продовження програми
У цьому, вельми типовому фрагменті ми "вручну" зменшуємо вміст лічильника циклу і порівнюємо отримане значення з 0. Якщо СХ = О, це означає, що в циклі виконано задане число кроків, і командою умовного переходу je здійснюється перехід на продовження програми (мітка finish ). Якщо СХ ще не дорівнює нулю, командою безумовного переходу jmp здійснюється повернення в початок циклу. Як було показано в гл. 2, команда jmp дозволяє перейти в будь-яку точку сегмента, і обмеження на розмір тіла циклу знімається.
При необхідності організувати вкладені цикли, для збереження лічильника зовнішнього циклу на час виконання внутрішнього зручно скористатися стеком. У наступному фрагменті організовується тимчасова затримка тривалістю декілька секунд (конкретна величина затримки залежить від швидкості роботи процесора).
outer: push CX; Збережемо його в стеці
mov CX, 0; Лічильник внутрішнього циклу
inner: loop inner; loop внутрішнього циклу
pop CX; Відновимо зовнішній лічильник
loop outher; loop зовнішнього циклу
cmp AX, BX; Порівняння двох регістрів
je equal; Перехід, якщо AX = BX
cmp SI, mem; Порівняння регістра і осередки пам'яті
jne notequ; Перехід, якщо SI<>mem
int 21h; Виклик DOS
jc syserr; Перехід, якщо була помилка
or BX, BX; Аналіз BX
jz zero; Перехід, якщо BX = 0
inpt: in AL, DX; Введення даного з пристрою
test AL, 80h; Аналіз біта 7 в даному
je inpt; Введення до тих пір. Бувай
; Біт 7 = 0 (очікування установки біта 7)
test AX, 7; Аналіз бітів 0,1,2 в AX
jne found; Перехід, якщо хоча б 1 біт
; З них встановлено
test DI, OFh; Аналіз бітів 0. 3 в DI
jz reset; Перехід, якщо всі вони скинуті
mov AX, data; Ініціалізація
move DS, AX; Регістр DS
; Виведемо службове повідомлення
mov AH, 09h; Функція виведення
; Поставимо запит до DOS на введення рядка
mov AH, 3Fh; Функція введення
mov BX, 0; Дескриптор клавіатури
mov CX, 80; Введення максимум 80 байт
mov actlen, AX; Фактично введено
; Перетворимо рядкові українські літери на прописні
mov CX, actlen; Довжина введеного рядка
mov SI, 0; Покажчик в буфері
filter: mov AL, buf [SI]; Візьмемо символ
cmp AL, 'a'; Менше 'a'?
jb noletter; Так, не перетворювати
cmp AL, 'я'; Більше 'я'?
ja noletter; Так, не перетворювати
cmp AL, 'п'; Більше 'п'?
ja more; Так, на подальшу перевірку
sub AL, 20h; 'a' .. 'п'. Перетворимо в прописну
jmp store; На збереження в буфері
more: cmp AL, 'p'; Менше 'p1' (псевдографіка)?
jb noletter;> 'п',<'p'. Не изменять
sub AL, 50h; 'p'. 'Я'. Перетворимо в прописну
store: mov buf [SI], AL; Відправимо назад в buf
noletter: inc SI; пройти показник
loop filter; Цикл по всім символам
; Виведемо результат перетворення на екран для контролю
mov AX, 40h; Функція виведення
mov BX, 1; Дескриптор екрану
mov CX, actlen; Довжина повідомлення
mov AH, 01; Зупинимо програму
int 21h; в очікуванні натиснення клавіші
msg db "Введіть! $"
buf db 80 dup ( ''); Буфер введення
stk segment stack
mul - команда множення чисел без знака;
imul - команда множення чисел зі знаком;
div - команда ділення чисел без знака;
idiv - команда ділення чисел зі знаком.
Пояснимо відмінності цих команд на формальних прикладах.
; Множення позитивних чисел із знаком
mov AL, 5; Перший співмножник дорівнює 5
mov BL, 7; Другий співмножник дорівнює 7
mul BL; AX = 0023h = 35
mov AL, 5; Перший співмножник дорівнює 5
mov BL, 7; Другий співмножник дорівнює 7
imul BL; AX = 0023h = 35
Обидві команди, mul і imul, дають в даному випадку однаковий результат, так як позитивні числа зі знаком збігаються з числами без знака. Не так стоїть справа при множенні негативних чисел.
; Множення негативних чисел зі знаком
mov AL, OFCh; Перший співмножник = 252
mov BL, 4; Другий співмножник = 4
mul BL; AX = 03F0h = 1008
mov AL, OFCh; Перший співмножник = -4
mov BL, 4; Другий співмножник = 4
imul BL; AX = FFFO = -16
Тут дія команд mul і imul над одними і тими ж операндами дає різні результати. У першому прикладі число без знака FCh, яке інтерпретується, як 252, множиться на 4, даючи в результаті число без знака 3F0, тобто 1008. У другому прикладі те ж число FCh розглядається, як число зі знаком. У цьому випадку воно складає -4. Множення на 4 дає FFF0h, тобто -16.