前面的教程已经说了怎么由机器启动然后转到执行我们自己的代码,现在已经可以显示出一个粗糙的图形界面了,我们的程序可以先启动loader,然后再由loader里面的代码把放在磁盘里面的kernel文件载入到一个指定的位置,再转到kernel里面执行,这样一来,代码就可以突破512个字节的限制,任意由你爱写多长就写多长了,但是如果所有的代码都用汇编来写的话,我想你首先尝到的是身体上病痛的折磨,而不是完成一个自己操作系统雏形的喜悦。
那么,我们就需要一个好用的工具来代替汇编。
C语言是一个非常好的工具,全世界使用者都有很多,虽然也可以用basic来写,但是它的面好像没有C那么广泛,既然流行,我们也就随波逐流吧。据说UNIX后来就是用C写的。
要用C的话,那么就要看看它和汇编混合起来的情况。
错了,在使用C之前,我们应该把系统转换成32位的模式(人家intel都出了64位的cpu现在讲32位不知道还有没有前途),因为在系统启动的时候就是16位的模式
为什么?
“在8086/8088时代,处理器只存在一种操作模式,而到了286、386的时代处理器增加了两种操作模式――保护模式(Protected Mode)和系统管理模式(System Management Mode),而以前的模式就被称为实地址模式(Real address Mode),保护模式可以使处理器支持所有的指令和所有的体系结构,提供最高的性能和兼容性,而当主机被启动(开机或者重启动)的时候处理器处于RM状态下”
为什么用小字体,因为看着会头晕,干脆就不要看了,说的是计算机在启动的时候是处于16位的RM状态下的,而C编译出来的代码是32位的,你想在一个16位系统里面运行32位的程序似乎是行不通的,所以,我们先要把系统置为32位的PM状态。好处先不说了一大堆的,先看看怎么进去。(因为我也不懂,大家一起学习)
文章说只要设置一个位就可以进入保护模式了:
; 准备切换到保护模式
mov eax, cr0
or eax, 1
mov cr0, eax
这里的CR0要说一下:它是cpu的控制寄存器
包括工作方式的控制位,
包含分页管理机制的控制位,
包含浮点协处理器的控制位
|
CR0
|
PG
|
0000000000000000
|
ET
|
TS
|
EM
|
MP
|
PE
|
|
位
|
31
|
5~30
|
4
|
3
|
2
|
1
|
0
|
保护控制位 PG/PE控制分段和分页管理机制的操作
|
PG
|
PE
|
处理器工作方式
|
|
0
|
0
|
实模式
|
|
0
|
1
|
保护模式,禁用分页机制
|
|
1
|
0
|
非法组合
|
|
1
|
1
|
保护方式,启用分页机制
|
控制浮点协处理器的操作:
MP(算术存在位)
EM(模拟位)
TS(任务切换位)
ET(扩展类型位)。
…
or eax, 1
这句话的意思就是把pe置1,应该就是处在保护模式下面了,但是是不是分页先不管了。
我们原来16位的时候使用地址是这样的:
Segment:Offset
Segment的最大可以表示的值就是64k,16位嘛(0x0000~0xffff)
Offset 也一样64k
得到的线性地址就是Segment * 0x10 + Offset = 0x00000 ~0xfffff
算来就是20位有效长度的地址1MB的内存可以访问
下来到了32位的PM模式,顾名思义保护模式就要有保护模式的样子,靠什么东西保护呢?就需要为它设置一个
这个内存地方的访问权限
Access
因为是32位的所以
Segment(0xffffffff)
是32位的
还有一个界限
Limit
限制所使用的内存大小,不知道是干什么用的,好像是不让你占用过多的内存
三个最主要的东西构成了一个数据结构
GDT(Global Descriptor Table)全局描述表
64位长度据说这个表可以放在内存的任何位置只要用GDTR指令让cpu知道在哪里找GDT就可以了。
怎么操作这样的地址呢?
在段寄存器里装入
Segment Selector(段选择器)
通过这个选择器里面的内容在全局描述表里面找到相应的段描述内容,实际上段选择器相当于一个索引目录,比如说我有一本书,开头一页是总纲,就有目录(GDT),你想要找哪一章,就打开总纲找,然后在目录下面有章,每一章能指示从书的哪一页开始(Base address),看到第几页(Offset)
先看一个GDT的例子
gdtr :
dw gdtend - gdt - 1 ; gdt的长度
dd gdt ; gdt的物理地址
gdt:
gdt0:
dw 0,0,0,0 ; 据说是一定要为0否则会有错
codesel_gdt:
dw 0xffff ; 界限Limit值 = 0x100000 * 0x1000 = 4GB
dw 0 ; 基地址 = 0
dw 0x9A00 ; 表示代码段可读可执行
dw 0x00CF ; 粒度(不知道是什么意思)
datasel_gdt:
dw 0xffff ; 4GB
dw 0x0 ; 基地址
dw 0x9200 ; 数据段可读可写
dw 0x00CF ; 粒度
gdtend:
codesel equ codesel_gdt - gdt
datasel equ datasel_gdt – gdt
先试一下,完整的程序如下:
;======================
; 操作系统入门(四) -痛并学习中
; 进入保护模式的测试
; 2006年4月19日
; http://blog.youkuaiyun.com/flyback
; fly-back@163.com
;======================
org 0x7c00
entry:
jmp short begin
begin:
cli ; 关中断,防止意外中断打断程序执行
mov ax,cs ;
mov ds, ax ; 设置数据段
mov es, ax ;
lgdt [cs:gdtr]
mov eax, cr0
or eax,1
mov cr0, eax
jmp codesel:pmnow
bits 32
pmnow:
mov ax, cs
mov ds, ax
mov ax, datasel
mov es, ax
mov byte [es:0xb8000], 'p'
jmp $
gdtr :
dw gdtend - gdt - 1 ; gdt的长度
dd gdt ; gdt的物理地址
gdt:
gdt0:
dw 0,0,0,0 ; 据说是一定要为0否则会有错
codesel_gdt:
dw 0xffff ; 界限Limit值 = 0x100000 * 0x1000 = 4GB
dw 0 ; 基地址 = 0
dw 0x9A00 ; 表示代码段可读可执行
dw 0x00CF ; 粒度(不知道是什么意思)
datasel_gdt:
dw 0xffff ; 4GB
dw 0x0 ; 基地址
dw 0x9200 ; 数据段可读可写
dw 0x00CF ; 粒度
gdtend:
codesel equ codesel_gdt - gdt
datasel equ datasel_gdt - gdt
times 510-($-$$) db 0
dw 0x0aa55
试一下,启动后屏幕上出现了一个白色的P字,不相信的话可以把
lgdt
…
mov cr0,eax
删除再试一下,应该什么反映都没有了。完了之后我们就可以在loader或者kernel里面加上进入保护模式的代码了,以后才能用C来做工作。
本文介绍了如何将计算机系统从16位实地址模式切换到32位保护模式,并详细讲解了通过设置CR0寄存器和配置全局描述符表(GDT)实现这一过程的方法。
3369

被折叠的 条评论
为什么被折叠?



