Материалы сайта
Это интересно
Керiвництво программиста
2 РОЗРОБКА ПРОГРАМНОГО ЕМУЛЯТОРА 1 2.1 Постанова задачі Емулятором називається програмний продукт, що відтворює (повторює) роботу усіх (або частини) блоків фізичного влаштування. Емулятор необхіден для отладки програмного або апаратного забезпечення, коли влаштування ,що вимагається або буде відстунє, або в чинність будь-яких особливостей не підтримує отладку або підтримує неповністю. Інколи для підготовки фахівців в меті підвищення розуміння роботи влаштування нелишнє візуально продемонструвати роботу того або іншого вузла або блоку. Жодне влаштування, як правило, не дозволяє виробляти такого роду манипуляції. Метою даного проекту є створення програми-емулятора якого-або процесора фірми Intel. Даний емулятор передвизначений для отладки програмного забезпечення і навчання фахівців, а значить достатньо буде створити емулятор процесора з точки зору програмного забезпечення, т. є. Емулятор програмної моделі процесора. Вибір эмулюємого процесора. Необхідність в поглибленій отладці може виникнути при створенні системного програмного забезпечення для керуюч або систем ,що контролюють на базі стандартних процесорів сімейства х86. Дані системи накладують ряд обмежень на тип процесора ,що використається. В якості основних критеріїв тут виступають: вартість, продуктивність, простота зовнішнього інтерфейсу і його протоколи обміну, а також можливість побудови функціонально надлишкових систем для дублювання або тройовання обчислювального процесу. Немаловажным параметром є також і спектр функцій ,що підтримуються при мінімумі зовнішнього обладнання, а стало бути і простоті системної плати. Проаналізувавши ряд процесорів можна з певністю сказати, що найкращими параметрами володіє процесор 80486. У Порівнянні З іншими процесорами він володіє наступними перевагами: . Наявність внутрішньої кеш-пам'яті значно знижує навантаження на локальну шину; . Наявність вбудованого блоку FPU. Алгоритми керуючих систем буяють операціями над числами з плаваючою крапкою; . Апаратна підтримка функціонально надлишкової двохпроцесорной системи при мінімумі зовнішнього обладнання; . Можливість зміни розрядність зовнішньої шини. Многие прилади працюють з 8- ми або 16-разрядной шиною даних; . Контроль вірогідності і що приймаються даних ,що передаються шляхом перевірки паритету; . Робота процесора на частоті локальної шини. У порівнянні з процесором 80386 дана обставина значно спрощує вимоги до чипсету, бо чипсет повинен працювати на частоті вдвічі меншої (наприклад, для 80386-33Мгц частота локальної шини повинна складати 66Мгц); . Невисока вартість; . Розрядність цілочисельного АЛУ і РОНів 32 біти, що достатньо для широкого кола задач ,що вирішуються; . У порівнянні з іншими сімействами процесорів для сімейства х86 є найбільший обсяг готового програмного забезпечення різноманітного призначення. Отже, на підставі вищенаведених достоїнств можна припустити, що для більшості керуючих систем середньої продуктивності процесор 80486 є найбільш більш прийнятним. Вимога до емулятора. В коло задач даного емулятора входить отладка труднодоступних дільниць коду програмного забезпечення, неможлива на звичайних отладчиках. Крім того, даний програмний продукт припускається включити в навчальний процес в якості навчальної програми. На підставі вищезгаданого можна сформулювати наступні вимогу до програми-емулятору процесора: . Широкий набір эмулюємих команд і блоків процесора; . Мінімум вимоги до програмного і апаратного забезпечення комп'ютера; . Можливість покрокового виконання дільниць коду отлаживаємой програми; . Можливість перегляду стану эмулюємого процесора і оперативної пам'яті при покроковом виконанні команд; . Можливість внесення змін в вміст регістрів і хід виконання отлаживаємой програми; . Якомога більш точне відтворення роботи кожної команди; . В меті підвищення розуміння роботи окремих блоків можливість змінювати деякі параметри цих блоків. Крім того в процесі створення емулятора можуть виникнути інші функціональні можливості, що або є побічним продуктом при реалізації будь- яких алгоритмів, або можуть бути створені шляхом незначного доповнення або зміни вже наявних. 2.2 Вибір мови програмування, платформи і структури програми Вибір мови програмування. На даний момент самою гнучкою мовою програмування для створення немережових додатків для настольних систем є мова З, і його функціональне розширення С++, що підтримує концепції об'єктно-орієптованого програмування. Створення будь-якої більш менш складної програми спрощується при застосуванні принципів об'єктно-орієнтованого програмування. Отже для створення даної програми краще буде вибрати мову С++. Вибір платформи. Під платформою в даному випадку ми маємо операційну систему, в якій буде працювати програмний продукт. На даний момент є декілька операційних систем, що достатньо розповсюджені. Це DOS, Windows, OS/2 і UNIX (LINUX). Перша з них є однозадачной і може працювати практично на будь-якому процесорі сімейства х86, в той час як інші три вимагають для нормальної роботи процесор Pentium і досить великий обсяг оперативної пам'яті. Такі ресурси ще не всякий користувач може собі дозволити. Згідно вимозі до програми-емулятору, вона не повинна подавати високих вимоги до програмного і апаратного забезпечення. Крім Того, програма написана для роботи в DOS буде успішно працювати і на інших перерахованих платформах. На підставі вищезгаданого доцільно в якості платформи вибрати DOS. Вибір структури програми. Для полегшення створення емулятора доцільно розбити програму на два функціональних блоку: . Операційна частина. Включає необхідні структури і підпрограми, емуляцію роботи ,що реалізують внутрішніх блоків процесора і виконання команд; . Інтерфейс з користувачем. Включає підпрограми і дані, що реалізують діалог з користувачем, відображення результатів виконання команд і стан емулюємих блоків процесора і управління обчислювальним процесом. Такий розподіл добре ще і тим, що безпосередній користувач може самий створити потрібний йому інтерфейс і підключити до нього функціонально незалежну операційну частину без її перекомпіляції. Дана обставина підвищує гнучкість програми. Отже така структура буде оптимальної. 2.3 Операційна частина 1 2.3.1 Подання даних Будь-який більш менш складний проект доцільно починати з аналізу інформації ,що обробляється. В даному проекті нам предстоїть обробляти дані, наведені в форматах що підтримуються процесором. Проаналізувавши можливі типи операндів, можна вибрати оптимальний засіб їхнього подання в програмі. Крім того, необхідно ще розробити складні типи для подання внутрішніх блоків, бо такий шлях максимально наближає емулятор до реального процесора. В таблиці 2.1 уявлені типи даних, якими оперує процесор. Таблиця 2.1. Типи даних 80486. |Тип |Довжина в байтах |Примітка | |Байт зі знаком |1 |Цілий | |Байт без знаку |1 |Цілий | |Слово зі знаком |2 |Цілий | |Слово без знаку |2 |Цілий | |Подвійне слово зі знаком|4 |Цілий | |Подвійне слово без знаку|4 |Цілий | |Ближній покажчик (16 б) |2 | | |Ближній покажчик (32 б) |4 | | |Дальній покажчик (16 б) |4 | | |Дальній покажчик (32 б) |6 | | |Коротке |4 |Речовинний | |Довге |8 |Речовинний | |Повне |10 |Речовинний | Як видно з таблиці формати операндів процесора відповідають простим типам мови С++. Отже, для роботи з операндами команд доцільно виробляти з допомогою простих типів мови. Для подання різноманітних блоків слідує використати структури і класи. Для блоків, в яких інформація тільки зберігається краще усього використати структури, а якщо необхідно цю інформацію якось обробляти, доцільно використати тип класу, інкапсулювавши в нього засоби для обробки. Всі основні типи, що використаються в емуляторі зібрані в файлі comand. h. Об'єднання _num. Об'єкт цього типу передвизначений для зберігання числового значення операнда команди. Поля мають прості типи, і будучи в обігу до них, можна трактувати вміст, як будь-який тип. Структура _op. Об'єкт цього типу передвизначений для зберігання інформації, необхідної для доступу до операнду. Якщо операнд знаходиться в пам'яті, поля base, indx, sft, off і seg містять відповідно індекси базового і індексного регістрів, масштабний коефіцієнт, додаткове зміщення і індекс сегментного регістру для формування лінійної ефективної адреси. Якщо операнд знаходиться в регістрі, поле off зберігає індекс цього регістру, а якщо операнд безпосередній, в тому же поле зберігається його безпосереднє числове значення. Безпосередні дальні покажчики зберігаються як два операнда. Поле id містить ідентифікатор розрядність операнда (байт, слово, подвійне слово). Структура _decoder. Об'єкт цього типу зберігає декодовану команду. В полях структури міститься вся необхідна інформація для виконання команди, а також для її відображення на екрані. . _op op [3]. Масив містить три операнда команди. Якщо операндів менш, то зайві компоненти не визначені. . char id [3]. Масив містить інформацію про місцезнаходження операндів. Якщо будь-який операнд не визначений, в відповідному осередку буде-1. . char comstr [8]. Рядок містить мнемонику команди (без операндів). Поле заповнюється при декодуванні і використовується для подання команди на екрані спільно з полями op і id. . uchar codes [16]. Масив зберігає кодову послідовність, якої уявлена команда в пам'яті разом зі всіма префіксами. Це поле заповнюється послідовно під час декодування. . uchar com. Поле містить інформацію, що дозволить відрізнити команду в групі. Команди коди ,що є схожі і операнди об'єднані в групи з метою зменшення обсягу програми. . char len. Поле містить довжину команди в байтах разом зі всіма префіксами. Те є в ньому міститься індекс останнього певного осередка в масиві codes []. . char ccc. Поле містить код умови для команд умовних переходів і умовної настанови байтів. . char seg. Поле містить ознака переопределення сегменту. Якщо перед командою стояє префікс переопределення сегменту, в цьому полі буде міститися індекс сегментного регістру, інакше-1. . char ad, od. Поля містять ідентифікатори розрядність адреси і операнда відповідно. Перед декодурованням ці поля заповнюються ідентифікаторами поточної розрядність. Якщо перед командою стояє префікс переопределення розрядність адреси (67h) або операнда (66h), дані поля коректуються. . char rep. Поле зберігає ознака префіксів rep/repe. Якщо один з цих префіксів стояє перед командою, дане поле буде визначене, інакше-1. При декодуванні строкових команд дане поле коректується згідно специфіці команди. При виконанні в розгляд приймаються тільки значення ,що скоректувалися, інакше вміст поля ігнорується. . char jmp. Поле зберігає ідентифікатор переходу. В реальному процесорі при виконанні команди переходу черга команд і конвейєр скидаються, і вибірка починається ще раз з цільової адреси переходу. Для імітації передвиборки і конвейєра в емуляторі виробляється вибірка і дешифрація наступної команди до виконання попередньої. За наявності переходу наступна декодована команда анулюється, а вибірка виробляється ще раз з цільової адреси переходу. . int err. Поле зберігає ознака помилки, виниклої при вибірці або дешифрації. Якщо виникла така помилка, поле буде містити номер виключення. Безпосередно перед виконанням команди поле перевіряється на предмет наявності виключення, і в залежності від цього виробляється або виконання команди, або виклик виключення. Номер виключення виниклого під час виконання команди заноситься в глобальну змінну error. . ulong baseadr. Поле містить базова адреса команди. Перед дешифрацієй префіксів сюди записується вміст регістру eip. . comand *p. Покажчик на об'єкт виконання ,що реалізує даної команди. З допомогою цього покажчика в наслідку буде викликаний засіб die для виконання. Структура має засоби для здійснення вибірки команди з кодового сегменту. Є також конструктор, що виробляє початкову ініціалізацію полів структури. Об'єднання _eflag. Об'єкт цього типу зберігає вміст регістру прапорів. Об'єднання створене для можливості доступу як до окремих прапорів, так і до регістру в цілому. Клас _mem. Об'єкт цього типу передвизначений для виконання доступу до оперативної пам'яті емулятора. Конструктор класу створює на диску файл, в що після цього переписує весь перший мегабайт оперативної пам'яті комп'ютера. Єдине поле класу зберігає дескриптор цього файлу. Засіб pageadr виробляє трансляцію лінійної адреси в фізичний при включеній сторінковій адресації. Інші засоби виробляють переадресацію запитань по читанню або записи в пам'ять до дискового файлу. Класи _gdt, _idt. Об'єкти цих типів подають регістри GDTR і IDTR відповідно, а також інтерфейс для доступу до глобальної дескрипторної таблиці і таблиці переривань. Класи містять по два поля: base і limit, в яких містяться покажчики і розміри таблиць. Засіб get вертає лінійну адресу запрошеного дескриптора, а засіб geta слово атрибутів цього дескриптора. Клас _reg. Об'єкти цього типу об'єднані в масив з восьмих елементів і подають собою файл регістрів загального призначення. Поле r містить числове значення операнда в регістрі, а засоби виробляють звертання як до цілого регістру, так і до його частин. Клас _sreg. Об'єкти цього типу об'єднані в масив з шести елементів і подають собою файл сегментных регістрів. Поле sel зберігає селектор. Поля base і limit, базова лінійна адреса і розмір сегменту відповідно, а поле access зберігає слово атрибутів сегменту. Поле id передвизначене для ідентифікації сегментного регістру. Коли створюється масив примірників, конструктор класу заносить в це поле вміст змінної identify, а після цього збільшує цю змінну на одиницю. Таким Чином створюються об'єкти, що “знають”, які регістри вони подають. Обработчик перевантаженого оператора “=” виконує команду завантаження сегментного регістру. При Цьому на підставі поля id, глобальної змінної cpl, а також поточного режиму процесора виконується перевірка можливості завантаження регістру. Якщо завантаження можлива, відбувається звертання до об'єкту типу _gdt або _ldt для її виконання. Інакше в змінну error заноситься номер виключення, а в змінну ercode, код помилки. Інші засоби класу виконують доступ до операндам по запису або читанню шляхом виклика відповідних засобів об'єкту типу _mem. В цих засобах також виробляється перевірка прав доступу до сегменту і “генерація” виключення в випадку їхнього порушення. Клас _ldt. Об'єкт цього класу подає собою регістр LDTR і інтерфейс доступу до локальної дескрипторної таблиці. Даний клас схожий на попередній за винятком того, що засоби доступу замінені на засоби get і geta, що повністю идентичны відповідним засобам класу _gdt. Таким Чином, даний клас об'єднує властивості класів _gdt і _sreg, що по суті і відбувається в реальному процесорі. Клас _tss. Об'єкт цього класу подає собою регістр TR і інтерфейс для доступу до сегменту стану задачі. Клас схожий на попередній, але має власні засоби для доступу. Засіб stack виробляє переключення стека при міжсегментної передачі керування зі зміною рівня привілей. Засіб port провадить перевірку можливості доступу до порту введення/висновку шляхом аналізу відповідного б в БКВВ. Засіб swtask виробляє переключення задач з виконанням відповідних перевірок і “генерацією” виключення в випадку неуспіху. Клас _comand. Цей базовий клас є предком для класів, що реалізують команди процесора. Засіб mod виконує дешифрацію mod і sib байтів. Засоби deasm і die проголошені віртуальними і підлягають перевантаженню в кінцевих класах для реалізації декодування і виконання конкретних команд або груп команд. Далі в файлі слідують оголошення класів, що реалізують конкретні команди і групи команд. В модулі structs.cpp знаходиться реалізація засобів описаних класів (за винятком класу _mem, засоби якого реалізовані в окремому модулі mem. cpp в меті можливості заміни реалізації оперативної пам'яті емулятора). В модулі structs. cpp виробляються також оголошення об'єктів описаних вище класів, а також реалізація деяких сервисних процедур, як те формування виконавчої лінійної адреси на підставі об'єкту типу _op, збереження/відновлення вмісту регістрів з сегменту стану задачі і ін. Пам'ять під об'єкти, що подають регістри виділяється статично, тому при старті програми на треба піклуватися про створення примірників класів. Реалізація засобів класів команд і груп виробляється в модулях com_ands.cpp і com_movs. cpp. Арифметичні і логічні команди реалізовані в першому модулі, а команди пересилки і керування в другому. Процедури керування декодуванням і виконанням зібрані в модулі control. cpp. Цих процедур чотири: . init. Викликається при старті програми і виконує створення примірників класів команд. Покажчики на примірники заносяться в масив покажчиків mas на 256 елементів. При декодуванні виробляється вибірка першого байта команди і використання його вмісту в якості індексу в цьому масиві. Замість нереалізованих команд створюється примірник базового класу comand, і у разі звертання до ним виконується повернення з помилкою. . int_call. Процедура виконує виклик переривання або виключення. . step. Дана процедура викликається при виконанні декодирования команд. В якості параметру передається знов створений примірник класу _decoder. При поверненні примірник буде містити декодовану команду. В цій же процедурі виробляється дешифрація всіх префіксів команди. . exec_com. Ця процедура виконує керування виконанням команди шляхом виклика засобу die відповідного об'єкту. 2.3.2 Деколування і виконання команд Процесори сімейства х86 мають самий просторий набір команд з всіх наявних на сьогодняшній день процесорів. Ця обставина зв'язана з тим, що для цього сімейства є величезна кількість програмного забезпечення, і щоб попит на нові моделі процесорів не зменшувався, фірма Intel проектує їхн з урахуванням стопроцентной єдності знизу вверх. Нові моделі додають до вже наявній безлічі команд нові, що наводить до ускладнення декодерів. В зв'язку з вищезгаданим вибір ефективного засобу декодування дозволить значно прискорити емуляцію команд і зменшити тимчасові параметри декодування і виконання. В даній ситуації применими три засоби декодування. Перший засіб заснований на конструкції типу if.. then. Послідовно перевіряючи ті або інші дільниці коду команди можна поступово вибрати потрібну процедуру. Нажаль цей засіб сильно громіздкий і медлителен. Вихідний текст процедури дешифрації буде позичати багато місця, що погіршить читабельність програми і збільшить імовірність появи помилок. Перебор всіх комбінацій призведе до істотному уповільненню виконання, що в кінці кінців відіб'ється на неудобстві користування емулятором. Інший засіб заснований на застосуванні конструкції типу case. Многі компілятори створюють внутрішні таблиці для швидкого знаходження рядка переходу. Ця обставина значно збільшує швидкодія. Але по-давньому залишається проблема громіздкості вихідного тексту. Третій засіб заснований на застосуванні віртуальних функцій. Виклик віртуальних функцій також заснований на застосуванні таблиць, що не зменшує швидкодії у порівнянні з попереднім засобом. Але цей засіб дозволяє уникнути громіздкого тексту і реалізується при мінімумі мовних операторів. Цей засіб дозволяє створювати ще і ієрархії команд (конструкція case також дозволяє створювати вкладеність, але при отладці виникають ситуації, коли отладчик не може виконати рядок операторів із-за цієї вкладеності). Кожна команда або група команд має свої процедури, що одержують управління тоді, коли конкретна команда вже виявлена, і залишилося тільки виявити форму і типи операторів. Це дозволяє групувати схожі команди, що відрізняються тільки декількома бітами. Отже з певністю можна сказати, що засіб віртуальних функцій об'єднує в собі все гідності попередніх засобів і мінімізує їхні нестатки, а значить, є найбільш ефективним. В основі засобу лежить властивість спадкування, що полягає в тому, що змінної типу покажчика на батьківський клас можна привласнити покажчик на дочірній клас. В батьківському класі є два віртуальних засобу: deasm і die для декодування і виконання відповідно. В дочірніх класах ці засоби перевантажуються засобами, що обробляють тільки свою команду. Таким Чином, маючи тільки покажчик на команду можна виробити її виконання шляхом виклика засобу die, передавши йому відомості про операнди. Отримати покажчик на команду можна, виконавши вибірку першого байта команди і використавши цей байт як індекс покажчика в масиві. В пам'яті створюється масив, елементи якого мають тип покажчика на базовий клас. В модулі control. cpp є процедура init, що виробляє ініціалізацію масиву покажчиками на динамічні створені примірники класів команд і груп. Хоча текст цієї процедури і має порядний обсяг, але він лінійний і читабельний (в тексті немає ветвленнь). Декодування. Для декодування необхідно створити примірник структури _decoder і передати його знаходящейся в модулі control. cpp процедурі step. Дана процедура реалізує стадію 1 декодування в конвейєрі процесора. На цій стадії виробляється дешифрация всіх префіксів і формування RISC-інструкції для виконання. Дана функція також виробляє дешифрацію префіксів, а в якості RISC-інструкції використовується покажчик на об'єкт типу класу конкретної команди. Дешифрация виконується слідуючим чином. З сегменту кодів за адресою, записаному в поле baseadr, вибирається байт. Якщо це префікс, виробляється відповідне корегування полів структури і повторна вибірка по наступній адресі. Якщо вибраний не префікс, виробляється виклик засобу deasm по покажчику з масиву mas, використовуючи вміст вибраного байта як індекс елемента масиву. Примірник структури _decoder передається засобу в якості параметру. Засіб deasm реалізує стадію 2 декодування. На цій стадії формуються виконавчі адреси операндів і видаються відповідні запитання на читання (якщо дозволяє попередня команда). В даному засобі дешифруються байти mod і sib і заповнюються відповідні поля операндів. Для спрощення алгоритму запитання на читання не видаються. Також не формуються лінійні адреси операндов, бо може з'явитися необхідність відобразити мнемонику команди на екрані, а зберігати двічі одна і та же адреса недоцільно. Тут же виробляється заповнення поля *p покажчиком this для наступного виконання команди. Декодування може бути перерване виникненням якого-або виключення. При цьому в поле err записується номер виключення і виробляється повернення з засобу. Згодом замість виконання цієї команди буде вироблена обробка виключення. При поверненні з процедури декодування, передана їй структура буде заповнена, і команда готова до виконання. Виконання. В процесорі 80486 присутня передвиборка команд і конвейєризація. В зв'язку з цим на різноманітних стадіях обробки може бути біля двох десятків команд. В функції емулятора не входить повне відтворення конвейєра реального процесора, тому можна обмежиться передвиборкой однієї команди. Для створення повного еквівалента конвейєра необхідно також в точності повторити роботу всіх внутрішніх блоків процесора, що виходить за рамки даного проекту. Виконанням команд управляє процедура exec_com, що знаходиться в модулі control.cpp. Кожний виклик цієї процедури наводить до виконання однієї команди. В модулі проголошений масив з двох змінних типу _decoder. Процедура виконує команду, що знаходиться в нульовому елементі масиву, копіює в цей елемент вміст першого елемента і викликає процедуру step з першим елементом в якості параметру, попередно викликавши його конструктор. Таким чином досягається часткова емуляція передвиборки. Якщо була виконана команда переходу, процедура викличе декодування для обох елементів масиву. Процедура exec_com здійснює також обчислення вмісту регістру eip при лінійному характері коду. При виконанні команд переходу виконуючі засоби цих команд самі виробляють корекцію покажчика команд. Якщо перед строковой командою стояє префікс повторення, процедура не буде інкрементувати покажчик команд до обнулення регістру-лічильника (е)сх. В цьому випадку також не буде вироблятися декодування черговий команди. В виконавчому механізмі викликає інтерес ще і вибірка операндів. В реальному процесорі стадії 2 декодування команди видається запитання на читання операнда з пам'яті (якщо такий є). Далі команда надходить на стадію виконання. Більшість запитань задовольняється з кэша, тому на цій стадії операнд надходить в АЛУ. В емуляторі обчислення ефективної адреси, сегментація і сторінкове перетворення виконуються на стадії виконання. Процедура conv_adr з модуля structs. cpp виконує компоновку ефективного зміщення операнда в сегменті. Далі зміщення надходить на блок сегментації, реалізований засобами класу _sreg. Виробляється виклик одного з цих засобів шляхом об'єкту, відповідного потрібному сегментному регістру. Після перевірки прав доступу запитання передається засобу типу _mem. Якщо включений сторінковий механізм, засіб pageadr виробляє трансляцію лінійної адреси в фізичний, а також перевірку прав доступу до сторінки. Далі ДОСу передає запитання на читання дільниці своп-файлу. При поверненні по ланцюжку передається безпосереднє значення операнда. Даний механізм доступу уявлений на рисунку 2.1. Даний ланцюжок може перерватися із-за порушень прав доступу. Якщо же операнд знаходиться в регістрі, виробляється виклик засобу відповідного об'єкту типу класу, що подає цей регістр. 2.4 Розробка інтерфейсу з користувачем 2.4.1 Постанова задачі Проаналізувавши кількість інформації, що повинна бути доступній користувачу, можна сказати, що вивести її на екран всю водночас неможливо. Для рішення цієї проблеми є два шляху: розбити інформацію на групи і виводити послідовно шляхом зміни зображення на всьому екрані, або розбити інформацію на більш дрібні групи і надати користувачу вирішувати, що буде відображене на екрані водночас. Перший засіб відрізняється простотою реалізації, але погіршує гнучкість програми і, отже, розширяємість в випадку додання нових функцій. Другий засіб вимагає створення багатовіконного інтерфейсу і застосування подійної моделі керування, але зате досягається значне підвищення вигоди користування і спрощується розширяємість. На підставі вищезгаданого слідує зупинитися на багатовіконному інтерфейсі. Можна також сформулювати два основних вимога до структури інтерфейсу. Це передусім мінімальне втручання в роботу операційної частини і максимальна легкість додання нових компонент без перекомпіляції старих. Для прискорення процесу навчання користувача роботі з емулятором слідує використати набір керуючих комбінацій клавіш з якої-нибудь добре відомої програми, наприклад, середи Borland C++. Слідує також відзначити, що працювати інтерфейс повинен в одному з стандартних режимів адаптерів EGA або VGA, і краще, якщо це буде текстовий режим. Всіма вищенаведеними вимогою буде володіти інтерфейс побудований на базі бібліотеки Turbo Vision фірми Borland. Але результуючий виконавчий файл після приєднання операційної частини буде дуже більшим, що ускладнить отладку і ужорсточить вимогу до вільної пам'яті. 2.4.2 Розробка бібліотеки базових класів Багатовіконний інтерфейс зі спроможністю розширення зручніше будувати з використанням обєктно-орієнтованого підходу. Допоміжні типи. Для зберігання координат об'єктів і їхніх розмірів доцільно розробити допоміжні типи. Клас Tpoint. Об'єкт цього типу зберігає двумірні координати або розмір. Конструктор класу виконує перетворення двох чисел X і Y до типу класу. Перевантажені оператори “+” і “-“ дозволяють виконувати арифметичні дії над обома координатами відразу. Клас Trect. Об'єкт цього типу зберігає інформацію про прямокутник: координати верхнього лівого куту, координати правого нижнього куту і розмір (для вигоди). Конструктори дозволяють створювати об'єкт класу на основі або чотирьох координат, або на основі двох об'єктів типу Tpoint. Перевантажені оператори “+” і “-“ дозволяють змінювати розмір і положення прямокутника, а оператори “|” і ”&” - виконувати бульові функції. Клас Tmask. Об'єкт цього типу зберігає область висновку і дозволяє виконувати з нею бульові функції. Справа В Тому, що при перерисовці вмісту вікна воно може частково перекриються іншим вікном, і щоб при висновку не перерисовувати всі вікна верхнє вікно вичте логічні свою область висновку з області висновку перекриваючого вікна. Процедура, що буде позичатися безпосередно висновком, перевірить область і не буде виводити перекриття знакомісця. Клас TEvent. Об'єкт цього типу зберігає інформацію про виниклу подію. Існує два джерела подій: клавiатура і внутрішній об'єкт. При натиску клавіші всім об'єктам передається контейнер події, в якому містяться код клавіші і стан службових клавіш. При Необхідності оновити інформацію в інших вікнах об'єкт генерує подію оновлення, в цьому випадку передається контейнер, в якому міститься код команди оновлення. Тип події (клавіша або команда) зберігається в поле what. Видні об'єкти. Для відображення інформації на екрані служать видні об'єкти. Видний об'єкт подає собою область висновку обмежену прямокутником. До видних об'єктів відносяться вікна, рядки введення, колонки вибору і ін. Всі ці класи є нащадками класу TView. Клас TView. Об'єкт даного класу подає на екрані прямокутну область, заповнену яким-або кольорем. Засоби класу дозволяють створювати ієрархічне дерево об'єктів і маніпулювати ними. Всі об'єкти на екрані спадкуються від цього класу. Всі засоби класу проголошені як віртуальні і можуть бути перевантажені в похідних класах. Даний клас дозволяє зосередити всіх дії, зв'язані з прорисовкою і арбітражем подій в одному місці і засобом спадкування встроювати їхн в інші класи. Підлеглі об'єкти розташовуються у кольцевому буфері з дуплексними зв'язками. Об'єкт, що знаходиться в кореневому вузлі буферу вважається поточним і перекриваючим всі інші об'єкти даної групи. Клас TPackMenu. Об'єкт даного класу подає елемент меню. Спадкується від TView і зберігає інформацію про назву пункту меню, гарячому клавіші і покажчик на підміню. Допускається відсутність якого-або параметру. Для функціонування елементів меню необхідні декілька інші відношення в ієрархії, тому деякі функції базового класу перевантажені і додані нові зв'язки. Клас TMenu. Об'єкт даного класу передвизначений для зв'язку елементів меню з іншими об'єктами. Цей об'єкт як правило невидим і служить для передачі подій. Клас TWindow. Об'єкт даного класу подає вікно на екрані. В полях класу знаходяться ім'я вікна, допустимий розмір і покажчик на локальне меню. Засоби класу виконують зміну розміру і положення вікна на екрані. Спадкується від TView. Клас TEdit. Об'єкт цього класу подає собою рядок введення. Поля класу зберігають поточне положення і форму курсору, розмір рядка і покажчик на неї, а також покажчик на рядок зі списком допустимих символів. При надходженні події натиску клавіші даний об'єкт перевіряє можливість введення символу, якщо символ знаходиться в рядку допустимих, те він вставляється в результуючий рядок і виводиться на екран. Клас TList. Об'єкт даного класу подає список об'єктів типу рядка. Список завжди розташується вертикально. Весь список в пам'яті не зберігається. При створенні цього об'єкту конструктору в якості параметру передається покажчик на функцію. При Необхідності висновку рядка на екран Об'єкт списку викликає цю функцію з необхідними параметрами для формування рядка. Клас TSetBox. Об'єкт даного класу подає собою групу настанов. В кожному рядку розміщується ім'я параметру і ліворуч від нього ознака настанови в вигляді хрестика в квадратних дужках. Кожний параметр в групі може бути незалежно встановлений або скинутий. Клас TSelect. Об'єкт даного класу подає собою групу взаємозависимих настанов. Він дозволяє вибрати один пункт з списку можливих. Пункти розміщуються вертикально і мають ліворуч ознаку настанови в вигляді звездочки в круглих дужках. Клас TLabel. Об'єкт цього класу подає собою рядок заголовку над об'єктом на екрані. Він встроюються в кольцевий буфер замість об'єкту і транслює події і команди, відрізняя команду включення або виключення фокуса, те є ознаки поточного об'єкту. При включенні фокуса ім'я помітки підсвічується зеленим кольорем, а при виключенні - кольорем тла. Клас TButton. Об'єкт даного класу подає собою кнопку. Для кнопки існує ім'я і гарячий клавіша при натиску введення, коли фокус знаходиться на кнопці або натиск гарячого клавіші в області чинності кнопки об'єкт даного класу поміщає в чергу подій передопреділену команду. Клас TApp. Об'єкт даного класу здійснює зв'язок з ресурсами комп'ютера: клавiатурою і відеоадаптером. Спадкується від класу TView, але перевантажує всіх віртуальні функції зв'язані з трансляцією подій і запитань на перерисовку. Для реєстрації і зберігання подій є черга з політикою FIFO. Включення інтерфейсу в роботу здійснюється шляхом створення примірника даного класу і виклика засобу run (). При цьому відбувається очистка екрану і створення рядка статусу. 2.4.3 Опис роботи бібліотеки базових класів В основі принципу керування лежить концепція взаємодії тільки з найближчими підлеглими об'єктами і безпосереднім родичем. Безпосереднім спілкуванням з ресурсами комп'ютера позичається тільки клас TApp, що дозволяє зберегти контроль над цими ресурсами. Всі класи видних об'єктів спадкуються від класу TView, в якому зосереджені всі основні функції по керуванню об'єктами. При створенні об'єкту йому звичайно передаються координати лівого верхнього куту і розмір. При цьому об'єкт включається в кольцевий буфер якого-або вже існуючого об'єкту. Предком об'єктів верхнього рівня є об'єкт класу TApp. Для включення об'єкту в буфер використовується процедура add (). Об'єкт може бути виключений з буферу викликом засобу sub (). Що виникає подія реєстрація угоди в черзі подій і передається всім підлеглим об'єктам по ієрархії. Першим контейнер події одержує об'єкт, що знаходиться в фокусе те є що є поточним. Він в свою чергу передає контейнер своїм нащадкам. Якщо подія розпізнана виконується його обробка, і керування вертається предку з кодом повернення, що говорять про розпізнанні події. Якщо подія була розпізнана, наступному об'єкту воно не передається. Кожний об'єкт володіє мінімальним набором функцій висновку. В даний набір входять: Функція висновку рядка з означеним атрибутом; Функція висновку рядка з поточним атрибутом (поточний атрибут формується з кольору тла об'єкту, що задається при створенні, і контрасного йому кольору символів); Функція заповнення прямокутної області певним символом з означеним або поточним атрибутами; Функція заповнення прямокутної області означеним атрибутом без зміни вмісту символьної частини знакомісця. Виклик будь-який з вищенаведених функцій проводить до передачі керування тієї частини об'єкту, що була наслідувана від класу TView. Базові функції даного класу створюють область висновку типу TMask необхідного розміру і заповнення цієї області переданими символами і атрибутами. Далі покажчик на область висновку передається предку, що виробляє виклик функції workmask () перекриваючих об'єктів. Ця функція виробляє виключення знакомісць, що безпосередно перекриваються даним об'єктом. Так по ієрархічному дереву область висновку доходить до об'єкту класу TApp, що виробляє аналіз області і висновок неперекритых знакомісць на екран. При необхідності перерисовки будь-якої області на екрані викликається функція draw (), якої в якості параметру передаються координати прямокутної області, що повинна перерисоватись. Якщо ця область не перетинається з прямокутником об'єкту, керування вертається назад без виробництва будь-яких дій. Інакше виробляється виклик функції перерисовки підлеглих об'єктів. Слідує відзначити, що підлеглий об'єкт не може “вылезти” за область свого предка. Дана обставина не виконується тільки для об'єктів, що подають пункти меню. Їхні нащадки розташуються поверх зі зміщенням. 2.4.4 Опис об'єктів віконного інтерфейсу Всі класи, безпосередно стосовні до віконного інтерфейсу емулятора спадкуються від класу TWindow. Це дозволяє зосередитися на створенні функціональності не піклуючись про взаємодію цих об'єктів. Клас TCpu. Об'єкт даного класу подає собою вікно з дізасемблірованим фрагментом коду відлагодживаємої програми. В конструкторі об'єкту створюється примірник списку для висновку рядків команд. Функція handleEvent () перевантажена з метою відспостереження комбінацій клавіш, керуючим процесом виконання програми. Клас TFile. Об'єкт даного класу подає собою вікно зі списком файлів поточного каталогу. Тут також використаний об'єкт типу списку. При створенні об'єкту відбувається читання поточного каталогу і виділення файлів типу“. com” і”. exe”, а також вкладених директорій. Клас TRegs. Об'єкт даного класу подає собою вікно, в якому відображений стан регістрів загального призначення процесора ,що емулюється. Кожний рядок містить метку з ім'ям регістру і рядок введення з вмістом регістру. Натиском клавіші “Tab” виробляється зміна фокуса на наступний по списку регістр. В якості допустимих символів в рядок введення передаються символи, що використаються для відображення шістнадцятеричних чисел. Клас TSregs. Об'єкт даного класу подає собою вікно, в якому відображений стан сегментних регістрів, а також регістру локальної таблиці дескрипторів і регістру сегменту стану задачі. Кожний рядок містить метку з ім'ям регістру і три рядки введення для бази, розміру і атрибутів регістру. Для рядків введення бази і розміру застосовуються символи, що використовуються в написанні шістнадцятеричних чисел, а для рядка введення атрибутів - символи для позначки двоїчних чисел. Клас TSysReg. Об'єкт даного класу подає собою вікно, в якому відображається вміст системних регістрів. В дану групу війшли: покажчик команд EIP, системні регістри CRx, а також регістр таблиця глобальних дескрипторів і регістр таблиці переривань. Для відображення також використовуються метки і рядки введення. Клас TFags. Об'єкт даного класу подає собою вікно, в якому відображається вміст регістру прапорів. Для відображення прапорів і зміни їхнього стану застосований об'єкт типу TSetBox. Коли вікно активно (знаходиться в фокусі) клавішами зі стрілками можна міняти поточний прапор, а клавішею пробілу або встановлювати його, або скидати. Клас TDump. Об'єкт даного класу подає собою вікно, в якому відображається вміст вибраної дільниці пам'яті. Для відображення пам'яті використаний об'єкт типу списку. Це вікно має локальне меню, що викликається по клавішам “Alt+F10”, і служить для підтримання діалога з користувачем при зміні адреси дільниці ,що відображається або засобу подання інформації в вікні. Клас TStack. Об'єкт даного класу подає собою вікно, в якому відображений поточний кадр стека відлагодживаємої програми. В кожному рядку відображається зміщення і вміст осередка. Розрядність осередка залежить від розрядність сегменту стека (16 або 32 розряду). Для відображення кадру стека використаний об'єкт типу списку. Поточний осередок стека (на яку вказує регістр (e) sp) помічається стрілкою ліворуч від адреси. ----------------------- Exec_com Conv_adr Компоненти адреси Ефективне зміщення _sreg Масив srg _op.seg Ефективне зміщення _mem _mem.pageadr драйвер Himem.sys Лінійна адреса дані дані Рисунок 2.1. Механізм доступа до операнду