30天自制操作系统:第三天 进入32位模式并导入C语言

今天的内容稍稍有点多,一起看看吧

1.制作真正的IPL

到昨天为止,讲到的启动区虽然也称为IPL(Initial Program Loader,启动程序装载器),但它实质上并没有装载任何程序。
小节中作者给出了将之前的 hello-os 改名为 "纸娃娃操作系统" 的深意:用纸糊起来的,笔者将其理解为目前还有太多功能没有实现,只能是一个看起来像操作系统的东西。

从简单的程序开始,磁盘最初的512字节是启动区,所以要装载下一个512字节的内容。看第一段程序 harib00a:
30天自制操作系统:第三天 进入32位模式并导入C语言JC指令
jump if carry, 如果进位标志(carry flag) 是1的话,指令就执行(完成跳转)

对于指令 INT 0x13,这里是调用BIOS的0x13号函数,函数作用如下图:
BIOS中断调用–百度百科

30天自制操作系统:第三天 进入32位模式并导入C语言
↑截自百度百科

图中也说明了当AH = 0x02时为读盘动作,其中0x13号函数的功能如下:

  • AH = 0x02(读盘)
  • AH = 0x03(写盘)
  • AH = 0x04(校验)
  • AH = 0x0c(寻道)
  • AL = 处理对象的扇区数(只能同时处理连续的扇区)
  • CH = 柱面号 & 0xff
  • CL = 扇区号(0 ~ 5位) | (柱面号&0x300) * * 2
  • DH = 磁头号
  • DL = 驱动器号
  • ES:BX = 缓冲地址(校验及寻道时不使用)
  • 返回值
  • FLAGS.CF == 0 没有错误,AH = 0
  • FLAGS.CF == 1 有错误,错误号码存入AH内(与重置(reset)功能一样)

FLAGS.CF是什么意思?光看意思也能够理解个大概:进位标志。
因此这里就明晰了JC指令的使用,如果出错,进位标志CF会被设置为1,进而跳转到error程序段。

在多个软盘驱动器的时候,用磁盘驱动器号来指定从哪个驱动器的软盘上读取数据。这里指定0号。
30天自制操作系统:第三天 进入32位模式并导入C语言


软盘的结构示意图:
30天自制操作系统:第三天 进入32位模式并导入C语言

一张软盘有80个柱面,2个磁头,18个扇区,一个扇区有512字节,所以一张软盘的容量为:
80 x 2 x 18 x 512 = 1 474 560 Byte = 1 440KB

含有IPL的启动区,位于C0-H0-S1(柱面0,磁头0,扇区1的缩写),下一个扇区是C0-H0-S2,本次要装载的就是这个扇区。

如果没有汇编基础,那么肯定有这样一个疑惑:如果单纯只用寄存器(16位)来表示内存地址,那么最大可以表示的地址为0xFFFFH,一共10000H = 64KB,显然我们对此并不满足,因此有了后来的EBX寄存器(32位),这样能够处理的空间大小变成了2 ^ 32 B= 4 GB,但在此BIOS阶段,用不着这么大的空间,所以只需要使用段寄存器来拓展即可。

段寄存器:通用寄存器 的组合能够访问到最多 FFFF:FFFFH即 (FFFF) * 16 + FFFF H = 1 114 095字节,也就是说可以指定1MB以内的内存了。

这里指定 ES = 0x0820, BX = 0, 软盘的数据会被装载到0x8200 ~ 0x83ff(一共512字节)的地方,使用0x8200的原因是这一块区域没有程序使用,可以将我们的操作系统装载到这片区域,

注:0x7c00 ~ 0x7dff用于启动区,0x7e00以后直到0x9fbff为止的区域都没有特别的用途,操作系统可以随便使用。


双击 !cons_nt.bat 文件 >>> 输入make完成文件编译 >>> 再次输入 make run 运行操作系统
30天自制操作系统:第三天 进入32位模式并导入C语言

2.试错

由于是机械型存储介质,发生一些硬件错误也是难免的,有时会发生不能读数据的状况,所以应该设计程序重复读几次盘,实在不行那就只能放弃然后打印错误信息来告知程序员这块出错了。

改良后的harib00b程序:
30天自制操作系统:第三天 进入32位模式并导入C语言
JNC指令的含义与JC相反,jump if not carry,意为没有进位则发生跳转。JAE,jump if above or equal,意思是大于或等于时跳转。

程序中给出了较为详细的注释,看几遍应该可以理清楚。

3.读到18扇区

harib00c程序:
30天自制操作系统:第三天 进入32位模式并导入C语言
慢慢理解本程序:
首先是JBE指令,jump if below or equal,意思是小于等于则跳转。
程序做的事情很简单,要读下一个扇区,只需要给CL加1,ES加上0x20就行了。CL是扇区号,这里有一点疑惑是在换扇区读盘时只加上了0x20H = 32Byte,前面有提到过,一个扇区的大小为512字节,按理来说每次读完一个扇区偏移地址需要偏移0x200,这里作者给出的是0x20,解释如下:
30天自制操作系统:第三天 进入32位模式并导入C语言
笔者认为这块可能是作者写错了(狗头保命,有待求证)

这里为什么要循环呢,笔者初次读到程序也是这么想的,我们已经知道自己的目标是18扇区,那么直接将AL的值设置为17(连续读前2 ~ 18 共 17个扇区)不就好了吗。

确实,这么想是没问题的,但是作者查阅了BIOS读盘函数说明的“补充说明”部分:
30天自制操作系统:第三天 进入32位模式并导入C语言
这一部分暂且到这,只需要明白使用AL = 17 一次完成效果也是一样的,但为了章节推进,这里不做详细讲解。

到此,我们已经把磁盘上C0-H0-S2到C0-H0-S18的512 x 17 = 8704字节的内容,装载到了内存的0x8200 ~ 0xa3ff处。


4.读入10个柱面

C0-H0-S18扇区的下一扇区,是磁盘反面的C0-H1-S1,这次也从0xa400读入,按顺序读到C0-H1-S18后,接着读下一个柱面C1-H0-S1,一直读到C9-H1-S18。

harib00d程序:
30天自制操作系统:第三天 进入32位模式并导入C语言
JB指令:jump below,意思是如果小于,就跳转。
程序起始位置的EQU指令:相当于C语言的 #define,进行宏定义。
“CYLS EQU 10”意思是:“CYLS = 10”,EQU是equal的缩写(CYLS为cylinders的简写:柱面)。
30天自制操作系统:第三天 进入32位模式并导入C语言

5.着手开发操作系统

编写了一个非常小的程序:

fin:
	HLT
	JMP fin

通过作者提供的makefile文件做成了一个img文件,按照指示用二进制编辑器打开 haribote.img:
30天自制操作系统:第三天 进入32位模式并导入C语言
↑ 0x002600附近
30天自制操作系统:第三天 进入32位模式并导入C语言
↑ 0x004200附近

再打开haribote.sys,可以惊奇地发现:
30天自制操作系统:第三天 进入32位模式并导入C语言
与img文件中0x004200附近的内容是一样的,作者是想让我们知道:
一般向一个空软盘保存文件时,

  1. 文件名会写在0x002600以后的地方
  2. 文件的内容会写在0x004200以后的地方

接下来就是将操作系统本身的内容写到 haribote.sys 中,再把它保存到磁盘中,最后从启动区执行这个文件就可以了。

6.从启动区执行操作系统

30天自制操作系统:第三天 进入32位模式并导入C语言

7.确认操作系统的执行情况

这里详细介绍了画面模式,先看一下本次的 haribote.nas文件:
30天自制操作系统:第三天 进入32位模式并导入C语言
设置了AH = 0,然后调用了0x10中断:

INT 10h
显示服务 - 由BIOS或操作系统设定以供软件调用。AH=00h 设定显示模式;AH=01h 设定游标形态;AH=02h 设置游标位置;AH=03h 获取光标位置与形态;AH=04h 获取光标位置;AH=05h 设置显示页;AH=06h 清除或滚动栏画面(上);AH=07h 清除或滚动栏画面(下);AH=08h 读取游标处字符与属性;AH=09h 更改游标处字符与属性;AH=0Ah 更改游标处字符;AH=0Bh 设定边界颜色;AH=0Eh 在TTY模式下写字符;AH=0Fh 获取当前显示模式;AH=13h 写字符串。

可以发现这里调用了显卡BIOS的函数,这样就可以切换到显示模式了。
30天自制操作系统:第三天 进入32位模式并导入C语言

8.32位模式前期准备

30天自制操作系统:第三天 进入32位模式并导入C语言
↑为什么要切换到32位模式

一旦使用了32位模式就不可以再调用BIOS:
30天自制操作系统:第三天 进入32位模式并导入C语言
从BIOS得到键盘状态:
30天自制操作系统:第三天 进入32位模式并导入C语言
设置好了画面模式之后还把与画面相关的信息保存在了内存中(保存起来以后备用)

关于VRAM
30天自制操作系统:第三天 进入32位模式并导入C语言

9.开始导入C语言

这一部分均为作者的创意部分,笔者学识浅薄,暂时不能够对此做出深刻的解读,就先放上图书截图。
30天自制操作系统:第三天 进入32位模式并导入C语言
30天自制操作系统:第三天 进入32位模式并导入C语言
30天自制操作系统:第三天 进入32位模式并导入C语言
30天自制操作系统:第三天 进入32位模式并导入C语言
运行效果:一片漆黑
30天自制操作系统:第三天 进入32位模式并导入C语言

10.实现HLT

在前面的C语言程序,因为没有办法使用HLT指令(这是汇编指令),所以程序做了大量的循环。这里作者还是用汇编写了一个带HLT指令的程序:
30天自制操作系统:第三天 进入32位模式并导入C语言
函数名前面必须加上"_",否则不能与C语言函数链接(C语言中的函数在编译之后会在头部加上该符号)

在C语言中调用这个函数:
30天自制操作系统:第三天 进入32位模式并导入C语言

感受

今天的内容相较前两天确实多了很多,虽然写下了这篇博客,但还是有很多地方没有搞懂,需要慢慢理解,不断渗透,截图非常多的原因也很简单,对于很多内容作者已经给出了非常详尽的解析,在这里就不班门弄斧了。
坚持就是胜利,加油!

上一篇:应用流策略做固定选路


下一篇:华为交换机策略路由配置