Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекція № 2 МЗКІТ (20 тестових питань).docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
3.64 Mб
Скачать

Підведемо деякі підсумки:

  • Розуміння архітектури ЕОМ є ключовим для вивчення асемблера. Це стосується будь-якого типу комп'ютера. Структура асемблера, формат його команд, адресація операндів тощо повністю відбивають особливості архітектури комп'ютера. Є загальні архітектурні властивості, властиві всім сучасним машинам фон-неймановской архітектури, і є приватні властивості, властиві конкретному типу комп'ютерів.

  • Метою вивчення архітектури є:

• виявлення набору доступних для програмування регістрів, їх функціонального призначення й структури;

• розуміння організації оперативної пам'яті й порядку її використання;

• знайомство з типами даних;

• вивчення формату машинних команд;

• з'ясування організації обробки переривань.

  • Мікропроцесор містить 32 доступних тим або іншим способом регістрів. Вони діляться на користувацькі й системні регістри.

  • Користувацькі регістри мають певне функціональне призначення. Серед них особливо потрібно виділити регістр прапорів eflags і регістр покажчика команди eip. Призначення регістру eflags — відбивати стан мікропроцесора після виконання останньої машинної команди. Регістр eip містить адреса наступної виконуваної машинної команди. Доступ до цих регістрів, у силу їх специфіки, з боку програм користувача обмежений.

  • Мікропроцесор має три режими роботи:

• реальний режим, який використовувався для i8086 і підтримується дотепер для забезпечення сумісності програмного забезпечення;

• захищений режим, який уперше з'явився в i80286;

• режим віртуального i8086. Забезпечує повну емуляцію мікропроцесора i8086, дозволяючи при цьому організувати багатозадачну роботу декількох таких програм.

  • Мікропроцесор має складну систему керування пам'яттю, робота якої залежить від режиму мікропроцесора.

  • Мікропроцесор завдяки гнучкій системі команд підтримує досить велику номенклатуру типів даних.

Розробка простої програми на асемблері

Під час обговорення архітектури ми перелічили велику кількість регістрів. Як правило, більшість із них задіяне при роботі практично будь-якої програми. Було б цікаво під час роботи програми подивитися їхній уміст. Це неважко, якщо використовувати спеціальну програму — отладчик. Але як зробити це динамічно, не використовуючи інших програм? Або, приміром, як розв'язати зворотне завдання — увести із клавіатури значення в регістр? Можна, звичайно, написати відповідну програму. Той, хто працював на одному із сучасних мов високого рівня, скаже: «Подумаєш, я викличу функцію, призначену для виводу вмісту регістру, і немає проблем». Дійсно, проблем немає, якщо нас не цікавить ефективність коду. Не можна забувати, що між залізом комп'ютера й будь-якою мовою високого рівня існує компілятор, який може генерувати, м'яко кажучи, не дуже ефективний код. Для критичних по розміру системних програм, при написанні яких часом ураховується кожний байт, практика використання винятково мов високого рівня виглядає сумнівною. На допомогу приходить асемблер. Якщо представлення в машинному вигляді програми мовою високого рівня — це чорна скринька ( у тому розумінні, що ми мало в чому можемо вплинути на компіляцію), то, застосовуючи мову асемблера, ми можемо враховувати найтонші системні й архітектурні нюанси. Саме тому не слід принижувати значення низькорівневого програмування в порівнянні з потужними мовами високого рівня.

Розроблювачі компіляторів донині обов'язково залишають можливість виходу на рівень асемблера. Ця можливість може бути реалізована у формі асемблерних вставок у програму або підключення зовнішніх процедур на асемблері. У кожному разі, це дозволяє підвищити якість коду, що отримується.

Якщо ви переконалися в корисності (природно, у розумних межах) використання асемблера при розробці ваших програм, то продовжимо розбиратися з нашою проблемою, яка полягає в тому, що нам потрібно візуалізувати уміст деякого регістру або ввести в нього значення. При цьому відразу зіштовхнемося з іншої приватною проблемою — перетворення даних. Причина тут у тому, що комп'ютер розуміє тільки ті типи даних, які підтримуються його системою команд. Тому на практиці часто виникає необхідність перетворення даних з одного представлення в інше. Тому ми тільки намітимо контури розв'язку цього завдання й розберемо окремий випадок. Повністю ми розв'яжемо його, коли познайомимося з поняттям макрокоманди.

Для початку потрібно продумати алгоритм. Уведення інформації із клавіатури й вивід її на екран здійснюються в символьному виді. Кодування цієї інформації проводиться згідно з таблицею ASCII (див. довідник). У таблиці ASCII кожний символ кодується одним байтом. Якщо робота відбувається із числами, то при спробі їх обробити відразу виникає проблема: команд для арифметичної обробки чисел у символьному виді немає. Що робити? Вихід очевидний: потрібно перетворити символьну інформацію до формату, підтримуваного машинними командами. Після такого перетворення потрібно виконати необхідні обчислення й перетворити результат назад до символьного виду. Потім слід відобразити інформацію на моніторі.

Раніше ми обговорювали логічні типи даних, підтримувані на рівні машинних команд. З наведеного переліку видне, що підтримуються арифметичні операції над двійковими числами, зі знаком і без знака, і операції над десятковими числами. Десяткові числа, інша їхня назва BCD-числа (Binary Code Decimal — двоїчно-десятковий код), найбільш зручні для програмування прикладних завдань. Недолік цих чисел у тому, що для них потрібна розробка специфічних алгоритмів. Зараз ми розглянемо перетворення шістнадцяткового числа із двох цифр, що вводиться із клавіатури ( тобто в символьному виді), до двійкового виду. Після виконання цієї операції отримане число можна використовувати як операнд у двійкових арифметичних операціях.

Програма, представлена в лістингу 3.1, є одним з варіантів розв'язку цього завдання. В основу її алгоритму покладена особливість, пов'язана з ASCII -кодами символів відповідних шістнадцяткових цифр. Шістнадцяткові цифри формуються із символів 0, 1,..., 9, А, В, С, D, Е, F, а, b, с, d, е, f, наприклад: 12Af, 34ad.

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

Якщо нам потрібно проаналізувати, наприклад, стан деякої області пам'яті, то розібратися в накопиченні нулів і одиниць буде дуже непросто. Для прикладу розглянемо двійкову послідовність:

010100010101011110101101110101010101000101001010

Це представлення дуже не наочне. Ми говорили, що оперативна пам'ять складається із комірок — байтів — по 8 біт. Наведений вище ланцюжок біт при розбивці її на байти буде виглядати так:

01010001 01010111 10101101 11010101 01010001 01001010

З наочністю стало краще, але якщо область пам'яті буде більше, то розібратися буде однаково складно. Давайте проведемо ще одну операцію: кожний байт розіб'ємо на дві частини по 4 біта — тетради:

0101 0001 0101 0111 1010 1101 1101 0101 0101 0001 0100 1010

І от отут проявляється чудова властивість шістнадцяткових чисел — кожної тетраді можна поставити у відповідність одну шістнадцяткову цифру (табл. 3.1).

Таблиця 3.1. Шістнадцяткові цифри

Якщо замінити в останньому отриманому нами рядку тетради на відповідні шістнадцяткові цифри, то одержимо послідовність 51 57 ad d5 51 8а:

0101 0001 0101 0111 1010 1101 1101 0101 0101 0001 0100 1010

5 1 5 7 a d d 5 5 1 8 а

Кожний байт наочно представляється двома шістнадцятковими цифрами. Якщо звикнути до такого запису, то він виявляється дуже зручним при роботі з пам'яттю, дисками і т.д.

Повернемося до нашого завдання. У табл. 3.2 наведені шістнадцяткові цифри і їх кодування в коді ACSII.

Таблиця 3.2. Кодування шістнадцяткових цифр

Подивитеся уважно й зрівняєте ці представлення. Приміром, розглянемо шістнадцятковий 0. Відповідний йому код ASCII — 30h, який у двійковому записується як 0011 0000, а відповідне двійкове число збігається із двійковим уявленням нуля 0000 0000.

Перший висновок: для шістнадцяткових цифр 0...9 код ACSII відрізняється від відповідного двійкового представлення на 0011 0000, або 30h. Тому для перетворення коду ACSII у шістнадцяткове число є два шляхи:

1. Виконати двійкове вирахування: (код ACSII)h — 30h.

2. Обнулить старшу тетраду байта із символом шістнадцяткової цифри в коді ACSII.

Видне, що із шістнадцятковими цифрами в діапазоні від 0 до 9 усе просто. Що стосується шістнадцяткових цифр а, b, с, d, е, f, те тут ситуація трохи складніше. Алгоритм повинен розпізнавати ці символи й робити додаткові дії при їхнім перетворенні у відповідне двійкове число. Подивиться уважно на символи шістнадцяткових цифр і відповідні їм двійкові представлення (див. табл. 3.2). Видно, що для перетворення вже недостатньо простого вирахування або обнуління старшої тетради. При аналізі таблиці ASCII (див. додаток 3) видне, що символи прописних букв шістнадцяткових цифр відрізняються від свого двійкового еквівалента на величину 37h. Відповідні малі літери шістнадцяткових цифр відрізняються від свого двійкового еквівалента на 67h.

Звідси випливає другий висновок: алгоритм перетворення повинен розрізняти прописні й рядкові буквені символи шістнадцяткових цифр і коректувати значення коду ASCII на величину 37h або 67h.

Ви, напевно, помітили, що після запису значення шістнадцяткової цифри випливає символ «h». Це зроблене для того, щоб транслятор міг відрізнити в програмі однакові за формою запису десяткові й шістнадцяткові числа. Приміром, числа 1578 і 1578h виглядають однаково, але мають різні значення. Більше того, як ви думаєте, яке значення в тексті вихідної програми має лексема fe023? Це може бути й деякий ідентифікатор, і, судячи з набору символів, шістнадцяткове число. Для того щоб однозначно описати в тексті програми на асемблері подібне шістнадцяткове число, його доповнюють провідним нулем і наприкінці ставлять «h». У нашому прикладі це буде виглядати так: 0fe023h.

Розберемо програму, представлену в лістингу 3.1. Якщо у вас усе ще залишилися неясні моменти з представленням чисел у різних форматах, то не зневіряйтеся, тому що ці питання будуть розглянуті більш систематизировано пізніше.

Лістинг 3.1. Приклад програми на асемблері

Докладно структура й правила оформлення програм будуть викладені пізніше. Зараз розглянемо ці правила лише в частині, що стосується нашого прикладу. Раніше ми з'ясували, що мова асемблера є символічним представленням машинної мови. Тому природнім представляється та обставину, що сама мова й програми на ній відбивають архітектуру комп'ютера.

Подивитеся уважно на програму. У ній є три ділянки, укладені між директивами segment і ends (рядки 9 і 11, 12 і 14, 15 і 44). Мікропроцесор апаратно підтримує шість адресно-незалежних областей пам'яті: сегмент коду, сегмент даних, сегмент стека й три додаткові сегменти даних. Наша програма використовує тільки перші три з них. Таким чином, уже сама структура програми відбиває особливості організації пам'яті.

Рядки 9-11 визначають сегмент даних. У рядку (10) описаний текстовий рядок з повідомленням «Уведіть дві шістнадцяткові цифри».

Рядки 12-14 описують сегмент стека, який є просто областю пам'яті довжиною 256 байт і яка ініціалізована символами ««?»». Відмінність сегмента стека від сегментів інших типів полягає у використанні й адресації пам'яті. На відміну від сегмента даних — наявність якого необов'язкова, якщо програма не працює з даними, — сегмент стека бажано визначати завжди.

Рядки 15-44 містять сегмент коду. У цьому сегменті в рядках (16)-(43) визначено одна процедура main.

Рядок 17 містить директиву асемблера, яка зв'язує сегментні регістри з іменами сегментів.

Рядки 18-19 виконують ініціалізацію сегментного регістру ds.

Рядки 20-22 виводять на екран повідомлення message:

Рядок 23 підготовляє регістр ах до роботи, обнуляя його. Уміст ах після цієї операції наступне: ах = 0000 0000 0000 0000

Рядки 24-25 звертаються до засобів операційної системи для введення символу із клавіатури. Уведений символ операційна система поміщає в регістр al. Приміром, у відповідь на повідомлення ви ввели із клавіатури дві шістнадцяткові цифри:

У результаті після відпрацьовування команди в рядку 25 буде введено один символ у коді ASCII — 5, і стан регістру ах стане таким:

ах = 0000 0001 0011 0101

Рядок 26 пересилає вміст al у регістр dl. Це робиться для того, щоб звільнити al для введення другої цифри. Уміст регістру dx після цього пересилання наступне:

dx = 0000 0000 0011 0101

Рядок 27 перетворить символьну 5 у її двійковий еквівалент шляхом вирахування 30h, у результаті чого в регістрі dl буде двійкове значення числа 5:

dx = 0000 0000 0000 0101

Рядки 28-29 з'ясовують, чи потрібне коректувати двійкове значення в dl. Якщо воно в діапазоні 0...9, то в dl — правильний двійковий еквівалент уведеного символу шістнадцяткової цифри. Якщо значення в dl більше 9, то введена цифра є одним із символів А, В, С, D, Е, F ( малі літери для економії місця обробляти не будемо). У першому випадку рядок 29 передасть керування на мітку М1. При обробці цифри 5 ця умова саме виконується, тому відбувається перехід на мітку М1 (рядок 31).

Кожна шістнадцяткова цифра займає одну тетраду. У нас — дві такі цифри, тому потрібно їх розмістити так, щоб старшинство розрядів зберігалося. Рядки 32-33 зрушують значення в dl на 4 розряду вліво, тим самим звільняючи місце в молодшій тетраді під молодшу шістнадцяткову цифру.

Рядок 34 уводить другу шістнадцяткову цифру С (її ASCII-код 63h) у регістр al:

ах = 0000 0001 0100 0011

Рядки 35-37 з'ясовують, чи попадає двійковий еквівалент другого символу шістнадцяткової цифри в діапазон 0...9. Наша друга цифра не попадає в діапазон, тому для одержання правильного двійкового еквівалента потрібно зробити додаткове коректування. Це робить рядок 38. Стан al після виконання рядка 35 наступний:

ах = 0000 0001 0001 0011

В al записано 13h, а потрібно, щоб було 0Сh (помніть про правила запису шістнадцяткових чисел!). Враховуючи що 0Сh не попадає в діапазон 0...9, то відбувається перехід на рядок 38. У результаті роботи команди вирахування в регістрі al виходить правильне значення (al) = 0Сh:

ах = 0000 0001 0000 1100

І нарешті, рядок 40 робить додавання зрушеного значення в dl із числом в al:

dx = 0000 0000 0101 0000

+

ах = 0000 0001 0000 1100

=

dx = 0000 0000 0101 1100

Таким чином, у регістрі dl ми одержали двійковий еквівалент двох уведених символів, що зображують двозначне шістнадцяткове число:

(dl) = 05Ch

Рядки 41-42 призначені для завершення програми й повернення керування операційній системі.

Ми не випадково настільки детально розглянули цей приклад. Він відбиває практично всі специфічні особливості програмування на асемблері. Залишився неясним питання про те, як організувати виконання даної програми. Цим займемося на наступній лекції.