Структура програми на мові Асемблер.
Команди розгалуження та циклу
Команда JMP здійснює безумовний перехід, тобто передачу керування за вказаною адресою при будь-яких обставинах. Зрозуміло, що при написані програми визначення точної адреси переходу — дуже складна задача, хоча при написані програм на асемблер її вирішення є можливим. Саме тому для визначення місця передачі керування в мові асемблер ефективно використовуються позначки. Визначення позначок… Читати ще >
Структура програми на мові Асемблер. Команди розгалуження та циклу (реферат, курсова, диплом, контрольна)
Лабораторна робота № 3
Тема роботи: Структура програми мовою асемблер. Команди розгалуження та циклу
1. Теоретичні відомості
1.1 Механізми передачі керування та зміна послідовності виконання команд. Їх реалізація у мові асемблера. Типи адресації
Мета роботи: Навчитися створювати програми з нетривіальною логікою на мові асемблер, реалізуючи запропонований алгоритм за допомогою команд розгалуження та циклу.
До цього моменту Ви працювали лише з асемблерними програмами, які мали тривіальну логіку і всі команди в них виконувались послідовно. Проте більшість задач програмування не є такими тривіальними. Як вам відомо, складні структурні алгоритми завжди містять безліч команд, які змінюють послідовність виконання інструкцій програми, як-то:
перевірки та вітвлення, що визначають, яку послідовність дій з кількох визначених слід виконати при отриманих результатах розрахунків;
цикли, в яких визначена послідовність команд повторюється багато разів для досягнення необхідного результату, який визначається за певними вимогами.
Особливість цих команд полягає в тому, що вони потребують передачі керування на іншу частину програми, яка знаходиться не безпосередньо після команди, що виконується. Така передача керування в програмі може здійснюватись вперед для виконання нової групи команд або назад для повторного виконання вже виконаних команд. При передачі керування певним чином змінюються всі базові регістри процесора, в тому числі лічильник команд (командний вказівник) та регістри адрес.
Як вам відомо з теорії алгоритмів, програми, які містять вітвлення та цикли, треба писати дуже охайно. Некоректна передача керування приводить не тільки до отримання невірних результатів, але в багатьох випадках до некоректного виконання всієї програми. Одним з прикладів таких помилок є так звані «вічні цикли». Більш того, коректність написання програми і вірність отриманих результатів для програм зі складною логікою ще не свідчать про те, що написана програма є оптимальною. Серед стандартних помилок програмістів, які не мають досвіду програмування, потрібно вказати такі:
включення до циклу команд, які можна було б без зміни логіки алгоритму винести за межі циклу. При великій кількості повторень команд циклу така помилка програмування приводить до значного збільшення часу виконання програми;
використання перевірок, які при значеннях даних, визначених програмістом, завжди будуть давати однаковий результат. Така помилка майже не впливає на хід виконання програми, але ускладнює її розуміння іншою людиною та редагування. Наприклад, немає ніякого сенсу послідовно розташовувати два оператора, перший з яких перевіряє чи є число в регістрі AX додатним, а другий, чи є те ж саме число від'ємним (поясніть чому);
реалізація циклічного процесу через команди умовного та безумовного переходів у тих випадках, коли це можливо зробити через команду організації циклу. Така помилка також значно ускладнює розуміння програми, хоча і не впливає на коректність її виконання;
некоректне звернення до комірок пам’яті при обробці в циклах даних зі складною структурою та рядків великої довжини, яке приводить до отримання невірних результатів, а при програмуванні на мові асемблера навіть до збоїв у роботі програми («підвисання» комп’ютера або аварійне завершення з виведенням на монітор коду системної помилки MS-DOS).
У більшості випадків таких прикрих помилок можна уникнути, якщо починати вирішення поставленої задачі не безпосередньо з програмування, а з її аналізу та побудови алгоритму рішення. Дуже допомагають при цьому як визначення алгоритму за допомогою тезових формуліровок та їх систематизації (нумерації пунктів алгоритму та визначення переходів на той чи інший пункт), так і використання блок-схем алгоритму. Тому побудова алгоритму виконання задачі та його блок-схеми є необхідною умовою виконання цієї роботи. У мові асемблера існують команди, які передають керування в програмі через безпосередню зміну адреси у лічильнику команд (для процесора Intel 8086 через зміну зміщення у командному вказівнику). Для організації циклів та вітвлення передбачено три головних засоби передачі керування:
1. Безумовний перехід | Команда JMP | |
2. Умовний перехід | Команди Jxxx (перехід при певних значеннях прапорів регістру стану) | |
3. Організація циклу | Команда LOOP | |
В асемблері існує три типи зміни адрес при передачі керування в програмі, які називаються типами адресацій: SHORT, NEAR та FAR.
SHORT — це найкоротша адресація в межах циклу. Допускає зміщення на 128 байт у напрямку зменшення адрес (зміщення вгору) або на 127 байт у напрямку зменшення адрес (зміщення вниз). Використовується в циклах по замовченню та може бути використано в командах умовного та безумовного переходу, а в деяких випадках навіть при викликах функцій та процедур.
NEAR — це коротка адресація в межах сегмента. В циклах не може бути застосованою. Використовується в командах безумовного переходу, якщо в них не визначена адресація типу SHORT. Може також бути використана для виклику функцій та процедур. Якщо ваша програма написана в межах одного сегмента, то адресація NEAR дозволяє передавати керування в будь-яку точку програми. FAR — передача керування за межу сегмента. В деяких випадках цей тип адресації використовується для виклику функцій та процедур. Як можна побачити, всі три типа адресації змінюють адресу в командному вказівнику IP, а тип адресації FAR змінює також регістр сегменту CS.
1.2 Команда безумовного переходу JMP
Команда JMP здійснює безумовний перехід, тобто передачу керування за вказаною адресою при будь-яких обставинах. Зрозуміло, що при написані програми визначення точної адреси переходу — дуже складна задача, хоча при написані програм на асемблер її вирішення є можливим. Саме тому для визначення місця передачі керування в мові асемблер ефективно використовуються позначки. Визначення позначок та правила їх написання дивіться в методичних вказівках до роботи № 1. Треба лише звернути увагу на те, що після позначок перед кодом команди слід ставити символ дві крапки (:) для уникнення некоректних перехресних міжмодульних посилань. Цей символ зазначує, що посилання на цю позначку можливе тільки в рамках процедури. Відсутність двох крапок у внутрішніх позначках процедури — це досить часта помилка програмістів, які не мають досвіду. Таким чином, формат команди JMP такий:
{позначка}JMP[позначка-операнд]
Позначку можна ставити як в одному рядку з кодом команди, так і окремим рядком — в обох випадках посилання буде вірним. При використанні команди JMP дуже важливо враховувати тип адресації. Розглянемо такий приклад:
M1:ADDAX, 1
ADDBX, AX
SHLCX, 1
JMPM1
Щоб з’ясувати, на скільки зменшиться адреса в командному вказівнику IP після виконання команди JMP, необхідно чітко розрахувати, скільки байтів у комп’ютерних кодах містить кожна з команд. Відомо, що коди команд ADD та SHL містять два байти. При асемблюванні програми транслятор автоматично підрахує необхідну величину, на яку слід зменшити число, що знаходиться в командному вказівнику після виконання команди JMP. Оскільки в наведеному прикладі це число є меншим за 127, буде використана адресація типу SHORT, яка відповідає чотирьохбайтному коду команди JMP. Старші її байти містять безпосередньо код операції, а молодші - від'ємне число, яке потрібно додати до числа, що знаходиться у командному вказівнику, після виконання команди JMP.
Таким чином, при зміщенні вгору на величину, меншу за 127 байт, транслятор асемблера автоматично генерує чотирьохбайтний код команди JMP, який відповідає найкоротшій адресації. Але якщо в програмі передбачений перехід не вгору, а вниз, то при аналізі коду команди JMP на першому проходженні програми транслятора ще «не відомо», де саме знаходиться позначка і який відповідно обрати тип адресації. Тому двопрохідний асемблер, яким є програма tasm, в цьому випадку буде по замовченню генерувати шестибайтний код команди JMP, який відповідає адресації типу NEAR. Два старші байти цього коду містять код операції, а чотири молодші - число, яке слід додати до адреси, що знаходиться в лічильнику команд. Якщо ви хочете генерувати чотирьох байтний код команди JMP при зміщенні вниз, необхідно використовувати в асемблерному коді цієї команди директиву SHORT, наприклад:
JMPSHORTM1
M1
1.3 Команда циклу LOOP
Наведений у попередньому розділі фрагмент програми не може бути робочим прикладом, оскільки він реалізує «вічний цикл». Але у робочих програмах часто-густо треба реалізовувати цикли, які повторюються певне число раз. Для реалізації таких циклів може бути ефективно використана команда LOOP, яка використовує значення регістру CX, автоматично зменшуючи його на 1 після кожного проходу всіх команд циклу. Формат команди циклу ідентичний формату команди JMP:
{позначка}LOOP[позначка-операнд]
Керування передається за адресою, яка вказана в операнді, доки значення регістру CX не дорівнює нулю, а при виконанні цієї умови здійснюється перехід на команду, що стоїть безпосередньо після оператора LOOP. При реалізації циклів часто-густо використовуються двобайтні команди збільшення та зменшення на 1, які є більш ефективними, ніж добре знайомі вам команди ADD та SUB. Формат команд збільшення на 1 INC та зменшення DEC такий:
{позначка}[код команди][операнд]
Операндом у командах INCта DEC може бути регістр або комірка пам’яті.
Приклад використання команди LOOP:
MOVAX, 1; Ініциалізація регістрів процесора.
MOVBX, 1
MOVDX, 1
MOVCX, 10
M1:INCAX
ADDBX, AX
SHLDX, 1
LOOPM1;
RET;Завершити програму.
Зменшити СХ на 1. Якщо в регістрі СХ число 0 — ;перейти до виконання наступної команди, якщо ні -;перейти до позначки М1. Аналогічно команді JMP команда LOOP визначає відстань до кінця циклу. Але при реалізації циклів є два принципових правила, які повинні завжди виконуватись:
1. Позначка, на яку здійснюється перехід, завжди мусить стояти у програмі вище команди циклу.
2. В циклах передбачена тільки найкоротша адресація. Тому якщо зміщення буде перевищувати 127 байт, то такий цикл буде розглянутий як помилка програмування. Транслятор у такому випадку видасть повідомлення «Relative jump out of rang» (Перевищення відносної межі переходу, яка допускається).
Існують також дві додаткові реалізації команди LOOP — це LOOPE (або LOOPZ) та LOOPNE (або LOOPNZ). Ці команди дозволяють аналізувати не тільки стан регістру CX, але й результат виконання останньої операції циклу. Передача керування за адресою позначки-операнда командою LOOPE здійснюється тоді, коли регістр CX має ненульове значення та встановлений прапор нуля (результат виконання останньої операції циклу був 0). Аналогічно команда LOOPNE передає керування за адресою позначки-операнда, коли регістр CX має ненульове значення та не встановлений прапор нуля (результат виконання останньої операції циклу не був 0).
1.4 Команда порівняння CMP
У теоретичній частині роботи № 1 був розглянутий регістр стану процесора Intel 8086, його структура та розташування бітів. Прикладом команди, яка змінює прапори регістру стану, є команда асемблера CMP. Формат команди:
{позначка}CMP[операнд1], [операнд2]
Команда зрівнює два операнда та, не змінюючи їх, впливає на прапори AF, CF, OF, PF, SF, ZF. Як операнди можуть бути використані регістри процесора, комірки пам’яті, безпосередні дані. Тому команда CMP ефективно використовується разом з командами умовного переходу, результат виконання яких безпосередньо залежить від значень прапорів регістру стану. Саме ці команди і будуть розглядатися далі.
1.5 Команди умовного переходу в мові асемблер
асемблер розгалуження адресація цикл Асемблер підтримує велику кількість команд умовного переходу, які здійснюють передачу керування у програмі залежно від вмісту регістру стану. Зміст головних з них: здійснити перехід на позначку-операнд, якщо результат операції нуль (або не нуль), якщо одне число більше (менше) другого, якщо результат є додатним (від'ємним), парним (непарним), якщо є (немає) перенесення.
Часто-густо команди умовного переходу використовуються після команди CMP. Загальний формат команд умовного переходу такий:
{позначка}[код команди][позначка-операнд]
Аналогічно командам JMP та LOOP, комп’ютерний код команди умовного переходу містить значення відстані між кінцем команди та адресою передачі керування. Ця відстань, як і для команди LOOP, мусить відповідати межам найкоротшої адресації (тип SHORT), а при виході за ці межі транслятор видає повідомлення «Relative jump out of rang».
При розгляді команд умовного переходу слід відзначити, що використання тієї чи іншої команди залежить від типу даних, що зрівнюються. Розглядаючи у попередній роботі представлення від'ємних чисел, ми з’ясували, що від'ємні та великі додатні числа комп’ютер представляє однаково, і вся відповідальність по визначенню знака числа лежить на програмістові.
Насправді, як визначити яке з чисел більше — E8H чи 15H? Результат залежить від того, як розглядати число E8H — як додатне чи як від'ємне. Тому у мові асемблер існує дві групи операцій умовного переходу — для знакових та для беззнакових даних (табл. 1, 2). У табл. 3 наведені команди для особливих арифметичних перевірок. Якщо команда допускає два різних написання, то вони наведені через символ / (прямий слеш).
Таблиця 1. Переходи для знакових даних
Мнемоніка команди | Що виконує | Прапори, що перевіряються | |
JE / JZ | Перехід, якщо рівно (нуль) | ZF | |
JNE / JNZ | Перехід, якщо не рівно (не нуль) | ZF | |
JG / JNLE | Перехід, якщо більше (не менше або рівно) | ZF, SF, OF | |
JGE / JNL | Перехід, якщо більше або рівно (не менше) | SF, OF | |
JL / JNGE | Перехід, якщо менше (не більше або рівно) | SF, OF | |
JLE / JNG | Перехід, якщо менше або рівно (не більше) | ZF, SF, OF | |
Таблиця 2. Переходи для беззнакових даних
Мнемоніка команди | Що виконує | Прапори, що перевіряються | |
JE / JZ | Перехід, якщо рівно (нуль) | ZF | |
JNE / JNZ | Перехід, якщо не рівно (не нуль) | ZF | |
JA / JNBE | Перехід, якщо вище (не нижче або рівно) | ZF, CF | |
JAE / JNB | Перехід, якщо вище або рівно (не нижче) | CF | |
JB / JNAE | Перехід, якщо нижче (не вище або рівно) | CF | |
JBE / JNA | Перехід, якщо нижче або рівно (не вище) | CF, AF | |
Таблиця 3. Особливі арифметичні перевірки
Мнемоніка команди | Що виконує | Прапори, що перевіряються | |
JS | Перехід, якщо є знак (від'ємне) | SF | |
JNS | Перехід, якщо немає знаку (додатне) | ZF | |
JC | Перехід, якщо є перенесення | CF | |
JNC | Перехід, якщо немає перенесення | CF | |
JO | Перехід, якщо є переповнення | OF | |
JNO | Перехід, якщо немає переповнення | OF | |
JP / JPE | Перехід, якщо паритет парний | PF | |
JNP / JPO | Перехід, якщо паритет непарний | PF | |
Зазначимо, що команди переходу за умовою «рівно або нуль» однакові для знакових та беззнакових даних.
Оскільки регістр СХ є дуже важливим і використовується процесором як лічильник, існує окрема команда JCXZ, яка здійснює передачу керування за позначкою, якщо в регістрі CX міститься число 0. Приклад використання команд умовного та безумовного переходів та циклу:
A10: MOV SUM, CX
ADD SUM, AX
JP A11
SUB AX, CX
JMP A12
A11: ADD AX, BX
A12: CMPAX, 100
JGA13;
LOOP A10
A13:NOP
RET
Організувати цикл, послідовно зменшуючи число у регістрі CX на 1. У циклі зменшувати число, що знаходиться у регістрі BX на величину, що знаходиться у регістрі AX, доки значення регістру CX не стане дорівнювати 2
На підставі програми з лабораторної роботи № 2 розробимо програму у відповідності з завданням. Текст програми приведений нижче.
org 100h
mov ax, b1;b1
imul b2;b1*b2
mov bx, ax;bx=b1*b2
mov ax, a1;a1
sub ax, a2;ax=a1-a2
mov cx, c1;c1
add cx, c2;cx=c1+c2
mov dx, c1 ;dx=c1
and dx, a2 ;dx=c1&a2
xor dx, a1 ;dx=(c1&a2) xor a1
not dx ;dx=not ((c1&a2) xor a1)
lp:cmp cx, 2;порівняти cx з 2
je fin;якщо cx = 2, то закінчити цикл
sub bx, ax;зменшувати число, яке знаходиться в регістрі BX, на величину в регістрі AX
dec cx;зменшувати число у регістрі CX на 1
jmp lp;продовжити цикл
fin:ret
a1 DW 10 ;вихідні дані
a2 DW 15
b1 DW 40
b2 DW 25
c1 DW 5
c2 DW 6
Блок-схема алгоритму програми наведена на рисунку 1.
Рисунок 1 — Блок-схема алгоритму Рисунок 2 — Результат виконання програми
Висновок
В ході виконання лабораторної роботи навчилися створювати програми з нетривіальною логікою на мові асемблер, реалізуючи запропонований алгоритм за допомогою команд розгалуження та циклу.