操作系统入门(四)痛并学习中

前面的教程已经说了怎么由机器启动然后转到执行我们自己的代码,现在已经可以显示出一个粗糙的图形界面了,我们的程序可以先启动 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
 
先试一下,完整的程序如下:
;======================
; 操作系统入门() 痛并学习中
; 进入保护模式的测试
; 2006419
; http://blog.csdn.net/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 来做工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值