Федеральное агентство по науке и образованию
Тверской государственный технический университет
Кафедра ЭВМ
Переключение в защищенный режим и возврат в реальный режим. Страничная адресация.
Методические указания к лабораторной работе по курсу
«Организация ЭВМ, комплексов и систем»
для студентов специальности 23.01
«Вычислительные машины, комплексы, системы и сети»
Тверь
2009 г
Методическое указание разработано в соответствии с рабочей программой по дисциплине «Организация ЭВМ, комплексов и систем» для студентов специальности 23.01. Предназначено для оказания помощи студентам в подготовке к выполнению лабораторных работ по данной дисциплине.
Составили: к. т.н.
студент
© Тверской государственный технический университет
Оглавление
1. Введение - 4
1.1. Основы защищённого режима. 4
1.2. Адресация памяти в защищённом режиме. 5
2. Структуры данных защищённого режима - 7
2.1. Дескриптор сегмента - 7
2.2. Селектор сегмента - 9
2.3. Глобальная дескрипторная таблица - 10
3. Пример программы защищённого режима - 11
3.1. Подготовка таблицы глобальных дескрипторов GDT - 11
3.2. Инициализация необходимых дескрипторов в GDT - 13
3.3. Загрузка в регистр gdtr адреса и размера таблицы GDT - 14
3.4. Запрет обработки аппаратных прерываний - 14
3.5. Переключение микропроцессора в защищенный режим - 15
3.6. Организация работы в защищённом режиме - 16
3.7. Подготовка к возврату в реальный режим - 17
3.8. Переключение микропроцессора в реальный режим - 18
3.9. Разрешение прерываний - 18
3.10. Стандартное для MS-DOS завершение работы программы - 19
4. Страничная организация памяти - 19
4.1. Подготовка каталога страниц - 21
4.2. Подготовка таблицы страниц - 22
4.3. Загрузка регистра CR3- 23
4.4. Включение страничного преобразования - 23
5. Литература - 25
6. Листинг программы-- 26
1. Введение
1.1. Основы защищённого режима.
Микропроцессоры Pentium, так же, как и его предшественники (начиная с 80268), могут работать в двух режимах: реального адреса и виртуального защищенного адреса. Обычно эти режимы называют просто реальным и защищенным. В реальном режиме 32-разрядные микропроцессоры функционируют фактически так же, как МП 86 с повышенным быстродействием и расширенным набором команд. Многие весьма привлекательные возможности микропроцессоров принципиально не реализуются в реальном режиме, который введен лишь для обеспечения совместимости с предыдущими моделями процессоров.
Характерной особенностью реального режима является ограничение объема адресуемой оперативной памяти величиной 1 Мбайт.
Только перевод микропроцессора в защищенный режим позволяет полностью реализовать все возможности, загаженные в его архитектуру и недоступные в реальном режиме.
Сюда можно отнести:
· Увеличение адресуемого пространства до 4 Гбайт;
· Возможность работать в виртуальном адресном пространстве, превышающем максимально возможный объем физической памяти и составляющем огромную величину 64 Тбайт;
· Организация многозадачного режима с параллельным выполнением нескольких программ (процессов);
· Страничная организация памяти, повышающая уровень защиты задач;
1.2. Адресация памяти в защищённом режиме.
Первое, с чем сталкивается программа при переходе в защищённый режим - это совершенно другая система адресации памяти. Для начала, давайте вспомним, как это происходит в режиме реальных адресов.
Для обращения к памяти используется пара 16-разрядных регистров: сегментный регистр и смещение.
В сегментном регистре находится адрес сегмента. Сегмент - это область памяти размером в 64 Кб, которая должна начинаться на границе параграфа или, другими словами, на 16-байтной границе, то есть сегмент может начинаться только по адресу 0, 16, 32, 48 и т. д. Адресное пространство процессора 8086 равно одному мегабайту - это адреса в диапазоне от 00000h до FFFFFh. Информация об адресе содержится в старших четырёх шестнадцатеричных разрядах - их и хранят в сегментном регистре. Другими словами, можно взять значение из сегментного регистра, умножить его наh) и получится адрес начала сегмента.
Для указания конкретного адреса внутри сегмента используется второй 16-разрядный регистр, так называемое смещение. Использование пары регистров сегмент:смещение обеспечивает доступ ко всему мегабайту адресного пространства.
Каждый раз, перед тем как процессор обратится к памяти по адресу, указанному в паре регистров сегмент:смещение, он вычисляет адрес памяти по следующей схеме:
физический_адрес = сегмент * 10h + смещение.
Теперь давайте рассмотрим схему образования адреса в защищённом режиме, как это происходит в процессоре iэто базовая модель для любого Pentium-а).
Адрес памяти в 32-разрядном процессоре является также 32-разрядным. Это значит, что адресное пространство для такого процессора равно 4 Гббайт).
Адресация памяти также производится через сегмент и смещение в сегменте, для чего используется пара регистров, но для описания сегмента используется специальная структура – дескриптор сегмента, который хранит:
· 32-разрядный адрес начала сегмента (база);
· 20-разрядный предел сегмента (предел = размер - 1);
· Номер уровня привилегий сегмента;
· Тип сегмента (код, стек, данные или системный объект);
· Права доступа к сегменту
· Атрибуты сегменты
Все дескрипторы хранятся в одной из трёх дескрипторных таблиц (в какой именно зависит от назначения дескриптора). Адрес по которому размещаются дескрипторные таблицы может быть любым, и хранится в специальном регистре процессора.
Сегмент по-прежнему указывается в сегментном регистре, но теперь в нём хранится номер сегмента в дескрипторной таблице. Этот номер называется селектор.
В качестве смещения используется 16- или 32-разрядный регистр.
При обращении к памяти, процессор проверяет возможность доступа к сегменту по уровню привилегий, проверяет, не превысил ли адрес предел сегмента и можно ли обращаться к этому сегменту в данном случае (например, запрещена передача управления в сегмент, описывающий данные или стек). Если в результате проверки будет обнаружено нарушение какого-либо условия, то процессор сгенерирует исключение и тем самым обеспечит защиту.
Процессор измеряет размер сегмента двумя типами величин: либо байтами, либо страницами. Страница - это блок памяти размером в 4Кб.
В описании сегмента можно указать, в каких единицах измеряется сегмент и тогда можно получить два типа сегментов с максимальными размерами:
· в 1Мб байт ) или
· в 4Гб страниц = 2 20 * 4Кб = 2 20 * 2 12 = 2 32 байт )
Эта способность измерять сегмент либо байтами, либо страницами, называется гранулярностью.
Значение предела сегмента может быть любым, от 0 до 2, гранулярность устанавливается по усмотрению программиста и может быть либо байтная, либо страничная. Всё это позволяет определять сегменты размером до 4Гб.
2. Структуры данных защищённого режима
2.1. Дескриптор сегмента
Прежде чем программа сможет обратиться по какому-либо адресу памяти, она должна определить набор сегментов, через которые она сможет получить доступ к памяти.
Сегмент определяется в виде структуры данных, которая называется дескриптор. Размер дескриптора - 8 байт.
Ниже приведен формат дескриптора:

Адрес сегмента - также называется базовым адресом, - 32-разрядный адрес области памяти, с которой начинается сегмент.
Предел сегмента - предельное значение смещения в сегменте; также можно рассматривать предел как размер сегмента минус один элемент размера - байт или страницу, смотря в чём измеряется сегмент.
Бит A (Acessed) - бит доступа в сегмент. Этот бит показывает, был ли произведен доступ к сегменту, описываемому этим дескриптором, или нет
Тип сегмента - трёхбитовое поле, определяющее тип сегмента
Тип | Назначение сегмента |
000 | Сегмент данных, только для чтения |
001 | Сегмент данных с разрешением чтения и записи |
010 | Не определено |
011 | Сегмент стека с разрешением чтения и записи |
100 | Сегмент кода с разрешением только выполнения из него |
101 | Сегмент кода с разрешением выполнения и чтения из него |
110 | Подчинённый сегмент кода с разрешением только выполнения из него |
111 | Подчинённый сегмент кода с разрешением выполнения и чтения из него |
Бит S (System) - определяет системный объект. Если этот бит установлен, то дескриптор определяет сегмент кода или данных, а если сброшен, то системный объект (например, сегмент состояния задачи, локальную дескрипторную таблицу, шлюз).
Поле DPL (Descriptor Privilege Level) - Уровень привилегий, который имеет объект, описываемый данным дескриптором. Это двухбитовое поле, в него при создании дескриптора записывают значения от 0 до 3, определяющее уровень привилегий.
Бит P (Present) - Присутствие сегмента в памяти. Если этот бит установлен, то сегмент есть в памяти, если сброшен, то его нет
Бит U (User) - Бит пользователя. Этот бит процессор не использует и позволяет программе использовать его в своих целях.
Бит X - Зарезервированный бит. Intel не рекомендует использовать этот бит, так как он может понадобится в более поздних моделях процессоров.
Бит D (Default size) - Размер операндов по умолчанию. Если бит сброшен, то процессор использует объект, описываемый данным дескриптором, как 16-разрядный, если бит установлен - то как 32-разрядный.
Бит G (Granularity) - Гранулярность сегмента, т. е. единицы измерения его размера. Если бит G=0, то сегмент имеет байтную гранулярность, иначе - страничную (одна страница - это 4Кб).
2.2. Селектор сегмента
При адресации памяти в защищённом режиме команды ссылаются на сегменты, указывая не их адреса (как в режиме реальных адресов), а описания сегментов (их дескрипторы)
Селектор имеет следующий формат:
· Двухбитовое поле RPL (Requested Privilege Level) содержит номер уровня привилегий, которое имеет текущая программа.
· Бит TI (Table Indicator) определяет таблицу, из которой выбирается нужный дескриптор. Если бит TI = 0, то обращение производится к глобальной дескрипторной таблице GDT (она одна на всю систему), если TI = 1 - то к текущей локальной дескрипторной таблице LDT (таких может быть много).
· Index - это собственной номер дескриптора, от 0 до 8191.
В программе мы будем использовать только глобальную дескрипторную таблицу и нулевой уровень привилегий, поэтому младшие три бита будут равны 0
2.3. Глобальная дескрипторная таблица
Прежде, чем процессор перейдёт в защищённый режим, должна быть определена глобальная дескрипторная таблица GDT (Global Descriptor Table), так как все сегменты и прочие системные объекты должны быть описаны в дескрипторной таблице. Адрес GDT храниться в специальном регистре процессора GDTR.
Формат регистра GDTR:

Адрес начала GDT - это тот адрес, по которому вы разместили GDT.
Предел таблицы GDT - это максимальное смещение относительно её начала.
Например, вы создаёте GDT, состоящую из 3-х дескрипторов - для сегментов кода, стека и данных. Общее число дескрипторов будет равно четырём, потому что первым по счёту будет идти нулевой дескриптор, а за ним уже остальные три:
Смещение от начала GDT | Назначение дескриптора |
0 | Нулевой |
8 | Сегмент кода |
16 | Сегмент стека |
24 | Сегмент данных |
Размер GDT в данном случае будет равен 32 байтам, следовательно, предельное смещение в таблице будет равно 31 - это и есть предел GDT.
3. Пример программы защищённого режима
Для перевода процессора в режим защищённых адресов необходимо выполнить ряд действий:
1. Подготовка в оперативной памяти таблицы глобальных дескрипторов GDT.
2. Инициализация необходимых дескрипторов в таблице GDT.
3. Загрузка в регистр gdtr адреса и размера таблицы GDT.
4. Запрет обработки аппаратных прерываний.
5. Переключение микропроцессора в защищенный режим.
6. Организация работы в защищенном режиме:
· настроить сегментные регистры;
· выполнить содержательную работу программы; в нашем случае мы выводим строку в видеобуфер;
· подготовиться к возврату в реальный режим;
· запретить аппаратные прерывания.
7. Настройка сегментных регистров для работы в реальном режиме.
8. Переключение микропроцессора в реальный режим.
9. Разрешение прерываний.
10. Стандартное для MS-DOS завершение работы программы.
Рассмотрим каждый этап подробнее:
3.1. Подготовка таблицы глобальных дескрипторов GDT
В нашей программе достаточно определить одну дескрипторную таблицу – GDT. Таблицу LDT есть смысл применять, когда в системе работают несколько задач и необходимо изолировать их друг от друга.
Дескриптор сегмента удобно представить в виде структуры:
descr struc
limit dw 0
base_1 dw 0
base_2 db 0
AR db 0
attr db 0
base_3 db 0
ends
Поля структуры соответствуют формату дескриптора.
Дескрипторную таблицу представим в виде сегмента, который содержит массив структур типа descr.
gdt_segment segment para public 'data' use32
null_descr descr <0,0,0,0,0,0> ;нулевой дескриптор 0h
data32_descr descr <0,0,0,b,0,0> ; дескриптор ; сегмента данных защищённого режима 8h
code32_descr descr <0,0,0,b,0,0> ; дескриптор ; сегмента кода защищённого режима 10h
vbuf32_descr descr <0,0,0,b,0,0> ; дескриптор ; видеобуфера защищённого режима 18h
code16_descr descr <0,0,0,b,0,0> ; дескриптор ; кода реального режима 20h
data16_descr descr <0,0,0,b,0,0> ; дескриптор ; данных реального режима 28h
gdt_size = $-null_descr-1 ; размер таблицы GDT
point_gdt gdtr <gdt_size,0> ; псевдодескриптор для команды lgdt
gdt_segment ends
3.2. Инициализация необходимых дескрипторов в GDT
Для заполнения поля адреса и предела в дескрипторе сегмента удобнее использовать макрос:
load_descr macro descriptor, seg_address, seg_size
mov descriptor. limit, seg_size
xor eax, eax
mov ax,seg_address ; адрес сегмента в ах
shl eax,4 ; сдвигом на 4 разряда получим ;физический 20-разрядный адрес сегмента
mov descriptor.base_1,ax
rol eax,16 ; получаем оставшуюся часть адреса
mov descriptor. base_2,al
endm
Заполнение полей дескрипторов:
load_descr data32_descr, data, data_size
load_descr code32_descr,0,0FFFFh ; 0FFFF – ;младшие разряды поля предел, старшие разряды установим ;в поле attr
load_descr vbuf32_descr,0,0FFFFh
load_descr code16_descr, rm_code,0FFFFh
load_descr data16_descr,0,0FFFFh
Поле attr структуры descr заполним командой mov:
mov data32_descr.attr,b ; устанавливаем биты ; G и D в 1, для 32-х разрядной адресации
mov code32_descr.attr,b ; старшие 4 разряда ; пределасегмента = 1111b
mov vbuf32_descr. attr,b
3.3. Загрузка в регистр gdtr адреса и размера таблицы GDT
Адрес и размер сформированной таблицы GDT нужно поместить в регистр gdtr.
Для загрузки именно этого регистра в системе команд микропроцессора есть специальная команда:
lgdt адрес_48-битного_поля (Load GDT register) — загрузить регистр gdtr. Команда lgdt загружает системный регистр gdtr содержимым 6-байтового поля, адрес которого указан в качестве операнда.
Из описания команды следует, что вначале необходимо сформировать поле из шести байт со структурой, аналогичной формату регистра gdtr, а затем указать адрес этого поля в качестве операнда команды lgdt.
Структура для описания псевдодескриптора gdtr:
gdtr struc
limit dw 0
adress dd 0
ends
Вычисляем физический адрес GDT и загружаем регистр gdtr :
xor eax, eax
mov ax, gdt_segment
shl eax,4
mov point_gdt. adress, eax ; сохраняем полученный ; адрес в структуре gdtr – point_gdt
lgdt point_gdt ; загружаем регистр gdtr
3.4. Запрет обработки аппаратных прерываний
Обработка прерываний в защищенном режиме принципиально отличается от обработки прерываний в реальном режиме. Поэтому, как только микропроцессор переключится в защищенный режим, первое же прерывание от таймера, которое происходит 18,2 раза в секунду, «подвесит» компьютер. Пока не будем обрабатывать прерывания, поэтому их надо запретить.
;запрещаем прерывания:
cli ;маскируемые
mov al,80h ;немаскируемые
out 70h, al
3.5. Переключение микропроцессора в защищенный режим
Для перевода процессора в защищённый режим необходимо установить бит pe в регистре управления cr0:
mov eax,cr0 ; получаем содержимое cr0
or al,1 ; устанавливаем бит pe
mov cr0,eax ; загружаем в регистр cr0 новое ; значение
Также необходимо вычислить адрес точки входа в защищённый режим:
xor eax,eax
mov ax,pm_code ; адрес сегмента кода ; защищённого режима
shl eax,4 ; получаем физический адрес
add eax,offset pm_entry_point ; добавляем ; к адресу смещение метки pm_entry_point
; сохраняем полученный адрес в переменной ;protected_mode
mov dword ptr protected_mode, eax
Настройка сегментных регистров:
В защищённом режиме необходимо загрузить в сегментные регистры селекторы соответствующих сегментов в таблице GDT. Регистр cs в защищённом режиме программно недоступен, поэтому загрузить в него селектор сегмента кода можно при помощи моделирования команды дальнего перехода far jmp на метку protected_mode. Команды ближнего перехода изменяют только содержимое eip\ip, а команды дальнего перехода — оба регистра cs и eip\ip. Исходя из этого получаем:
db 66h ;префикс замены разрядности ; операнда
db 0EAh ;машинный код команды ; far jmp
protected_mode dd ? ;смещение метки перехода в ; сегменте команд
dw 10h ;селектор дескриптора ; сегмента кода в таблице GDT
Теперь загрузим селекторы остальных сегментов:
mov ax,8h ; селектор сегмента данных
mov ds,ax
mov ax,18h ; селектор видеобуфера
mov es, ax
3.6. Организация работы в защищённом режиме
Здесь мы просто выводим две строки, используя механизм страничной адресации, который рассмотрим в следующем разделе. Вывод строки в видеобуфер оформим в виде макроса:
print_str macro message,msg_len,atr
local outstr
mov cx,msg_len ;длина сообщения
mov esi,offset message ;адрес строки ;сообщения
mov ah,atr ;атрибут выводимых символов
outstr:
mov al,[si]
mov es:[edi],ax ; в es:[edi] должны ; указывать на видеобуфер
inc si
inc edi
inc edi
loop outstr
endm
3.7. Подготовка к возврату в реальный режим
Каждому сегментному регистру соответствует программно недоступный теневой регистр, значение которого обновляется при загрузке соответствующего сегментного регистра. Теневые регистры в защищённом режиме содержат селекторы дескрипторов в соответствующей таблице дескрипторов.
Здесь возникает примерно та же проблема с сегментными регистрами, что была при входе в защищенный режим. Микропроцессор использует теневые регистры, даже работая в реальном режиме. При этом поля этих регистров заполнены, конечно, в соответствии с требованиями реального режима:
· предел должен быть равен 64 К = 0ffffh;
· бит G - 0 (значение размера в поле предела) — это значение в байтах;
· байт атрибута равен = 92h;
· базовый адрес значения не имеет.
Следовательно, перед переходом в реальный режим нужно сформировать эти значения в соответствующих дескрипторах и сделать актуальными эти изменения в теневых регистрах, для чего нужно перезагрузить сегментные регистры. После этого можно переходить в реальный режим. В программе все эти действия могут выглядеть так:
;переход в сегмент rm_code на метку real_mode
db 0EAh
dd offset real_mode
dw 20h ; селектор дескриптора сегмента кода ; реального режима!!!
mov ax,28h ; селектор дескриптора сегмента ;данных для реального режима!!!
mov ds, ax
mov es, ax
;загрузить в регистр cs адрес rm_code
db 0EAh
dw offset next
dw rm_code
3.8. Переключение микропроцессора в реальный режим
Для перевода процессора в защищённый режим необходимо сбросить бит pe в регистре управления cr0:
mov eax,cr0 ; получаем содержимое cr0
and al,0FEh ; сбрасываем бит pe
mov cr0,eax ; загружаем в регистр cr0 новое ; значение
3.9. Разрешение прерываний
Теперь можно разрешить прерывания.
sti
xor al,al
out 70h,al
3.10. Стандартное для MS-DOS завершение работы программы
;окончание работы программы:
mov ax,4c00h
int 21h
4. Страничная организация памяти
При обращении к памяти процессор всегда использует пару значений - селектор:смещение (в защищённом режиме сегменты определяются селекторами соответствующих дескрипторов и механизм управления памятью возможно реализовать только в защищённом режиме Адрес, указанный таким образом называется логическим адресом, так как смещение задаётся относительно начала сегмента, размещённого по любому адресу.
Каждый раз, при выполнении команды, процессор производит преобразование логического адреса в линейный адрес - 32-разрядный абсолютный адрес в памяти. После вычисления линейного адреса процессор преобразует его в физический адрес, по которому и производит обращение к памяти.
Смысл в использовании линейных и физических адресов появляется, когда программа включает в процессоре механизм трансляции страниц. Страница - это 4Кб-область памяти. Всё адресное пространство разбивается на страницы. Получается 220 страниц (т. е. 1048576 или 1М), которые полностью покрывают 32-разрядное адресное пространство.
Механизм страничного преобразования:

Структура элемента каталога страниц

Структура элемента таблицы страниц

Для включения механизма страничного преобразования необходимо выполнить ряд действий:
1. Подготовить каталог страниц
2. Заполнить таблицу страниц
3. Адрес каталога страниц поместить в регистр CR3
4. Включить страничное преобразование (установка бита 31 в регистре СR0)
4.1. Подготовка каталога страниц
Каталог страниц – это набор 32 – разрядных записей (элементов). Начало каталога страниц в оперативной памяти будет располагаться по адресу 1МБ (100000h).
В нашем примере единственное, что мы делаем – выводим на экран заданную строку. При этом используется один единственный адрес –0B8000h. Следовательно, в данном примере для демонстрации возможностей страничной адресации достаточно заполнить лишь один элемент каталога страниц :
mov edi,100000h ;начало каталога страниц (1Мб)
mov eax,101007h ;единственный значащий элемент
stosd
mov ecx,1023 ; остальные 1023 элемента
xor eax,eax
rep stosd ; заполним нулями
Элемент 101007h означает, что страница присутствует в оперативной памяти (бит Р), доступна для чтения – записи (бит R/W) и доступна с любого уровня привилегий (бит U/S). А 1010000h = 1МБ + 4Кб, а 4КБ – потому что сам каталог занимает столько.
4.2. Подготовка таблицы страниц
Заполним таблицу страниц:
mov eax,h ; первая запись - адрес ; нулевой страницы равен 0
mov ecx,1024 ; количество страниц в таблице
fill_page_table:
stosd ; запишем первый элемент
add eax,1000h ; добавим 4 Кб
loop fill_page_table ; заполним всю таблицу ; страниц
Первая запись таблицы ссылается на страницу с адресом 0h (h), длинна страницы равна 4 Кб. Далее мы еще создаем 1023 записи таблицы страниц, которые ссылаются на адреса, вычисляемые по формуле:
Адрес страницы n=4Кб+n*4Кб, где n меняется от 1 до 1023. Последняя запись страницы (запись с номером 1023) ссылается на страницу с адресом = 4Кб+1023*4Кб = 4Мб.
4.3. Загрузка регистра CR3
mov eax,h ; базовый адрес = 1 Мб
mov cr3,eax ; cr3 хранит базовый адрес каталога страниц
4.4. Включение страничного преобразования
mov eax, cr0
bts eax,31 ; установка31 бита регистра eax
mov cr0,eax ; обновление регистра cr0
Изменяем физический адрес страницы 12000h на 0B8000h:
mov eax,000B8007h
mov es:h+012h*4,eax
Если страничное преобразование выключено то линейный адрес равен ES (0000h) + смещение (равное 101000h + 12h*4) = 101048h. Далее процессор, видя, что включена страничная адресация, начинает преобразовывать линейный адрес в физический адрес по правилам страничного преобразоания:
Старшие 10 бит (b)=00h – номер записи в каталоге страниц
Средние 10 бит (b)=101h – номер записи в таблице страниц
Младшие 12 бит ()=048h – смещение в странице
Нулевая запись каталога страниц указывает на начало нашей таблицы страниц. В сформированной нами таблице страниц получается, что запись с номером 101h отображена на адрес 101000h. И в результате мы имеем физический адрес (выставляемый процессором на шину): 101000h + 048h = 101048h.
Физический адрес совпал с линейным, только потому что мы сформировали такую таблицу страниц.
У нас получилось так, что запись с номером h) ссылается на 1 Мб (256*4Кб), а запись h) на 1 Мб + 4 Кб (1010000), значит она ссылается на саму таблицу страниц. Таким образом, если мы будем записывать по адресу – запись 257 + смещение, то мы будем изменять значение записей таблицы страниц. Причем чтобы изменить нужную запись, нам надо сформировать смещение равно n*4, где n – номер одной из 1024-ёх записей, а 4 – так как размер одной записи равен 4 байта (или 32 бита).
Например, в нашем случае - мы берем запись каталога страниц (на него ссылается регистр CR0) с номером ноль (00h), она ссылается на определенную нами в таблицу страниц. В таблице страниц мы берем запись номер h), она ссылается на адрес памяти равный 1 Мб + 4 Кб (1010000h). Далее мы прибавляем к этому адресу смещение 012h*4 и записываем уже по адресу 1010000h + 012h*4 регистр EAX (000В8000h). Итак, получилось, что мы записали в запись с номером 18(12h) таблицы страниц значение 0В8000h. Теперь она ссылается на область видеобуфера.
Выводим строку в видеобуфер:
mov edi,0B8000h ; стандартный адрес ;видеобуфера
print_str msg1,msg_length1,0Ah
Так как включён механизм страничной трансляции, то:
старшие 10 битов: 0b = 0
средние 10 битов:b = B8h
младшие 12 битов: 0b = 0
Нулевая запись в каталоге страниц указывает на нашу таблицу страниц. Запись номер В8h содержит адрес 0B8000h (Начало видеобуфера)
Смещение – 0 (см. младшие 12 битов) к началу страницы 0B8000h не нужно ничего прибавлять.
Физический адрес = 0B8000h
mov edi,0120A0h ; теперь адрес 12000h ;отображён на физический адрес 0B8000
print_str msg2,msg_length2,0Ch
Аналогично:
старшие 10 битов: 0b = 0
средние 10 битов:b = 12h
младшие 12 битов: 0b = A0h
Только запись с номером 12h в таблице страниц теперь отображена на адрес В8000h. И получилось так, что мы выводим строку по адресу видеобуфера (0B800h) + смещение A0h (чтобы на экрани выводимые строки не накладывались друг на друга)
5. Литература
1. , Язык ассемблера: уроки программирования. – Диалог-МИФИ, 2001. – с. 640.
2. Assembler для DOS, Windows и UNIX. – М.: ДМК Пресс, 2000.
3. http://sasm. *****/ - Защищённый режим процессора с архитектурой IA-32
6. Листинг программы
;
; TASM:
; tasm /m p_mode,,,
; tlink /3 p_mode
;
.586P ;разрешение инструкций Pentium
.model large
;Структура для описания дескриптора сегмента:
descr struc
limit dw 0
base_1 dw 0
base_2 db 0
AR db 0
attr db 0
base_3 db 0
ends
;Макрос инициализации дескрипторов:
load_descr macro descriptor, seg_address, seg_size
mov descriptor. limit, seg_size
xor eax, eax
mov ax, seg_address
shl eax,4
mov descriptor. base_1,ax
rol eax,16
mov descriptor. base_2,al
endm
;Макрос вывода строки в видеобуффер:
print_str macro message, msg_len, atr
local outstr
mov cx, msg_len ;длина сообщения
mov esi, offset message ;адрес строки сообщения
mov ah, atr ;атрибут выводимых символов
outstr:
mov al,[si]
mov es:[edi],ax
inc si
inc edi
inc edi
loop outstr
endm
;Структура для описания псевдодескриптора gdtr:
gdtr struc
limit dw 0
adress dd 0
ends
;Сегмент стека реального режима:
stk segment stack 'stack' use16
db 256 dup (0)
stk ends
;Таблица глобальных дескрипторов:
gdt_segment segment para public 'data' use32
null_descr descr <0,0,0,0,0,0> ; нулевой ;дескриптор 0h
data32_descr descr <0,0,0,b,0,0> ; дескриптор ;сегмента данных защищённого режима 8h
code32_descr descr <0,0,0,b,0,0> ; дескриптор ;сегмента кода защищённого режима 10h
vbuf32_descr descr <0,0,0,b,0,0> ; дескриптор ;видеобуфера защищённого режима 18h
code16_descr descr <0,0,0,b,0,0> ; дескриптор ;кода реального режима 20h
data16_descr descr <0,0,0,b,0,0> ; дескриптор ;данных реального режима 28h
gdt_size = $-null_descr-1 ; размер таблицы GDT
point_gdt gdtr <gdt_size,0> ; псевдодескриптор для ;команды lgdt
gdt_segment ends
;Сегмент данных защищённого режима :
data segment para public 'data' use32
msg1 db "Welcome to"
msg_length1 = $-msg1
msg2 db "Protected mode!!!"
msg_length2 = $-msg2
crlf db 0Ah,0Dh,'$'
data_size=$-msg1-1
data ends
;Сегмент кода реального режима:
rm_code segment para public 'code' use16
assume cs:rm_code, ss:stk
main proc
mov ax, stk
mov ss, ax
mov ax,03h ; Установка текстового режима ;80x25 и
int 10h ; очистка экрана
; открываем линию А20 для 32-х битной адресации:
in al,92h
or al,2
out 92h, al
;заполняем таблицу глобальных дескрипторов:
assume ds:gdt_segment
mov ax, gdt_segment
mov ds, ax
load_descr data32_descr, data, data_size
load_descr code32_descr,0,0FFFFh
load_descr vbuf32_descr,0,0FFFFh
load_descr code16_descr, rm_code,0FFFFh
load_descr data16_descr,0,0FFFFh
mov data32_descr. attr,b
mov code32_descr. attr,b
mov vbuf32_descr. attr,b
;вычисляем адрес точки входа в защищённый режим:
xor eax, eax
mov ax, pm_code
shl eax,4
add eax, offset pm_entry_point
mov dword ptr protected_mode, eax
;загружаем регистр gdtr:
xor eax, eax
mov ax, gdt_segment
shl eax,4
mov point_gdt. adress, eax
lgdt point_gdt
;запрещаем прерывания:
cli ;маскируемые
mov al,80h ;немаскируемые
out 70h, al
;переключение в защищенный режим:
mov eax, cr0
or al,1
mov cr0,eax
;настройка сегментных регистров:
;загрузка селектора сегмента кода в регистр cs:
db 66h ;префикс ;замены разрядности операнда
db 0EAh ; машинный код команды ;far jmp
protected_mode dd? ;смещение метки перехода в ;сегменте команд
dw 10h ;селектор сегмента кода в ;таблице GDT
real_mode:
;переключение в реальный режим режим:
mov eax, cr0
and al,0FEh
mov cr0,eax
;загрузить в регистр cs адрес rm_code
db 0EAh
dw offset next
dw rm_code
next:
mov ax, data
mov ds, ax
mov ax, stk
mov ss, ax
;разрешаем прерывания:
sti
xor al, al
out 70h, al
; перевод строки и возврат каретки:
mov ah,9
lea dx, crlf
int 21h
;окончание работы программы:
mov ax,4c00h
int 21h
main endp
rm_code ends
; Сегмент кода защищённого режима:
pm_code segment para public 'code' use32
assume cs:pm_code
pm_entry_point:
; загрузка селекторов для сегмента данных и видеобуфера:
mov ax,8h
mov ds, ax
mov ax,18h
mov es, ax
; Работаем в защищенном режиме:
; заполняем каталог страниц:
mov edi,100000h ; начало каталога страниц (1Мб)
mov eax,101007h ; единственный значащий ;элемент
stosd
mov ecx,1023 ; остальные 1023 элемента
xor eax, eax
rep stosd ; заполним нулями
; заполняем таблицу страниц:
mov eax,h ; первая запись - адрес нулевой страницы равен 0
mov ecx,1024 ; количество страниц в ;таблице
fill_page_table:
stosd ; запишем первый элемент
add eax,1000h ; добавим 4 Кб
loop fill_page_table ; заполним всю таблицу страниц
mov eax,h ; базовый адрес = 1 Мб
mov cr3,eax ; cr3 хранит базовый адрес ;каталога страниц
; включить страничную адресацию:
mov eax, cr0
bts eax,31
mov cr0,eax
; изменяем физический адрес страницы 12000h на 0B8000h:
mov eax,000B8007h
mov es:h+012h*4,eax
; выводим строку в видеобуфер:
mov edi,0B8000h ; стандартный адресс ;видеобуфера
print_str msg1,msg_length1,0Ah
mov edi,0120A0h ; теперь адрес 12000h ;отображён на физический адрес 0B8000
print_str msg2,msg_length2,0Ch
; выключить страничную адресацию:
mov eax, cr0
btc eax,31
mov cr0,eax
;Выход из защищённого режима:
;формирование дескрипторов для реального режима:
;загрузка теневых дескрипторов:
mov ax,28h
mov ds, ax
mov es, ax
;переход в сегмент rm_code на метку real_mode
db 0EAh
dd offset real_mode
dw 20h
pm_code ends
end main


