Базовые средства программирования защищенного режима
Команда дальнего перехода, помимо загрузки в 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, "не устроит" реальный режим.