Базовые средства программирования защищенного режима
Рассмотрим теперь для примера простую программу, которая, будучи запущена обычным образом под управлением MS-DOS, переключает процессор в защищенный режим, выводит на экран для контроля символ, переходит назад в реальный режим (чтобы не вывести компьютер из равновесия) и завершается стандартным для DOS образом.
Для того, чтобы наша программа могла бы хоть что-то сделать в защищенном режиме, для нее необходимо создать среду защищенного режима, в первую очередь, таблицу глобальных дескрипторов с описанием всех сегментов, с которыми программа будет работать. Кроме нас никто эту таблицу (при работе в DOS) не создаст. Таким образом, наша программа будет в какой-то мере выполнять функции операционной системы защищенного режима.
Для практического исследования защищенного режима придется выполнить некоторую работу по переконфигурированию компьютера. В наше время компьютеры обычно конфигурируются так, что при их включении сразу загружается система Windows. Работы, для которых требуется DOS, выполняются либо в режиме эмуляции DOS, либо в сеансе DOS, организуемом системой Windows. Для запуска прикладной программы защищенного режима такой способ не годится. Нам понадобится DOS в "чистом виде", без следов Windows.
Более того, перед запуском программы необходимо выгрузить все драйверы обслуживания расширенной памяти (HIMEM.SYS и EMM386.EXE) и программы, использующие расширенную память, например, SMARTDRV.EXE. Лучше всего загружать DOS с системной дискеты, подготовив файлы CONFIG.SYS и AUTOEXEC.BAT в минимальном варианте.
Обсуждая основы защищенного режима, мы не затронули многие, в том числе принципиальные вопросы, с которыми придется столкнуться при написании работоспособной программы. Необходимые пояснения будут даны в конце этого раздела.
Пример 4.4. Программирование защищенного режима.
0.586Р; Разрешение трансляции всех команд МП 586 ;Структура для описания дескрипторов сегментов dcr struc; Имя структуры limit dw 0; Граница (биты 0…15) base_1 dw 0; База, биты 0…15 base_m db 0; База, биты 16…23 attr_1 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-1.0.0.92h,0.0>; Селектор 8, сегмент данных gdt_code dcr <code_size-1.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 ;---------------------------------------------------------; ;Теперь процессор работает в защищенном режиме; ;---------------------------------------------------------;