Goto... Search.. Hext Change.. Follou Previous 13 глава




Дескриптор gdt_code сегмента команд заполняется схожим образом.

Дескриптор gdt_stack сегмента стека имеет, как и любой сегмент дан­ных, код атрибута 92h, что разрешает его чтение и запись, и явным обра­зом заданную границу — 255 байт, что соответствует размеру стека. Базо­вый адрес сегмента стека так же придется вычислить на этапе выполне­ния программы.

Последний дескриптор,gdt_scrcen описывает страницу 0 видеобуфера. Размер видеостраницы, как известно, составляет 4096 байт, поэтому в поле границы указано число 4095. Базовый физический адрес страницы известен, он равен BSOOOh. Младшие 16 бит базы (число SOOOh) заполня­ют слово base_l дескриптора, биты 16...19 (число OBh) — байт base_m. Биты 20...31 базового адреса равны 0, поскольку видеобуфер размещается в первом мегабайте адресного пространства.

Первая половина программы посвящена подготовке перехода в защи­щенный режим. Прежде всего надо завершить формирование дескрипто­ров сегментов программы, в которых остались незаполненными базовые адреса сегментов.

Базовые (32-битовые) адреса определяются путем умножения значе­ний сегментных адресов на 16. После обнуления регистра ЕАХ и инициа­лизации сегментного регистра DS, которая позволит нам обращаться к


Fll


усширенные возможности современных микропроцессоров



шям данных профаммы в реальном режиме, содержимое ЕАХ командой

сдвигается влево на 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.

Банты

 

1 1 1 Линейный базовый адрес i i i Гранина

Рис. 4.10. Формат псеадодескриптора.

В нашем примере заполнение псевдодескриптора упрощается. вслед­ствие того, что таблица глобальных дескрипторов расположена в начале сегмента данных, и ее базовый адрес совпадает с базовым адресом всего сегмента, который мы благоразумно сохранили в регистре ЕВР. Границу GDT в нашем случае легко вычислить в уме: 5 дескрипторов по 8 байт занимают объем 40 байт, и, следовательно, граница равна 39. Команда Igdt зафужает рсгисф GDTR и сообщает процессору о местонахождении и размере GDT.

Еще одна важная операция, которую необходимо выполнить перед пе­реходом в защищенный режим, заключается в запрете всех аппаратных пре­рываний. Дело в том, что в защищенном режиме процессор выполняет про­цедуру прерывания не так, как в реальном. При поступлении сигнала преры­вания процессор не обращается к таблице векторов прерываний в первом килобайте памяти, как в реальном режиме, а извлекает адрес программы обработки прерывания из таблицы дескрипторов прерываний, построенной схоже с таблицей глобальных дескрипторов и располагаемой в программе пользователя (или в операционной системе). В примере 4.4 такой таблицы



Глава -t


 


нет, и на время работы нашей программы прерывания придется запретить. Запрет всех аппаратных прерываний осуществляется командой cli.

Теперь, наконец, можно перейти в защищенный режим, что делается на удивление просто. Для перевода процессора в защищенный режим до­статочно установить бит 0 в управляющем регистре CRO. Всего в процес­соре имеется 4 программно-адресуемых управляющих регистра с мнемо­ническими именами CRO, CR1, CR2 и CR3. Регистр CR1 зарезервирован, регистры CR2 и CR3 управляют страничным преобразованием, которое у нас выключено, а регистр CRO содержит целый ряд управляющих битов, из которых нас сейчас будут интересовать только биты 31 (разрешение страничного преобразования) и 0 (включение защиты). При включении процессора оба эти бита сбрасываются, и в процессоре устанавливается реальный режим с выключенным страничным преобразованием. Установ­ка в 1 младшего бита CRO переводит процессор в защищенный режим, сброс этого бита возвращает его в режим реальных адресов.

Для того, чтобы в процессе установки бита 0 не изменить состояние других битов регистра CRO, сначала его содержимое считывается командой mov в регистр ЕАХ, там с помощью команды or устанавливается младший бит, после чего второй командой mov измененное значение загружается в CRO. Процессор начинает работать по правилам защищенного режима.

Хотя защищенный режим установлен, однако действия по настройке системы еще не закончены. Действительно, во всех используемых в про­грамме сегментных регистрах хранятся не селекторы дескрипторов сег­ментов, а базовые сегментные адреса, не имеющие смысла в защищен­ном режиме. Между прочим, отсюда можно сделать вывод, что после пе­рехода в защищенный режим программа не должна работать, так как в регистре CS пока еще нет селектора сегмента команд, и процессор не может обращаться к этому сегменту. В действительности это не совсем так.

В процессоре для каждого из сегментных регистров имеется так называ­емый теневой регистр дескриптора, который имеет формат дескриптора (рис. 4.11). Теневые регистры недоступны программисту; они автоматически загружаются процессором из таблицы дескрипторов каждый раз, когда про­цессор загружает соответствующий сегментный регистр. Таким образом, в защищенном режиме программист имеет дело с селекторами, т.е. номера­ми дескрипторов, а процессор — с самими дескрипторами, хранящимися в теневых регистрах. Именно содержимое теневого регистра (в первую оче­редь, линейный адрес сегмента) определяет область памяти, к которой обращается процессор при выполнении конкретной команды.

В рештьном режиме теневые регистры заполняются не из таблицы дес­крипторов, а непосредственно самим процессором. В частности, процес­сор заполняет поле базы каждого теневого регистра линейным базовым адресом сегмента, полученным путем умножения на 16 содержимого сег­ментного регистра, как это и положено в реальном режиме. Поэтому пос­ле перехода в защищенный режим в теневых регистрах находятся пра­вильные линейные базовые адреса, и программа будет выполняться пра­вильно, хотя с точки зрения правил адресации защищенного режима содержимое сегментных регистров лишено смысла.


кширенные возможности современных микропроцессоров



 


Сегментные регистры, доступные программе


Теневые регистры дескрипторов, недоступные и невидимые


 


 

 

 

 

 

 

 

 

 

 

CS DS ES FS GS SS Селектор
 
Селектор
 
Селектор
 
Селектор
 
Селектор
 
Селектор

 

База Граница Атрибуты
База Граница Атрибуты
   
База Граница Атрибуты
   
База Граница Атрибуты
База Граница Атрибуты
 
База Граница Атрибуты

Рис. 4.11. Сегментные регистры и теневые регистры дескрипторов.

Тем не менее после перехода в защищенный режим прежде всего сле­дует загрузить в используемые сегментные регистры селекторы соответ­ствующих сегментов. Это позволит процессору правильно заполнить все поля теневых регистров из таблицы дескрипторов. Пока эта операция не выполнена, некоторые поля теневых регистров (в частности, границы сегментов) содержат неверную информацию.

Загрузить селекторы в сегментные регистры DS, SS и ES не представ­ляет труда. Но как загрузить селектор в регистр CS, в который запрещена прямая запись' Для этого можно воспользоваться искусственно сконстру­ированной командой дальнего перехода, которая, как известно, приво­дит к смене содержимого и IP, и CS. Фрагмент


db dw dw


OEAh

offset continue


;Код команды far jmp

; Смещение

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


выглядящий совершенно нелепо в сегменте команд, как раз и демонстри­рует эту методику. В реальном режиме мы поместили бы во второе слово адреса сегментный адрес сегмента команд, в защищенном же мы записы­ваем в него селектор этого сегмента (число 16).

Команда дальнего перехода, помимо загрузки в CS селектора, выпол­няет еще одну функцию — она очищает очередь команд в блоке предвы-борки команд процессора. Как известно, в современных процессорах с целью повышения скорости выполнения программы используется кон­вейерная обработка команд программы, позволяющая совместить во вре­мени фазы их обработки. Одновременно с выполнением текущей (пер­вой) команды осуществляется выборка операндов следующей (второй), дешифрация третьей и выборка из памяти четвертой команды. Таким об­разом, в момент перехода в защищенный режим уже могут быть расшиф­рованы несколько следующих команд и выбраны из памяти их операнды. Однако эти действия выполнялись, очевидно, по правилам реального, а



Глава -f


не защищенного режима, что может привести к нарушениям в работе программы. Команда перехода очищает очередь предвыборки, заставляя процессор заполнить ее заново уже в защищенном режиме.

Далее выполняется загрузка в сегментные регистры DS и SS значений соответствующих селекторов, и на этот, наконец, заканчивается проце­дура перехода в защищенный режим.

Следующий фрагмент программы является, можно сказать, диагнос­тическим.. В нем инициализируется (по правилам защищенного режима!) сегментный регистр ES и в видеобуфер из регистра АХ выводится один символ. Код OFh соответствует изображению большой звездочки, а атри­бут 9Fh — ярко-белому мерцающему символу на синем поле. Появление этого символа на экране служит подтверждением правильного функцио­нирования программы в защищенном режиме.

Почему мы не предусмотрели вывод на экран хотя бы одной содержа­тельной строки' Дело в том, что в защищенном режиме запрещены любые обращения к функциям DOS или BIOS. Причина этого совершенно оче­видна — и DOS, и BIOS являются программами реального режима, в кото­рых широко используется сегментная адресация реального режима, т.е. заг­рузка в сегментные регистры сегментных адресов. В защищенном же режиме в сегментные регистры загружаются не сегментные адреса, а селекторы. Кроме того, обращение к функциям DOS и BIOS осуществляется с помо­щью команд программного прерывания int с определенными номерами, а в защищенном режиме эти команды приведут к совершенно иным резуль­татам. Поэтому в программе, работающей в защищенном режиме и не име­ющей специальных и довольно сложных средств перехода в так называе­мый режим виртуального 86-го процессора, вывод на экран можно осуще­ствить только прямым программированием видеобуфера. Нельзя также выполнить запись или чтение файла; более того, нельзя даже завершить программу средствами DOS. Сначала ее надо вернуть в реальный режим.

Возврат в реальный режим можно осуществить разными способами. Мы воспользуемся для этого тем же регистром CRO, с помощью которого мы перевели процессор а защищенный режим. Казалось бы, для возврата в ре-ачьный режим достаточно сбросить бит 0 этого регистра Однако дело обсто­ит не так просто. Для корректного возврата в реальный режим надо выпол­нить некоторые подготовительные, операции, рассмотрение которых позво­лит нам глубже вникнуть в различия реального и защищенного режимов.

При работе в защищенном режиме в дескрипторах сегментов записа­ны, среди прочего, их линейные адреса и границы. Процессор при выпол­нении команды с адресацией к TONry или иному сегменту сравнивает по­лученный им относительный адрес с границей сегмента и, если команда пытается адресоваться за пределами сегмента, формирует прерывание (исключение) нарушения общей защиты. Если в программе предусмотре­на обработка исключений, такую ситуацию можно обнаружить и как-то исправить. Таким образом, в защищенном режиме программа не может выйти за пределы объявленных ею сегментов, а также не может выпол­нить действия, запрещенные атрибутами сегмента. Так, если сегмент объяв­лен исполняемым (код атрибута 1 98h), то данные из этого сегмента нельзя


^t

Расширенные возможности современных микропроцессоров_____________________ 195

i

|читать или модифицировать; если атрибут сегмента равен 92h, то в таком; сегменте не может быть исполняемых команд, на зато данные можно как: читать, так и модифицировать. Указав для какого-то сегмента код атрибу­та 90h, мы получим сегмент с запрещением записи. При попытке записи в этот сегмент процессор сформирует исключение общей защиты.

Как уже отмечаюсь, дескрипторы сегментов хранятся в процессе вы­полнения программы в теневых регистрах (см. рис. 4.11), которые загружа­ются автоматически при записи в сегментный регистр селектора.

При работе в реальном режиме некоторые поля теневых регистров должны быть заполнены вполне определенным образом. В частности, поле границы любого сегмента должно содержать число FFFFli, а бит дробно­сти сброшен. Следует подчеркнуть, что границы всех сегментов должны быть точно равны FFFFh; любое другое число, например, FFFEh,, «не устроит» реальный режим.

Если мы просто перейдем в реальный режим сбросом бита 0 в регистре CRO, то в теневых регистрах останутся дескрипторы защищенного режима и. при первом же обращении к любому сегменту программы возникнет исклю­чение общей защиты, так как ни один из наших сегментов не имеет границы, равной FFFFh. Поскольку мы не обрабатываем исключения, произойдет либо сброс процессора и перезагрузка компьютера, либо зависание. Таким обра­зом, перед переходом в реальный режим необходимо исправить дескрипторы всех наших сегментов: команд, данных, стека и видеобуфера. К сегментным регистрам FS и GS мы не обращались, и о них можно не заботиться.

Теневые регистры, куда, собственно, надо записать значение грани­цы, нам недоступны. Для из модификации придется прибегнуть к околь­ному маневру: записать в поля границ всех четырех дескрипторов значе­ние FFFFh, а затем повторно загрузить селекторы в сегментные регист-'ры, что приведет к перезаписи содержимого теневых регистров. С сегментным регистром CS так поступить нельзя, поэтому его загрузку придется выполнить, как и ранее, с помощью искусственно сформиро­ванной команды дальнего перехода.

Настроив все использовавшиеся в программе сегментные регистры, (ожно сбросить бит 0 в CRO. После перехода в реальный режим нам при-|[ется еще раз выполнить команду дальнего перехода, чтобы очистить оче-гдь команд в блоке предвыборки и загрузить в регистр CS вместо храня-1егося там селектора обычный сегментный адрес регистра команд.

Теперь процессор снова работает в реальном режиме, причем, хотя в Рсегментных регистрах DS, ES и SS остались незаконные для реального режи-селекторы, программа будет какое-то время выполняться правильно, так \ как в теневых регистрах находятся правильные линейные адреса (оставшиеся "< от защищенного режима) и законные для реального режима границы (загру-;женные туда нами). Если, однако, в программе встретятся команды сохране­ния и восстаноштения содержимого сегментных регистров, например

push DS pop DS



Глава


выполнение программы будет нарушено, так как команда pop DS загру­зит в DS не сегментный адрес реального режима, а селектор, т.е. число 8 в нашем случае. Это число будет рассматриваться процессором, как сег­ментный адрес, и дальнейшие обращения к полям данных приведут к адресации физической памяти начиная с абсолютного адреса 80h, что, конечна, лишено смысла. Даже если в нашей программе нет строк сохра­нения и восстановления сегментных регистров, они неминуемо встретят­ся, как только произойдет переход в DOS по команде int 21h, так как диспетчер DOS сохраняет, а затем восстанавливает все регистры задачи, в том числе и сегментные. Поэтому после перехода в реальный режим необходимо загрузить в используемые далее сегментные регистры соот­ветствующие сегментные адреса, что и выполняется в программе для ре­гистров DS и SS. Надо также не забыть разрешить запрещенные нами ра­нее аппаратные прерывания (команда sti).

Можно еще заметить, что в той части программы, которая выполня­ется в защищенном режиме, не используется стек. Учитывая это, можно было несколько сократить текст программы, удалив из нее строки на­стройки регистра SS как при подготовке перехода в защищенный режим, так и при возврате в реальный. Не было также необходимости заново ини­циализировать указатель стека, так как его исходное содержимое — сме­щение дна стека, равное 512, никуда из SP не делось бы.

Программа завершается обычным образом функцией DOS 4Ch. Hop* мальное завершение программы и переход в DOS в какой-то мере свиде!--. •тсльствуст о ее правильности.

У рассмотренной программы имеется серьезный недостаток — пол­ное отсутствие средств отладки. Для отладки программ защищенного pev жима используется механизм прерываний и исключений, в нашей же программе этот механизм не активизирован. Поэтому всякие неполадки при работе в защищенном режиме, которые с помощью указанного меха­низма можно было бы обнаружить и проанализировать, в данном случае будут приводить к сбросу процессора.

В приведенном примере проиллюстрированы лишь базовые средства программирования защищенного режима: понятие селекторов и дескрип­торов, создание глобальной таблицы дескрипторов, переход в защищен­ный режим и обратно, адресация в защищенном режиме. За кадром оста­лись такие важные вопросы, как обработка исключений и аппаратных прерываний, уровни привилегий и защита по привилегиям, раздельные операционные среды и таблицы локальных дескрипторов, создание и вза­имодействие задач, режим виртуального процессора 8086 и другие. Жела­ющие познакомиться с этими вопросами могут обратиться к одному из изданий книги: П.И.Рудаков, К.Г.Финогенов «Программируем на языке ассемблера IBM PC» (Обнинск, «Принтер»), где основы программирова­ния защищенного режима рассмотрены значительно более подробно (хотя тоже далеко не исчерпывающе).


Приложение.

СИСТЕМА КОМАНД ПРОЦЕССОРОВ Intel

Ниже приводится алфавитный перечень команд процессоров Intel с кратким описанием действия каждой команды и примерами ее использо­вания.

В разделах статей, начинающихся с обозначения 386+, описываются отличия действия рассматриваемой команды в современных 32-разряд­ных процессорах (80386, i486, Pentium). Как. правило, эти отличия заклю­чаются в возможности использования не только 8- и 16-разрядных, 'но и 32-разрядных операндов, а также расширенных режимов адресации памя­ти. Обычные 16-разрядные программы реального режима вполне могут использовать расширенные регистры процессора (ЕАХ, ЕВХ и проч.), 32-битовые ячейки памяти и варианты команд для их обработки. Для того, чтобы ассемблер правильно транслировал команды с 32-разрядными опе­рандами, в программу необходимо включить директиву ассемблера.386 (можно также использовать директивы.486 или.586), а сегменту команд (и во многих случаях сегменту данных) придать описатель use!6:

.386

codes segment use 16

assume CS:codcs


codes ends data segment


use!6


 


data


ends


Кроме этого, необходимо разрешить компоновщику обрабатывать 32-разрядные операнды, что для компоновщика TLINK осуществляется ука-j занием ключа/3.

Отдельные статьи, начинающиеся с обозначений 386+, 486+ Pentium+, посвящены командам, отсутствующим в МП 86. Многие из этих команд (например, команды проверки бита Ыили условной уста­новки байта set) носят прикладной характер и могут использоваться в обычных программах реального режима.

Новые команды, реализованные впервые в МП 80386, сохраняют свое значение и в более современных процессорах. Для того, чтобы ассемблер распознавал команды МП 80386, в программе должна присутствовать ди­ректива.386.

Новые команды, решшзованные впервые в МП 80486, сохраняют свое значение и в процессорах Pentium. Для того, чтобы ассемблер распознавал команды МП 80486, в программе должна присутствовать директива.486.



Приложение


Для того, чтобы ассемблер распознавал команды, реализованные впер­вые в процессоре Pentium, в программе должна присутствовать директива.586.

Ряд команд требует для своего выполнения специальных условий, обычно отсутствующих в приложениях MS-DOS. Так, например, команда bound (проверки индекса массива на выход за границы) при обнаруже­нии выхода за границы генерирует прерывание с вектором 5. Это преры­вание в защищенном режиме как раз и является исключением нарушения границ массива, но в приложениях MS-DOS используется для вывода на печать содержимого экрана. Поэтому использование таких команд в ре­альном режиме затруднено.

Отдельные статьи, начинающиеся с обозначения 386Р+, посвящены привилегированным командам современных процессоров, работающих в расширенном режиме, и отсутствующим в МП 86. Для использования этих команд в программу необходимо включить директиву ассемблера.386Р (можно также использовать директивы.486Р или.586Р). Если при этом программа реализуется, как 16-разрядное приложение MS-DOS, сегмент команд должен иметь описатель use!6 (при наличии директивы.386 транс­лятор по умолчанию создает 32-разрядное приложение). Следует, однако, иметь в виду, что привилегированные команды защищенного режима предназначены для использования не в прикладных программах, а в опе­рационных системах защищенного режима. В прикладных программах при­вилегированные команды приходится использовать лишь в весьма специ­альных случаях, когда, например, прикладная программа запускается в реальном режиме под управлением MS-DOS, но затем переводит процес­сор в защищенный режим и далее использует преимущества этого режи­ма. Типичный пример программы такого рода — приложение DOS, кото­рому требуется использовать всю оперативную память компьютера. В на­стоящей книге, посвященной, в основном, реальному режиму, дается лишь перечисление привилегированных команд защищенного режима.

ААА ASCII-коррекция регистра АХ после сложения

Команда ааа используется вслед за операцией сложения add в регистре. AL двух неупакованных двоично-десятичных (BCD) чисел, если в АХ находится двухразрядное неупакованное двоично-десятичное число. Ко­манда не имеет параметров. Она преобразует результат сложения в неупа­кованное двоично-десятичное число, младший десятичный разряд кото­рого находится в AL. Если результат превышает 9, выполняется инкре--мент содержимого регистра АН. Команда воздействует на флаги AF и CR


Пример

mov AX,0605h add AL,09h ааа


; Неупакованное BCD 65;Неупакованное BCD 9, AX=060Eh;AX=0704h, неупакованное BCD 74


AAD ASCII-коррекция регистра АХ перед делением

Команда aad используется перед операцией деления неупакованного двоично-десятичного (BCD) числа в регистре АХ на другое неупакован-


Система команд процессоров Intel



ное двоично-десятичное число. Команда не имеет параметров. Она преоб­разует делимое в регистре АХ в двоичное число без знака, чтобы в резуль­тате деления получились правильные неупакованные двоично-десятич­ные числа (частное в AL, остаток в АН). Команда воздействует на флаги SF, ZF и PF.


Пример

raov

. mov

aad

div


AX,0207h;Неупакованное BCD 27

DL,06h;Неупакованное BCD 6

;AX=001Bh=27
DL;AX=0304h, т.е. 4 и З в остатке


ДАМ ASCII-коррекция регистра АХ после умножения

Команда аат используется вслед за операцией умножения двух неупа­кованных двоично-десятичных чисел. Команда не имеет параметров. Она преобразует результат умножения, являющийся двоичным числом, в пра­вильное неупакованное двоично-десятичное (BCD) число, младший раз­ряд которого помещается в AL, а старший — в АН. Команда воздействует на флаги SF, ZF и PF.


Пример

mov mov mul aam


AL,08h;Неупакованное BCD 8

CL,07h;Неупакованное BCD 7

CL;AX=0038h=56

;AX=0506h, BCD 56




Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2016-02-16 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: