Цикли і умовні переходи
ADO в Delphi AJAX Android C++ CakePHP CMS COM CSS Delphi Flash Flex HTML Internet Java JavaScript MySQL PHP RIA SCORM Silverlight SQL UML XML Бази даних Веб-розробка Генетичні алгоритми ГІС Гітара Дизайн Економіка Інтелектуальні СДН Колір Масаж Математика Медицина Музика Нечітка логіка ООП Патерни Подання знань Розкрутка сайту, SEO САПР Сесії в PHP Системне програмування Системний аналіз Тестологія Тестування ПЗ Фреймворки Штучний інтелект
|
Цикли і умовні переходи
ЦиклиЦикли, програми, що дозволяють виконати деяку ділянку, багато разів, в будь-якій мові є одній з найбільш споживаних конструкцій. У системі команд МП 86 циклів реалізуються, головним чином, за допомогою команди loop (петливши), хоча є і інші способи організації циклів. У всіх випадках число кроків в циклі визначається вмістом регістра СХ, тому максимальне число кроків складає 64 До. Розглянемо простий приклад організації циклу. Хай в програмі зарезервовано місце для масиву розміром 10000 слів, і цей масив треба заповнити натуральним рядом чисел від 0 до 9999. Ці числа, що заповнюють послідовні елементи масиву, іноді називають числами-заповнювачами. Відповідний фрагмент програми виглядатиме таким чином:
;У сегменті даних
array dw 10000 dup(0)
;У програмному сегменті
mov Bx,offset array ; Адреса масиву
mov Si,0 ;Индекс
mov Ax,0 ; Початкове значення заповнювача
mov Cx,10000 ; Лічильник циклу
fill: mov [BX] [SI],ax ;Заполнитель пошлемо в масив
inc AX ;Инкремент заповнювача
add Si,2 ; модифікація індексу
loop fill ; Команда циклу
На етапі підготовки ми заносимо в регістр ВХ відносну адресу початку масиву, що ототожнюється з його ім'ям array, встановлюємо початкове значення індексу елементу масиву в регістрі SI (з таким же успіхом можна бьшо узяти DI) і початкове значення числа-заповнювача. Сам цикл складається з трьох команд - єдиної змістовної команди засилання числа-заповнювача в черговий елемент масиву (за адресою, яка обчислюється, як сума вмісту регістрів ВХ і SI), а також модифікації числа-заповнювача і індексу чергового елементу масиву. Завершуючою командою loop управління передається на мітку fill, і цикл повторюється стільки раз, яке вміст СХ, в даному випадку 10000 кроків. Слід звернути увагу на команду модифікації індексу - в кожному кроці до вмісту SI додається 2, оскільки масив складається з двобайтових слів. Якби потрібно було заповнити байтовий масив, то в кожному кроці вміст регістра циклу SI слід було збільшувати на 1. Варто відзначити деякі деталі, пов'язані з механізмом виконання команди loop. При реалізації цієї команди процесор спочатку зменшує вміст регістра СХ на 1, а потім порівнює отримане число з нулем. Якщо СХ > 0, перехід на вказану мітку виконується. Якщо СХ = 0, цикл розривається і процесор переходить на команду, наступну за командою loop. Тому після нормального виходу з циклу вміст СХ завжди дорівнює 0. Інша обставина пов'язана з кодуванням команди loop. У її коді під зсув до точки переходу відводиться всього 1 байт. Оскільки зсув має бути величиною із знаком, максимальна відстань, на яку можна передати управління командою loop, складає від -128 до +127 байт (хоча досить важко уявити собі цикл, в якому перехід здійснюється вперед). Іншими словами, тіло циклу обмежується всього 128 байтами. Якщо циклічно повторюваний фрагмент програми має велику довжину, цикл доведеться організувати іншим, складнішим способом:
;Організація довгого циклу
mov Cx,10000 ;Счетчик циклу
fill: ; Влучна початки циклу
... ; Тіло довгого циклу
dec CX ; Декремент лічильника циклу
cmp Cx,0 ; Відпрацьовано задане число кроків?
je finish ; Так, на мітку продовження програми
jmp fill ; Ні, на початок циклу
finish: ; Продовження програми
У цьому, вельми типовому фрагменті ми "уручну" зменшуємо вміст лічильника циклу і порівнюємо набутого значення з 0. Якщо СХ = Про, це означає, що в циклі виконано задане число кроків, і командою умовного переходу je здійснюється перехід на продовження програми (влучна finish). Якщо СХ ще не дорівнює нулю, командою безумовного переходу jmp здійснюється повернення в початок циклу. Як було показано в гл. 2, команда jmp дозволяє перейти в будь-яку точку сегменту, і обмеження на розмір тіла циклу знімається. При необхідності організувати вкладені цикли, для збереження лічильника зовнішнього циклу на час виконання внутрішнього зручно скористатися стеком. У наступному фрагменті організовується тимчасова затримка тривалістю декілька секунд (конкретна величина затримки залежить від швидкості роботи процесора).
mov Cx,2000 ;Счетчик зовнішнього циклу
outer: push CX ; Збережемо його в стеку
mov Cx,0 ;Счетчик внутрішнього циклу
inner: loop inner ; loop внутрішнього циклу
pop CX ;Восстановим зовнішній лічильник
loop outher ; loop зовнішнього циклу
Програмні затримки зручно використовувати при відладці програм, щоб уповільнити їх роботу і встигнути розглянути їх часткові результати; іноді програмні затримки дозволяють синхронізовать роботу апаратури, підключеної до комп'ютера, якщо швидкість відробітку апаратурою посиланих в неї з комп'ютера команд менше швидкості процесора. У приведеному вище фрагменті зовнішній цикл виконується 2000 разів; внутрішній - 65536 разів. За рахунку числа кроків внутрішнього циклу використовується явище обертання, яке вже згадувалося раніше. Початкове значення в регістрі СХ дорівнює нулю; після виконання тіла циклу 1 раз команда loop зменшує вміст СХ на 1, що дає число Ffffh (яке можна розглядати, як -1). В результаті цикл повторюється ще 65535 разів, а в сумі - точно 64 До кроків.Команда loop внутрішнього циклу передає управління на саму себе, тобто тіло внутрішнього циклу складається з єдиної команди loop. У цьому немає нічого незаконного. Будь-яка команда, у тому числі і loop, вимагає якогось часу для свого виконання, і повторення 64 До раз команди loop дає деяку тимчасову затримку (на сучасних процесорах порядку тисячної частки секунди).
Перейдемо тепер до розгляду команд умовних переходів. У приведеному вище фрагменті для реалізації довгого циклу використовувалася команда умовного переходу по рівності je. У системі команд МП 86 є понад три десятки команд умовних переходів, що дозволяють здійснювати переходи за наявності різноманітних умов: рівності, нерівності, позитивності або отріцательності результату і інш. При виконанні всіх цих команд процесор аналізує вміст регістра прапорів і здійснює (або не здійснює) перехід на вказану мітку залежно від стану окремих прапорів або їх комбінацій. Оскільки на стан регістра прапорів впливають багато команд процесора, командами умовних переходів можна користуватися не тільки після команд порівняння або аналізу, але і після багатьох інших команд, якщо уважно вивчити вплив цих команд на прапори процесора. Приведемо декілька абстрактних прикладів.
cmp Ax,bx ;Сравнение двох регістрів
je equal ;Переход, якщо Ax=bx
cmp Si,mem ;Сравнение регістра і елементу пам'яті
jne notequ ;Переход, якщо Si<>mem
int 21h ;Вызов DOS
jc syserr ;Переход, якщо була помилка
;і прапор Cf=1
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 ;Переход, якщо всі вони скинуті
У гл. 2 наголошувалося, що двійкові числа, записувані в регістри процесора або елементу пам'яті, можна розглядати, або як числа істотно позитивні, тобто числа без знаку, або як числа із знаком. Наприклад, адреси осередків, зрозуміло, не можуть бути негативними. Тому число Ffffh, якщо по сенсу програми воно є адресою, позначає 65535. Якщо, проте, то ж число Ffffh вийшло в арифметичній операції віднімання 2 з 1, то його треба розглядати, як - 1. Так само поняття знаку безглузде по відношенню до кодів символів, які з рівним успіхом можуть набувати будь-якого значення з діапазону 0...255. З іншого боку, ми можемо умовно вважати, що коди символів першої половини таблиці ASCII позитивні, а коди другої половини таблиці (у них встановлений старший біт) негативні, і використовувати для обробки символів команди, чутливі до знаку.У складі команд умовних переходів є дві групи команд для порівняння чисел без знаку (це команди ja, jae, jb, jbc, jna, jnae, jnb і jnbe) і чисел із знаком (jg, jge, jl, jle, jng, jnge, jnl і jnle). У абревіатурах цих команд для порівняння чисел без знаку використовуються слова above (вище) і below (нижче), а для чисел із знаком - слова greater (більше) і less (менше). Різниця між тими і іншими командами умовних переходів полягає в тому, що команди для чисел із знаком розглядають поняття "Больше- менше" стосовно числової осі -32К...0...+32К, а команди для чисел без знаку - стосовно числової осі 0...64к. Тому для перших команд число 7fffh (+32767) більше числа S000h (-32768), а для других число 7fffh (32767) менше числа S000h (32768). Аналогічно, команди для чисел із знаком вважають, що 0 більше, ніж Ffffh (-1), а команди для чисел без знаку - менше. Розглянемо приклад використання команд умовних переходів для обробки символів. Хай ми вводимо з клавіатури деякий рядок символів (наприклад, ім'я файлу), і хочемо, щоб в програмі цей рядок був записаний прописними буквами, незалежно від того, які букви використовувалися при її введенні. Між іншим, при введенні з клавіатури команд DOS система завжди виконує цю операцію, тому і команди, і ключі, і імена файлів можна вводити як прописними, так і рядковими буквами - DOS у всіх випадках перетворить всі букви в прописних.
code segment
assume cs:code,ds:data
main proc
mov Ax,data ;Инициализация
move Ds,ax ;Регистр DS
;Виведемо службове повідомлення
mov Ah,09h ;Функция виводу
mov Dx,offset msg ;Адрес повідомлення
int 21h
;Поставимо запит до DOS на введення рядка
mov Ah,3fh ;Функция введення
mov Bx,0 ;Дескриптор клавіатури
mov Cx,80 ;Ввод максимум 80 байт
mov DX, offset buf ;Адрес буфера введення
int 21h
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 Dx,offset buf ;Адрес повідомлення
int 21h
mov Ah,01 ;Остановим програму
int 21h ;в очікуванні натиснення клавіші
;Завершимо програму
mov Ax,4c00h
int 21h
main endp
code ends
data segment
msg db "Вводите!$"
buf db 80 dup (' ') ;Буфер введення
actlen dw 0
data ends
stk segment stack
dw 128 dup(')
stk ends
end main
На початку програми на екран виводиться службове повідомлення "Вводите!", яке служить запитом програми, адресованим користувачеві. Далі за допомогою функції DOS 3fh виконується введення рядка тексту з клавіатури. Функція 3fh може вводити дані з різних пристроїв - файлів, послідовного порту, клавіатури. Різні пристрої ідентифікуються їх дескрипторами. При роботі з файлами дескриптор кожного файлу створюється системою в процесі операції відкриття або створення цього файлу, а для стандартних пристроїв - клавіатури, екрану, принтера і послідовного порту діють дескриптори, що закріплюються за цими пристроями при завантаженні системи. Для введення з клавіатури використовується дескриптор 0, для виводу на екран дескриптор 1.При виклику функції 3fh в регістр ВХ слід зане сти необхідний дескриптор, в регістр DX - адреса області в програмі, виділені й для прийому символів, що вводяться з клавіатури, а в регістр СХ - максимальне число символів, що вводяться. Ми вважаємо, що користувач не вводитиме більше 80 символів. Можна ввести і менш е; у будь-якому випадку введення рядка слід завершити натисненням клавіші <Enter>. Функція 3fh, відпрацювавши, поверне в регістрі АХ реальне число введених символів (включаючи коди 13 і 10, утворювані при натисненні клавіші <Enter>). У прикладі 3.5 число введених символів зберігається в осередку actlen з метою використання далі по ходу програми.Далі в циклі з actlen кроків виконується аналіз кожного введеного символу шляхом порівняння з межами діапазонів рядкових російських букв. Російські рядкові букви розміщуються в двох діапазонах код ASCII (а...п і р...с), причому для перетворення в прописних букв першого діапазону їх код слід зменшувати на 20h, а для перетворення букв другого діапазону - на 50h. Тому аналіз проводиться за допомогою чотирьох команд порівняння сmр і відповідних команд умовних переходів. Модифікований символ записується на те ж місце в буфері buf. Після завершення аналізу і перетворення введених символів, виконується контрольне виведення вмісту buf на екран. Оскільки ми заздалегідь не знаємо, скільки символів буде введено, вивід на екран здійснюється функцією 40h, серед параметрів якої указується число символів, що виводяться. Так само, як і у разі функції введення 3fh, для функції виведення 40h в регістрі ВХ необхідно вказати дескриптор пристрою введення, в даному випадку екрану, а в регістрі DX - адреса рядка, що виводиться.
Коди символів є числами без знаку, і використання в даному випадку команд умовних переходів для чисел без знаку представляється логічним і навіть єдино можливим. Якщо, проте, уважно розглянути поняття больше- менше для чисел із знаком і без знаку, то легко побачити, що поки ми порівнюємо один з одним тільки "позитивні" або тільки "негативні" числа, команда ja еквівалентна команді jg, а команда jb еквівалентна команді jl. Проте при порівнянні, наприклад, код цифр з кодами російських букв, правильний результат можна отримати лише при використанні команд переходів для чисел без знаку. Втім, завжди наочніше і надійніше використовувати ті команди, які відповідають істоті даних даних, навіть якщо такий же правильний результат вийде і при використанні "неправильних" команд.
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. Зверніть увагу на додаткові посиланняГоловний розділСторінки, близькі за змістомзагрузка...
|
Сторінки, близькі за змістом Асемблер (англ. assembler) — загальноприйнята назва транслятора з автокоду. Асемблер переводить початкову програму, написану на автокоді, в переміщувану програму на мові машинній. Оскільки асемблер здійснює трансляцію на мову завантажувача, при завантаженні програми необхідна налаштування умовних адрес, тобто адрес, значення яких залежать від розташування даної програми в пам'яті ЦВМ і від її зв'язків з іншими незалежно трансльованими програмами. |
Copyright © 2008—2024 Портал Знань.
При використанні матеріалів посилання, для інтернет-ресурсів — гіперпосилання, на Znannya.org обов'язкове.
Зв'язок
|
НТУУ "КПІ" Інженерія програмного забезпечення КПІ Лабораторія СЕТ |
|