Lesson 6: Boot Loader
到现在为止,我们所做的都被限制在引导扇区里面。如果我们的操作系统只能容纳在一个扇区里,那它绝对大不了。我们需要有个方法来拓展。我们将通过写一个程序来读取并且运行一个可执行文件,以达到这个目标。这个程序称为boot loader。我们希望读取的那个程序的大小可以随我们的希望而定,因为它不再受一个扇区大小的限制了。
这将比我们已经完成的工作都要难。在FAT文件系统中指定一个参考点可能是个好主意(你有可能正使用其他文件系统,不过假定你使用FAT)。我将简单介绍一下启动加载过程。
一张软盘依次包含下面的部分:DOS的引导记录(我们已经在上面忙活了半天的那第一个扇区),文件分配表(FAT),根目录,然后是磁盘上的文件。(硬盘的话更复杂一点,它包含一个主引导记录Master Boot Record和多个分区。)设想我们现在已经写好了操作系统,编译并且命名为LOADER.BIN,然后把它复制到磁盘上。boot loader将如下所示的加载它。
DOS引导记录(DBR)将读取DBR,FAT,和根目录的大小。它们在磁盘上的位置便相应的确定了。
把根目录读入内存。
在根目录里寻找名为LOADER.BIN的文件。如果找到了,我们将读取目录信息来获知文件的第一个簇(文件分配的单位)在哪里。如果没有找到,给出一条错误信息。
把文件分配表读入内存。
从文件第一个簇开始,借由文件分配表我们可以找到这个文件所有的簇。我们把这些簇读到内存中特定的位置。
我们跳转到那里,然后开始执行操作系统。
所有的读盘工作都将通过BIOS完成。如果你愿意接受挑战,找本关于BIOS函数的指南来学学如何从磁盘上读取扇区,然后试着写一个自己的boot loader。不过,我也提供了一个从John S. Fine那里得到并且稍做改变了的FAT12 bootstrap loader。如果你能找到一份他的“partcopy”,那你就可以按他写的编译和安装指令做了(请告诉我你是在哪里找到的)。否则,用我们在前几课的方法,来把这个boot loader复制到软盘上。
在John Fine的bootstrap loader中有很多可供用户设置的参数。这个加载器假定你使用FAT12文件系统(这是软盘使用的文件系统)。对于其他的系统来说,你需要不同的加载器。你能够设置的参数包括操作系统的位置,把什么样的FAT数据读入内存。你也可以改变加载器读入的文件名。
默认情况下,加载器将在根目录(如果有的话)下找到名为LOADER.BIN的文件,并且把它读到以0x1000:0000(这可以通过改变%define IMAGE_SEG调节)开始的内存中。这样你就能够编一个操作系统,并且把它命名为LOADER.BIN复制到软盘上。
作为举例,我们将使用boot loader来加载我们第四课的Hello,World操作系统。但是我们不能仅仅使用和第四课一样的文件。我们需要稍做改变。首先你需要意识到这个文件现在被加载到了不一样的位置(原来是0000:7C00,现在是0x1000:0000)。其次,我们可以去掉DOS引导记录的数据了。
在代码的一开始,你可以设置数据段和堆栈段以及堆栈的指针。下面的代码完成这些。当前的代码段由CS寄存器存储,同时静态数据也被组装在可执行代码中,所以我们也可以认为这个段是数据段。同时,我们还可以把它当作堆栈段来用,不过将来我们可能要改变。
mov ax, cs ; 得到当前段
mov ds, ax ; 数据也在当前段里
cli ; 变动堆栈时屏蔽掉中断
mov ss, ax ; 我们也把这个段当做堆栈段来用
mov sp, 0xfffe ; 将段的顶部作为堆栈的开始
sti ; 恢复中断
最后,我们可以把源码最后的几行去掉了。包括添加引导扇区署名和检查文件是否正好为一个扇区大小的代码。其他的代码应该都差不多。结果可以在lesson6.asm下载。
用下面的命令编译这个文件并且把它复制到你得磁盘上:
nasmw lesson6.asm -o lesson6.bin
copy lesson6.bin a:/LOADER.BIN
然后,如果你已经安装了boot loader,你就可以用这张盘启动了。如果你获得了成功,修改一下前几课的其他程序来使boot loader能够加载它们。以后的绝大多数课程里,我将假定你使用这个boot loader(当然也可以是其他你选择的)来载入你的操作系统文件。
现在,我们可以使我们的操作系统比一个扇区更大了!