64位操作系统-BOOT加载LOADER
FAT12文件系统数据
1 | [org 0x7c00] |
BaseOfLoader与OffsetOfLoader组成了Loader的起始物理地址,即BaseOfLoader:OffsetOfLoader=0x10000。
RootDirSectors equ 14定义了根目录占用的扇区数,这个数值是FAT12文件系统提供的信息计算得到的。即(BPB_RootEntCnt * 32 + BPB_BytesPerSec - 1) / BPB_BytesPerSec = (224 * 32 + 512 - 1) / 512 = 14
SectorNumOfRootDirStart equ 19定义了根目录的起始扇区,这个数组也是计算得到的。保留扇区数+FAT表扇区数*FAT表份数=1+2*9=19,扇区编号从0开始,所以根目录扇区编号为19
SectorBalance equ 17用于平衡文件(目录)的起始簇号与数据区起始簇号的差值。因为数据区对应的有效簇号是2,为了正确计算出FAT表项对应的数据区起始扇区号,则FAT表项值减2,或者将数据区的起始簇号/扇区号减2(仅在每簇一个扇区组成时可用)。暂用一种取巧的方式,将根目录起始扇区减2(19-2=17),进而间接把数据区的起始扇区号(数据区起始扇区号=根目录起始扇区号+根目录所占的扇区数)减2
FAT中读取文件
读取磁盘扇区模块
1 | ; read one sector from floppy |
通过中断int 13,从驱动器中读取开始地址(ax号扇区)后指定数量(cl个)扇区数据到目标地址(es:bp)
ReadOneSector负责实现读取。仅仅是对BIOS中断服务程序的再次封装,以简化读取磁盘扇区需要的操作,
INT13, AH=0x02功能:读取磁盘扇区
- AL=读取扇区数(非0)
- CH=磁道号(柱面号)的低8位
- CL=扇区号1
63(bit 05),磁道号(柱面号)的高2位(bit 6~7,只对硬盘有效) - DH=磁头号
- DL=驱动器号(如果操作的是硬盘驱动器,bit 7必须被置位)
- ES:BX=>数据缓冲区
模块ReadOneSector仅仅是对BIOS的0x13号中断0x02号功能的封装,以简化磁盘读取扇区的的操作过程,该模块是需要参数的,参数说明如下:
- AX = 待读取的磁盘起始扇区号
- CL= 读入的扇区数量
- EX : BX => 数据缓冲区起始地址
模块ReadOneSector的参数中传入的磁盘扇区号是 LBA(Logical Block Address,逻辑块寻址) 格式的,而BIOS的0x13号中断0x02号功能只能接受 CHS(Cylinder/Head/Sector,柱面/磁头/扇区) 格式的磁盘扇区号,那么就需要将LBA格式的转换为CHS格式的,通过以下公式,便可以转换:
$$LAB扇区号 \div 每磁道扇区数 \begin{cases} 商Q \rightarrow \begin{cases} 柱面号=Q>=1 \ 磁头号=Q \end{cases}\ 余数R \rightarrow 起始扇区号 = R + 1 \end{cases}$$
具体流程如下:
- 模块
ReadOneSector一开始,先保存栈帧寄存器(bp)和栈寄存器(sp)中的数值(bp存储在栈中,sp保存在bp寄存器中) - 然后从栈中开辟两个字节的存储空间,即是
sp向下移动两个字节(sub esp, 2)。然后将CL中的内容(1个字节,参数,读入扇区数)存入栈中,因为接下来要使用到CL寄存器。注:CL中的内容是一个字节,而栈项要对齐,所以要要用两个字节保存一个字节的内容,即使浪费了一个字节。 - 然后就是把
BX的内容也保存到栈中,因为接下来要用到BX。 - 使用
AX寄存器(待读取的磁盘起始扇区号)除以BL寄存器(每磁道扇区数),计算出目标磁道号(商:AL寄存器)和目标磁道内的起始扇区号(余数:AH寄存器),又因为磁道内的起始扇区号是从1开始计数的,故将此余数值加1(inc ah; mov cl, ah)紧接着按照以上公式计算出磁道号(即是柱面号)、磁头号。 - 恢复
BX - 获取驱动器号
BS_DrvNum - 设置
0x13中断功能号AH=0x02,设置读入扇区数mov al, byte [bp - 2](我们之前把参数CL读入扇区数保存在了栈中,所以现在用bp去索引它)。 - 然后调用
0x13号中断。 - 判断是否读取成功(即是若CF位为0,则成功),否则(CF=1)回到步骤
7。
LOADER.BIN文件搜索
1 | ; search loader.bin |
找不到LOADER.BIN文件
1 | ; display on screen: Error LEADER.BIN file not found |
屏幕上显示错误信息
FAT表项解析
当找到loader.bin文件之后,根据FAT表项提供的簇号顺序依次加载扇区数据到内存中,这会涉及到FAT表项的解析工作:
1 | ; get FAT Entry |
该模块GetFATEntry的功能:根据当前FAT表项索引出下一个FAT表项,参数如下:
- AH=FAT表项号(输入/输出参数)
首先把FAT表项号(AX)保存,并将奇偶标志变量置0,因为每个FAT表项占1.5B,并不是偶数对齐,所以就将FAT表项乘以3再除以2(也就是扩大1.5倍),然后判断余数的奇偶性并保存在变量[Odd]中(奇数为1,偶数为0),再将计算结果除以每扇区字节数,商即为FAT表项的偏移扇区号,余数为FAT表项在扇区中的位置。然后通过ReadOneSector模块,连续读入两个扇区,这样做的目的是,为了解决FAT表项横跨两个扇区的问题,最后根据奇偶标志变量[Odd]处理奇偶项错位问题,即是奇数项向右移动4位。
加载loader.bin到内存
1 | ; found loader.bin name in root dir |
临时变量和字符串变量
1 | ; temp variable |
生成镜像测试
编译boot.bin
1 | nasm -f bin boot.asm -o boot.bin |
生成软盘镜像:
1 | bximage -q -fd=1.44M -func=create ./boot.img |
写入引导扇区:
1 | dd if=$(BUILD)/boot.bin of=boot.img bs=512 count=1 conv=notrunc |
boches配置:
1 | # configuration file generated by Bochs |
boches加载镜像:
1 | bochs -q -f bochsrc.floppy -unlock |
此时可以看到虚拟机输出:
1 | ERROR: LOADER.BIN FILE NOT FOUND |
加载简单的LOADER.BIN
首先修改加载文件后的跳转地址:
1 | FileLoaded: |
编写简单的loader.bin文件:
1 | [org 0x10000] |
就是在屏幕上输出:Start Loader...
加载测试
测试编译loader.bin
1 | nasm -f bin loader.asm -o loader.bin |
将loader.bin拷贝进软盘中:
1 | mkdir ./tmp |
再次运行测试:
1 | bochs -q -f bochsrc.floppy -unlock |
此时可以看到屏幕输出:
1 | Start Loader... |
Loader.bin成功加载


