close

Вход

Забыли?

вход по аккаунту

?

Организация ЭВМ3

код для вставкиСкачать
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ
ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ
"ТВЕРСКОЙ ГОСУДАРСТВЕННЫЙ ТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ"
(ГОУВПО "ТГТУ")
Кафедра ЭВМ
Переключение в защищенный режим и возврат в реальный режим. Страничная адресация.
Методические указания к лабораторной работе по курсу
" Организация ЭВМ, комплексов и систем"
для студентов специальности 22.01
"Вычислительные машины, комплексы, системы и сети"
Тверь,
2006
Методическое указание разработано в соответствии с рабочей программой по дисциплине "Организация ЭВМ, комплексов и систем" для студентов специальности 22.01. Предназначено для оказания помощи студентам в подготовке к выполнению лабораторных работ по данной дисциплине.
Составили:к.т.н.Быков П.В.
студентМорозов Д.А.
(c) Тверской государственный технический университет
Оглавление
1.Введение4
2.Как это можно сделать иначе4
2.1.Заполнение таблицы GDT4
2.2.Инициализируем регистр GDTR5
2.3.Определяем точку входа в защищенный режим6
2.4.Запрещение прерываний7
2.5.Осуществляем переход в защищенный режим7
2.6.Восстанавливаем значения сегментных регистров7
3.Страничная адресация9
3.1.Каталог страниц10
3.2.Таблица страниц12
3.3.Адрес каталога страниц помещаем в регистр CR312
3.4.Включение страничной адресации12
3.5.Демонстрация страничной адресации14
4.Литература17
5.Листинг программы18
1. Введение
В предыдущей лабораторной работе был рассмотрен пример переключения процессора в защищенный режим без использования страничной адресации. В данной работе мы рассмотрим страничную адресацию. Еще одним отличаем этой работы от предыдущей будет в использовании несколько иных способов выполнения некоторых шагов предыдущей работы. Тем самым я пытаюсь показать, как можно выполнить один и те же действия разными способами.
Приступим. В пункте 2 я опишу изменения, внесенные в предыдущую программу. А пункт 3 будет целиком посвящен страничной адресации.
2. Как это можно сделать иначе
Итак, я попытаюсь выделить основные изменения, коснувшиеся программы:
1) Заполнение таблицы GDT
2) Инициализация регистра GDTR
3) Определение точки входа в защищенный режим
4) Восстановление значения сегментных регистров
В остальных пунктах кардинальных изменений нет. Еще в нашей новой программе не будет явного выхода из защищенного режима (дабы её не усложнять), по завершению программы нужно перезагрузит компьютер. Еще не нужно проводить подготовку перед запуском программы, описанную в пункте 5 предыдущей лабораторной работы.
2.1. Заполнение таблицы GDT
Прежде всего, в нашей новой программе мы сразу приступаем к инициализации таблицы GDT. Если вы помните, то в предыдущей лабораторной работе мы сначала определили структуру дескриптора, потом с помощью неё определяли таблицу GDT,а затем инициализировали таблицу GDT. Тем самым мы выполняем все эти операции за три шага.
Но в нашей новой лабораторной работе совместили эти три шага в один.
Итак, вот как мы будем заполнять ТАБЛИЦУ ГЛОБАЛЬНЫХ ДЕСКРИПТОРОВ:
GDT:
; нулевой дескриптор (обязательно должен присутствовать в GDT!)
NULL_descr db 8 dup(0)
; дескриптор 32-разрядного сегмента кода: база = 00000000h, размер = FFFFFFFFh
CODE_descr db 0FFh, 0FFh, 00h,00h, 00h, 10011010b, 11001111b, 00h
;дескриптор 32-разрядного сегмента данных: база = 00000000h, размер = FFFFFFFFh
DATA_descr db 0FFh, 0FFh, 00h, 00h, 00h, 10010010b, 11001111b, 00h
; размер таблицы GDT:
GDT_size db $-GDT
Как видите, мы уложили три шага в один. Но основные моменты остались теми же. В данном примере, инициализация дескрипторов похожа на инициализацию массивов в Си.
Вы, наверно, заметили, что я не объявил дескриптора видео памяти, он нам просто не нужен, вы поймете это из главы 3.
2.2. Инициализируем регистр GDTR
Этот шаг практически аналогичен действиям, выполняемым в предыдущей работе. Но все же есть свои особенности.
Сначала вычислим линейный адрес таблицы GDT. Зачем? Чтоб загрузить регистр GDTR. Линейный адрес GDT = линейный адрес базы сегмента RM_CODE (потому что GDT расположена именно в этом сегменте у нас в программе) + смещение метки GDT в нем.
GDTR dw GDT_size - 1; граница GDT (байты 1,0)
dd?; линейный базовый адрес GDT (байты 5 ...2)
xor EAX, EAX ; обнуление регистра EAX
mov AX, RM_CODE; теперь AX= номер сегмента RM_CODE
shl EAX,4; сдвигаем значение в EFX на 4 влево
; вот теперь EAX = линейный адрес базы сегмента RM_CODE
add AX, offset GDT ; теперь EAX = линейный адрес GDT
; линейный адрес GDT кладем в заранее подготовленную переменную:
mov dword ptr GDTR+2,EAX
; собственно, загрузка регистра GDTR:
lgdt fword ptr GDTR
Вообще желательно просмотреть весь листинг программы целиком, потому что RM_CODE сбивает с толку.
2.3. Определяем точку входа в защищенный режим
Теперь нам ее нужно вычислить точно таким же образом линейный адрес ТОЧКИ ВХОДА В ЗАЩИЩЕННЫЙ РЕЖИМ. Как я говорил в прошлой работе, что после переключения в PM мы окажемся в неком подвешенном состоянии - когда регистр CS еще содержит номер сегмента из реального режима, а не селектор. Мы не можем просто сделать mov CS, селектор, CS можно изменить только дальним jmp-ом. Поэтому нам все же придется делать дальний jmp для изменения CS. НО КУДА? Вот именно на ТОЧКУ ВХОДА В ЗАЩИЩЕННЫЙ РЕЖИМ.
Этого пункта не было в прошлой работе. И вам, наверное, не совсем понятно, зачем он нужен. Я это объясню в пункте 2.5, а то я боюсь, что сейчас это только вас запутает. Пока вам главное усвоить, что в ТОЧКЕ ВХОДА В ЗАЩИЩЕННЫЙ РЕЖИМ начинаются команды, которые мы описываем для защищенного режима.
; вычисляем линейный адрес метки ENTRY_POINT(точка входа в защищенный
; режим, находится в PM_CODE сегменте, поэтому от него и начинаем):
xor EAX, EAX; обнуляем регистр EAX
mov AX,PM_CODE; AX = номер сегмента PM_CODE
sh1 EAX,4; EAX = линейный адрес PM_CODE
add EAX,offset ENTRY_POINT ; EAX = линейный адрес ENTRY_POINT
mov dword ptr NTRY_OFF,EAX ; сохраняем его в переменной ENTRY_OFF
Вам следует внимательно посмотреть на листинг и разобраться с тем, что мы сотворили. А сотворили мы следующее. В переменной ENTRY_OFF храниться линейный адрес метки ENTRY_POINT. С метки ENTRY_POINT начинаются команды защищенного режима.
2.4. Запрещение прерываний
В принципе действия аналогичны, но мы запрещаем, как маскируемые так и не маскируемые прерывания.
; запрещаем сначала маскируемые прерывания:
cli
; а затем и немаскируемые:
in AL,70h; читаем 70h порт
or AL,80h; ставит восьмой бит
out 70h,AL; запихиваем на место
; теперь все прерывания запрещены
2.5. Осуществляем переход в защищенный режим
Этот шаг абсолютно аналогичен предыдущей работе, поэтому я думаю, не следует заострять на нем ваше внимание.
2.6. Восстанавливаем значения сегментных регистров
Итак, этот пункт показывает основное отличие нашей нынешней программы от предыдущей. А заключается это отличие в том, что в нашей предыдущей программе у всех дескрипторов после инициализации [с.м. предыдущую Л.Р. пункт 2.3] адрес базы был равен соответствующему линейному адресу сегментов нашей программы. Т.е. GDTData = DS; GDTCode = CS; GDTStc = SS (конечно, сегменту ровен не сам дескриптор, а база его адреса). В нашей нынешней программе адреса базы всех наших дескрипторов равны 0. Т.е. CODE_descr = 0; DATA_descr = 0
Теперь внимательно смотрите на код:
; загрузить новый сектор в регистр CS
db 66h;префикс изменения разрядности операнда
db 0EAh; опкод команды JMP FAR
ENTRY_OFF dd ? ; 32-битное смещение
dw 00001000b; селектор первого дескриптора(CODE_descr)
Вы видите, что мы делаем JMP FAR. Но если в предыдущей работе дескриптор кода ссылался на сегмент ода (CODE), внутри которого было смещение (@)1), с которого начинались команды защищенного режима [с.м. предыдущую Л.Р. пункт 3.3]. То теперь роль этих двух компонент у нас выполняет только смещение - переменная ENTRY_OFF (т.к. база дескриптора все равно ноль), которую мы инициализировали в пункте 2.3. И так в результате мы попадаем в точку входу в защищенный режим. Далее наши действия аналогичны предыдущей работе:
ENTRY_POINT:
; загрузим сегментные регистры селекторами на соответствующие дескрипторы:
mov AX,00010000b;AX=селектор дескриптора данных (№2)
mov DS,AX; кладем его в DS
Вот и все, что мы изменили. Теперь остается разобраться со страничной адресацией.
PS: Некоторым из вас не понятно, зачем перед JMP FAR, мы поставили префикс изменения разрядности операнда (db 66h). Объясняю - дело в том, что, запуская программу в реальном режиме (а мы ее там и запускаем) операнды имеют разрядность 16 бит.
Т.е. если убрать 66h, то получается, что произойдет jmp на адрес, старшая часть ENTRY_OFF: младшая часть ENTRY_OFF до 00001000b дело даже не дойдет.
Еще раз: Если db 66h, будет отсутствовать, то jmp far, будет воспринимать смещение внутри дескриптора кода как 16 битное, а т.к ENTRY_OFF у нас 32 битный, то смещение у нас будет браться из младшей части ENTRY_OFF, а старшая часть ENTRY_OFF будет определять дескриптор вместо строки dw 00001000b.
3. Страничная адресация
Следует иметь ввиду, что все изменения для включения страничной адресации производится уже ПОСЛЕ ПЕРЕХОДА В ЗАЩИЩЕННЫЙ РЕЖИМ!!! И еще - страницы у нас будут 4 - килобайтными!!! И вот они, эти изменения:
1. Подготовить каталог страниц
2. Заполнить таблицу страниц
3. Адрес каталога страниц поместить в регистр CR3
4. Включить страничную адресацию (установка бита 31 в регистре СR0)
5. Демонстрация страничной адресации
С.м. картинку (методическое пособие, рис.2), из которой видно для чего нужны первые 4 пункта. Прежде, чем приступить, хочу вкратце рассказать, чего же мы будем делать. Вопрос в том, КАК НАГЛЯДНО ПОКАЗАТЬ РАЗНИЦУ МЕЖДУ СЕГМЕНТНОЙ И СЕГМЕНТНО-СТРАНИЧНОЙ адресацией? Оказывается, это не так - то уж и просто. Но вот как я попытаюсь этого добиться. В предыдущей работе мы рассмотрели код, который выводит строку на экран, все свелось, в конце концов, к тому, что по адресу ES:0 вывелась надпись, где ES - селектор на предварительно подготовленный дескриптор, база которого равна 0B8000h (т.е. начало видеопамять в текстовом режиме). Все однозначно и понятно. Теперь, как вы уже знаете, при страничной адресации адрес 0B8000h препарируется на три части: старшие 10 бит - номер элемента в каталоге страниц, средние 10 бит - номер элемента в таблице страниц, и, наконец, младшие 12 бит - это СМЕЩЕНИЕ В СТРАНИЦЕ.
Недолго думая, можно умозаключить, что, попытавшись, что-либо записать по адресу 0B8000h при СТРАНИЧНОЙ адресации, мы не обнаружим там никакого начала видеопамяти, разумеется, если ПРЕДВАРИТЕЬНО (!) мы корректно не настроим каталог и таблицы страниц. Вот она, виртуальность! Вроде, вот же он адрес: адрес начала видеопамяти! 0B8000h! У нас перед носом! Ан, нет....Это всего лишь иллюзия, т.к. этот адрес - не линейный, а виртуальный. Вот разбить его на три части, пройтись по каталогу и таблице страниц - вот тогда сформируется ОКОНЧАТЕЛЬНЫЙ АДРЕС (ВЫСТАВЯЕМЫЙ ПРОЦЕССОРОМ НА ШИНУ), который (при страницах в 4 Кб) в отсутствие расширения физического адресного пространства (сброшен флаг PAE), может принять значение от 0 до 4Мб!!! Надеюсь, понятно, почему именно эти значения? Т.к. 1024 записи по 4 Кб. Значит, к чему я все это говорю? А к тому, что если представить все это дело НАОБОРОТ, то придем точно к такому же выводу! Т.е. я хочу сказать, что если 0B8000h - это на самом деле нечто совершенно иное, то с точно таким же успехом, ЛЮБОЙ ВИРТУАЛЬНЫЙ АДРЕС ПОСЛЕ ПРОЕБРАЗОВАНИЙ МОЖЕТ ПРЕВРАТИТЬСЯ В 0B8000!!! НАМ СТОИТ ТОЛЬКО ЗАХОТЕТЬ ЭТОГО! А что значит хотеть? А это значит, соответствующим образом заполнить элементы каталога и таблицы страниц, только и всего.... На примере все станет, совершено ясно.
3.1. Каталог страниц
Итак, где - нибудь сразу после перехода в защищенный режим и загрузки сегментных регистров с соответствующими селекторами нужно создать КАТАЛОГ СТРАНИЦ.
ENTRY_POINT:
;загрузим сегментные регистры селекторами на соответствующие дескрипторы:
mov AX,00010000b;селектор на второй дескриптор (DATA_descr)
mov DS,AX;в DS его
mov ES,AX;и в ES его же
Вот теперь создадим каталог страниц. Каталог страниц - это набор 32 - разрядных записей (элементов); Начало каталога страниц в оперативной памяти будет располагаться по адресу 1МБ (100000h).
Теперь секундочку внимания! В нашем примере единственное, что мы делаем полезного - выводим на экран некоторую надпись. По сути, при этом используется один единственный адрес - ES:ESI, или 0B8000h. Следовательно, в данном примере для демонстрации возможностей страничной адресации достаточно заполнить лишь ОИН ЕДИНСТВЕННЫЙ элемент каталога страниц. Так и сделаем:
mov EDI,100000h;начало каталога страниц(1Мб)
mov EAX,101007h;это наш один единственный значащий элемент
stosd;ES:[EDI]<- EAX
mov ECX,1023;остальные 1023 элементов
xor EAX,EAX
rep stosd;забьем нулями все остальные элементы каталога
Кстати, почему такой загадочный элемент - 101007h? Открываем формат элемента каталога страниц и все становится ясно. Открываем методическое пособие, рис.4 и смотрим на картинку. 7 - (установлены младшие 3 бита адреса) - означает, что страница присутствует в оперативной памяти (бит Р), доступна для чтения - записи (бит R/W) и доступна с любого уровня привилегий (бит U/S). А что такое 1010? Не забыли, что младшие 12 бит для АДРЕСА таблицы страниц ВСЕГДА РАВНЫ нулю? Значит, это 1010000h, что соответствует 1МБ + 4Кб, а 4КБ - потому что САМ каталог занимает столько. Все. С каталогом покончено. Итак, давайте подробнее разберем наши действия. Мы объявили каталог страниц, его первая запись ссылается на таблицу с адресом 1010000h. Остальные, 1023 записи каталога страниц, ссылаются на таблицу страниц с адресом 0h (причем это отсутствующие таблицы, т.к. бит Р=0). Вот и все. Кстати я вам очень советую посмотреть в справочнике Assembler, как действует команда stosd, это сразу устранит многие из ваших вопросов. У некоторых может возникнуть вопрос: если нам нужна одна запись из 1024 записей каталога страниц, зачем нам инициализировать другие? Дело в том, что по правилам мы должны определить все 1024 записи, даже если некоторые из них не используются.
3.2. Таблица страниц
Теперь примемся за ТАБЛИЦУ СТРАНИЦ
mov EAX,00000007h;первая запись - адрес нулевой страницы равен 0
mov ECX,1024;количество страниц в таблице
fill_page_table:
stosd;запишем первый элемент
add EAX,1000h; добавим 4 Кб
loop fill_page_table;и повторим для всех элементов таблицы страниц
Снова разберем "все по косточкам". Мы создали таблицу страниц, первая запись, которой ссылается на страницу с адресом 0h (00000007h), длинна страницы равна 4 Кб. Далее мы еще создаем 1023 записи таблицы страниц, которые ссылаются на адреса, вычисляемые по формуле: Адрес страницы n=4Кб+n*4Кб, где n меняется от 1 до 1023. Т.е. последняя запись страницы (запись с номером 1023) ссылается на страницу с адресом = 4Кб+1023*4Кб = 4Мб. Вот и все, что мы сделали. 3.3. Адрес каталога страниц помещаем в регистр CR3
mov EAX,00100000h;базовый адрес = 1Мб
mov CR3,EAX; в CR3 его!(база каталога страниц ВСЕГДА должна лежать в CR3)
3.4. Включение страничной адресации
; включить страничную адресацию
mov EAX,CR0
or EAX,80000000h
mov CR0,EAX
Теперь можно посмотреть, как действует страничная адресация.
;а теперь изменить физический адрес страницы 12000h на OB8000h
mov EAX,000B8000h
mov ES:00101000h + 4,EAX
Вот здесь надо проникнуться всем существом. Чего это мы такого сотворили?
Сначала нам нужно разобраться, что делает строка mov ES:00101000h + 4,EAX
В первую очередь, не глядя не на какое страничное преобразование происходит формирование линейного адреса: база из дескриптора, на который указывает ES (равна нулю) + смещение (равное 101000h + 12h*4) = 101048h. Далее процессор, видя, что включена страничная адресация, начинает преобразовывать линейный адрес в физический адрес по правилам страничной адресации. Замечу, что если бы страничная адресация была бы выключена, то линейный адрес сразу был бы выставлен на адресную шину, т.к. при сегментной адресации линейный адрес равен физическому адресу. Итак, процессор разбивает линейный адрес 101048h по правилам страничной адресации следующим образом:
Старшие 10 бит (0000000000b)=00h - номер записи в каталоге страниц
Средние 10 бит (0100000001b)=101h - номер записи в таблице страниц
Младшие 12 бит (000001001000)=048h - смещение в странице
Нулевая запись каталога страниц указывает на начало нашей таблицы страниц. Если мы посмотрим на сформированную нами таблицу страниц, то получится, что запись номер 101h в таблице страниц содержит адрес 101000h. Итак, в результате мы имеем ФИЗИЧЕСКИЙ АДРЕС (выставляемый процессором на шину): 101000h + 048h = 101048h.
Ничего не напоминает?! Да, именно так. Получилось, что линейный адрес совпал с физическим адресом. НО ЭТО ТОЛЬКО ПОТОМУ, ЧТО МЫ ТАК СФОРМИРОВАЛИ ТАБЛИЦУ СТРАНИЦ!!! (т.е. каждая следующая запись аккуратненько содержит адрес на 4 Кб (страница) старше предыдущей), поэтому линейный адрес совпал с физическим!!! Вот это важно понять. Давайте рассмотрим все поподробнее и проанализируем с самого начала: мы имеем таблицу страниц, она начинается по адресу 1Мб + 4Кб (1010000) и содержит 1024 записи (можно сказать указателя) каждая из которых ссылается на страницу в 4Кб, размер всей таблицы страниц равен 4 Кб (1024 записи по 4 байта). Причем мы инициализировали записи таблицы таким образом, что все они ссылаются на область памяти с началом 0 и концом 4Мб (с.м. пункт 3.2).
У нас получилось так, что запись с номером 256 (100h) ссылается на 1 Мб (т.к. 256*4Кб), а запись 257 (101h) на 1 Мб + 4 Кб (1010000), т.е. она ссылается на саму таблицу страниц. Таким образом, если мы будем записывать по адресу - запись 257+смещение, то мы будем изменять значение записей таблицы страниц. Причем чтобы изменить нужную запись, нам надо сформировать смещение равно n*4, где n - номер одной из 1024-ёх записей, а 4 - т.к. размер одной записи равен 4 байта (или 32 бита с.м. рис. 5 в методическом пособии).
Например, в нашем случае - мы берем запись каталога страниц (на него ссылается регистр CR0) с номером ноль (00h), она ссылается на определенную нами в пункте 3.2 таблицу страниц. В таблице страниц мы берем запись номер 257 (101h), она ссылается на адрес памяти равный 1 Мб + 4 Кб (1010000h). Далее мы прибавляем к этому адресу смещение 012h*4 и записываем уже по адресу 1010000h + 012h*4 регистр EAX (=000В8000h). Итак, получилось, что мы записали в запись с номером 18(12h) таблицы страниц значение 0В8000h. Т.е. теперь она ссылается на порт видео памяти.
3.5. Демонстрация страничной адресации
А вот теперь демонстрация силы и мощи страничной адресации:
; вывод mes1 по стандартному адресу (начало видеопамяти 0В8000h)
mov EDI,0B8000h ; для команды movsw, EDI = начало видеопамяти
mov ESI, PM_DATA
shl ESI,4
add ESI,offset mes1 ; ESI = адрес начала mes1
mov ECX,mes_len ; длина текста в ECX
rep movsw ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)
; вывод mes2 по НЕСТАНДАРТНОМУ АДРЕСУ 12000h
mov EDI,0120A0h ; 12000h ( же можешь считать, что это 0В8000h) + A0h
mov ESI, PM_DATA
shl ESI,4
add ESI,offset mes2 ; ESI = адрес начала mes2
mov ECX,mes_len ; длина текста в ECX
rep movsw ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)
Хорошо теперь осталось расшифровать, что мы тут сделали, приступим:
Команда mov EDI,0B8000h. Тут никаких разбиений адреса не происходит, процессор просто кладет в EDI число 0В8000h. РАЗБИЕНИЕ ЛИНЕЙНОГО АДРЕСА (при страничной арганизации) ПРОИСХОДИТ ТОЛЬКО КОГДА ПРОЦ ОБРАЩАЕТСЯ К ПАМЯТИ!!! (причем не важно, читает он данные, или записывает).
Вот если бы было mov EAX,[B8000h] - тут уже другое дело. Обрати внимание на квадратные скобки, они означают, что в EAX кладется не число B8000h, а значение, ХРАНЯЩЕЕСЯ ПО АДРЕСУ B8000h, т.е. процессору необходимо будет обратиться к памяти по адресу B8000h, ВОТ ЗДЕСЬ УЖЕ СРАБОТАЮТ ВСЕ МЕХАНИЗМЫ страничной организации.
Где же у нас идет обращение к памяти? А вот здесь:
rep movsw ; DS:ESI (наше сообщение) -> ES:EDI
т.е. процессор берет данные (слово), логический адрес которого равен DS:ESI и помещает по логическому адресу ES:EDI.
Но процессор не знает ничего о физическом адресе, имея в наличии (пока) только логический адрес. Значит, он не знает, откуда брать данные и куда их помещать. ОН НЕ ЗНАЕТ ФИЗИЧЕСКИХ АДРЕСОВ ЭТИХ ДАННЫХ!
Поэтому, начиная с этого момента процессор, начинает преобразовывать логический адрес в физический адрес. Это первый шаг, который вам нужно уяснить. Итак, первым делом процессор преобразует этот логический адрес в линейный адрес. Давайте рассмотрим на примере ES:EDI (т.е. КУДА процессор запишет данные): База из дескриптора, на который указывает селектор в ES (=0) + смещение (=ESI = 0B8000h) = 0B8000h.
Т.е. процессор знает, что он должен записать данные по ЛИНЕЙНОМУ АДРЕСУ 0В8000h. Он уже готов выставить его на адресную шину (т.е. попросту сделать его физическим, не изменяя), но тут вдруг обнаруживает, что включена страничная адресация (стоит бит 80000000h в регистре CR)!!! Процессор принимает, что 0B8000h нельзя выставлять на шину адреса! Его нужно ПРЕОБРАЗОВЫВАТЬ В ФИЗИЧЕСКИЙ АДРЕС!
И процессор тут же разбивает его на три части:
старшие 10 битов: 0000 0000 00b = 0
средние 10 битов: 00 1011 1000b = B8
младшие 12 битов: 0000 0000 0000 b = 0
Нулевая запись в каталоге страниц (см. старшие 10 бит адреса) указывает на нашу таблицу страниц. Запись номер В8h (см. средние 10 бит адреса) содержит адрес 0B8000h. Это и есть адрес начала страничного фрейма (или попросту - начало страницы в памяти).
Смещение - 0 (см. младшие 12 битов) ничего не дают в итоге (т.е. к началу страницы 0B8000h не нужно ничего прибавлять).
Итак, процессор, наконец, сделал своё дело! Он сформировал ФИЗИЧЕСКИЙ АДРЕС! И он равен... 0B8000h!!! Обрати внимание, что линейный адрес совпал с физическим адресом.
Строки
mov EDI,0120A0h ; 12000h
...
rep movsw ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)
работают аналогично.
Только есть одно отличие, линейный адрес 0B8000h СОВПАЛ с точно таким же физическим адресом (0B8000h), но линейный адрес 120А0h НЕ СОВПАДЕТ с точно таким же физическим адресом (120А0h). А с чем? Думаю, вы уже догадались, линейный адрес 120А0h в нашем примере СОВПАДЕТ С ФИЗИЧЕСКИМ АДРЕСОМ В80А0h!!!
И знаете почему? ТОЛЬКО ПОТОМУ, ЧТО В ЗАПИСЬ НОМЕР 12h в таблице страниц мы положили число В8000h (см. пункт 3.4.), а 0А0h (младшие 12 бит) - смещение. Вот она магия страничной адресации.
PS: Кстати хочу сказать тем из вас кому не понятно, зачем мы используем в программе линию А20, не заостряя пока не ней вашего внимания одна из работ будет посвящена отдельно ей.
4. Литература
1.Григорьев В.Л. Микропроцессор i486. Архитектура и программирование (в 4-х книгах). Книга 1. Программная архитектура. - М.: ГРАНАЛ, 1993. - с. 346, ил. 87.
2.http://subscribe.ru/catalog/comp.soft.prog.intelpm рассылка - ПРОЦЕССОР INTEL В ЗАЩИТНОМ РЕЖИМЕ
5. Листинг программы
; TASM:
; TASM /m PM.asm
; TLINK /x /3 PM.obj
; PM.exe
; MASM:
; ML /c PM.asm
; LINK PM.obj,,NUL,,,
; PM.exe
.386p ; разрешить привилегированные инструкции i386
; СЕГМЕНТ КОДА (для Real Mode)
; ---------------------------------------------------------------------------
RM_CODE segment para public 'CODE' use16
assume CS:RM_CODE,SS:RM_STACK
@@start:
mov AX,03h
int 10h ; текстовый режим 80х25 + очистка экрана
; открываем линию А20 (для 32-х битной адресации):
int AL,92h
or AL,2
out 92h,AL
; вычисляем линейный адрес метки ENTRY_POINT (точка входа в защитный режим):
xor EAX,EAX ; обнуляем регистр EAX
mov AX,PM_CODE ; AX = номер сегмента PM_CODE
shl EAX,4 ; EAX = линейный адрес PM_CODE
add EAX,offset ENTRY_POINT ; EAX = линейный адрес ENTRY_POINT
mov dword ptr ENTRY_OFF,EAX ; сохраняем его в переменной
; (кстати, подобный "трюк" называется SMC - самомодифицирующий код)
; теперь надо вычислить линейный адрес GDT (для загрузки регистра GDTR)
xor EAX,EAX
mov AX,RM_CODE ; AX = номер сегмента RM_CODE
shl EAX,4 ; EAX = линейный адрес RM_CODE
add EAX,offset GDT ; теперь EAX = линейный адрес GDT
; линейный адрес GDT кладем в заранее подготовленную переменную:
mov dword ptr GDTR+2,EAX
; а подобный трюк назвать SMC уже нельзя, потому как по сути мы модифицируем данные :)
; собственно, загрузка регистра GDTR:
lgdt fword ptr GDTR
; запрет маскируемых прерываний:
cli
; запрет немаскируемых прерываний:
in AL,70h
or AL,80h
out 70h,AL
; переключение в защитном режиме:
mov EAX,CR0
or AL,1
mov CR0,EAX
; загрузить новый селектор в регистр CS
db 66h ; префикс изменения разрядности операнда
db 0EAh ; опкод команды JMP FAR
ENTRY_OFF dd ? ; 32-битное смещение
Dw 00001000b ; селектор первого дескриптора (CODE_descr)
; ТАБЛИЦА ГЛОБАЛЬНЫХ ДЕСКРИПТОРОВ:
GDT:
; нулевой дескриптор (обязательно должен присутствовать в GDT!):
NULL_descr db 8 dup(0)
CODE_descr db 0FFh,0FFh,00h,00h,00h,10011010b,11001111b,00h
DATA_descr db 0FFh,0FFh,00h,00h,00h,10010010b,11001111b,00h
GDT_size equ $-GDT ; размер GDT
GDTR dw GDT_size-1 ; 16-битный лимит GDT
dd ? ; здесь будет 32-битный линейный адрес GDT
RM_CODE ends
;----------------------------------------------------------------------------
; СЕГМЕНТ СТЕКА (для Real Mode)
;----------------------------------------------------------------------------
RM_STACK segment para stack 'STACK' use16
db 100h dup(?) ; 256 байт под стек - это даже много
RM_STACK ends
;----------------------------------------------------------------------------
; СЕГМЕНТ КОДА (для Protected Mode)
; ---------------------------------------------------------------------------
PM_CODE segment para public 'CODE' use32
assume CS:PM_CODE,DS:PM_DATA
ENTRY_POINT:
; загрузим сегментные регистры селекторами на соответствующие дескрипторы:
mov AX,00010000b ; селектор на второй дескриптор (DATA_descr) mov DS,AX ; в DS го
mov ES,Ax ; его же - в ES
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; создать каталог страниц
mov EDI,00100000h ; его физический адрес - 1 Мб
mov EAX,00101007h ; адрес таблицы 0 = 1 Мб + 4 Мб
stosd ; записать первый элемент каталога
mov ECX,1023 ; остальные элементы каталога - xor EAX,EAX ; нули
rep stosd
; заполнить таблицу страниц 0
mov EAX,00000007h ; 0 - адрес страницы 0
mov ECX,1024 ; число страниц в таблице
fill_page_table:
stosd ; записать элемент таблицы
add EAX,00001000h ; добавить к адресу 4096 байтов
loop fill_page_table ; и повторить для всех элементов
; поместить адрес каталога страниц в CR3
mov EAX,00100000h ; базовый адрес = 1 Мб
mov CR3,EAX
; включить страничную адресацию,
mov EAX,CR0
or EAX,80000000h
mov CR0,EAX
; а теперь изменить физический адрес страницы 12000h на 0B8000h
mov EAX,000B8007h
mov ES:00101000h+012*4,EAX
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; вывод mes1 по стандартному адресу (начало видеопамяти 0B8000h)
mov EDI,0B8000h ; для команды movsw, EDI = начало видеопамяти mov ESI,PM_DATA
shl ESI,4
add ESI,offset mes1 ; ESI = адрес начала mes1
mov ECX,mes_len ; длина текста в ECX
rep movsw ; DS:ESI (наше сообщение) -> ES:EDI (видеопамять)
; вывод mes2 оп НЕСТАНДАРТНОМУ АДРЕСУ 12000h:
mov EDI,0120A0h ; 12000h (уже может считать, что это 0B8000h) + A0h
mov ESI,PM_DATA
shl ESI,4
add ESI,offset mes2 ; ESI = адрес начала mes2
mov ECX,mes_len ; длина текста в ECX
rep movsw ; DS:ESI (наше сообщение) -> ES:12000h (видеопамять)
jmp $
PM_CODE ends
; ---------------------------------------------------------------------------
; СЕГМЕНТ ДАННЫХ (для Protected Mode)
; ---------------------------------------------------------------------------
PM_DATA segment para public 'DATA' use32
assume CS:PM_DATA
; сообщение, которое мы будем выводить на экран
; (оформим его в виде блока повторений irpc):
mes1:
irpc mes1, This string was outputted to standart address 0B8000h...
db '&mes1&',0Dh
endm
mes2:
irpc mes2, And this one - to dummy address 0120A0h. Cool? Now press
RESET...
db '&mes2&',0Bh
endm
mes_len equ 66 ; длина в байтах
PM_DATA ends
; ---------------------------------------------------------------------------
end @@start
2
Документ
Категория
Рефераты
Просмотров
12
Размер файла
146 Кб
Теги
эвм3, организации
1/--страниц
Пожаловаться на содержимое документа