F AT12是DOS时代就开始使用的文件系统(File System),直到现在仍然在软盘上使用。几乎所有的文件系统都会把磁盘划分为若干层次以方便管理和组织,这些层次主要包括:
-
扇区(Sector):磁盘上的最小数据单元
-
簇(Cluster):一个或多个扇区
-
分区(Partition):通常是指整个文件系统
下面是FAT12格式的软盘存储图:
FAT12引导扇区的格式
名称 |
偏移 |
长度 |
内容 |
示例值 |
BS_jmpBoot |
0 |
3 |
一个跳转指令 |
jmp LABEL_START
nop
|
BS_OEMNane |
3 |
8 |
厂商名 |
'ForrestY' |
BPB_BytePerSec |
11 |
2 |
每扇区字节数 |
0x200 |
BPB_SecPerClus |
13 |
1 |
每簇扇区数 |
0x1 |
BPB_RsvdSecCnt |
14 |
2 |
Boot记录占用多少扇区 |
0x1 |
BPB_NumFATs |
16 |
1 |
共有多少FAT表 |
0x2 |
BPB_RootEntCnt |
17 |
2 |
根目录文件数最大值 |
0xE0 |
BPB_TotSec16 |
19 |
2 |
扇区总数 |
0xB40 |
BPB_Media |
21 |
1 |
介质描述符 |
0xF0 |
BPB_FATSz16 |
22 |
2 |
每FAT扇区数 |
0x9 |
BPB_SecPerTrk |
24 |
2 |
每磁道扇区数 |
0x12 |
BPB_Numheads |
26 |
2 |
磁头数(面数) |
0x2 |
BPB_HiddSec |
28 |
4 |
隐藏扇区数 |
0 |
BPB_TotSec32 |
32 |
4 |
如果BPB_TotSec16是0,由这个值记录扇区数 |
0 |
BS_DrvNum |
36 |
1 |
中断13的驱动器号 |
0 |
BS_Reserved1 |
37 |
1 |
未使用 |
0 |
BS_BootSig |
38 |
1 |
扩展引用标记(29h) |
0x29 |
BS_VolID |
39 |
4 |
卷序列号 |
0 |
BS_VolLab |
43 |
11 |
卷标 |
'OrangesS0.02' |
BS_FileSysType |
54 |
8 |
文件系统类型 |
'FAT12' |
引导代码及其他 |
62 |
448 |
引导代码、数据及其他填充字符等 |
引导代码(其余为0) |
结束标志 |
510 |
2 |
0xAA55 |
0xAA55 |
|
|
|
|
|
FAT表
FAT表占1-18扇区,共两个FAT表,FAT1和FAT2,一般FAT2不用,FAT2仅是FAT1的拷贝,每个FAT表占9个分区,即
(512*9=4608)字节,FAT表由FAT项组成,每个FAT项占12位,共3072个FAT项,去掉两个不用的FAT的项(0和1,这是因为数据
区的簇号是从2开始的0和1不用,故对应的FAT表中的0和1也不用),故FAT12功能管理3070个簇区即
(1.499M*BPB_SecPerClus,这里是1.499M)的大小。
要注意的是,由于每个FAT项占12位,包含一个字节和另一个字节的一半。如上图所示连续的3个字节表示两个FAT项,BYTE1是Fat
Entry1的低8位,BYTE2的低4位是Fat Entry1的高4位,BYTE2的高4位是Fat Entry2的低4位,BYTE3是Fat
Entry2的高8位。
下面介绍根目录:
仅跟FAT表的是根目录,从第19个分区开始,它是由若干个目录条目(Directory Entry)组成,条目最多有BPB_RootEntCnt个。由于根目录区的大小依赖于BPB_RootEntCnt的大小,所以长度不固定。
根目录中的每一个条目占用32字节,它的格式如下表所示:
名称 |
开始字节 |
长度 |
内容 |
DIR_Name |
0 |
0xB |
文件名8字节,扩展名3字节 |
DIR_Attr |
0xB |
1 |
文件属性 |
保留位 |
0xC |
10 |
保留 |
DIR_WrtTime |
0x16 |
2 |
最后一次写入的时间 |
DIR_WrtDate |
0x18 |
2 |
最后一次写入的日期 |
DIR_FstClus |
0x1A |
2 |
此文件在数据区和FAT表中的开始簇号 |
DIR_FileSize |
0x1C |
4 |
文件大小 |
数据区
需要注意的是数据区的第一个簇号是2,而不是0或者1,(簇号和扇区号在这里是一样的,因为一簇只有一个扇区),假设根目录区共占有RootDirSectors个扇区,则有:
RootDirSectors =[ (BPB_RootEntCnt*32)+(BPB_BytesPerSec-1)]/BPB_BytsPerSec
只所以分子上要加上(BPB_BytesPerSec-1),是为了保证此公式在根目录区无法填满整数个扇区时仍然成立。
DOS可以识别的引导盘
- ;===============================================================================
- jmp short LABEL_START
- nop ;必不可少的
- ;**以下是FAT12的磁盘头的定义************
- BS_OEMName DB 'SongYang' ;OEM String, 必须 8 个字节
-
- BSB_BytePerSec DW 512 ;每扇区字节数
- BSP_SecPerClus Db 1 ;每簇多少扇区
- BPB_RsvdSecCnt DW 1 ;Boot 记录占用多少扇区
- BSP_NumFats DB 2 ;共有多少 FAT 表
- BSP_RootEntCnt DW 224 ;根目录文件数最大值
- BSP_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_Volab DB 'Tinix0.01 ' ;卷标, 必须 11 个字节
- BS_FileSysType DB 'FAT12 ' ;文件系统类型, 必须 8个字节
-
- LABEL_START:
软盘的读写
要读写软盘的话,这时就需要BIOS中断int 13h。用法如下:
中断号 |
寄存器 |
作用 |
13h
|
ah=00h
|
dl=驱动器号(0表示A盘) |
复位软驱 |
ah=02h ch=柱面(磁道)号 dh=磁头号 es:bx->数据缓冲区
|
al=要读扇区数 cl=起始扇区号 dl=驱动器号(0表示A盘) |
从磁盘读数据入es:bx 指向的缓冲区 |
下面我们编写一个示例,按上述 FAT12引导扇区的格式 编写一个引导程序读出FAT12软盘中的一个文件(test.txt)并将其打印在屏幕上,然后将编写的引导程序写入引导扇区即0号扇区,这样软盘的文件格式就是FAT12了,这时就可以在 DOS或者其他操作系统下访问软盘了,向里面新建一个文件test.txt并些入一些什么,然后将其作为启动盘启动,就会自动运行引导程序,找到文件test.txt并打印出文件中的东西。下面介绍具体的做法:
1.扇区的读写
从上表中 我们发现int 13h的参数不是扇区号 ,而是用磁头号、柱面和起始扇区表示的,那我们如何知道扇区号来读取扇区呢,首先进行转换,公式如下:
扇区号/每磁道扇区数(BPB_SecPerTrk,18) |
商Q=> |
柱面号=Q>>1
|
磁头号=Q&1 |
余数R=> |
起起始扇区号=R+1 |
下面我们实现一个函数来读取扇区ReadSector():
参数:
ax |
从第axSector开始读 |
cl |
读取cl个Sector |
es:bx |
读到缓冲区es:bx |
代码如下:
- ;----------------------------------------------------------------------------
- ; 函数名: ReadSector
- ;----------------------------------------------------------------------------
- ; 作用:
- ; 从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
- ReadSector:
- ; -----------------------------------------------------------------------
- ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
- ; -----------------------------------------------------------------------
- ; 设扇区号为 x
- ; ┌ 柱面号 = y >> 1
- ; x ┌ 商 y ┤
- ; -------------- => ┤ └ 磁头号 = y & 1
- ; 每磁道扇区数 │
- ; └ 余 z => 起始扇区号 = z + 1
- push bp
- mov bp, sp
- sub esp, 2 ; 辟出两个字节的堆栈区域保存要读的扇区数: byte
-
- [bp-2]
-
- mov byte [bp-2], cl
- push bx ; 保存 bx
- mov bl, [BPB_SecPerTrk] ; bl: 除数
- div bl ; y 在 al 中, z 在 ah 中
- inc ah ; z ++
- mov cl, ah ; cl <- 起始扇区号
- mov dh, al ; dh <- y
- shr al, 1 ; y >> 1 (其实是 y/BPB_NumHeads, 这里
-
- BPB_NumHeads=2)
- mov ch, al ; ch <- 柱面号
- and dh, 1 ; dh & 1 = 磁头号
- pop bx ; 恢复 bx
- ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^
- mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘)
- .GoOnReading:
- mov ah, 2 ; 读
- mov al, byte [bp-2] ; 读 al 个扇区
- int 13h
- jc .GoOnReading ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到
-
- 正确为止
-
- add esp, 2
- pop bp
-
- ret
注意事项:
- 注意各个参数的取值范围的问题,并不是随意取值的;每次读取扇区只能在同一个磁道中读取,也就是说一次最多能读取BPB_SecPerTrk(18)个扇区,也就是说cl的取值小于等于[BPB_SecPerTrk-(ax%BPB_SecPerTrk)].
- es:bx的选择问题,如果es:bx 和 cl选取的不当 会出现【09h = DMA across 64K boundary】这个错误,原因不知道,有知道的可以告诉我。
举个例子:读取9个扇区到8EE0:0000h不出错,但是读到8EE0:0001h就出错!但是读取8个以下的扇区则没问题!很奇怪的问题。以下是我的猜测:
DMA的缓存是64k的大
小,8000:0000h-9000:0000h正好是64K,也就是内存按64K划分,DMA缓冲区对应于每个64K的内存,而8EE0:0000h-
9000:0000h正好是9个扇区的大小,8EE0:0001h则少了一个字节,故出错!这只是本人的猜测,查了很长时间也没差到原因,有知道的可以告
诉我。
2.获得文件的第一个簇号(这里也是区号,二则相等)
要想获某个文件的第一个簇号 需要在根目录中查找对应的目录项(这里假设文件都放在了根目录中个),步骤如下:
- 将整个根目录区域加载进内存,(根目录不是很大,相对现在的大内存),直接加载进内存方便操作。
- ;**将根目录整个的都读到es:bx处
- mov ax, BaseOfLoader
- mov es, ax ; es <- BaseOfLoader
- mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx =
-
- BaseOfLoader:OffsetOfLoader
- mov ax, SectorNoOfRootDirectory ; ax <- Root Directory 中的某 Sector 号
- mov cl, RootDirSectors ;将根目录都读出来
- call ReadSector
这里得到的簇号就是区号SecNo,因为这里一个簇里面只有一个扇区,但是数据区的第一个簇号是从2开始的,要计算其对应的扇区号应按下面的公式计算:
SecNo = SecNo + (1+9*2 +RootDirSectors -2 -1)
其1个引导扇区,2个FAT表,0个隐藏扇区,RootDirSectors个根目录,由于第一个簇号事2故要减去2,编号是从0开始的故要减去个1。
- mov si, LoaderFileName ; ds:si -> "TEST TXT"
- mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 =
-
- BaseOfLoader*10h+100
- cld ;设置方向标志
-
- mov cx, [BSP_RootEntCnt]
- mov [RootDirectItemNum], cx ;根目录的条数
-
- LABEL_SEARCH_FOR_LOADERBIN:
- cmp word [RootDirectItemNum], 0
- jz NOLOADER
- dec word [RootDirectItemNum]
-
- mov cx, 11
- LABEL_CMP_FILENAME:
- cmp cx, 0
- jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到
- dec cx
- lodsb ; ds:si -> al
- cmp al, byte [es:di]
- jz LABEL_GO_ON
- jmp LABEL_DIFFERENT ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
-
- LABEL_GO_ON:
- inc di
- jmp LABEL_CMP_FILENAME
- LABEL_DIFFERENT:
- and di, 0FFE0h
- add di, 020h
- and si, LoaderFileName
- jmp LABEL_SEARCH_FOR_LOADERBIN
- NOLOADER:
- mov dh, 0
- call DispStr
- jmp $
- LABEL_FILENAME_FOUND:
- and di, 0FFE0h
-
- mov eax, [es:di+0x1C]
- mov [FileLength], eax
-
- add di, 01Ah ; di -> 首 Sector(偏移)
-
- mov cx, word [es:di]
- push cx ;cx里面放的是Loader的第一个扇区号(数据区的从2开始)
3.FAT表中的下一个簇号
同样的道理先将整个FAT表读到内存中:
- ;**读取Fat表
- mov ax, BaseOfLoader
- sub ax, FatOffset ;分配5k内存(512*10/1024)
- mov es, ax
- mov bx, 0 ;将Fat读到es:bx
- mov ax, [BPB_RsvdSecCnt];Fat开始扇区 1
- mov cl, [BPB_FATSz16]
- call ReadSector
然后查找下一个簇号,由于可能多次调用写成了一个函数GetFATEntry():
参数ax:簇号
返回值ax:下一个簇号,需要注意簇号大于等于0xFF8表示文件的最后一个簇,为0xFF7表示坏簇。
- ;----------------------------------------------------------------------------
- ; 函数名: GetFATEntry
- ;----------------------------------------------------------------------------
- ; 作用: ax
- ;
- GetFATEntry:
- push bp
- mov bp, sp
- sub esp, 2
- mov word [bp-2], 0
- push es
- push bx
- push dx
- mov bx, 3
- mul bx ;ax = ax * 3
- mov bx, 2
- div bx ;商在ax 余数在dx
- cmp dx, 0
- jz LABEL_EVEN ;偶数
- mov word [bp-2], 1 ;奇数
- LABEL_EVEN:
- mov bx, ax
- mov ax, BaseOfLoader
- sub ax, FatOffset
- mov es, ax
- mov word ax, [es:bx]
- cmp word [bp-2], 0
- jz LABEL_EVEN_2
- shr ax, 4
- LABEL_EVEN_2:
- and ax, 0FFFh ;低十二位
- pop dx
- pop bx
- pop es
- add esp, 2
- pop bp
- ret
最后给出整个的程序:
- ;*******************************************************************************80
- ;**boot.asm 软盘引导分区
- ;*******************************************************************************
- org 07c00h ;BIOS将把Boot Sector开机加载到
- ;地址0000:7c00 并执行
-
- ;==宏===========================================================================
- BaseOfStack equ 07c00h ;boot状态下的堆栈基地址(注意堆栈是向下
- ;生长的)
- ;Loader address
- BaseOfLoader equ 09000h ;TEST.TXT 被加载到的位置 ---- 段地址
- OffsetOfLoader equ 0100h ;TEST.TXT 被加载到的位置 ---- 偏移地
- ;址 (共63K的大小)
- ;FAT12
- SectorNoOfRootDirectory equ 19 ;Root Directory 的第一个扇区号=
- ;BPB_RsvdSecCnt + (BPB_NumFATs * FATSz)
- RootDirSectors equ 14 ; 根目录占用空间: RootDirSectors =
- ;((BPB_RootEntCnt * 32) + (BPB_BytsPerSec
- ; – 1)) / BPB_BytsPerSec; 但如果按照此公
- ;式代码过长
- FatOffset equ 120h ;FAT表缓冲偏移
- ;===============================================================================
- jmp short LABEL_START
- nop ;必不可少的
- ;**以下是FAT12的磁盘头的定义************
- BS_OEMName DB 'SongYang' ;OEM String, 必须 8 个字节
-
- BSB_BytePerSec DW 512 ;每扇区字节数
- BSP_SecPerClus Db 1 ;每簇多少扇区
- BPB_RsvdSecCnt DW 1 ;Boot 记录占用多少扇区
- BSP_NumFats DB 2 ;共有多少 FAT 表
- BSP_RootEntCnt DW 224 ;根目录文件数最大值
- BSP_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_Volab DB 'Tinix0.01 ' ;卷标, 必须 11 个字节
- BS_FileSysType DB 'FAT12 ' ;文件系统类型, 必须 8个字节
-
- LABEL_START:
- ;**初始化寄存器*************************
- mov ax, cs
- mov ds, ax
- mov es, ax
- mov ss, ax
- mov sp, BaseOfStack
- ;**清屏*********************************
- mov ax, 0600h ; AH = 6, AL = 0h
- mov bx, 0700h ; 黑底白字(BL = 07h)
- mov cx, 0 ; 左上角: (0, 0)
- mov dx, 0184fh ; 右下角: (80, 50)
- int 10h ; int 10h
- ;**软驱复位*****************************
- xor ah, ah ; ┓
- xor dl, dl ; ┣ 软驱复位
- int 13h ; ┛
- ;**在软盘中寻找Loader.bin(FAT12)********
- ;**将根目录整个的都读到es:bx处
- mov ax, BaseOfLoader
- mov es, ax ; es <- BaseOfLoader
- mov bx, OffsetOfLoader ; bx <- OffsetOfLoader 于是, es:bx =
-
- BaseOfLoader:OffsetOfLoader
- mov ax, SectorNoOfRootDirectory ; ax <- Root Directory 中的某 Sector 号
- mov cl, RootDirSectors ;将根目录都读出来
- call ReadSector
-
- ;mov ax, BaseOfLoader
- ;mov es, ax
- ;mov ax, cs
- ;mov ds, ax
-
- mov si, LoaderFileName ; ds:si -> "TEST TXT"
- mov di, OffsetOfLoader ; es:di -> BaseOfLoader:0100 =
-
- BaseOfLoader*10h+100
- cld ;设置方向标志
-
- mov cx, [BSP_RootEntCnt]
- mov [RootDirectItemNum], cx ;根目录的条数
-
- LABEL_SEARCH_FOR_LOADERBIN:
- cmp word [RootDirectItemNum], 0
- jz NOLOADER
- dec word [RootDirectItemNum]
-
- mov cx, 11
- LABEL_CMP_FILENAME:
- cmp cx, 0
- jz LABEL_FILENAME_FOUND ; 如果比较了 11 个字符都相等, 表示找到
- dec cx
- lodsb ; ds:si -> al
- cmp al, byte [es:di]
- jz LABEL_GO_ON
- jmp LABEL_DIFFERENT ; 只要发现不一样的字符就表明本 DirectoryEntry 不是
-
- LABEL_GO_ON:
- inc di
- jmp LABEL_CMP_FILENAME
- LABEL_DIFFERENT:
- and di, 0FFE0h
- add di, 020h
- and si, LoaderFileName
- jmp LABEL_SEARCH_FOR_LOADERBIN
- NOLOADER:
- mov dh, 0
- call DispStr
- jmp $
- LABEL_FILENAME_FOUND:
- and di, 0FFE0h
-
- mov eax, [es:di+0x1C]
- mov [FileLength], eax
-
- add di, 01Ah ; di -> 首 Sector(偏移)
-
- mov cx, word [es:di]
- push cx ;cx里面放的是Loader的第一个扇区号(数据区的从2开始)
- ;******************************
- ;**读取Fat表
- mov ax, BaseOfLoader
- sub ax, FatOffset ;分配5k内存(512*10/1024)
- mov es, ax
- mov bx, 0 ;将Fat读到es:bx
- mov ax, [BPB_RsvdSecCnt];Fat开始扇区 1
- mov cl, [BPB_FATSz16]
- call ReadSector
- ;**读取Loader到内存*******
-
- mov ax, BaseOfLoader
- mov es, ax
- mov bx, OffsetOfLoader
- pop ax
- LABEL_GOON_LOADING_FILE:
- push ax ;保存ax
- add ax, 17 + 14
- mov cl, 1 ;一个扇区
- call ReadSector
- ;
- pop ax ;回复ax
- call GetFATEntry;获得下个号码
-
- cmp ax, 0FFFh
- jz LABEL_FILE_LOADED
- add bx, [BSB_BytePerSec]
- jmp LABEL_GOON_LOADING_FILE
- LABEL_FILE_LOADED:
- ;打印文件内容
- mov ax, BaseOfLoader
- mov es, ax
- mov bp, OffsetOfLoader
-
- mov cx, [FileLength] ; CX = 串长度
- mov ax, 01301h ; AH = 13, AL = 01h
- mov bx, 0006h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
- mov dl, 0
- mov dh, 0
- int 10h ; int 10h
-
- jmp $
-
-
- ;============================================================================
- ;字符串
- ;----------------------------------------------------------------------------
- LoaderFileName db "TEST TXT", 0 ; LOADER.BIN 之文件名
- RootDirectItemNum DW 0
- FileLength DW 0
- ; 为简化代码, 下面每个字符串的长度均为 MessageLength
- MessageLength equ 9
- BootMessage: db "No FILE " ; 7字节
- ;============================================================================
-
- ;----------------------------------------------------------------------------
- ; 函数名: DispStr
- ;----------------------------------------------------------------------------
- ; 作用:
- ; 显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
- DispStr:
- push ax
- push bx
- push cx
- push dx
- push es
- mov ax, MessageLength
- mul dh
- add ax, BootMessage
- mov bp, ax ; ┓
- mov ax, ds ; ┣ ES:BP = 串地址
- mov es, ax ; ┛
- mov cx, MessageLength ; CX = 串长度
- mov ax, 01301h ; AH = 13, AL = 01h
- mov bx, 0006h ; 页号为0(BH = 0) 黑底白字(BL = 07h)
- mov dl, 0
- int 10h ; int 10h
- pop es
- pop dx
- pop cx
- pop bx
- pop ax
- ret
- ;----------------------------------------------------------------------------
- ; 函数名: ReadSector
- ;----------------------------------------------------------------------------
- ; 作用:
- ; 从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中
- ReadSector:
- ; -----------------------------------------------------------------------
- ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)
- ; -----------------------------------------------------------------------
- ; 设扇区号为 x
- ; ┌ 柱面号 = y >> 1
- ; x ┌ 商 y ┤
- ; -------------- => ┤ └ 磁头号 = y & 1
- ; 每磁道扇区数 │
- ; └ 余 z => 起始扇区号 = z + 1
- push bp
- mov bp, sp
- sub esp, 2 ; 辟出两个字节的堆栈区域保存要读的扇区数: byte
-
- [bp-2]
-
- mov byte [bp-2], cl
- push bx ; 保存 bx
- mov bl, [BPB_SecPerTrk] ; bl: 除数
- div bl ; y 在 al 中, z 在 ah 中
- inc ah ; z ++
- mov cl, ah ; cl <- 起始扇区号
- mov dh, al ; dh <- y
- shr al, 1 ; y >> 1 (其实是 y/BPB_NumHeads, 这里
-
- BPB_NumHeads=2)
- mov ch, al ; ch <- 柱面号
- and dh, 1 ; dh & 1 = 磁头号
- pop bx ; 恢复 bx
- ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^
- mov dl, [BS_DrvNum] ; 驱动器号 (0 表示 A 盘)
- .GoOnReading:
- mov ah, 2 ; 读
- mov al, byte [bp-2] ; 读 al 个扇区
- int 13h
- jc .GoOnReading ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到
-
- 正确为止
-
- add esp, 2
- pop bp
-
- ret
-
- ;----------------------------------------------------------------------------
- ; 函数名: GetFATEntry
- ;----------------------------------------------------------------------------
- ; 作用: ax
- ;
- GetFATEntry:
- push bp
- mov bp, sp
- sub esp, 2
- mov word [bp-2], 0
- push es
- push bx
- push dx
- mov bx, 3
- mul bx ;ax = ax * 3
- mov bx, 2
- div bx ;商在ax 余数在dx
- cmp dx, 0
- jz LABEL_EVEN ;偶数
- mov word [bp-2], 1 ;奇数
- LABEL_EVEN:
- mov bx, ax
- mov ax, BaseOfLoader
- sub ax, FatOffset
- mov es, ax
- mov word ax, [es:bx]
- cmp word [bp-2], 0
- jz LABEL_EVEN_2
- shr ax, 4
- LABEL_EVEN_2:
- and ax, 0FFFh ;低十二位
- pop dx
- pop bx
- pop es
- add esp, 2
- pop bp
- ret
- times 510-($-$$) db 0 ;填充剩余的空间,是生成的代码正好为512B
- dw 0xaa55 ;结束标记
编译命令:
- nasm -I .\include\ boot1.asm -o boot.bin
- WriteFlopy.exe
WriteFlopy.exe的功能是见生成的boot.bin文件写道虚拟软盘TINIX.IMG的引导扇区中,然后可以将该虚拟软盘挂载,向里面新建一个文件TEST.TXT然后写一些东西测试。
|