§ Возврат в режим реальных адресов

Практически защищённый режим процессора можно использовать двумя способами:
  • Операционная система. Процессор входит в P-Mode при загрузке ОС и не возвращается в R-Mode. Примером таких ОС являются Windows 95 и старше либо специализированные ОС, управляющие контроллерами на базе 32-разрядных процессоров.
  • DOS-программа. Программа запускается в ОС, которая работает в R-Mode (например, MS-DOS), переходит в P-Mode, выполняет свою работу, возвращается обратно в R-Mode и завершает свою работу. Примеров множество: это hymem.sys, обеспечивающий через P-Mode работу с памятью выше 1-го мегабайта (XMS-память); также это игрушки, работающие через Dos4GW - предзагружаемую ОС защищённого режима; это Windows 3.xx.
Второй способ, с возвратом в режим реальных адресов, предоставляет программисту больше возможностей - ведь не все пишут полнофункциональные операционные системы...
Фактически, переход в режим реальных адресов может быть только из защищённого режима и осуществляется сбросом бита PE в CR0:
1mov    eax, cr0
2and    al, 0feh
3mov    cr0, eax
или так:
1mov    eax, cr0
2btr    eax, 0
3mov    cr0, eax
Вы наверное уже догадались, что на самом деле не всё так просто. Действительно, выполнение трёх вышеприведенных команд переведёт процессор в R-Mode, но дальше он повиснет либо произойдёт аппаратный сброс, потому что программная среда не будет соответствовать режиму реальных адресов.
Для корректного перехода из P-Mode в R-Mode необходимо подготовить процессор следующим образом:
  • Запретить прерывания (CLI)
  • Передать управление в читаемый сегмент кода, имеющий предел в 64Кб (FFFFh)
Загрузить в SS, DS, ES, FS и GS селекторы дескрипторов, имеющих следующие параметры
  • Предел = 64 Кб (FFFFh)
  • Байтная гранулярность (G = 0)
  • Расширяется вверх (E = 0)
  • Записываемый (W = 1)
  • Присутствующий (P = 1)
  • Базовый адрес = любое значение
Сегментные регистры должны быть загружены ненулевыми селекторами. Те сегментные регистры, в которые не будут загружены описанные выше значения, будут использоваться с атрибутами, установленными в защищённом режиме.
  • Сбросить флаг PE в CR0
  • Выполнить команду far jmp на программу режима реальных адресов
  • Загрузить в регистры SS, DS, ES, FS и GS необходимые значения или 0
  • Разрешить прерывания (STI)
В предыдущем примере мы просто перевели процессор в защищённый режим, при этом не используя особенности прерываний в P-Mode и не подключали механизм виртуальной памяти. Реализация каждой из этих технологий немного усложнит возврат в R-Mode и в разделах, описывающих эти технологии будут приведены соответствующие примеры перехода в P-mode и возврата из него.
Здесь подразумевается, что вы уже ознакомились со всеми предыдущими главами, поэтому предварительного подробного описания перехода в R-Mode не будет и мы перейдём сразу к примеру.
Этот пример является полноценной программой и в нём без комментариев повторяется переход в P-Mode. Процесс подготовки дескрипторов и GDTR зависит от предназначения каждого примера и по этой причине я не вынес его в отдельную функцию а полностью описываю каждый раз.
Пример ориентирован на использование как самостоятельная .com-программа. Это сделано по следующим причинам:
  • Пользоваться обычной программной средой - библиотеки языков высокого уровня, прерывания MS-DOS и прочими особенностями этой ОС из защищённого режима вы не сможете.
  • Размеры кода и данных в данном примере не большие и удобно использовать формат файла .com.
  • COM-программу можно просто загрузить в память как блок данных и, передав управление на смещение 100h, использовать как оверлей.
  • Вся мощь защищённого режима раскрывается при использовании 32-разрядного кода и данных. Инициализация защищённого режима должна происходить 16-разрядным кодом и 32-разрядные программы удобно использовать как отдельные модули.
Также хочу обратить ваше внимание на то, что в этом примере адреса сегментов для защищённого режима совпадают с адресами сегментов, используемых в R-Mode. В результате, обращение к памяти происходит непосредственно через метки, определённые в исходнике (в предыдущем примере для этого требовалось из адреса метки вычитать адрес начала сегмента данных).
Пример используется вместе с файлом "pmode.lib.asm".
Пример 3. Вход в защищённый режим и возврат в режим реальных адресов.
1    org     100h
2;------------------------------------------------------------------------
3; Определяем селекторы как константы. У всех у них биты TI = 0 (выборка
4;  дескрипторов производится из GDT), RPL = 00B - уровень привилегий -
5;  нулевой.
6
7    Code_selector       equ  8
8    Stack_selector      equ 16
9    Data_selector       equ 24
10    Screen_selector     equ 32
11    R_Mode_Code         equ 40    ; Селектор дескриптора сегмента кода для возврата в режим реальных адресов
12    R_Mode_Data         equ 48    ; Селектор дескриптора сегментов стека и данных
13
14;------------------------------------------------------------------------
15
16; Сохраняем сегментные регистры, используемые в R-Mode:
17
18    mov    [R_Mode_SS], ss
19    mov    [R_Mode_DS], ds
20    mov    [R_Mode_ES], es
21    mov    [R_Mode_FS], fs
22    mov    [R_Mode_GS], gs
23
24; Подготавливаем адрес возврата в R-Mode:
25
26    mov    [R_Mode_segment], cs
27    lea    ax, [R_Mode_entry]
28    mov    [R_Mode_offset], ax
29
30; Подготовка к переходу в защищённый режим:
31
32    mov    bx, GDT + 8
33    xor    eax, eax
34    mov    edx, eax
35
36    push   cs
37    pop    ax
38
39    shl    eax, 4
40    mov    dx, 65535
41    mov    cl, 10011000b
42    call   set_descriptor        ; Code
43
44    lea    dx, [Stack_seg_start]
45    add    eax, edx
46    mov    dx, 1024
47    mov    cl, 10010110b
48    call   set_descriptor        ; Stack
49
50    xor    eax, eax
51    mov    ax, ds
52    shl    eax, 4
53    mov    dx,0ffffh
54    mov    cl, 10010010b
55    call   set_descriptor        ; Data
56
57    mov    eax, 0b8000h
58    mov    edx, 4000
59    mov    cl, 10010010b
60    call   set_descriptor        ; Screen
61
62; Готовим дополнительные дескрипторы для возврата в R-Mode:
63
64    xor    eax,eax
65    push   cs
66    pop    ax
67    shl    eax, 4       ; EAX = физический адрес сегмента кода
68                        ;  (и всех остальных сегментов, т.к.
69                        ;  это .com-программа)
70
71    mov    edx,0ffffh
72    mov    cl, 10011010b       ; P=1, DPL=00b, S=1, Тип=101b, A=0
73    call   set_descriptor      ; R_Mode_Code
74
75    mov    cl, 10010010b       ; P=1, DPL=00b, S=1, Тип=001b, A=0
76    call   set_descriptor      ; R_Mode_Data
77
78; Устанавливаем GDTR:
79
80    xor    eax, eax
81    mov    edx, eax
82
83    mov    ax, ds
84    shl    eax, 4
85    lea    dx, [GDT]
86    add    eax, edx
87    mov    [GDT_adr],eax
88
89    mov    dx, 55           ; Предел GDT = 8 * (1 + 6) - 1
90    mov    [GDT_lim], dx
91
92    cli
93    lgdt   [GDTR]
94    mov    [R_Mode_SP], sp  ; Указатель на стек сохраняем в последний момент.
95
96; Переходим в защищённый режим:
97
98    mov    eax, cr0
99    or     al, 1
100    mov    cr0, eax
101
102; Процессор в защищённом режиме
103
104    db    0eah              ; Команда far jmp Code_selector:P_Mode_entry.
105    dw    P_Mode_entry
106    dw    Code_selector
107
108    include    "pmode.lib.asm"
109
110;------------------------------------------------------------------------
111P_Mode_entry:
112
113    mov    ax, Screen_selector
114    mov    es, ax
115
116    mov    ax, Data_selector
117    mov    ds, ax
118
119    mov    ax, Stack_selector
120    mov    ss, ax
121    mov    sp, 0
122
123; Сообщаем о входе в P-Mode (выводим ZS-строку):
124
125    lea    bx, [Start_P_Mode_ZS]
126    mov    di, 480
127    call   putzs
128
129; Работа программы в защищённом режиме (здесь - только вывод строки):
130
131    lea    bx, [P_Mode_ZS]
132    add    di, 160
133    call   putzs
134
135; ----------------------------------------------------------------------
136; Возвращаемся в режим реальных адресов.
137; 1. Запретить прерывания (CLI).
138;    Прерывания уже запрещены при входе в P-Mode.
139
140; 2. Передать управление в читаемый сегмент кода, имеющий предел в 64Кб.
141; ----------------------------------------------------------------------
142
143    db    0eah              ; Команда far jmp R_Mode_Code:Pre_R_Mode_entry.
144    dw    Pre_R_Mode_entry
145    dw    R_Mode_Code
146
147
148Pre_R_Mode_entry:
149
150; 3. Загрузить в SS, DS, ES, FS и GS селекторы дескрипторов, имеющих
151;    следующие параметры:
152;  1) Предел = 64 Кб (FFFFh)
153;  2) Байтная гранулярность (G = 0)
154;  3) Расширяется вверх (E = 0)
155;  4) Записываемый (W = 1)
156;  5) Присутствующий (P = 1)
157;  6) Базовый адрес = любое значение
158
159    mov    ax, R_Mode_Data    ; Селектор R_Mode_Data - "один на всех".
160    mov    ss, ax
161    mov    ds, ax
162    mov    es, ax
163    mov    fs, ax
164    mov    gs, ax
165
166; 4. Сбросить флаг PE в CR0.
167
168    mov    eax, cr0
169    and    al, 0feh        ; FEh = 1111'1110b
170    mov    cr0, eax
171
172; 5. Выполнить команду far jump на программу режима реальных адресов.
173
174        db    0eah
175        R_Mode_offset       dw    ?    ; Значения R_Mode_offset и R_Mode_segment
176        R_Mode_segment      dw    ?    ; сюда прописались перед входом в
177                                       ; защищённый режим (в начале программы).
178;------------------------------------------------------------------------
179R_Mode_entry:
180
181; 6. Загрузить в регистры SS, DS, ES, FS и GS необходимые значения или 0
182;    (восстанавливаем сохранённые значения):
183
184    mov    ss, [R_Mode_SS]
185    mov    ds, [R_Mode_DS]
186    mov    es, [R_Mode_ES]
187    mov    fs, [R_Mode_FS]
188    mov    gs, [R_Mode_GS]
189    mov    sp, [R_Mode_SP]    ; Восстанавливаем указатель стека
190                              ;  непосредственно перед разрешением
191                              ;  прерываний.
192
193; 7. Разрешить прерывания (STI).
194
195    sti
196
197; Выводим ZS-строку "Back to real address mode..."
198
199    lea    bx, [R_Mode_ZS]
200    mov    ax, 0b800h
201    mov    es, ax
202    mov    di, 800
203    call   putzs        ; Функция putzs универсальна и работает
204                        ;  в обоих режимах.
205
206    int    20h          ; Конец программы (здесь - выход в MS-DOS).
207
208;------------------------------------------------------------------------
209; ZS-строка для вывода при входе в P-Mode:
210Start_P_Mode_ZS:    db    "Entering to protected mode...",0
211
212; ZS-строка для вывода при работе в P-Mode:
213P_Mode_ZS:    db    "Working in P-mode...",0
214
215; ZS-строка для вывода в R-Mode:
216R_Mode_ZS:    db    "Back to real address mode...",0
217
218;------------------------------------------------------------------------
219; Значения регистров, которые программа имела до перехода в P-Mode:
220R_Mode_SP    dw    ?
221R_Mode_SS    dw    ?
222R_Mode_DS    dw    ?
223R_Mode_ES    dw    ?
224R_Mode_FS    dw    ?
225R_Mode_GS    dw    ?
226;------------------------------------------------------------------------
227; Образ регистра GDTR:
228
229GDTR:
230GDT_lim        dw    ?
231GDT_adr        dd    ?
232;------------------------------------------------------------------------
233GDT:
234    dd    ?,?    ; 0-й дескриптор
235    dd    ?,?    ; 1-й дескриптор (кода)
236    dd    ?,?    ; 2-й дескриптор (стека)
237    dd    ?,?    ; 3-й дескриптор (данных)
238    dd    ?,?    ; 4-й дескриптор (видеопамяти)
239    dd    ?,?    ; 5-й дескриптор (код для перехода в R-Mode)
240    dd    ?,?    ; 6-й дескриптор (стек и данные для перехода в R-Mode)
241;------------------------------------------------------------------------
242    db    1024 dup (?)      ; Зарезервировано для стека.
243
244Stack_seg_start:            ; Последняя метка программы - отсюда будет расти стек.