1.加载操作系统代码到内存
bootsect利用int 0x13中断指令,将setup和system加载到内存里面。到目前为止,操作系统代码已经加载到内存里面,其中bootsect为1个扇区大小,setup为4个扇区大小,system为240个扇区的大小。
2.setup源码分析
2.1.setup中断处理
;// ok, 整个读磁盘过程都正常,现在将光标位置保存以备今后使用。
mov ax,INITSEG ;// 将ds 置成INITSEG(9000)。这已经在bootsect 程序中
mov ds,ax ;// 设置过,但是现在是setup 程序,Linus 觉得需要再重新
;// 设置一下。
mov ah,03h ;// BIOS 中断10 的读光标功能号ah = 03
xor bh,bh ;// 输入:bh = 页号
int 10h ;// 返回:ch = 扫描开始线,cl = 扫描结束线,
mov ds:[0],dx ;// dh = 行号(00 是顶端),dl = 列号(00 是左边)。
;// 将光标位置信息存放在90000 处,控制台初始化时会来取。
mov ah,88h ;// 这3句取扩展内存的大小值(KB)。
int 15h ;// 是调用中断15,功能号ah = 88
mov ds:[2],ax ;// 返回:ax = 从100000(1M)处开始的扩展内存大小(KB)。
;// 若出错则CF 置位,ax = 出错码。
首先,我们看到int 10这个指令,是bios给我们提供的10号中断指令,是用于显示服务中断的处理程序。我们通过设置ax寄存器的值。控制显示效果。dx存储返回值。不难看出来,一开始操作系统的一切操作都离不开BIOS的协助操作。通过该中断,我们可以获取一些操作系统的信息,比如内存信息,显卡显示模式,硬盘信息等等。
进入保护模式,关闭是因为后面我们需要自己写中断程序去覆盖BIOS的中断,
;// 从这里开始我们要保护模式方面的工作了。
cli ;// 此时不允许中断。 ;//
之后对已经加载的程序,setup,和bootsect,system进行重新布局,方便管理。
0-0x80000存储着system的系统代码
0x90000:一些设备信息
0x90200:存储着setup的代码
3.模式转变
模式转变,主要是因为历史原因,以前是x86系统,现在更多的是32位或者64位系统,所以需要写一段模式转换的代码。实模式和保护模式的区别和保护模式的起源,侧重点在二者寻址方式上的差异。
实模式下:物理地址=段寄存器ds+偏移地址,即可得到物理地址
保护模式下:物理地址的计算有点繁琐复杂,这些都是为了解决历史遗留的问题。在实模式下的ds叫段基址,在保护模式下,叫段选择子,段选择子里面存储着段描述符索引,可以从全局描述符表里面找到段描述符,段描述符里面存储着真正的段基址,然后和之前的偏移地址进行相加,即可获得物理地址。这时,段寄存器里面存储的是段选择子,不是段地址了
4.GDT表
GDT表,即是全局描述符表,gdt表的存储位置由一个叫gdtr的寄存器进行存储
gdt_48:
dw 800h ;// 全局表长度为2k 字节,因为每8 字节组成一个段描述符项
;// 所以表中共可有256 项。
dw 512+gdt,9h ;// 4 个字节构成的内存线性地址:0009<<16 + 0200+gdt
;// 也即90200 + gdt(即在本程序段中的偏移地址,205 行)。
该标签可以看出来,该描述符表是一个48位的数据表,其中高32位存储着GDT的数据,该GDT表是存储在setup里面的, gdt_48是一个标签,即为一个偏移地址。段描述符表里面存储着段描述符。目前GDT表里面存储着三个段描述符,分别为空,代码段描述符,数据段描述符,后面两个的段基址都是0(也就是说,程序员给的偏移地址是什么,实际的物理地址就是什么)。
5.保护模式
在前面介绍中,我们已经开启了保护模式,现在我们需要去开地址线,cpu默认是20位地址线,这是8086的问题,所以需要我们手动开启。真正的保护模式切换,是将cr0寄存器里面的PE位置1就是开启。