上回说到操作系统上电到引导扇区之间的程序,但是在结尾只是随便用一个测试程序来代替引导程序(就是第一个扇区的程序),今天我们来讲讲真正的引导程序。

但是在讲引导程序之前我们需要先学习FAT12文件系统,学了之后才会了解数据是如何存储在硬盘上面的(注意:是硬盘,此时数据还没有加载到内存上面)。

所有的文件系统会把磁盘分为若干个层次方便组织和管理,包括
1.扇区:磁盘上最小的数据单元
2.簇:一个或多个扇区
3.分区:通常指整个文件系统

引导扇区位于整个磁盘的0柱面(磁道)0磁头第一个扇区,在这个扇区有个很重要的数据结构叫做BPB(BIOS ParameterBlock)

linux 使用引导盘进入rescue模式 查看fstab_文件系统


以上是第一个扇区内部的格式紧接着第一个扇区的是FAT1和FAT2,分别占用9个扇区,FAT2之后是根目录区的第一个扇区,根目录区后面是数据区,整个文件系统如下

linux 使用引导盘进入rescue模式 查看fstab_数据_02

软盘(用当前时代的物代替就是硬盘、U盘)的作用是用来放置数据,简单来说就是读数据和写数据。读和写只是一个标志位的问题(权限问题,暂且不表)。它们的共同点都是寻找数据,而数据都是以文件的形式存在,那么整个文件系统的主要职责便是查找文件读写文件

首先来分析根目录,它位于FAT2之后,开始的扇区是19号,它由若干个目录条目组成,条目最多有BPB_RootEntCnt个(这个数据在引导扇区中),所以长度不固定。根目录区中每一个条目占用32字节,格式如下

名称

偏移

长度

描述

DIR-Name

0

0xB

长度名8字节,扩展名三字节

DIR-Attr

0xB

1

文件属性

保留位

0xC

10

保留位

DIR_WrtTime

0x16

2

最后一次写入时间

DIR_WrtDate

0x18

2

最后一次写入日期

DIR_FstClus

0x1A

2

此条目对应开始簇号

DIR_FileSize

0x1C

4

文件大小

通过访问根目录,可以得到根目录下的文件名,还有文件属性和文件地址以及最后读写日期。

属性表示文件可读可写的权限。
查找文件从文件开始的簇号开始
这样就完成了文件系统的主要任务,查找文件,读写文件。

由于根目录区的大小是不确定的,所以我们需要算一算第一个数据区的是在第几个扇区
假设根目录区共占用RootDirSectors=[(BPB_RootEntCnt * 32) + (BPB_BytsPerSec-1)]/BPB_BytsPerSec
之所以要加上BPB_BytsPerSec-1就是为了保证此公式在根目录区无法填满整个扇区时仍然能够成立。
数据区开始扇区号=根目录区开始扇区号+14

接着来说数FAT1和FAT2的作用,上文已经讲到通过一些公式可以直接查找到数据,那FAT的作用岂不是多余了吗?

其实并不然,上文讲述的方法只能访问到小于512字节的文件,若是该文件大于一个扇区,这个时候就需要FAT出马了。

FAT有两个,FAT2可以看作是FAT1的备份,他们通常是一样的。

在FAT中,每12位称为一个FAT项,代表一个簇。第零个和第一个FAT项始终不用,从第2个FAT项开始表示数据区的每一个簇,也就是说,第2个FAT项表示数据区的第一个簇。这和前面提到的数据区的第一个簇号是2相呼应。

通常FAT项的值代表的是文件下一个簇号,但如果值大于或等于0xFF8,则表示当前簇已经是本文件最后一个簇,如果值是0xFF7,表示它是一个坏簇。

访问一个文件有如下流程,首先通过根目录文件查找文件名,确定是哪一个条目,接着在条目中访问DIR_FstClus,这个里面有对应的开始簇号,若是文件大于512字节,需要分好几个扇区来存储数据,当第一个扇区号访问完后,通过FAT可以访问下一个簇号,对应的则是下一个扇区。这样即实现了文件访问的原理。

FAT12文件系统原理讲完了,现在来具体实现,首先按照对数据进行填充,若是我们用U盘重装过操作系统,应该会了解,这里面就是从U盘启动的第一份代码,它的主要功能自然是知道这个磁盘的相关信息,为以后继续从磁盘读取数据作准备,毕竟,操作系统还在磁盘里没有加载进来呢。

这份代码里面呢,我继续偷个懒,只是让引导程序能够工作就行,打印个字符串就行,引导代码放在下章写(忘记了就不写了),

;======================================
;boot.asm
;编译方法:nasm boot.asm -o boot.bin
;======================================
;%define    _BOOT_DEBUG_  ;做Boot Sector时要注释掉 

%ifdef  _BOOT_DEBUG_
    org 0100h
%else
    org 07c00h      ;Boot状态 bios将把boot sector加载到0:07c00处开始执行
%endif

    jmp short   LABEL_START
    nop

    ; 下面是 FAT12 磁盘的头
    BS_OEMName  DB 'ForrestY'   ; OEM String, 必须 8 个字节
    BPB_BytsPerSec  DW 512      ; 每扇区字节数
    BPB_SecPerClus  DB 1        ; 每簇多少扇区
    BPB_RsvdSecCnt  DW 1        ; Boot 记录占用多少扇区
    BPB_NumFATs DB 2        ; 共有多少 FAT 表
    BPB_RootEntCnt  DW 224      ; 根目录文件数最大值
    BPB_TotSec16    DW 2880     ; 逻辑扇区总数
    BPB_Media   DB 0xF0     ; 媒体描述符
    BPB_FATSz16 DW 9        ; 每FAT扇区数
    BPB_SecPerTrk   DW 18       ; 每磁道扇区数
    BPB_NumHeads    DW 2        ; 磁头数(面数)
    BPB_HiddSec DD 0        ; 隐藏扇区数
    BPB_TotSec32    DD 0        ; wTotalSectorCount为0时这个值记录扇区数
    BS_DrvNum   DB 0        ; 中断 13 的驱动器号
    BS_Reserved1    DB 0        ; 未使用
    BS_BootSig  DB 29h      ; 扩展引导标记 (29h)
    BS_VolID    DD 0        ; 卷序列号
    BS_VolLab   DB 'OrangeS0.02'; 卷标, 必须 11 个字节
    BS_FileSysType  DB 'FAT12   '   ; 文件系统类型, 必须 8个字节  


LABEL_START:
    mov ax, cs
    mov ds, ax
    mov es, ax
    Call    DispStr         ; 调用显示字符串例程
    jmp $          ; 无限循环

DispStr:
    mov ax, BootMessage
    mov bp, ax          ; ES:BP = 串地址
    mov cx, 16          ; CX = 串长度
    mov ax, 01301h      ; AH = 13,  AL = 01h
    mov bx, 000ch       ; 页号为0(BH = 0) 黑底红字(BL = 0Ch,高亮)
    mov dl, 0
    int 10h         ; int 10h
    ret

BootMessage:        db  "Hello, OS world!"
times   510-($-$$)   db  0   ; 填充剩下的空间,使生成的二进制代码恰好为512字节
dw  0xaa55              ; 结束标志

将这个代码写入磁盘引导扇区后,软盘已经能够被DOS以及linux识别了,可以方便的往上添加或删除程序了。