Все про Assembler українською мовою на сайті net.kr.ua

 

:: Меню ::

Головна
Введення
Архітектура реального режиму
Основи програмування
Команди і алгоритми
Розширені можливості сучасних мікропроцесорів
Система команд процесорів Intel
Книга для гостей
Контакти
Добавити у вибране

:: Друзі ::

 
 

:: Лічильники ::

=

 

 

 

 

fff00e50

Основи захищеного режиму

Мікропроцесори Pentium, так само, як і його попередники (починаючи з 80268), можуть працювати в двох режимах: реальної адреси і віртуальної захищеної адреси. Зазвичай ці режими називають просто реальним і захищеним. У реальному режимі 32-розрядні мікропроцесори функціонують фактично так само, як МП 86 з підвищеною швидкодією і розширеним набором команд. Багато вельми привабливих можливостей мікропроцесорів принципово не реалізуються в реальному режимі, який введений лише для забезпечення сумісності з попередніми моделями процесорів. Характерною особливістю реального режиму є обмеження об'єму оперативної пам'яті, що адресується, величиною 1 Мбайт.
Тільки переклад мікропроцесора в захищений режим дозволяє повністю реалізувати всі можливості, загиджені в його архітектуру і недоступні в реальному режимі. Сюди можна віднести:
- збільшення простору, що адресується, до 4 Гбайт;
- можливість працювати у віртуальному адресному просторі, що перевищує максимально можливий об'єм фізичної пам'яті і що становить величезну величину 64 Тбайт;
- організація багатозадачного режиму з паралельним виконанням декількох програм (процесів). Власне кажучи, багатозадачний режим організовує багатозадачна операційна система, проте мікропроцесор надає необхідний для цього режиму могутній і надійний механізм захисту завдань один від одного за допомогою чотирьохрівневої системи привілеїв;
- сторінкова організація пам'яті, що підвищує рівень захисту завдань
один від одного і ефективність їх виконання.
При включенні мікропроцесора в нім автоматично встановлюється режим реальної адреси. Перехід в захищений режим здійснюється програмно шляхом виконання відповідної послідовності команд. Оскільки багато деталей функціонування мікропроцесора в реальному і захищеному режимах істотно розрізняються, програми, призначені для захищеного режиму, мають бути написані особливим чином. При цьому відмінності реального і захищеного режимів настільки великі, що програми реального режиму не можуть виконуватися в захищеному режимі і навпаки. Іншими словами, реальний і захищений режими не сумісні.
Архітектура сучасного мікропроцесора незвичайно складна. Такими ж складними виявляються і засоби захищеного режиму, а також програми, що використовують ці засоби. З іншого боку, при вирішенні багатьох прикладних завдань (наприклад, при відладці додатків Windows, що працюють, природно, в захищеному режимі), немає необхідності в розумінні всіх деталей функціонування захищеного режиму; досить мати знайомство з його основними поняттями. У справжньому розділі дається короткий опис основ захищеного режиму і його найбільш важливих структур і алгоритмів функціонування.
Мабуть, найбільш важливою відмінністю захищеного режиму від реального є інший принцип формування фізичної адреси. Пригадаємо, що в реальному режимі фізична адреса елементу пам'яті, що адресується, складається з двох компонентів - сегментної адреси і зсуву. Обидва компоненти мають розмір 16 битий, і процесор, звертаючись до пам'яті, користується наступним правилом обчислення фізичної адреси:

Фізична адреса = сегментна адреса * 16 + зсув

І сегментна адреса, і зсув не можуть бути більше Ffffh, звідки слідують два найважливіші обмеження реального режиму: об'єм адресного простору складає всього 1 Мбайт, а сегменти не можуть мати розміру, що перевищує 64 Кбайт.
У захищеному режимі програма як і раніше складається з сегментів, що адресуються за допомогою 16-розрядних сегментних регістрів, проте місцеположення сегментів у фізичній пам'яті визначається іншим способом.
У сегментні регістри в захищеному режимі записуються не сегментні адреси, а так звані селектори, биті 3...15 яких розглядаються, як номери (індекси) елементів спеціальної таблиці, що містить дескриптори сегментів програми. Таблиця дескрипторів зазвичай створиться операційною системою захищеного режиму (наприклад, системою Windows) і, як правило, недоступна програмі. Кожен дескриптор таблиці дескрипторів має розмір 8 байт, і в нім зберігаються всі характеристики, необхідні процесору для обслуговування цього сегменту. Серед цих характеристик необхідно виділити насамперед дві: адресу сегменту і його довжину (мал. 4.4).

Мал. 4.4. Дескриптори сегментів і їх селектори.

Під адресу сегменту в дескрипторі виділяється 32 битий, і, таким чином, сегмент може починатися в будь-якій точці адресного простору об'ємом 23- = 4 Гбайт. Це адресний простір носить назву лінійну. У простому випадку, коли вимкнене сторінкове перетворення, про яке мова йтиме пізніше, лінійні адреси відповідають фізичним. Таким чином, процесор може працювати з оперативною пам'яттю об'ємом до 4 Гбайт.
Як і в реальному режимі, адреса осередку, що адресується, обчислюється процесором, як сума базової адреси сегменту і зсуву:
Лінійна адреса = базова адреса сегменту + зсув
У 32-розрядних процесорах зсув має розмір 32 битий, тому максимальна довжина сегменту складає 2" = 4 Гбайт.
На мал. 4.4 приведений гіпотетичний приклад програми, що складається з трьох сегментів, перший з яких має довжину 1 Мбайт і розташований на початку адресного простору, другий, розміром 100 Кбайт, впритул примикає до першого, а третій, такий, що має розмір всього 256 байт, розташований в середині дев'ятого по рахунку мегабайта.
Адреси, використовувані програмою для звернення до осередків пам'яті, і що складаються завжди з двох компонентів - селектори і зсуви - іноді називаються віртуальними. Система сегментної адресації перетворить віртуальні адреси в лінійні. Оскільки таблиця дескрипторів, за допомогою якої здійснюється це перетворення, зазвичай недоступна програмі, програма може не знати, в яких саме ділянках логічного адресного простору знаходяться це компоненти. Фактично це зводиться до того, що, завантажуючи програму в пам'ять, ви не знаєте, в яких місцях пам'яті знаходитимуться її сегменти, і який буде порядок їх розміщення. Програмістові доступні тільки віртуальні адреси, перетворення ж їх в лінійних і потім у фізичних бере на себе операційна система.
Який об'єм віртуального адресного простору? Програма указує номер потрібного нею дескриптора за допомогою селектора, в якому для індексу дескриптора відведене 13 битий. Звідси витікає, що в дескрипторній таблиці може бути до 1" = 8. До дескрипторів. Проте насправді їх в два рази більше, оскільки програма може працювати не з однією, а з двома дескрипторними таблицями - однією глобальною, такою, що розділяється всіма виконуваними завданнями, і одному локальному, належному конкретному завданню. У селекторі передбачений спеціальний біт (битий 2), стан якого говорить про тип необхідній програмі дескрипторної таблиці. Таким чином, всього програмі можуть бути доступні 214 = 16 До дескрипторів, тобто 16 До сегментів. Оскільки розмір кожного сегменту, визначуваний максимальною велічиной зсуви, може досягати 2-1 = 4 Гбайт, об'єм віртуального адресного простору виявляється рівним 16 До * 4 Кбайт = = 64 Тбайт.
Реально, проте, оперативна пам'ять комп'ютера з 32-розрядною адресною шиною не може бути більше 4 Гбайт, тобто при зроблених вище припущеннях (16 До сегментів розміром 4 Гбайт кожен) в пам'яті може поміститися максимум один сегмент із понад 16 тисяч. Де ж знаходитимуться всі останні?
Повний об'єм віртуального простору може бути реалізований тільки за допомогою багатозадачної операційної системи, яка зберігає всі невживані зараз сегменти на диску, завантажуючи їх в пам'ять в міру необхідності. Зрозуміло, якщо ми хочемо повністю реалізувати можливості, закладені в сучасні процесори, нам буде потрібно диск досить великого об'єму - 64 Тбайт. Проте і при нинішніх скромніших технічних засобах (пам'ять до 100 Мбайт, жорсткий диск до 10 Гбайт) принцип віртуальної пам'яті використовується всіма багатозадачними операційними системами з великою ефективністю. З іншого боку, для прикладного програміста це питання не представляє особливого інтересу, оскільки скидання сегментів на диск і підкачка їх з диска здійснюються операційною системою, а не програмою, і втручання цю процедуру навряд чи доцільно.
Як вже наголошувалося, адреса, що обчислюється процесором на основі селектора і зсуву, відноситься до лінійного адресного простору, не обов'язково співпадаючого з фізичним. Перетворення лінійних адрес у фізичних здійснюється за допомогою так званої сторінкової трансляції, що частково реалізовується процесором, а частково - операційною системою. Якщо сторінкова трансляція вимкнена, всі лі-нейниє адреси в точності збігаються з фізичними; якщо сторінкова трансляція включена, то лінійні адреси перетворяться у фізичних відповідно до вмісту сторінкових таблиць (мал. 4.5).

Мал. 4.5. Ланцюжок перетворень віртуальної адреси у фізичний.

Сторінкою називається зв'язна ділянка лінійного або фізичного адресного простору об'ємом 4 Кбайт. Програма працює в лінійному адресному просторі, не підозрюючи про існування сторінкового перетворення або навіть самих сторінок. Механізм сторінкової трансляції відображає логічні сторінки на фізичних відповідно до інформації, що міститься в сторінкових таблицях. В результаті окремі 4х-килобайтовис ділянки програми можуть реально знаходитися в будь-яких незв'язкових один з одним 4х-килобайтовых областях фізичної пам'яті (мал. 4.6). Порядок розміщення фізичних сторінок в пам'яті може не відповідати (і зазвичай не відповідає) порядку проходження логічних сторінок. Більш того, деякі логічні сторінки можуть перекриватися, фактично співіснуючи в одній і тій же області фізичної пам'яті.
Сторінкова трансляція є досить складним механізмом, в якому беруть участь апаратні засоби процесора і перетворення, що знаходяться в пам'яті таблиці. Призначення і взаємодія елементів системи сторінкової трансляції схематично зображене на мал. 4.7.
Система сторінкових таблиць складається з двох рівнів. На першому рівні знаходиться каталог таблиць сторінок (або просто каталог сторінок) - резидентна в пам'яті таблиця, що містить 1024 4х-байтовых поля з адресами таблиць сторінок. На другому рівні знаходяться таблиці сторінок, кожна з яких містить так само 1024 4х-байтовых поля з адресами фізичних сторінок пам'яті. Максимально можливе число таблиць сторінок визначається числом полів в каталозі і може доходити до 1024. Оскільки розмір сторінки складає 4 Кбайт, 1024 таблиці по 1024 сторінки перекривають весь адресний простір (4 Гбайт).


Мал. 4.6. Відображення логічних адрес на фізичних.

Мал. 4.7. Сторінкова трансляція адрес.

Не все 1024 таблиці сторінок мають обов'язково бути в наявності (до речі, вони зайняли б в пам'яті задоволено багато місця - 4 Мбайт). Якщо програма реально використовує лише частину можливого лінійного адресного простору, а так завжди і буває, то невживані поля в каталозі сторінок позначаються, як відсутні. Для таких полів система, економлячи пам'ять, не виділяє сторінкові таблиці.
При включеній сторінковій трансляції лінійна адреса розглядається, як сукупність три полів: 10-бітового індексу в каталозі сторінок, 10-бітовбго індексу у вибраній таблиці сторінок і 12-бітового зсуву у вибраній сторінці. Нагадаємо, що лінійна адреса утворюється шляхом складання базової адреси сегменту, узятого з дескриптора сегменту, і зсуву в цьому сегменті, наданого програмою.
Старші 10 біт лінійної адреси утворюють номер поля в каталозі сторінок. Базова адреса каталога зберігається в одному з регістрів процесора, що управляють, конкретно, в регістрі Cr3. Через те, що каталог сам є сторінкою і вирівняний в пам'яті на межу 4 Кбайт, в регістрі Cr3 для адресації до каталога використовуються лише старші 20 битий, а молодші 12 біт зарезервовані для майбутніх застосувань.
Поля каталога мають розмір 4 байт, тому індекс, що витягує з лінійної адреси, зрушується вліво на 2 битий (тобто умножається на 4) і отримана величина складається з базовою адресою каталога, утворюючи адресу конкретного поля каталога. Кожне поле каталога містить фізична базова адреса однієї з таблиць сторінок, причому, оскільки таблиці сторінок самі є сторінками і вирівняні в пам'яті на межу 4 Кбайт, в цій адресі значущими є тільки старші 20 битий.
Далі з лінійної адреси витягується середня частина (биті 12...21), зрушується вліво на 2 битий і складається з базовою адресою, що зберігається у вибраному полі каталога. В результаті утворюється фізична адреса сторінки в пам'яті, в якому знову ж таки використовуються тільки старші 20 битий. Ця адреса, що розглядається, як старші 20 біт фізичної адреси осередку, що адресується, носить назву сторінкового кадру. Сторінковий кадр доповнюється з правого боку молодшими 12 бітами лінійної адреси, які проходять через сторінковий механізм без зміни і грають роль зсуву усередині вибраної фізичної сторінки.
Розглянемо абстрактний приклад, що дозволяє прослідкувати ланцюжок перетворення віртуальної адреси у фізичний. Хай програма виконує команду

mov Еах,ds:[ЕВХ]

при цьому вміст DS (селектор) складає 1167і, а вміст ЕВХ (зсув) 31678u.
Старші 13 біт селектора (число 116u) утворюють індекс дескриптора в системній дескрипторній таблиці. Кожен дескриптор включає досить великий об'єм інформації про конкретний сегмент і, зокрема, його лінійну адресу. Хай в елементі дескрипторної таблиці з номером 116h записана лінійна адреса (базова адреса сегменту) 0l0sl000h.
Тоді повна лінійна адреса осередку, що адресується, визначиться, як сума базової адреси і зсуву:


Базова адреса сегменту 0l0sl000h

Зсув 0003167811

Повна лінійна адреса 0108267811

При вимкненій табличній трансляції величина 010826У811 буде абсолютною фізичною адресою осередку, вміст якого має бути прочитане приведеною вище командою mov. Легко зміркувати, що цей осередок знаходиться на самому початку 17-го мегабайта оперативної пам'яті.
Подивимося, як утворюватиметься фізична адреса при використанні сторінкової трансляції адрес. Отриману лінійну адресу треба розділити на три складові для виділення індексів і зсуву (мал. 4.8)

Мал. 4.8. Приклад лінійної адреси.

Індекс каталога складає 4h. Множення його на 4 дасть зсув від початку каталога. Цей зсув рівний 10h.
Індекс таблиці сторінок опинився рівним 82h. Після множення на 4 отримуємо зсув в таблиці сторінок, рівне в даному випадку 210h.
Припустимо, що регістр Cr3 містить число S000h. Тоді фізична адреса осередку в каталозі, звідки треба отримати адресу закріпленої за даною ділянкою програми таблиці сторінок, складе S000h + l0h = 8010h. Хай за цією адресою записано число 4602lh. Його 12 молодших бітів складають службову інформацію (зокрема, битий 1 свідчить про присутність цієї таблиці сторінок в пам'яті, а битий 5 говорить про те, що до цієї таблиці вже були звернення), а старші біти, тобто число 46000h утворюють фізична базова адреса таблиці сторінок. Для отримання адреси необхідного елементу цієї таблиці до базової адреси треба додати зсув 210h. Результуюча адреса складе 462101г.
Вважатимемо, що за адресою 4621011 записано число 01FF502111. Відкинувши службові біти, отримаємо адресу фізичної сторінки в пам'яті 01ff5000u. Ця адреса завжди закінчується трьома нулями, оскільки сторінки вирівняні в пам'яті на межу 4 Кбайт. Для отримання фізичної адреси осередку, що адресується, слід заповнити 12 молодших біт отриманої адреси бітами зсуву з лінійної адреси нашого осередку, в яких в нашому прикладі записано число 678h. У результаті отримуємо фізичну адресу пам'яті 01FF567811, розташований в кінці 32-го Мбайта.
Як видно з цього прикладу, і із сторінковою трансляцією, і без неї обчислення фізичних адрес осередків, що адресуються, виконується в захищеному режимі зовсім не так, як в реальному. Неприємним практичним наслідком правил адресації захищеного режиму є вже згадувана "відірваність" прикладної програми від фізичної пам'яті. Програміст, що відладжує програму захищеного режиму (наприклад, додаток Windows), може легко заглянути в сегментні регістри і визначити селектори, виділені програмі. Проте селектори абсолютно нічого не говорять про фізичні адреси, використовувані програмою. Фізичні адреси знаходяться в таблицях дескрипторів, а ці таблиці недоступні прикладній програмі. Таким чином, програміст не знає, де в пам'яті знаходиться його програма або використовувані нею області даних.
З іншого боку, використання в процесі перетворення адрес захищених системою таблиць має свої переваги. Зазвичай багатозадачна операційна система створює для кожного виконуваного завдання свій набір таблиць перетворення адрес. Це дозволяє кожному із завдань використовувати весь діапазон віртуальних адрес, при цьому, хоча для різних завдань віртуальні адреси можуть збігатися (і, як правило, принаймні частково збігаються), проте сегментне і сторінкове перетворення забезпечують виділення для кожного завдання неспівпадаючих областей фізичної пам'яті, надійно ізолюючи віртуальні, адресні простори завдань один від одного.
Повернемося тепер до таблиць дескрипторів і розглянемо їх детальніше. Існує два типи дескрипторних таблиць: таблиця глобальних дескрипторів (GDT від Global Descriptor Table) і таблиці локальних дескрипторів (LDT від Local Descriptor Table) .обично для кожної з цих таблиць в пам'яті створюються окремі сегменти, хоча в принципі це не обов'язково. Таблиця глобальних дескрипторів існує в єдиному екземплярі і зазвичай належить операційній системі, а локальних таблиць може бути багато (це типово для багатозадачного режиму, в якому кожному завданню призначається своя локальна таблиця).
Віртуальний адресний простір ділиться на дві рівні половини. До однієї половини звернення відбувається через GDT, до іншої половини через LDT. Як вже наголошувалося, весь віртуальний простір складається з 214 сегментів, з яких 213 сегментів адресуються через GDT, і ще 213 - через LDT.
Коли багатозадачна система перемикає завдання, глобальна таблиця залишається незмінною, а поточна локальна таблиця замінюється на локальну таблицю нового завдання. Таким чином, половина віртуального простору в принципі доступна всім завданням в системі, а половина перемикається від одного завдання до іншої у міру перемикання самих завдань.
Для програмування захищеного режиму і навіть для відладки прикладних програм, що працюють в захищеному режимі, корисно уявляти собі структуру дескриптора і сенс його окремих полів. Слід відмітити, що існує декілька типів дескрипторів, яким властиві різні формати. Так, дескриптор сегменту пам'яті (найбільш поширений тип дескриптора) відрізняється від дескриптора шлюзу, використовуваного, зокрема, для обслуговування переривань. Розглянемо формат дескриптора пам'яті (мал. 4.9).

Мал. 4.9. Формат дескриптора пам'яті.

Як видно з малюнка, дескриптор займає 8 байт. У байтах 2...4 і 7 записується лінійна базова адреса сегменту. Повна довжина базової адреси - 32 битий. У байтах 0-1 записуються молодші 16 біт межі сегменту, а в молодших чотири біта байта атрибутів 2 - біти, що залишилися, 16...19. Межею сегменту називається номер його останнього байта. Ми бачимо, що межа описується 20-у бітами, і її чисельне значення не може перевищувати 1м. Проте, одиниці, в яких задається межа, можна змінювати, що здійснюється за допомогою біта дробу G (битий 7 байта атрибутів 2). Якщо G=0, межа указується в байтах; якщо 1 - в блоках по 4 Кбайт. Таким чином, розмір сегменту можна задавати з точністю до байта, але тоді він не може бути більше 1 Мбайт; якщо ж встановити G=l, то сегмент може досягати 4 Гбайт, проте його розмір буде кратний 4 Кбайт. База сегменту і в тому, і в іншому випадку задасться з точністю до байта.
Розглянемо тепер атрибути сегменту, які займають два байти дескриптора.
Битий A (Accessed, було звернення) встановлюється процесором в той момент, коли в який-небудь сегментний регістр завантажується селектор даного сегменту. Далі процесор цей біт не скидає, проте його може скинути програма (зрозуміло, якщо вона має доступ до вмісту дескриптора, що зазвичай є прерогативою операційної системи). Аналізуючи біти звернення різних сегментів, програма може судити про те, чи було звернення до даного сегменту' після того, як вона скинула біт А.
Тип сегменту займає 3 битий (іноді битий А включають в полі типу, і тоді тип займає 4 битий) і може мати 8 значень. Тип визначає правила доступу до сегменту. Так, якщо сегмент має тип 1, для нього дозволено читання і запис, що характерний для сегментів даних. Призначивши сегменту тип 0, ми вирішимо тільки читання цього сегменту, захистивши його тим самим від будь-яких модифікацій. Тип 4 позначає дозвіл виконання, що характерний для сегментів команд. Використовуються і інші типи сегментів.
Підкреслимо, що захист сегментів пам'яті від несанкціонованих його типом дій виконується не програмою, і навіть не операційною системою, а процесором на апаратному рівні. Так, при спробі запису в сегмент типу 0 виникне так зване виключення загального захисту. Виключенням називається внутрішнє переривання, що порушується процесором при виникненні яких-небудь неправильних із його точки зору ситуацій. Спроба запису в сегмент, для якого запис заборонений, і відноситься до такого роду ситуаціям. Виключенню загального захисту відповідає вектор 13, в якому повинна знаходитися адреса обробника цього виключення.
Варто ще звернути увагу на тип 4. Для сегменту команд вирішується тільки виконання, але не запису і навіть не читання. Це означає, що в захищеному режимі програма не може випадково залізти в свій сегмент команд і затерти його; не може вона також і свідомо модифікувати команди в процесі свого виконання - методика, іноді використовувана в програмах реального режиму для захисту від їх розшифровки допитливими програмістами
Битий 4 байти атрибутів 1 є ідентифікатором сегменту. Якщо він дорівнює 1, як це показано на мал. 4.9, дескриптор описує сегмент пам'яті. Значення цього біта 0 характеризує дескриптор системного сегменту.
Поле DPL (Descriptor Privilege Level, рівень привілеїв дескриптора) служить для захисту програм один від одного. Рівень привілеїв може набувати значень від 0 (максимальні привілеї) 3 (мінімальні). Програмам операційної системи зазвичай призначається рівень 0, прикладним програмам - рівень 3, внаслідок чого унеможливлюється некоректним програмам зруйнувати операційну систему. З іншого боку, якщо прикладна програма сама виконує функції операційної системи, переводячи процесор в захищений режим і працюючи далі в цьому режимі, її сегментам слід призначити найвищий (нульовий) рівень привілеїв, що відкриє їй доступ до всіх засобів захищеного режиму.
Битий Р говорить про присутність сегменту в пам'яті. В основному він використовується для організації віртуальної пам'яті. За допомогою цього біта система може визначити, чи знаходиться необхідний сегмент в пам'яті, і при необхідності завантажити його з диска. В процесі вивантаження непотрібного поки сегменту на диск битий Р в його дескрипторі скидається.
Молодша половина байта атрибутів 2 зайнята старшими бітами межі сегменту. Битий AVL (від Available, доступний) не використовується і не аналізується процесором і призначений для використання прикладними програмами.
Битий D (Default, умовчання) визначає розмір, що діє за умовчанням, для операндів і адрес. Він змінює характеристики сегментів двох типів: виконуваних і стека. Якщо битий D сегменту команд рівний 0, в сегменті за умовчанням використовуються 16-бітові адреси і операнди, якщо 1 - 32-бітові.
Атрибут сегменту, що діє за умовчанням, можна змінити на протилежний за допомогою префіксів заміни розміру операнда (66h) і заміни розміру адреси (67п). Таким чином, для сегменту з D=0 префікс 66h перед деякою командою примушує її розглядати свої операнди, як 32-бітові, а для сегменту з D=l той же префікс 66h, навпаки, зробить операнди 16-бітовими. В деяких випадках транслятор сам включає в об'єктний модуль необхідні префікси, в інших випадках їх доводиться вводити в програму "уручну".
Розглянемо тепер для прикладу просту програму, яка, будучи запущена звичайним способом під управлінням MS-DOS, перемикає процесор в захищений режим, виводить на екран для контролю символ, переходить назад в реальний режим (щоб не вивести комп'ютер з рівноваги) і завершується стандартним для DOS образом.
Для того, щоб наша програма могла б хоч щось зробити в захищеному режимі, для неї необхідно створити середовище захищеного режиму, насамперед, таблицю глобальних дескрипторів з описом всіх сегментів, з якими програма працюватиме. Окрім нас ніхто цю таблицю (при роботі в DOS) не створить. Таким чином, наша програма в якійсь мірі виконуватиме функції операційної системи захищеного режиму.
Для практичного дослідження захищеного режиму доведеться виконати деяку роботу по переконфігурації комп'ютера. У наш час комп'ютери зазвичай конфігуруються так, що при їх включенні відразу завантажується система Windows. Роботи, для яких потрібний DOS, виконуються або в режимі емуляції DOS, або в сеансі DOS, організовуваному системою Windows. Для запуску прикладної програми захищеного режиму такий спосіб не годиться. Нам знадобиться DOS в "чистому вигляді", без слідів Windows. Більш того, перед запуском програми необхідно вивантажити всі драйвери обслуговування розширеної пам'яті (HIMEM.SYS і Emm386.EXE) і програми, що використовують розширену пам'ять, наприклад, SMARTDRV.EXE. Краще всього завантажувати DOS з системної дискети, підготувавши файли CONFIG.SYS і AUTOEXEC.BAT у мінімальному варіанті.
Обговорюючи на початку цього розділу основи захищеного режиму, ми не торкнулися багато, зокрема принципові питання, з якими доведеться зіткнутися при написанні працездатної програми. Необхідні пояснення будуть дани в кінці цього розділу.


Приклад 4-4. Програмування захищеного режиму


.586р ;Разрешение трансляції всіх команд МП 586
;Структура для опису дескрипторів сегментів
dcr struc ;Имя структури
limit dw 0 ;Граница (биті 0...15)
base_l dw 0 ;База, биті 0...15
base_m db 0 ;База, биті 16...23
attr_l db 0 ;Байт атрибутів 1
attr_2 db ;Граница (биті 16...19) і атрибути 2
base_h db 0 ;База, биті 24...31
dcr ends ;
data segment use16 ;
;Таблиця глобальних дескрипторів GDT
gdt_null dcr <0,0,0,0,0,0> ;Селектор 0-обов'язковий
;нульовий дескриптор
gdt_data dcr <data_size-l,0,0,92h,0,0> ;Селектор 8
;сегмент даних
gdt_code dcr <code_size-l,0,0,98h,0,0>;Селектор 16
;сегмент команд
gdt_stack dcr <511,0,0,92h,0,0> ;Селектор 24 -
;сегмент стека
gdt_screen dcr <4095,B000h,OBh,92h,0,0> ;Селектор 32
;відеобуфер
pdescr df 0 ;Псевдодескриптор для команди Igdt
data_size=$-gdt_null ;Размер сегменту даних
data ends ;Конец сегменту даних
text segment use16 ;Сегмент команд, 16-розрядний режим
assume Cs:text,ds:data;
main proc ;
xor Eax,eax ;Очистим ЕАХ
mov Ax,data ;Загрузим у DS сегментний
mov Ds,ax ;адрес сегменту даних
;Обчислимо 32-бітову лінійну адресу сегменту даних
;и завантажимо його в дескриптор сегменту даних в GDT.
;У регістрі АХ вже знаходиться сегментна адреса.
;Помножимо його на 16 зрушенням вліво на 4 бита
shl Еах,4 ;В ЕАХ лінійна базова адреса
mov EBP, ЕАХ ;Сохраним його в ЕВР для майбутнього
mov Bx,offset gdt_data ;У ВХ адреса дескриптора
mov [BX].base_l,AX ;Загрузим молодшу частину бази
rol Еах,16 ;Обмен старшої і молодшої половин ЕАХ
mov [BX].base_m,AL ;Загрузим середню частину бази
;Обчислимо 32-бітову лінійну адресу -сегмента команд
;и завантажимо його в дескриптор сегменту команд в GDT
хог ЕАХ, ЕАХ ;Очистим ЕАХ
mov Ax,cs ;Сегментный адреса сегменту команд
shl Еах,4 ;В ЕАХ лінійна базова адреса
mov Bx,offset gdt_code ;У ВХ адреса дескриптора
mov [BX] .base_l,AX ;Загрузим молодшу частину бази
rol Еах,16 ;Обмен старшої і молодшої половин ЕАХ
mov [BX].base_m,AL ;Загрузим середню частину бази
;Обчислимо 32-бітову лінійну адресу сегменту стека
хог ЕАХ, ЕАХ ;Все, як і для інших
mov Ax,ss ;дескрипторов
shl Еах,4
mov Bx,offset gdt_stack
mov [BX].base_l,AX
rol Eax,16
mov [BX].base_m,AL
;Підготуємо псевдодескриптор pdescr для завантаження регістра GDTR
mov dword ptr pdescr+2,ebp ;База GDT
mov word ptr pdescr, 39 ;Граница GDT
Igdt pdescr ;Загрузим регістр GDTR
cli ;Запрет переривань
;Переходимо в захищений режим
mov Eax,cr0 ;Получим вміст Cr0
or Eax,1 ;Установим біт захищеного режиму
mov Cro,еах ;Запишем назад в Cr0
;---------------------------------------------------------;
;Тепер процесор працює в захищеному режимі ;
;---------------------------------------------------------;
;Завантажуємо в Cs:ip селектор:смещеніє точки continue
db Oeah ;Код команди far jmp
dw offset continue ;Смещение
dw 16 ;Селектор сегменту команд
continue:
;Робимо такими, що адресуються дані
mov AX, 8 ;Селектор сегменту даних
mov Ds,ax ;Загрузим у DS
;Робимо таким, що адресується стік
mov Ax,24 ;Селектор сегменту стека
mov Ss,ax ;Загрузим у SS
;Ініціалізували ES і виводимо символ
mov Ax,32 ;Селектор сегменту відеобуфера
mov Es,ax ;Загрузим у ES
mov Bx,2000 ;Начальное зсув на екрані
mov Ax,09fofh ;Символ з атрибутом
mov ES : [BX], АХ;Вывод у відеобуфер
;Повернемося в реальний режим
mov gdt_data.limit,0FFFFh ;Установим
mov gdt_code.limit,0FFFFh ;значение межі
mov gdt_stack.limit,0FFFFh;для реального
mov gdt_screen.limit,0FFFFh ;режима
mov Ax,8 ;Загрузим тіньовий регістр
mov Ds,ax ;сегмента даних
mov Ax,24 ;To же для
mov Ss,ax ;стека
mov Ax,32 ;To же
mov ES, AX ;для регістра ES
;Виконаємо дальній перехід, щоб наново завантажити
;селектор у CS і модифікувати його тіньовий регістр
db0eah ;Код команди jmp far
dwoffset go ;Смещение точки переходу
dw!6 ;Селектор сегменту команд
;Перемкнемо режим процесора
go: mov Eax,cr0 ;Получим вміст Cr0
and Eax,0fffffffeh;Сбросим біт РЕ
mov Cr0,eax ;Запишем назад в Cr0
db0eah ; Код команди far jmp
dwoffset return ;Смещение точки переходу
dwtext ;Сегментный адреса
;---------------------------------------------;
;Тепер процесор знову працює в реальному режимі ;
;---------------------------------------------;
;Відновимо операційне середовище реального режиму
return: mov Ax,data ;Загрузим сегментний
mov Ds,ax ;регистр DS
mov Ax,stk ;Загрузим сегментний
mov Ss,ax ;регистр SS
mov Sp,512 ;Восстановим SP
sti ;Разрешим переривання
mov Ax,4c00h ;Завершим програму
;звичайним способом
int 2 In main endp
code_size=$-main ;Размер сегменту команд
text ends /конец сегменту команд
stk segment stack ;Сегмент
db 512 dup (') ;стека stk ends
end main ;Конец програми і точка входу


Для того, щоб вирішити використання всіх, зокрема привілейованих команд 32-розрядних процесорів, в програму включена директива .586р.
Програма починається з оголошення структури dcr, за допомогою якої описуватимуться дескриптори сегментів. Порівнюючи опис структури dcr в програмі з мал. 4.9, неважко прослідкувати їх відповідність один одному. Для зручності програмного звернення в структурі dcr база описується трьома полями: молодшим словом (base_l) і двома байтами: середнім (base_m) і старшим (base_h).
У байті атрибутів 1 задається ряд характеристик сегменту. У прикладі 4.4 використовуються сегменти двох типів: сегмент команд, для якого байт attr_l повинен мати значення 98h (присутній, тільки виконання, Dpl=0), і сегмент даних (або стека) з кодом 92h (присутній, читання і запис, Dpl=0).
Деякі додаткові характеристики сегменту указуються в старшому півбайті байта attr_2. Для всіх наших сегментів значення цього півбайта дорівнює 0 (битий G= 0, оскільки межа указується в байтах, а D= 0, оскільки програма 16-розрядна).
Сегмент даних data починається з опису найважливішої системної структури - таблиці глобальних дескрипторів. Як вже наголошувалося вище, звернення до сегментів в захищеному режимі можливо виключно через дескриптори цих сегментів. Таким чином, в таблиці дескрипторів має бути описане стільки дескрипторів, скільки сегментів використовує програма. У нашому випадку в таблицю включено, окрім обов'язкового нульового дескриптора, що завжди займає перше місце в таблиці, чотири дескриптори для сегментів даних, команд, стека і додаткового сегменту даних, який ми накладемо на відеобуфер, щоб забезпечити можливість виводу в нього символів. Порядок дескрипторів в таблиці (окрім нульового) не має значення.
Поля дескрипторів для наочності заповнені конкретними даними явним чином, хоча оголошення структури dcr з нулями у всіх полях дозволяє описати дескриптори декілька коротше, наприклад:

gdt_null dcr <> ;Селектор 0 - обов'язковий
;нульовий дескриптор

gdt_data dcr <data_size - l,,, 92h> ;Селектор 8 - сегмент даних

У дескрипторі gdt_data, що описує сегмент даних програми, заповнюється поле межі сегменту (фактичне значення розміру сегменту data_size буде обчислено транслятором, див. остання пропозиція сегменту даних), а також байт атрибутів 1. База сегменту, тобто лінійна адреса його почата, в явній формі в програмі відсутній, тому її доведеться програмно обчислити і занести в дескриптор вже на етапі виконання.
Дескриптор gdt_codc сегменту команд заповнюється схожим чином.
Дескриптор gdt_stack сегменту стека має, як і будь-який сегмент даних, код атрибуту 92h, що вирішує його читання і запис, і явним чином задану межу - 255 байт, що відповідає розміру стека. Базову адресу сегменту стека так само доведеться обчислити на етапі виконання програми.
Останній дескриптор gdt_scrcen описує сторінку 0 відеобуфера. Розмір відеосторінки, як відомо, складає 4096 байт, тому в полі межі вказано число 4095. Базова фізична адреса сторінки відома, він рівний Bs000h. Молодші 16 біт бази (число 8000і) заповнюють слово base_l дескриптора, биті 16...19 (число OBU) - байт base_m. Биті 20...31 базової адреси рівні 0, оскільки відеобуфер розміщується в першому мегабайті адресного простору.
Перша половина програми присвячена підготовці переходу в захищений режим. Перш за все треба завершити формування дескрипторів сегментів програми, в яких залишилися незаповненими базові адреси сегментів.
Базові (32-бітові) адреси визначаються шляхом множення значень сегментних адрес на 16. Після обнулення регістра ЕАХ і ініціалізації сегментного регістра DS, яка дозволить нам звертатися до полів даних програми в реальному режимі, вміст ЕАХ командою sill зрушується вліво на 4 бита, утворюючи лінійну 32-бітову адресу. Оскільки ця адреса використовуватиметься і в подальших фрагментах програми, він запам'ятовується в регістрі ЕВР (або будь-якому іншому вільному регістрі загального призначення). У ВХ завантажується адреса дескриптора даних, після чого в дескриптор заноситься молодша половина лінійної адреси з регістра АХ. Оскільки до старшої половини регістра ЕАХ (де нас цікавлять біти 17...24) звернутися неможливо, над всім вмістом ЕАХ за допомогою команди rol виконується циклічне зрушення на 16 битий, в результаті якого молодша і старша половини ЕАХ міняються місцями.
Після зрушення вміст AL (де тепер знаходяться біти 17...24 лінійної адреси) заноситься в полі base_m дескриптора. Аналогічно Обчислюються лінійні адреси сегменту команд і сегменту стека.
Наступний етап підготовки до переходу в захищений режим - завантаження в регістр процесора GDTR (Global Descriptor Table Register, регістр таблиці глобальних дескрипторів) інформації про таблицю глобальних дескрипторів. Ця інформація включає лінійну базову адресу таблиці і її межу і розміщується в 6 байтах поля даних, званого іноді псевдодескриптором. Для завантаження GDTR передбачена спеціальна привілейована команда Igdt (load global descriptor table, завантаження таблиці глобальних дескрипторів), яка вимагає вказівки як операнда імені псевдодескриптора. Формат псевдодескриптора приведений на мал. 4.10.

Мал. 4.10. Формат псевдодескриптора.

У нашому прикладі заповнення псевдодескриптора спрощується унаслідок того, що таблиця глобальних дескрипторів розташована на початку сегменту даних, і її базова адреса збігається з базовою адресою всього сегменту, який ми розсудливо зберегли в регістрі ЕВР. Межу GDT в нашому випадку легко обчислити в думці: 5 дескрипторів по 8 байт займають об'єм 40 байт, і, отже, межа дорівнює 39. Команда Igdt завантажує регістр GDTR і повідомляє процесор про місцезнаходження і розмір GDT.
Ще одна важлива операція, яку необхідно виконати перед переходом в захищений режим, полягає в забороні всіх апаратних переривань. Річ у тому, що в захищеному режимі процесор виконує процедуру переривання не так, як в реальному. Під час вступу сигналу переривання процесор не звертається до таблиці векторів переривань в першому кілобайті пам'яті, як в реальному режимі, а витягує адресу програми обробки переривання з таблиці дескрипторів переривань, побудованої схоже з таблицею глобальних дескрипторів і що розташовується в програмі користувача (або в операційній системі). У прикладі 4.4 такої таблиці
ні, і па час роботи нашої програми переривання доведеться заборонити. Заборона всіх апаратних переривань здійснюється командою cli.
Тепер, нарешті, можна перейти в захищений режим, що робиться на подив простого. Для перекладу процесора в захищений режим досить встановити біт 0 в регістрі CRO, що управляє. Всього в процесорі є 4 керівників регістра, що програмно-адресуються, з мнемонічними іменами CRO, Cr1, Cr2 і Cr3. Регістр Cr1 зарезервований, регістри Cr2 і Cr3 управляють сторінковим перетворенням, яке у нас вимкнене, а регістр CRO містить цілий ряд бітів, що управляють, з яких нас зараз цікавитимуть тільки биті 31 (дозвіл сторінкового перетворення) і 0 (включення захисту). При включенні процесора обидва ці біта скидаються, і в процесорі встановлюється реальний режим з вимкненим сторінковим перетворенням. Установка в 1 молодшого біта Cr0 переводить процесор в захищений режим, скидання цього біта повертає його в режим реальних адрес.
Для того, щоб в процесі установки бита 0 не змінити стан інших бітів регістра Cr0, спочатку його вміст прочитується командою mov в регістр ЕАХ, там за допомогою команди or встановлюється молодший біт, після чого другою командою mov змінене значення завантажується в Cr0. Процесор починає працювати по правилах захищеного режиму.
Хоча захищений режим встановлений, проте дії з налаштування системи ще не закінчені. Дійсно, у всіх використовуваних в програмі сегментних регістрах зберігаються не селектори дескрипторів сегментів, а базові сегментні адреси, що не мають сенсу в захищеному режимі. Між іншим, звідси можна зробити вивід, що після переходу в захищений режим програма не повинна працювати, оскільки в регістрі CS поки що немає селектора сегменту команд, і процесор не може звертатися до цього сегменту. Насправді це не зовсім так.
У процесорі для кожного з сегментних регістрів є так званий тіньовий регістр дескриптора, який має формат дескриптора (мал. 4.11). Тіньові регістри недоступні програмістові; вони автоматично завантажуються процесором з таблиці дескрипторів кожного разу, коли процесор завантажує відповідний сегментний регістр. Таким чином, в захищеному режимі програміст має справу з селекторами, тобто номерами дескрипторів, а процесор - з самими дескрипторами, що зберігаються в тіньових регістрах. Саме вміст тіньового регістра (насамперед, лінійна адреса сегменту) визначає область пам'яті, до якої звертається процесор при виконанні конкретної команди.
У реальному режимі тіньові регістри заповнюються не з таблиці дескрипторів, а безпосередньо самим процесором. Зокрема, процесор заповнює поле бази кожного тіньового регістра лінійною базовою адресою сегменту, отриманим шляхом множення па 16 вмісту сегментного регістра, як це і належить в реальному режимі. Тому після переходу в захищений режим в тіньових регістрах знаходяться правильні лінійні базові адреси, і програма виконуватиметься правильно, хоча з погляду правил адресації захищеного режиму вміст сегментних регістрів позбавлений сенсу.

Мал. 4.11. Сегментні регістри і тіньові регістри дескрипторів.

Проте після переходу в захищений режим перш за все слід завантажити у використовувані сегментні регістри селектори відповідних сегментів. Це дозволить процесору правильно заповнити всі поля тіньових регістрів з таблиці дескрипторів. Поки ця операція не виконана, деякі поля тіньових регістрів (зокрема, межі сегментів) містять невірну інформацію.
Завантажити селектори в сегментні регістри DS, SS і ES не представляє праці. Але як завантажити селектор в регістр CS, в який заборонений прямий запис? Для цього можна скористатися штучно сконструйованою командою дальнього переходу, яка, як відомо, приводить до зміни вмісту і IP, і CS. Фрагмент

db Oeah ;Код команди far jmp

dw offset continue ;Смещение

dw 16 ;Селектор сегменту команд

що виглядає абсолютно безглуздо в сегменті команд, якраз і демонструє цю методику. У реальному режимі ми помістили б в друге слово адреси сегментну адресу сегменту команд, в захищеному ж ми записуємо в нього селектор цього сегменту (число 16).
Команда дальнього переходу, окрім завантаження в CS селектора, виконує ще одну функцію - вона очищає чергу команд в блоці передвибірки команд процесора. Як відомо, в сучасних процесорах з метою підвищення швидкості виконання програми використовується конвеєрна обробка команд програми, що дозволяє сумістити в часі фази їх обробки. Одночасно з виконанням поточної (першою) команди здійснюється вибірка операндів наступною (другий), дешифрування третьої і вибірка з пам'яті четвертої команди. Таким чином, у момент переходу в захищений режим вже можуть бути розшифровані декілька наступних команд і вибрані з пам'яті їх операнди. Проте ці дії виконувалися, очевидно, по правилах реального, а не захищеного режиму, що може привести до порушень в роботі програми. Команда переходу очищає чергу передвибірки, примушуючи процесор заповнити її наново вже в захищеному режимі.
Далі виконується завантаження в сегментні регістри DS і SS значень відповідних селекторів, і на цьому, нарешті, закінчується процедура переходу в захищений режим.
Наступний фрагмент програми є, можна сказати, діагностичним. У нім ініціалізувався (по правилах захищеного режиму!) сегментний регістр ES і у відеобуфер з регістра АХ виводиться один символ. Код 0fh відповідає зображенню великої зірочки, а атрибут 9fh - яскраво-білому мерехтливому символу на синьому полі. Поява цього символу на екрані служить підтвердженням правильного функціонування програми в захищеному режимі.
Чому ми не передбачили вивід на екран хоч би одного змістовного рядка? Річ у тому, що в захищеному режимі заборонені будь-які звернення до функцій DOS або BIOS. Причина цього абсолютно очевидна - і DOS, і BIOS є програмами реального режиму, в яких широко використовується сегментна адресація реального режиму, тобто завантаження в сегментні регістри сегментних адрес. У захищеному ж режимі в сегментні регістри завантажуються не сегментні адреси, а селектори. Крім того, звернення до функцій DOS і BIOS здійснюється за допомогою команд програмного переривання int з певними номерами, а в захищеному режимі ці команди приведуть до абсолютно іншим результатам. Тому в програмі, що працює в захищеному режимі і не має спеціальних і досить складних засобів переходу в так званий режим віртуального 86-го процесора, вивід на екран можна здійснити тільки прямим програмуванням відеобуфера. Не можна також виконати запис або читання файлу; більш того, не можна навіть завершити програму засобами DOS. Спочатку це треба повернути в реальний режим.
Повернення в реальний режим можна здійснити різними способами. Ми скористаємося для цього тим же регістром CRO, за допомогою якого ми перевели процесор а захищений режим. Здавалося б, для повернення в реальний режим досить скинути біт 0 цього регістра. Проте справа йде не так просто. Для коректного повернення в реальний режим треба виконати деякі підготовчі операції, розгляд яких дозволить нам глибше вникнути у відмінності реального і захищеного режимів.
При роботі в захищеному режимі в дескрипторах сегментів записані, серед іншого, їх лінійні адреси і межі. Процесор при виконанні команди з адресацією до того або іншого сегменту порівнює отриманий їм відносна адреса з межею сегменту і, якщо команда намагається адресуватися за межами сегменту, формує переривання (виключення) порушення загального захисту. Якщо в програмі передбачена обробка виключень, таку ситуацію можна виявити і як те виправити. Таким чином, в захищеному режимі програма не може вийти за межі оголошених нею сегментів, а також не може виконати дії, заборонені атрибутами сегменту. Так, якщо сегмент оголошений виконуваним (код атрибуту 1 981т), то дані з цього сегменту не можна читати або модифікувати; якщо атрибут сегменту рівний 92h, то в такому сегменті не може бути виконуваних команд, на зате дані можна як читати, так і модифікувати. Вказавши для якогось сегменту код атрибуту 90h, ми отримаємо сегмент із забороною запису. При спробі запису в цей сегмент процесор сформує виключення загального захисту.
Як вже наголошувалося, дескриптори сегментів зберігаються в процесі виконання програми в тіньових регістрах (див. мал. 4.11), які завантажуються автоматично при записі в сегментний регістр селектора.
При роботі в реальному режимі деякі поля тіньових регістрів мають бути заповнені цілком певним чином. Зокрема, поле межі будь-якого сегменту повинне містити число Ffffh, а біт дробу скинутий. Слід підкреслити, що межі всіх сегментів мають бути точно рівні Ffffh; будь-яке інше число, наприклад, Fffeh, "не влаштує" реальний режим.
Якщо ми просто перейдемо в реальний режим скиданням бита 0 в регістрі Cr0, то в тіньових регістрах залишаться дескриптори захищеного режиму і при першому ж зверненні до будь-якого сегменту програми виникне виключення загального захисту, оскільки жоден з наших сегментів не має межі, рівною Ffffh. Оскільки ми не обробляємо виключення, відбудеться або скидання процесора і перезавантаження комп'ютера, або зависання. Таким чином, перед переходом в реальний режим необхідно виправити дескриптори всіх наших сегментів: команд, даних, стека і відеобуфера До сегментних регістрів FS і GS ми не зверталися, і про них можна не піклуватися.
Тіньові регістри, куди, власне, треба записати значення межі, нам недоступні. Для з модифікації доведеться удатися до обхідного маневру: записати в поля меж всіх чотирьох дескрипторів значення Ffffh, а потім повторно завантажити селектори в сегментні регістри, що приведе до перезапису вмісту тіньових регістрів. З сегментним регістром CS так поступити не можна, тому його завантаження доведеться виконати, як і раніше, за допомогою штучної сформованої команди дальнього переходу.
Набудувавши сегментні регістри, що все використалися в програмі, можна скинути біт 0 в Cr0. Після переходу в реальний режим нам доведеться ще раз виконати команду дальнього переходу, щоб очистити чергу команд в блоці передвибірки і завантажити в регістр CS замість селектора, що зберігається там, звичайну сегментну адресу регістра команд.
Тепер процесор знову працює в реальному режимі, причому, хоча в сегментних регістрах DS, ES і SS залишилися незаконні для реального режиму селектори, програма якийсь час виконуватиметься правильно, оскільки в тіньових регістрах знаходяться правильні лінійні адреси (що залишилися від захищеного режиму) і законні для реального режиму межі (завантажені туди нами). Якщо, проте, в програмі зустрінуться команди збереження і відновлення вмісту сегментних регістрів, наприклад

push DS
...
pop DS

виконання програми буде порушено, оскільки команда pop DS завантажить в DS не сегментну адресу реального режиму, а селектор, тобто число 8 в нашому випадку. Це число розглядатиметься процесором, як сегментна адреса, і подальші звернення до полів даних приведуть до адресації фізичної пам'яті починаючи з абсолютної адреси 80h, що, звичайно, позбавлено сенсу. Навіть якщо в нашій програмі немає рядків збереження і відновлення сегментних регістрів, вони неминуче зустрінуться, як тільки відбудеться перехід в DOS по команді int 21h, оскільки диспетчер DOS зберігає, а потім відновлює всі регістри завдання, у тому числі і сегментні. Тому після переходу в реальний режим необхідно завантажити у використовувані далі сегментні регістри відповідні сегментні адреси, що і виконується в програмі для регістрів DS і SS. Треба також не забути вирішити заборонені нами раніше апаратні переривання (команда sti).
Можна ще відмітити, що в тій частині програми, яка виконується в захищеному режимі, не використовується стек. Враховуючи це, можна було декілька скоротити текст програми, видаливши з неї рядки налаштування регістра SS як при підготовці переходу в захищений режим, так і при поверненні в реальний. Не було також необхідності наново ініціалізувати покажчик стека, оскільки його початковий вміст - зсув дна стека, рівний 512, нікуди з SP не поділося б.
Програма завершується звичайним способом функцією DOS 4ch. Нормальне завершення програми і перехід в DOS в якійсь мірі свідчить про її правильність.
У розглянутої програми є серйозний недолік - повна відсутність засобів відладки. Для відладки програм захищеного режиму використовується механізм переривань і виключень, в нашій же програмі цей механізм не активізований. Тому всякі неполадки при роботі в захищеному режимі, які за допомогою вказаного механізму можна було б виявити і проаналізувати, в даному випадку приводитимуть до скидання процесора.
У приведеному прикладі проілюстровані лише базові засоби програмування захищеного режиму: поняття селекторів і дескрипторів, створення глобальної таблиці дескрипторів, перехід в захищений режим і назад, адресація в захищеному режимі. За кадром залишилися такі важливі питання, як обробка виключень і апаратних переривань, рівні привілеїв і захист по привілеях, роздільні операційні середовища і таблиці локальних дескрипторів, створення і взаємодія завдань, режим віртуального процесора 8086 та інші.

-

:: Наша кнопка ::

Отримати код:

Підтримайте наш сайт і розмістіть нашу кнопку на своєму ресурсі.


:: Популярне ::

-


:: Посилання ::

-


 

 

 


Copyright © net.kr.ua, 2019-2025 (assem.us)