深入理解bootloader(操作系统开发之)
深入理解bootloader(操作系统开发之)1flush: 2movdword[0xb8000 160 0] 'P' 3movdword[0xb8000 160 1] 0x2f 4 5 jmp $;在这停止,cpu进入睡眠模式 6hlt 7 8times4096-($-$$)db0 ;对齐内存这回截个图: 1... 2out0x92 al 3 4cli 5 6set_PE: 7moveax cr0 8oreax 1 9movcr0 eax 10 11entry_to_protection_mode: 12jmpdword0x8:flush 13... 14ddgdt_head 15 16[bits32] 17;这里已经是32位模式了,nasm就会编译成32位的代码 18flush:此时我们要验证保护模式是否真正开启,需要向显存写入信息,但是这次我们不需要再偏移地址了,直接0xb8000就行: 1
上一节我们已经掌握了进入保护模式的关键,这一节我们来具体实现他。
我们之前向屏幕输出都是通过设置中断实现,比较麻烦,这次我们换个方法,向显存输出信息,显存地址(CGA)是0xB8000~0xBFFFF,我们只需要用其中一段就行,显示功能不会用太多。使用方法很简单,两个显存地址分别存储字符和字符的前景,写入后就可以看到效果了:
1[bits16]
2;后面保护模式的代码就是32位了,先写着
3org0x70000
4
5START:
6;这次一定要初始化 设置堆栈段和栈指针
7movax cs
8movds ax
9moves ax
10movss ax
11movsp 0
12
13point:
14;清屏是个好习惯
15movax 0x2
16int0x10
17
18movax 0xb800
19moves ax
20movdi 0
21movbyte[es:di 0] 'L'
22movbyte[es:di 1] 0xcf
23movbyte[es:di 2] 'o'
24movbyte[es:di 3] 0xcf
25movbyte[es:di 4] 'a'
26movbyte[es:di 5] 0xcf
27movbyte[es:di 6] 'd'
28movbyte[es:di 7] 0xcf
29movbyte[es:di 8] 'i'
30movbyte[es:di 9] 0xcf
31movbyte[es:di 10] 'n'
32movbyte[es:di 11] 0xcf
33movbyte[es:di 12] 'g'
34movbyte[es:di 13] 0xcf
截图太麻烦了,我拿我的人格担保,一定可以显示出Loading,效果和之前是一样的。只是有人会发现,这样打印字符串太不舒服了。是的,有更好的办法,但是现在要先解决保护模式,保护模式进入也需要通过字符输出来确认是否成功,但是进入保护模式后,我们就不能使用BIOS中断来输出字符了,所以这里先简单介绍操作显存。
我们现在来初始化GDT并加载他,根据上一节初探保护模式,我们得知,我们需要填写表相应位的值来初始化,填写完后还要用GDTR加载:
1load_GDTR:
2;保存GDT地址到GDTR
3lgdt[gdt]
4
5gdt_head:
6dd0x0000000
7dd0x0000000
8
9 dw 0x000ffff ;段限制:0-15位
10 dw 0x0000000;段基地址:0-15位
11 db 0x0000000;段基地址:16-23位
12db10011010b;段描述符的第6字节属性(代码段可读写)
13 db 11001111b ;段描述符的第7字节属性:16-19位
14 db 0x0000000;段描述符的最后一个字节是段基地址的第二部分:24-31位
15
16 dw 0x000ffff ;段限制:0-15位
17 dw 0x0000000;段基地址:0-15位
18 db 0x0000000;段基地址:16-23位
19db10010010b;段描述符的第6字节属性(数据段可读写)
20 db 11001111b ;段描述符的第7字节属性:limit(位16-19)
21 db 0x0000000;段描述符的最后一个字节是段基地址的第二部分:24-31位
22
23gdt:
24dwgdt-gdt_head-1
25ddgdt_head
数字后缀b表示二进制。关于GDT的填写,注释已经写得很详细了,可以参考上一篇文章Intel的全局描述符示意图来理解。此时已经完成了GDT的初始化,接下来打开A20地址线。
读端口用in指令,写端口用out指令。有个规定就是在92h端口,如果al的二进制第二位是1则打开,反之关闭:
1...
2lgdt[gdt]
3
4enable_A20:
5inal 0x92
6oral 10b;一般直接写2
7out0x92 al
8
9cli
10
11gdt_head:
12...
然后就是设置PE位,跳转到保护模式:
1...
2out0x92 al
3
4cli
5
6set_PE:
7moveax cr0
8oreax 1
9movcr0 eax
10
11entry_to_protection_mode:
12jmpdword0x8:flush
13...
14ddgdt_head
15
16[bits32]
17;这里已经是32位模式了,nasm就会编译成32位的代码
18flush:
此时我们要验证保护模式是否真正开启,需要向显存写入信息,但是这次我们不需要再偏移地址了,直接0xb8000就行:
1flush:
2movdword[0xb8000 160 0] 'P'
3movdword[0xb8000 160 1] 0x2f
4
5 jmp $;在这停止,cpu进入睡眠模式
6hlt
7
8times4096-($-$$)db0 ;对齐内存
这回截个图:
进入保护模式之后,很多事情就方便做了。今天先这样。
关注"GuEes"公众号,了解更多消息!