HIT-OSLab1
实验目标:
- 改写 bootsect.s 的代码,使得屏幕上可以输出
YHellowOS is loading...
- 使 bootsect.s 能够完成 setup.s 的载入,并跳转到 setup.s 开始执行处,并输出
Now we are in SETUP
- 将 setup.s 获取到的硬件参数输出到屏幕上
- 完成了输出硬件参数的步骤后,不再继续加载 linux 内核,保持上述信息
实验过程
先简单解释分析一下 bootsect.s 的作用:
- bootsect.s 是一个汇编程序,是引导扇区的源代码(该扇区被称为引导扇区,是一个计算机启动时第一个读取的扇区,通常是硬盘或软盘的第一个扇区)
- 引导扇区通常包含一个引导扇区表(Boot Sector Table,BST),该表指向计算机的引导Loader(通常是BIOS)和操作系统
其核心代码如下:
1 | SETUPLEN = 4 ! nr of setup-sectors # setup程序代码占用扇区数 |
- 这一段指令使位于 0x07c00 的 bootsect 代码,被全部拷贝到了 0x90000
- 最后跳转到 0x90000 从 go 标号处开始执行
1 | go: mov ax,cs # cs:代码段基地址 |
- 指令
int 0x13
的中断服务程序实质上就是磁盘服务程序,用于读取 setup 的代码并将其加载到程序的内存 0x90200 中 - 如果 ax 寄存器的值大于0,则 jnc 会使程序跳转至 ok_load_setup,否则会尝试再次加载 setup
1 | ok_load_setup: |
- 指令
int 0x10
的中断服务程序可以将字符串输出到屏幕上 - 输出信息并进行初始化后,程序会调用 read_it 来读取磁盘上的 system 模块,并将其加载到 0x10000
- 最后跳转到 0x90200 执行 setup 的代码
计算机上电后,ROM BIOS 会在物理内存 0 处初始化中断向量表,其中有256个中断向量,每个中断向量占用4字节,共1KB,在物理内存地址 0x000-0x3ff 处,这些中断向量供 BIOS 中断使用
BIOS 初始化中断向量表后,启动设备的第一个扇区(即引导扇区)读入内存地址 0x7c00 处,并跳转到此处开始执行:
- 此时操作系统处于实模式,寻址方式为:16位段寄存器 * 0x10 + 16位目标寄存器
- 为了方便加载主模块,引导程序首先会将自己拷贝到内存相对靠后的位置(如 linux0.11 的 bootsect 程序先将自己移动到 0x90000 处),然后跳转过去执行
- 在接下来的步骤中,程序会依次加载 setup 和 system 的代码,并跳转执行 setup 的代码
setup.s 是一个汇编语言源代码文件,通常位于操作系统内核的源代码目录中,它的作用是设置操作系统的初始状态,为操作系统其他模块的运行做准备:
- 操作系统的主模块为了让其中代码地址等于实际的物理地址,需要将其加载到内存 0x0000 处,但此时会覆盖在 0x000-0x3ff 处的 BIOS 中断向量表
- 所以操作系统在加载时需要先将主模块加载到内存中不与 BIOS 中断向量表冲突的地址处,之后在可以覆盖中断向量表时才将其移动到 0x0000
- 例如在 Linux0.11 的 system 模块就是先在 bootsect 程序中被加载到了 0x10000,之后在 setup 程序中移到 0x0000 处
接下来的工作就是利用 int 0x10
来将我们需要的字符串打印到屏幕上,为此我们需要先把字符串构造好:
1 | msgmy: |
1 | msgmy: |
13,10
代表换行符
接下来就可以编写打印函数了:
1 | read_cur: |
- read_cur 用于获取光标位置(如果没有这一步,打印数据很可能会被后续数据覆盖),寄存器 cx 中的值就是该字符串的长度,寄存器 bx 中的值代表了输出字符的颜色
- print_string 用于打印字符串,需要传入两个参数(ax:字符串地址,bx:字符串长度)
这里有一点需要注意:
1 | mov ax,#INITSEG |
- 在 setup.s 中使用 read_cur 获取光标位置前,需要先重置 ds/es(主要是重置 es 格外数据段)
在输出硬件参数前我们需要知道 setup.s 读取了哪些硬件参数,找到其核心代码:
1 | entry start |
- 我们需要打印的设备有 HD0,HD1,VC(地址偏移记录在注释中)
为了方便读取地址中的数据,我们需要写一个打印函数:
1 | print_hex_4: |
最后我们可以按照数据的格式写出代码:
1 | ! Show HD Information: |
至于最后一个要求,我们只需要写一个死循环即可:
1 | l: jmp l |
最终效果如下: