Цифрові технології. Арифметичні і логічні команди. (Тема 8-9)

Содержание

Слайд 2

Parity Flag (PF) - контроль парності - цей прапор встановлюється в

Parity Flag (PF) - контроль парності - цей прапор встановлюється в

1, якщо в молодших 8-бітових даних парне число. Якщо число непарне, то цей біт встановлений в 0. Навіть якщо результат - це слово, то аналізуються тільки 8 молодших біт!

Auxiliary Flag (AF) - зовнішній перенесення - встановлений в 1, якщо трапилося переповнення без знака молодших 4-х бітів (тобто перенесення з 3-го біта).

Interrupt enable Flag (IF) - переривання - якщо цей прапор встановлений в 1, то процесор реагує на переривання від зовнішніх пристроїв.

Direction Flag (DF) - напрям - цей прапор використовується деякими командами для обробки ланцюжка даних. Якщо прапор встановлений в 0 - обробка відбувається в прямому напрямку, якщо 1 - в зворотному.

Є три групи команд.

Перша група: ADD, SUB,CMP, AND, TEST, OR, XOR

Ці типи операндів підтримуються:

REG, memory
memory, REG
REG, REG
memory, immediate
REG, immediate
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

Слайд 3

memory: [BX], [BX+SI+7], змінна, і т.д... immediate: 5, -24, 3Fh, 10001101b,

memory: [BX], [BX+SI+7], змінна, і т.д...
immediate: 5, -24, 3Fh, 10001101b, і

т.д...

Після операції між операндами результат завжди записується в перший операнд. Команди CMP і TEST впливають тільки на прапори і не записують результат (ця команда використовується для прийняття рішення під час виконання програми).

Ці команди впливають тільки на прапори:
CF, ZF, SF, OF, PF, AF.

ADD - Додати другий операнд до першого.
SUB - Відняти другий операнд з першого.
CMP - Відняти другий операнд з першого тільки для прапорів.
AND - Логічне І між усіма бітами двох операндів. При цьому дотримуються правила:

1 AND 1 = 1 1 AND 0 = 0 0 AND 1 = 0 0 AND 0 = 0

Як бачите, ми отримуємо 1 тільки в тому випадку, якщо обидва біти рівні 1.

Слайд 4

TEST - Те ж саме, що АND, але тільки для прапорів.

TEST - Те ж саме, що АND, але тільки для прапорів.
OR

- логічне АБО між усіма бітами двохоперандів. При цьому дотримуються правила:

1 OR 1 = 1 1 OR 0 = 1 0 OR 1 = 1 0 OR 0 = 0

Як бачите, ми отримуємо 1 кожен раз, коли хоча б один біт дорівнює 1.

XOR - логічне XOR (АБО з виключенням) між усіма бітами двох операндів. При цьому дотримуються правила:

1 XOR 1 = 0 1 XOR 0 = 1 0 XOR 1 = 1 0 XOR 0 = 0

Як бачите, ми отримуємо 1 кожен раз, коли біти мають різне значення.

Слайд 5

Друга група: MUL, IMUL, DIV, IDIV Ці типи операндів підтримуються: REG

Друга група: MUL, IMUL, DIV, IDIV

Ці типи операндів підтримуються:

REG memory REG: AX, BX,

CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.

memory: [BX], [BX+SI+7], змінна, і т.д...

Команди MUL и IMUL впливають тільки на ці прапори:    CF, OF

Якщо результат перевищує розмір операнда, то ці прапори встановлені в 1, якщо результат уміщається в розмір операнда, то ці прапори встановлені в 0.

Для команд DIV та IDIV прапори не визначені.

MUL - беззнаковое множення: якщо операнд - це байт: AX = AL * операнд. якщо операнд - це слово: (DX AX) = AX * операнд.

IMUL - множення зі знаком: якщо операнд - це байт: AX = AL * операнд. якщо операнд - це слово: (DX AX) = AX * операнд.

Слайд 6

DIV - беззнаковое розподіл: якщо операнд - це байт: AL =

DIV - беззнаковое розподіл: якщо операнд - це байт: AL = AX

/ операнд AH = залишок (модуль). якщо операнд - це слово: AX = (DX AX) / операнд DX = залишок (модуль).

IDIV - розподіл зі знаком: якщо операнд - це байт: AL = AX / операнд AH = залишок (модуль). якщо операнд - це слово: AX = (DX AX) / операнд DX = залишок (модуль).

Третя група: INC, DEC, NOT, NEG

Ці типи операндів підтримуються:

REG memory REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.
memory: [BX], [BX+SI+7], змінна, і т.д…
Команди INC и DEC впливають тільки на ці прапори:    ZF, SF, OF, PF, AF.

Команда NOT не впливає ні на які прапори!

Команда NEG впливає тільки на ці прапори:    CF, ZF, SF, OF, PF, AF.

Слайд 7

NOT - інвертування кожного байта операнда. NEG - Змінює знак операнда

NOT - інвертування кожного байта операнда.
NEG - Змінює знак операнда

(доповнення до двох). Зазвичай вона інвертує кожен біт операнда, а потім додає до нього одиницю. Наприклад, 5 перетвориться в -5, а -2 перетворюється в 2.

Управління ходом програми

Управління ходом програми - дуже важлива річ. Це те, що змушує програму приймати рішення, в залежності від деяких умов.

Безумовні переходи Основна команда, яка передає управління в іншу точку програми - це JMP. Основний синтаксис команди JMP: JMP мітка

Щоб оголосити мітку в вашій програмі, просто роздрукуйте її ім'я і в кінці додайте двокрапку ":". Мітка може бути будь-якою комбінацією символів, але не повинна починатися з цифри. Наприклад, нижче представлені три правильних оголошення міток:

label1: label2: a:

Слайд 8

Мітка може бути оголошена на окремому рядку або перед будь-якою іншою

Мітка може бути оголошена на окремому рядку або перед будь-якою іншою

командою, наприклад:

x1: MOV AX, 1 x2: MOV AX, 2

Приклад команди JMP:

Слайд 9

Звичайно, є більш простий шлях для обчислення результату з двома числами,

Звичайно, є більш простий шлях для обчислення результату з двома числами,

але це хороший приклад застосування команди JMP.

Як ви можете бачити з цього прикладу, команда JMP може передавати управління іншій ділянці програми, який може знаходитися як після цієї команди, так і перед нею. Цей перехід може бути здійснений в межах поточного сегмента коду (65,535 байтів).

Короткі умовні переходи

Подібно команді JMP, яка виконує безумовний перехід, існують команди, які здійснюють умовний перехід (перехід, який здійснюється тільки в тому випадку, якщо виконується певна умова). Ці команди поділяються на три групи. Перша група тільки перевіряє окремий прапор, друга - порівнює числа зі знаком, третя - порівнює числа без знака.

Слайд 10

Команди переходу, які перевіряють одиночний прапор

Команди переходу, які перевіряють одиночний прапор

Слайд 11

Як бачимо, існують команди, які виконують однакові дії. Це нормально. Вони

Як бачимо, існують команди, які виконують однакові дії. Це нормально. Вони

навіть ассемблюются в однаковий машинний код, тому буде непогано, якщо запам'ятаємо, що при компіляції команди JE, після дизассемблювання отримаємо її як: JZ.

Різні імена використовуються для того, щоб робити програми більш легкою для розуміння і кодування.

<> - цей знак означає "не дорівнює"

Слайд 12

Команди переходу для чисел без знаків

Команди переходу для чисел без знаків

Слайд 13

Зазвичай, якщо потрібно порівняти два числових значення, то використовують команду CMP

Зазвичай, якщо потрібно порівняти два числових значення, то використовують команду CMP

(вона робить те ж саме, що і команда SUB (віднімання), але не зберігає результат, а впливає тільки на прапори.

Логіка дуже проста, наприклад: потрібно порівняти числа 5 і 2, 5 - 2 = 3 результат - НЕ НУЛЬ (Прапор Нуля - Zero Flag (ZF) встановлено в 0).

Другой приклад: потрібно порівняти 7 і 7, 7 - 7 = 0 результат - НУЛЬ! (Прапор Нуля - Zero Flag (ZF) встановлено в 1 і команди JZ або JE виконають перехід).

Приклад команди CMP і умовного переходу:

Слайд 14

Всі умовні переходи мають одне серйозне обмеження - на відміну від

Всі умовні переходи мають одне серйозне обмеження - на відміну від

команди JMP, вони можуть виконувати перехід тільки на 127 байтів вперед або на 128 байтів назад (врахуйте, що великі команди ассемблюются в 3 і більше байтів).

Ми можемо легко подолати це обмеження, використовуючи наступний метод:

Взяти зворотню команду з наведеної вище таблиці і виконати перехід до мітки label_x.
Використовувати команду JMP для переходу до потрібної ділянки програми.
Визначити мітку label x: тільки після команди JMP.

label_x: - може бути будь-яким ім'ям.

Приклад:

Слайд 15

Інший, рідше використовується метод, являє собою застосування безпосереднього значення (адреси) замість

Інший, рідше використовується метод, являє собою застосування безпосереднього значення (адреси) замість

мітки. Якщо безпосереднє значення починається з символу '$', то виконується відносний перехід - перехід щодо поточногї адреси. Компілятор обчислює команду, яка знаходиться по заданому зміщенню, і виконує перехід безпосередньо до неї. Наприклад:
Слайд 16

Процедури Процедура - це частина коду, яка може бути викликана з

Процедури

Процедура - це частина коду, яка може бути викликана з вашої

програми для виконання будь-якої певної задачі. Процедури роблять програму більш структурної і доступною для розуміння. У загальному випадку процедура повертає програму до тієї ж самої точки, звідки вона була викликана.

Синтаксис для оголошення процедури: ім’я PROC       ; тут знаходиться       ; код процедури ... RET ім’я ENDP

ім’я - це ім'я процедури. Одне і теж ім'я повинно бути у верхній і нижній частині, це використовується для перевірки правильності закриття процедур.

Ви вже знаєте, що команда RET використовується для повернення в операційну систему. Ця ж команда використовується для повернення з процедури (фактично операційна система сприймає вашу програму, як спеціальну процедуру).

PROC і ENDP - це директиви компілятора, тому вони не асимілюються в який-небудь реальний машинний код. Компілятор тільки запам'ятовує адресу процедури.

Слайд 17

Команда CALL використовується для виклику процедури. Приклад: Вищеописаний приклад викликає процедуру

Команда CALL використовується для виклику процедури. Приклад:

Вищеописаний приклад викликає процедуру

m1, яка виконує команду MOV BX, 5. Після закінчення процедури, програма виконує команду, наступну після команди CALL, тобто команду: MOV AX, 2.
Слайд 18

Є кілька способів передачі параметрів процедурі. Найпростіший з них - використання

Є кілька способів передачі параметрів процедурі. Найпростіший з них - використання

регістрів. Тут представлений приклад процедури, яка приймає два параметри в регістрах AL і BL, примножує їх і повертає результат в регістр AX:

У цьому прикладі значення регістра AL змінюється кожен раз при виклику процедури, а стан регістра BL не змінюється. Таким чином ми отримали алгоритм обчислення числа 2 в 4-му ступені.
В результаті в регістрі AX буде число 16 (або 10h).

Слайд 19

Тут дано ще один приклад, в якому використовується процедура для виведення на екран повідомлення Hello World!:

Тут дано ще один приклад, в якому використовується процедура для виведення

на екран повідомлення Hello World!:
Слайд 20

"b." - префікс перед [SI] означає, що нам необхідно порівнювати байти,

"b." - префікс перед [SI] означає, що нам необхідно порівнювати байти,

а не слова. Якщо ви хочете порівняти слова, додайте префікс "w." замість "b.". Якщо один з порівнюваних операторів - регістр, то вставляти префікси не потрібно, так як компілятор знає розмір кожного регістру.

Стек

Стек - це область пам'яті для зберігання тимчасових даних. Стек використовується командою CALL для зберігання адреси, щоб програма могла повернутися до того місця, звідки була викликана процедура. Команда RET отримує цю адресу з стека і повертає керування з цього зміщення. те ж саме відбувається, коли команда INT викликає переривання, вона записує в стек регістр прапорів, сегмент і зсув коду. Команда IRET використовується для повернення після виклику переривання.

Ви можете використовувати стек для зберігання будь-яких даних. Для роботи зі стеком є дві команди:

PUSH - записує 16-ти бітні значення в стек. POP - отримує 16-ти бітні значення із стека.

Слайд 21

Слайд 22

Команди PUSH і POP працюють тільки з 16-ти бітними значеннями! Примітка:

Команди PUSH і POP працюють тільки з 16-ти бітними значеннями!
Примітка: PUSH

immediate працює тільки на процесорах 80186 і вище!

Стек використовує алгоритм LIFO (Last In First Out - Останнім прийшов - першим пішов), це означає, що якщо ми помістимо ці значення одне за іншим в стек: 1, 2, 3, 4, 5 то першим значенням, яке ми можемо отримати з стека, буде 5, потім 4, 3, 2 і тільки потім 1.

Слайд 23

Дуже важливо застосовувати рівну кількість команд PUSH і POP, інакше стек

Дуже важливо застосовувати рівну кількість команд PUSH і POP, інакше стек

може бути порушений і неможливо буде повернутися в операційну систему. Як ви вже знаєте, ми використовуємо команду RET для повернення в операційну систему. Коли програма запускається, її адреса записується в стек (зазвичай це 0000h).

Команди PUSH і POP дуже корисні, тому що для зберігання даних зазвичай недостатньо тільки регістрів. Ось вихід із ситуації:

Записати значення регістра в стек (використовуючи PUSH).
Використовувати цей регістр в своїх цілях.
Відновити попереднє значення регістра із стека (використовуючи POP).

Приклад:

Слайд 24

Стек можна також використовувати для того, щоб поміняти місцями значення в

Стек можна також використовувати для того, щоб поміняти місцями значення в

регістрах:

Обмін даними відбувається тому, що стек використовує алгоритм LIFO (Останнім прийшов - першим вийшов), тому коли ми поміщаємо в стек число 1212h, а потім - 3434h, то при зверненні до стека ми спочатку отримаємо число 3434h, і тільки потім - 1212h.

Область пам'яті стека встановлюється за допомогою регістрів SS (Stack Segment - сегмент стека) і SP (Stack Pointer - покажчик стека). Зазвичай операційна система встановлює значення цих регістрів на початок програми.

Команда «PUSH джерело» робить наступне:

Віднімає 2 з регістра SP.
Записує значення джерела за адресою SS: SP.

Слайд 25

Команда «POP приймач» робить наступне: Записує дані, розміщені за адресою SS:

Команда «POP приймач» робить наступне:

Записує дані, розміщені за адресою SS: SP

в приймач.
Збільшує на 2 значення регістра SP.

Поточна адреса покажчика в SS: SP називається вершиною стека.

Для COM-файлів сегмент стека - це зазвичай і сегмент коду, а покажчик стека встановлений в значення 0FFFEh. За адресою SS: 0FFFEh записується адреса повернення для команди RET, яка виконується в кінці програми.

Ви можете спостерігати за роботою стека, натиснувши кнопку [Stack] у вікні емулятора. Вершина стека відзначена знаком "<".

Макроси

Макроси - це ті ж процедури, тільки віртуальні. Макроси подібні процедурам, але вони виконуються тільки під час компіляції. Після компіляції всі макроси замінюються реальними командами. Якщо ви оголосите макрос і ніколи не будете використовувати його в вашому коді, компілятор просто проігнорує його. emu8086.inc містить хороші приклади використання макросів. Цей файл містить декілька макросів, які роблять створення коду більш легким.

Слайд 26

На відміну від процедур, макрос повинен бути визначений перед ділянкою коду,

На відміну від процедур, макрос повинен бути визначений перед ділянкою коду,

де він буде використовуватися, наприклад:

Вищеописаний код еквівалентний наступному набору команд: MOV AX, 00001h MOV BX, 00002h MOV CX, 00003h MOV AX, 00004h MOV BX, 00005h MOV CX, DX

Слайд 27

Слайд 28

Макрос розпаковується безпосередньо в коді, тому, якщо є мітки всередині макровизначення,

Макрос розпаковується безпосередньо в коді, тому, якщо є мітки всередині макровизначення,

ви можете отримати помилку "Duplicate declaration" (Подвійне оголошення), якщо макрос використовується два або більше разів. Щоб уникнути такої проблеми, використовуйте директиву LOCAL, яка супроводжує імена змінних, міток або імен процедур. наприклад:

Якщо ви плануєте використовувати ваші макроси в декількох програмах, то краще розмістити всі макроси в окремому файлі. Помістіть цей файл в каталог Inc і застосуєте директиву INCLUDE ім’я файлу для використання макросів.

Слайд 29

Як створити операційну систему Зазвичай, коли комп'ютер стартує, він намагається завантажитися

Як створити операційну систему

Зазвичай, коли комп'ютер стартує, він намагається завантажитися з

першого 512-байтового сектора (це Циліндр 0, Головка 0, Сектор 1) дискети в дисководі A: в пам'ять за адресою 0000h: 7C00h і передати їй управління. Якщо це не вдається, то BIOS намагається використовувати MBR першого жорсткого диска.

Це для дискети. Ті ж принципи використовуються при завантаженні з жорсткого диска. Але використання дискети має кілька переваг:

Ви можете зберегти існуючу операційну систему неушкодженою (Windows, DOS ...).
Завантажувальний запис дискети легко модифікувати.

Приклад простий завантажувальної програми для дискети:

Слайд 30

Слайд 31

Скопіюйте описаний вище приклад в редактор вихідного коду Emu8086 і натисніть

Скопіюйте описаний вище приклад в редактор вихідного коду Emu8086 і натисніть

кнопку [Compile and Emulate]. Емулятор автоматично завантажить ".boot" файл за адресою 0000h: 7C00h.

Ви можете керувати ним як звичайною програмою або використовувати меню Virtual Drive -> Write 512 bytes at 7C00h to -> Boot Sector віртуального дисковода (файл FLOPPY_0 в каталозі, де встановлений емулятор). Після запису вашої програми в Віртуальний Дисковод, ви можете вибрати Boot from Floppy з меню Virtual Drive.

".boot"-файлы имеют ограничение 512 байтов (размер сектора). Если ваша операционная система имеет размер, превышающий это ограничение, то вам придется использовать программу для загрузки из других секторов.

Светофор

Зазвичай для управління світлофором використовується масив (таблиця) значень. У певний період часу значення читається з масиву і відправляється в порт. наприклад:

Слайд 32

Слайд 33

Кроковий двигун Двигун може виконати "напівкрок" за допомогою пари магнітів, які

Кроковий двигун

Двигун може виконати "напівкрок" за допомогою пари магнітів, які повертають

ротор двигуна на певний кут. Потім включається наступна пара магнітів і повертає ротор двигуна на наступний «крок» і т.д.

Двигун може виконати повний крок, якщо його повернути парою магнітів, потім іншою парою магнітів і в кінці - одиночним магнітом і т.п. Кращий спосіб здійснити повний крок - це виконати його як два півкроку.

Напівкрок - це 11.25 градусів. Повний крок - це 22.5 градуса.

Слайд 34

Двигун може обертатися як за годинниковою стрілкою, так і проти годинникової стрілки. Робот

Двигун може обертатися як за годинниковою стрілкою, так і проти годинникової

стрілки.

Робот