操作系统开发系列—2.进入32位保护模式

源码如下:

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
; ==========================================
; pm.asm
; 编译方法:nasm pm.asm -o pm.bin
; ==========================================
 
%include    "pm.inc"    ; 常量, 宏, 以及一些说明
 
org 07c00h
     jmp LABEL_BEGIN
 
[SECTION .gdt]
; GDT
;                                                                         段基址,        段界限     ,                              属性
LABEL_GDT:                        Descriptor          0,                0,                                         0                  ; 空描述符
LABEL_DESC_CODE32: Descriptor           0,             SegCode32Len - 1,    DA_C + DA_32   ; 非一致代码段
LABEL_DESC_VIDEO:    Descriptor    0B8000h,     0ffffh,                              DA_DRW        ; 显存首地址
; GDT 结束
 
GdtLen      equ $ - LABEL_GDT   ; GDT长度
GdtPtr      dw  GdtLen - 1  ; GDT界限
                     dd  0       ; GDT基地址
 
; GDT 选择子
SelectorCode32      equ LABEL_DESC_CODE32   - LABEL_GDT
SelectorVideo       equ LABEL_DESC_VIDEO    - LABEL_GDT
; END of [SECTION .gdt]
 
[SECTION .s16]
[BITS   16]
LABEL_BEGIN:
     mov ax, cs
     mov ds, ax
     mov es, ax
     mov ss, ax
     mov sp, 0100h
 
     ; 初始化 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
 
     ; 为加载 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,
                     ; 并跳转到 SelectorCode32:0  处
; END of [SECTION .s16]
 
 
[SECTION .s32]; 32 位代码段. 由实模式跳入.
[BITS   32]
 
LABEL_SEG_CODE32:
     mov ax, SelectorVideo
     mov gs, ax          ; 视频段选择子(目的)
 
     mov ecx, 28
     mov edi, 0
     mov bx, BootMessage
.show:
     mov ah, 0Ch         ; 0000: 黑底    1100: 红字
     mov al, [bx]
     mov [gs:edi], ax
     inc edi
     inc edi
     inc bx
     loop .show
 
     ; 到此停止
     jmp $
 
BootMessage:        db  "Joey, I'm in protected mode!"
SegCode32Len    equ $ - LABEL_SEG_CODE32
; END of [SECTION .s32]

运行结果如下:

源码解析:

1.首先程序跳转至LABEL_BEGIN处,jmp LABEL_BEGIN。将ds、es、ss段寄存器全部初始化为当前代码段。

2.初始化32位代码段描述符

在实模式下,也就是8086的16位的CPU的寻址方式是段x16+偏移,而在保护模式下,段寄存器值变成了一个索引,这个索引指向段描述符,也就是GDT中对应的那个描述符,描述符中包含了最终的基地址。

 38-41行表示将当前程序代码段左移4位也就是x16,再加上LABEL_SEG_CODE32的偏移地址,最终形成LABEL_SEG_CODE32的物理地址,42-45表示把此物理地址加载到段描述符对应的段基址位置,段描述符格式如下图所示

段基地址0-15位也就是LABEL_DESC_CODE32 + 2地址处,将16位的ax传入,第43行向右移动16位表示已经传入的16位消除,然后将剩余的两个8位高低寄存器值传入对应的段基地址处。

Descriptor是在pm.inc中定义的宏,如下图所示,表示的就是段描述符的格式:

3.加载GDTR,GDRT的结构图如下所示

GDTR是唯一的一个指向段描述符表的寄存器,48-55行就是把段描述符表的基地址加载到GDTR寄存器当中。48行清除eax值,49-52是把LABEL_GDT的物理地址加载到20行的GdtPtr的双字处,也就是GDT基地址。55行把GdtPtr地址的内容加载到GDTR,双字节dw对应的是16位界限,双字dd对应的是32位基地址。

4.关中断和打开A20

保护模式下中断处理机制和实模式不同,所以先关闭中断,指令为cli。打开A20为历史原因防止偏移超出最大值时回滚。58-63行

5.切换到保护模式

只要把寄存器cr0的第0位置为1就行了。66-68行

6.真正进入保护模式的代码段

jmp dword SelectorCode32:0,这一行是真正进入保护模式的代码,SelectorCode32是个段选择子,段选择子的作用是找到对应的段描述符从而找到对应的段基地址。段选择子的格式如下图所示

从位3开始表示为段描述符表的索引,这句代码执行完之后就会把描述符LABEL_DESC_CODE32中的段基址也就是LABEL_SEG_CODE32加载到cs。

dword的作用是因为当前还是16位的代码,如果没有dword,SelectorCode32:0的偏移地址如果超出16位那么只会截取16位。

到此已经完全进入32位保护模式的代码了。

7.显示字符

接下来就会跳转到LABEL_SEG_CODE32处运行指令了,80-81行把段选择子SelectorVideo传入段寄存器gs中,也就是描述符LABEL_DESC_VIDEO的段基地址0B8000h,这就是显存的基地址。84行edi是显存的偏移地址,87-88行是字符及字符属性,89行为最终显示字符。

8.描述符属性

代码段的属性是DA_C + DA_32,根据pm.inc中的定义,DA_C是98h

对应的二进制为10011000。根据第2条的格式,DA_C其实是上面的第8-15位,第15位P是1表明这个段在内存中存在,S位是1表明这个段是代码段或数据段,TYPE为1000也就是8表明这个段是只执行的代码段,TYPE格式如下图所示

DA_32是4000h,

4000h为100000000000000,表明是从上面的位8开始计算的后面15位也就是D/B位为1,表明这个段是32位的代码段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值