《Orange’s 一个操作系统的实现》3.保护模式3----保护模式进阶

在前面的代码上进行修改后,测试读写大地址内存(实模式下的1MB限制),而且从保护模式再调回实模式.

上代码分析(省略前面重复的代码部分):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
; ==========================================
; pmtest2.asm
; 编译方法:nasm pmtest2.asm -o pmtest2.com
; ==========================================
 
%include    "pm.inc"     ; 常量, 宏, 以及一些说明
 
org 0100h                   ;参看DOS系统中.EXE文件的加载过程
     jmp LABEL_BEGIN
 
[SECTION .gdt]
; GDT
;                            段基址,        段界限 , 属性
LABEL_GDT:         Descriptor    0,              0, 0         ; 空描述符
LABEL_DESC_NORMAL: Descriptor    0,         0ffffh, DA_DRW    ; Normal 描述符
LABEL_DESC_CODE32: Descriptor    0, SegCode32Len-1, DA_C+DA_32; 非一致代码段, 32
LABEL_DESC_CODE16: Descriptor    0,         0ffffh, DA_C      ; 非一致代码段, 16
LABEL_DESC_DATA:   Descriptor    0,      DataLen-1, DA_DRW    ; Data
LABEL_DESC_STACK:  Descriptor    0,     TopOfStack, DA_DRWA+DA_32; Stack, 32 位
LABEL_DESC_TEST:   Descriptor 0500000h,     0ffffh, DA_DRW    ;5M基址
LABEL_DESC_VIDEO:  Descriptor  0B8000h,     0ffffh, DA_DRW    ; 显存首地址
; GDT 结束
 
GdtLen      equ $ - LABEL_GDT   ; GDT长度
GdtPtr      dw  GdtLen - 1  ; GDT界限
         dd  0       ; GDT基地址
 
; GDT 选择子
SelectorNormal      equ LABEL_DESC_NORMAL   - LABEL_GDT
SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT
SelectorCode16      equ LABEL_DESC_CODE16   - LABEL_GDT
SelectorData        equ LABEL_DESC_DATA     - LABEL_GDT
SelectorStack       equ LABEL_DESC_STACK    - LABEL_GDT
SelectorTest        equ LABEL_DESC_TEST     - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]
 
[SECTION .data1]     ; 32位数据段
ALIGN   32
[BITS   32]
LABEL_DATA:
SPValueInRealMode   dw  0                               ;保存实模式下的SP
; 字符串
PMMessage:      db  "In Protect Mode now. ^-^" , 0   ; 在保护模式中显示
OffsetPMMessage     equ PMMessage - $$                  ;PMMessage在LABEL_DATA节中的偏移
StrTest:        db  "ABCDEFGHIJKLMNOPQRSTUVWXYZ" , 0
OffsetStrTest       equ StrTest - $$                    ;StrTest在LABEL_DATA节中中的偏移
DataLen         equ $ - LABEL_DATA                  ;LABEL_DATA节的长度
; END of [SECTION .data1]
 
 
; 全局堆栈段
[SECTION .gs]
ALIGN   32
[BITS   32]
LABEL_STACK:
     times 512 db 0                      ;512字节的堆栈段
 
TopOfStack  equ $ - LABEL_STACK – 1 ;栈顶(偏移)
 
; END of [SECTION .gs]
 
 
[SECTION .s16]
[BITS   16]
LABEL_BEGIN:
     mov ax, cs
     mov ds, ax
     mov es, ax
     mov ss, ax
     mov sp, 0100h
 
     mov [LABEL_GO_BACK_TO_REAL+3], ax ;保存实模式下的CS
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
     mov [SPValueInRealMode], sp       ;保存实模式下的SP
 
     ; 初始化 16 位代码段描述符
     mov ax, cs
     movzx   eax, ax
     shl eax, 4
     add eax, LABEL_SEG_CODE16
     mov word [LABEL_DESC_CODE16 + 2], ax
     shr eax, 16
     mov byte  [LABEL_DESC_CODE16 + 4], al
     mov byte  [LABEL_DESC_CODE16 + 7], ah
 
     ; 初始化 32 位代码段描述符
     xor eax, eax
     mov ax, cs
     shl eax, 4
     add eax, LABEL_SEG_CODE32
     mov word [LABEL_DESC_CODE32 + 2], ax
     shr eax, 16
     mov byte  [LABEL_DESC_CODE32 + 4], al
     mov byte  [LABEL_DESC_CODE32 + 7], ah
 
     ; 初始化数据段描述符
     xor eax, eax
     mov ax, ds
     shl eax, 4
     add eax, LABEL_DATA
     mov word [LABEL_DESC_DATA + 2], ax
     shr eax, 16
     mov byte  [LABEL_DESC_DATA + 4], al
     mov byte  [LABEL_DESC_DATA + 7], ah
 
     ; 初始化堆栈段描述符
     xor eax, eax
     mov ax, ds
     shl eax, 4
     add eax, LABEL_STACK
     mov word [LABEL_DESC_STACK + 2], ax
     shr eax, 16
     mov byte  [LABEL_DESC_STACK + 4], al
     mov byte  [LABEL_DESC_STACK + 7], ah
 
     ; 为加载 GDTR 作准备
     xor eax, eax
     mov ax, ds
     shl eax, 4
     add eax, LABEL_GDT      ; eax <- gdt 基地址
     mov dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
 
     ; 加载 GDTR
     lgdt    [GdtPtr]
 
     ; 关中断
     cli
 
     ; 打开地址线A20
     in   al, 92h
     or  al, 00000010b
     out  92h, al
 
     ; 准备切换到保护模式
     mov eax, cr0
     or  eax, 1
     mov cr0, eax
 
     ; 真正进入保护模式
     jmp dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
LABEL_REAL_ENTRY:       ; 从保护模式跳回到实模式就到了这里
     mov ax, cs          ;jmp 0:LABEL_REAL_ENTRY 跳转到此处,恢复cs
     mov ds, ax          ;使用cs恢复ds,es,ss
     mov es, ax
     mov ss, ax
 
     mov sp, [SPValueInRealMode] ;恢复实模式下的sp
 
     in   al, 92h     ; `.
     and al, 11111101b   ;  | 关闭 A20 地址线
     out  92h, al     ; /
 
     sti         ; 开中断
 
     mov ax, 4c00h   ; `.
     int  21h     ; /  回到 DOS
; END of [SECTION .s16]
 
 
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS   32]
 
LABEL_SEG_CODE32:
     mov ax, SelectorData
     mov ds, ax          ; 数据段选择子
     mov ax, SelectorTest
     mov es, ax          ; 测试段选择子
     mov ax, SelectorVideo
     mov gs, ax          ; 视频段选择子
 
     mov ax, SelectorStack
     mov ss, ax          ; 堆栈段选择子
 
     mov esp, TopOfStack
 
 
     ; 下面显示一个字符串
     mov ah, 0Ch         ; 0000: 黑底    1100: 红字
     xor esi, esi                ; esi重置
     xor edi, edi                ; edi重置
     mov esi, OffsetPMMessage    ; 源数据偏移
     mov edi, (80 * 10 + 0) * 2  ; 目的数据偏移。屏幕第 10 行, 第 0 列。
     cld
.1:
     lodsb                   ;逐个加载ds:esi对应的字符到al中,其中DS中的选择子SelectData指向LABEL_DATA
     test    al, al          ;判断al字符是否为0(只是为了测试,请看PMMeesage的定义最后是0)
     jz  .2              ;为0跳转.2显示完毕
     mov [gs:edi], ax    ;非0将ax中的字符移到视频选择子对应的区域gs:edi
     add edi, 2          ;设置gs:edi指向下个字符,edi加2(ax为16位)
     jmp .1              ;跳转到.1处继续
.2: ; 显示完毕
 
     call    DispReturn      ;显示完毕后,换行显示
 
     call    TestRead        ;读5M地址内容
     call    TestWrite       ;写5M地址内容
     call    TestRead        ;在此读5M地址荣,测试写5M地址内容是否成功
 
     ; 到此停止
     jmp SelectorCode16:0 ;跳转到LABEL_SEG_CODE16处执行(调回实模式)
 
; ------------------------------------------------------------------------
TestRead:
     xor esi, esi      ;esi重置
     mov ecx, 8        ;设置读取字节数位8
.loop:
     mov al, [es:esi]  ;将es:esi中的字符移到al中(5M地址)
     call    DispAL        ;显示al中的值
     inc esi           ;增加esi的值,指向下个es:esi的下个字符
     loop    .loop         ;继续读取
 
     call    DispReturn    ;读取完毕显示回车
 
     ret                   ;返回调用处
; TestRead 结束-----------------------------------------------------------
 
 
; ------------------------------------------------------------------------
TestWrite:
     push    esi                     ;esi入栈
     push    edi                     ;edi入栈
     xor esi, esi                ;esi重置
     xor edi, edi                ;edi重置
     mov esi, OffsetStrTest  ;源数据偏移
     cld
.1:
     lodsb                           ;将OffsetStrTest对应的字符串逐个加载到al中
     test    al, al                  ;是否为0
     jz  .2                      ;为0,跳转.2读取完毕
     mov [es:edi], al            ;将al中的值写入测试大地址(5M)
     inc edi                     ;增加EDI,下个字符的位置
     jmp .1                      ;继续读取
.2:
 
     pop edi                     ;恢复edi,esi
     pop esi
 
     ret
; TestWrite 结束----------------------------------------------------------
 
 
; ------------------------------------------------------------------------
; 显示 AL 中的数字(将二进制转换为16进制显示)
; 默认地:
;   数字已经存在 AL 中
;   edi 始终指向要显示的下一个字符的位置
; 被改变的寄存器:
;   ax, edi
; ------------------------------------------------------------------------
DispAL:
     push    ecx                     ;ecx,edx入栈
     push    edx
 
     mov ah, 0Ch         ; 0000: 黑底    1100: 红字
     mov dl, al                  ; al暂存入dl中
     shr al, 4                   ; al右移4位
     mov ecx, 2                  ;  
.begin:
     and al, 01111b              ; 将al中的高4位清0,保留低四位(原高四位)
     cmp al, 9                   ; 是否大于9
     ja  .1                      ; 大于则跳转.1
     add al, '0'                  ;
     jmp .2
.1:
     sub al, 0Ah                 ;大于9将数字扣除0Ah(10)
     add al, 'A'                  ;加上 'A' (65)转换为A到F  例如11 – 10 + 65 = 66 = 'B'
.2:
     mov [gs:edi], ax            ;移入显存区域
     add edi, 2                  ;edi+2指向下个位置
     
     mov al, dl                  ;dl移入al中,进行低4位的16进制转换
     loop    .begin                  ;跳转.begin
     add edi, 2                  ;edi加2
 
     pop edx                     ;恢复edx,ecx
     pop ecx
 
     ret
; DispAL 结束-------------------------------------------------------------
 
 
; ------------------------------------------------------------------------
DispReturn:
     push    eax             ;eax,ebx入栈
     push    ebx       
     mov eax, edi        ;edi移入eax    
     mov bl, 160         ;bl设置为160(设定每行显示为(80*10 + 0)*2)
     div bl              ;al/bl = edi/bl   al位商   ah为余数
     and eax, 0FFh       ;清除eax的高16位,
     inc eax             ;eax+1
     mov bl, 160         ;
     mul bl              ;ah = al*160=(edi/160的商+1)*160-----达到换行的效果
     mov edi, eax        ;
     pop ebx
     pop eax
 
     ret
; DispReturn 结束---------------------------------------------------------
 
SegCode32Len    equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]
 
 
; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
[SECTION .s16code]
ALIGN   32
[BITS   16]
LABEL_SEG_CODE16:
     ; 跳回实模式:
     mov ax, SelectorNormal ;将SelectorNormal加载到ds,es,fs,gs,ss,去除在32保护模式中的段属性
?
1
2
3
4
5
6
7
8
9
10
11
12
13
                                    ;使描述符寄存器含有合适的段界限和属性(实模式下32段属性不符合实模式的要求)
     mov ds, ax
     mov es, ax
     mov fs, ax
     mov gs, ax
     mov ss, ax
 
     mov eax, cr0           ;设置cr0的PE为0
     and al, 11111110b
     mov cr0, eax
 
LABEL_GO_BACK_TO_REAL:
     jmp 0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值
?
1
;jmp segment:offset 根据段间转移(长转移),汇编指令为:
?
1
;0EAh Offset(2字节) Segment(2字节)
?
1
;这就是代码mov [LABEL_GO_BACK_TO_REAL+3], ax的作用
?
1
;将实模式下的cs值,写入LABEL_GO_BACK_TO_REAL地址偏移+3的位置,
?
1
2
3
4
5
                                         ;刚好就是jmp指令EA的Segment处,请参看实模式下的长跳转指令图示         
                                         ;所以此的指令为JPM  cs(实模式):LABEL_REAL_ENRY
Code16Len   equ $ - LABEL_SEG_CODE16
 
; END of [SECTION .s16code]
?
1
  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值