根据代码注释来看:
开启cpu,切换到32位保护模式,跳转到c代码。BIOS会将硬盘上第一个扇区加载到内存中,同时在实模式下cs=0,ip=7c00开始执行
1、16位模式开始执行,关闭中断,将DF置为0,在对串si和di都是增量加
2、初始化几个重要的段寄存器
3、使能A20
为了与最早的PC向后兼容,物理地址线20接低电平,因此高于1MB的地址默认为零。
从0x64端口读入一个字节到al寄存器中,test指令功能上类似and指令,只是它不影响控制寄存器;当al的第二位为1的时候就循环检测0x64端口的第二位,为0的时候就执行下一步,将0x6d端口置为0xd1
上面的代码就是写指令
从0x64端口读入一个字节到al寄存器中,test指令功能上类似and指令,只是它不影响控制寄存器;当al的第二位为1的时候就循环检测0x64端口的第二位,为0的时候就执行下一步,将0x60端口置为0xdf
上面的代码是使能A20的指令
4、从实模式切换到保护模式
加载全局描述符表寄存器,将cr0的最右位置为1。准备进入保护模式
每个段描述符占8个字节,第一个是NULL段描述符,没有意义,表示全局描述符表的开始,紧接着是代码段描述符(位于全局描述符表的0x8处的位置),具有可读(STA_R)和可执行(STA_X)的属性,并且段起始地址为0,段大小为4GB;接下来是数据段描述符(位于全局描述符表的0x10处的位置),具有可读(STA_R)和可写(STA_W)的属性,并且段起始地址为0,段大小为4GB。
通过长跳转指令进入保护模式
80386在执行长跳转指令时,会重新加载$PROT_MODE_CSEG的值(即0x8)到CS中,同时把$protcseg的值赋给EIP,这样80386就会把CS的值作为全局描述符表的索引来找到对应的代码段描述符,设定当前的EIP为0x7c32(即protcseg标号所在的段内偏移)
6、
bootloader让80386从实模式进入了保护模式。由于在访问数据或栈时需要用DS/ES/FS/GS和SS段寄存器作为全局描述符表的下标来找到相应的段描述符,所以还需要对DS/ES/FS/GS和SS段寄存器进行初始化,使它们都指向位于0x10处的段描述符(即gdt中的数据段描述符)。
7、成功进入c语言的代码,转向步骤9
8、失败,循环。
9、跳转到c代码bootmain.c中的主函数
bootloader让80386处理器进入保护模式后,下一步的工作就是从硬盘上加载并运行OS。考虑到实现的简单性,bootloader的访问硬盘都是LBA模式的PIO(Program IO)方式,即所有的I/O操作是通过CPU访问硬盘的I/O地址寄存器完成。
一般主板有2个IDE通道(是硬盘的I/O控制器),每个通道可以接2个IDE硬盘。第一个IDE通道通过访问I/O地址0x1f0-0x1f7来实现,第二个IDE通道通过访问0x170-0x17f实现。每个通道的主从盘的选择通过第6个I/O偏移地址寄存器来设置。具体参数见下表。
I/O地址 功能
0x1f0 读数据,当0x1f7不为忙状态时,可以读。
0x1f2 要读写的扇区数,每次读写前,需要指出要读写几个扇区。
0x1f3 如果是LBA模式,就是LBA参数的0-7位
0x1f4 如果是LBA模式,就是LBA参数的8-15位
0x1f5 如果是LBA模式,就是LBA参数的16-23位
0x1f6 第0~3位:如果是LBA模式就是24-27位 第4位:为0主盘;为1从盘
第6位:为1=LBA模式;0 = CHS模式 第7位和第5位必须为1
0x1f7 状态和命令寄存器。操作时先给命令,再读取内容;如果不是忙状态就从0x1f0端口读数据