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

Taylor Swift — Blank Space


Похожие статьи.

Понравилась статья? Поделиться с друзьями: