一个操作系统从开机到开始运行,大致经历“引导→加载内核入内存→跳入保护模式→开始执行内核”这样一个过程。也就是说,在内核开始执行之前不但要加载内核,而且还有准备保护模式等一系列工作,如果全都交给引导扇区来做,512字节很可能是不够用的,所以,不妨把这个过程交给另外的模块来完成,我们把这个模块叫做Loader。
引导扇区负责把Loader加载入内存并且把控制权交给它,其他工作放心地交给Loader来做,因为它没有512字节的限制,将会灵活得多。
一、准备工作
1.FAT12各区分布
引导扇区和Loader肯定是存在磁盘或软盘中,这里我们存在软盘中,并且使用的是FAT12文件系统,软盘中各分区的分布情况:
引导扇区位于磁盘的0柱面,0磁头,1扇区,在这个扇区中有一个很重要的数据结构叫做BPB(BIOS ParameterBlock)。关于FAT12的引导扇区和根目录区可以参考:FAT12引导扇区格式及根目录区条目格式其中就有对BPB各项的描述。
2.数据区位置计算
数据区的第一个扇区的位置可以通过计算根目录所占的扇区数得到。具体公式是:
RootDirSector = BPB_RootEntCnt×32÷BPB_BytsPerSec
这是余数为零的情况下,如果余数不为零,则根目录扇区数:
RootDirSector = BPB_RootEntCnt×32÷BPB_BytsPerSec+1
RootDirSector就是要计算的根目录区扇区数由FAT12引导扇区格式及根目录区条目格式中对BPB的介绍可以知道,BPB_RootEntCnt是根目录区条目数最大值,每个条目32字节,BPB_BytsPerSec是每扇区字节数。
3.FAT表
虽然由此可以找到文件,但是还不够,因为有的文件大于512字节(一个扇区512字节),超过512字节的内容我们需要根据FAT表找到。FAT表在引导扇区和根目录区之间,FAT表有两个,FAT2可看做是FAT1的备份,它们通常是一样的。
其中,每12位称为一个FAT项(FATEntry),代表一个簇。第0个和第1个FAT项始终不使用,从第2个FAT项开始表示数据区的每一个簇,也就是说,第2个FAT项表示数据区第一个簇,以此类推。
说到这里好像还没说清FAT表的具体功能,对于FAT表的具体功能后面再说,下面先来说说怎么辨认FAT表的每一项,我第一次看的时候还真没看懂。
假设下面是一组FAT表的表项:
0000200: F0 FF FF FF 8F 00 09 A0 00
因为FAT表的第0和第1项始终不使用,所以前三个字节不用管它。以第2、3表项为例(FF 8F 00),3个字节包含了两个FAT表表项,第2个表项的低8位就是第一个字节,即FF;第2个表项的高4位就是第二个字节的低4位,也就是F,所以第2个表项的值为0xF FF。而第3个表项的低4位就是第二个字节的高4位,即8,第3个表项的高8位就是第3个字节,即00,所以第3个表项的值为0x00 8;以此类推,第四个表项的值就是0x0 09,第五个表项的值为0x00 0。
然后再说说FAT表项的值的功能:FAT项的值代表的是文件下一个簇号,但如果值大于或等于0xFF8,则表示当前簇已经是本文件的最后一个簇。如果值为0xFF7,表示它是一个坏簇。
根目录条目中有一项是DIR_FstClus,这个条目对应的是文件的开始簇号,即在数据区中的簇号,同时它也对应了FAT表中的项,如果FAT表项的值小于0xFF8,则此值对应的就是文件的下一个簇号和FAT表的下一个表项。举个例子:假如一个长一点的文件FLOWER.TXT,它的DIR_FstClus值为3,对应第3个FAT项。此FAT项值为0x008,也就是说,这个簇不是文件的最后一个簇,下一个簇号为8。我们再找到第8个FAT项,发现值为0x009,接下来第9个FAT项值为0x00A,第0xA个FAT项值为0xFFF。所以,FLOWER.TXT占用了第3、8、9、10,共计4个簇。
二、写入过程
要加载Loader肯定要先找到Loader,我们规定Loader放在根分区也就是根目录中,所以从跟分区的第一个分区开始找,比较每个分区第一个条目(Loader的名称)是不是“LOADER BIN”,如果找到,就将LOADER.BIN的起始扇区号加载入内存,在此之前,将此扇区在FAT表中的序号入栈。起始扇区加载完后,将其扇区在FAT表中的序号出栈,然后计算出FAT表项的值,将此值和“0FFFh”比较,相等的话说明此簇是文件的最后一个簇之前说只要fat表项值大于等于0FF8h就说明此簇是文件的最后一个簇,所以为什么要和0FFFh比较呢,我在网上搜了相关资料,fat12的fat表文件结束簇是0FF8h~0FFFh,所以我将代码改了一下:
将
cmp ax, 0FFFh
jz LABEL_FILE_LOADED
改为
cmp ax, 0FF8h
jnb LABEL_FILE_LOADED
运行结果都一样,但是改为
cmp ax, 0FF8h
jz LABEL_FILE_LOADED
就不行,所以我认为判断此簇是否文件最后一个簇fat表项的值应比较到0FFFh。
本来想在解释一下代码的,但是代码太多,解释起来又麻烦,又得写好长,就算写好了,这么长的文章也很难让人耐着性子去看,所以决定关于代码另写几篇博客,这里就不在多说。