分享

FAT12文件格式

 rookie 2012-08-02

F AT12是DOS时代就开始使用的文件系统(File System),直到现在仍然在软盘上使用。几乎所有的文件系统都会把磁盘划分为若干层次以方便管理和组织,这些层次主要包括:

  • 扇区(Sector):磁盘上的最小数据单元
  • 簇(Cluster):一个或多个扇区
  • 分区(Partition):通常是指整个文件系统

下面是FAT12格式的软盘存储图:

 

 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可以识别的引导盘

Nasm代码  收藏代码
  1. ;===============================================================================  
  2. jmp short LABEL_START  
  3. nop                 ;必不可少的  
  4. ;**以下是FAT12的磁盘头的定义************  
  5. BS_OEMName  DB 'SongYang'       ;OEM String, 必须 8 个字节  
  6.   
  7. BSB_BytePerSec  DW 512          ;每扇区字节数  
  8. BSP_SecPerClus  Db 1            ;每簇多少扇区  
  9. BPB_RsvdSecCnt  DW 1            ;Boot 记录占用多少扇区  
  10. BSP_NumFats DB 2            ;共有多少 FAT 表  
  11. BSP_RootEntCnt  DW 224          ;根目录文件数最大值  
  12. BSP_TotSec16    DW 2880         ;逻辑扇区总数  
  13. BPB_Media   DB 0xF0         ;媒体描述符  
  14. BPB_FATSz16 DW 9            ;每FAT扇区数  
  15. BPB_SecPerTrk   DW 18           ;每磁道扇区数  
  16. BPB_NumHeads    DW 2            ;磁头数(面数)  
  17. BPB_HiddSec DD 0            ;隐藏扇区数  
  18. BPB_TotSec32    DD 0            ;如果 wTotalSectorCount 是0由这个值记录  
  19.                     ;扇区数  
  20.   
  21. BS_DrvNum   DB 0            ;中断 13 的驱动器号  
  22. BS_Reserved1    DB 0            ;未使用  
  23. BS_BootSig  DB 29h          ;扩展引导标记 (29h)  
  24. BS_VolID    DD 0            ;卷序列号  
  25. BS_Volab    DB 'Tinix0.01  '    ;卷标, 必须 11 个字节  
  26. BS_FileSysType  DB 'FAT12   '       ;文件系统类型, 必须 8个字节  
  27.   
  28. 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

代码如下:

  

Nasm代码  收藏代码
  1. ;----------------------------------------------------------------------------  
  2. ; 函数名: ReadSector  
  3. ;----------------------------------------------------------------------------  
  4. ; 作用:  
  5. ;   从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中  
  6. ReadSector:  
  7.     ; -----------------------------------------------------------------------  
  8.     ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)  
  9.     ; -----------------------------------------------------------------------  
  10.     ; 设扇区号为 x  
  11.     ;                           ┌ 柱面号 = y >> 1  
  12.     ;       x           ┌ 商 y ┤  
  13.     ; -------------- => ┤      └ 磁头号 = y & 1  
  14.     ;  每磁道扇区数     │  
  15.     ;                   └ 余 z => 起始扇区号 = z + 1  
  16.     push    bp  
  17.     mov bp, sp  
  18.     sub esp, 2          ; 辟出两个字节的堆栈区域保存要读的扇区数: byte   
  19.   
  20. [bp-2]  
  21.   
  22.     mov byte [bp-2], cl  
  23.     push    bx          ; 保存 bx  
  24.     mov bl, [BPB_SecPerTrk] ; bl: 除数  
  25.     div bl          ; y 在 al 中, z 在 ah 中  
  26.     inc ah          ; z ++  
  27.     mov cl, ah          ; cl <- 起始扇区号  
  28.     mov dh, al          ; dh <- y  
  29.     shr al, 1           ; y >> 1 (其实是 y/BPB_NumHeads, 这里  
  30.   
  31. BPB_NumHeads=2)  
  32.     mov ch, al          ; ch <- 柱面号  
  33.     and dh, 1           ; dh & 1 = 磁头号  
  34.     pop bx          ; 恢复 bx  
  35.     ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^  
  36.     mov dl, [BS_DrvNum]     ; 驱动器号 (0 表示 A 盘)  
  37. .GoOnReading:  
  38.     mov ah, 2           ; 读  
  39.     mov al, byte [bp-2]     ; 读 al 个扇区  
  40.     int 13h  
  41.     jc  .GoOnReading        ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到  
  42.   
  43. 正确为止  
  44.   
  45.     add esp, 2  
  46.     pop bp  
  47.   
  48.     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.获得文件的第一个簇号(这里也是区号,二则相等)

   要想获某个文件的第一个簇号 需要在根目录中查找对应的目录项(这里假设文件都放在了根目录中个),步骤如下:

  • 将整个根目录区域加载进内存,(根目录不是很大,相对现在的大内存),直接加载进内存方便操作。
Nasm代码  收藏代码
  1.     ;**将根目录整个的都读到es:bx处  
  2.     mov ax, BaseOfLoader  
  3.     mov es, ax          ; es <- BaseOfLoader  
  4.     mov bx, OffsetOfLoader  ; bx <- OffsetOfLoader   于是, es:bx =   
  5.   
  6. BaseOfLoader:OffsetOfLoader  
  7.     mov ax, SectorNoOfRootDirectory ; ax <- Root Directory 中的某 Sector 号  
  8.     mov cl, RootDirSectors      ;将根目录都读出来  
  9.     call    ReadSector  

  

  •  在根目录中逐条查找直到结束,代码如下: 

 

    这里得到的簇号就是区号SecNo,因为这里一个簇里面只有一个扇区,但是数据区的第一个簇号是从2开始的,要计算其对应的扇区号应按下面的公式计算:

SecNo = SecNo + (1+9*2 +RootDirSectors -2 -1) 

其1个引导扇区,2个FAT表,0个隐藏扇区,RootDirSectors个根目录,由于第一个簇号事2故要减去2,编号是从0开始的故要减去个1。

 

 

Nasm代码  收藏代码
  1.     mov si, LoaderFileName  ; ds:si -> "TEST    TXT"  
  2.     mov di, OffsetOfLoader  ; es:di -> BaseOfLoader:0100 =   
  3.   
  4. BaseOfLoader*10h+100  
  5.     cld             ;设置方向标志  
  6.       
  7.     mov cx, [BSP_RootEntCnt]  
  8.     mov [RootDirectItemNum], cx ;根目录的条数  
  9.   
  10. LABEL_SEARCH_FOR_LOADERBIN:  
  11.     cmp word [RootDirectItemNum], 0  
  12.     jz NOLOADER  
  13.     dec word [RootDirectItemNum]  
  14.       
  15.     mov cx, 11  
  16. LABEL_CMP_FILENAME:  
  17.     cmp cx, 0  
  18.     jz  LABEL_FILENAME_FOUND    ; 如果比较了 11 个字符都相等, 表示找到  
  19.     dec cx  
  20.     lodsb               ; ds:si -> al  
  21.     cmp al, byte [es:di]  
  22.     jz  LABEL_GO_ON  
  23.     jmp LABEL_DIFFERENT     ; 只要发现不一样的字符就表明本 DirectoryEntry 不是  
  24.   
  25. LABEL_GO_ON:  
  26.     inc di  
  27.     jmp LABEL_CMP_FILENAME  
  28. LABEL_DIFFERENT:  
  29.     and di, 0FFE0h  
  30.     add di, 020h  
  31.     and si, LoaderFileName  
  32.     jmp LABEL_SEARCH_FOR_LOADERBIN  
  33. NOLOADER:  
  34.     mov dh, 0  
  35.     call DispStr  
  36.     jmp $  
  37. LABEL_FILENAME_FOUND:  
  38.     and di, 0FFE0h  
  39.       
  40.     mov eax, [es:di+0x1C]  
  41.     mov [FileLength], eax  
  42.   
  43.     add di, 01Ah        ; di -> 首 Sector(偏移)   
  44.       
  45.         mov cx, word [es:di]  
  46.     push cx         ;cx里面放的是Loader的第一个扇区号(数据区的从2开始)  

 

 

 3.FAT表中的下一个簇号

  同样的道理先将整个FAT表读到内存中:

 

Nasm代码  收藏代码
  1. ;**读取Fat表   
  2.     mov ax, BaseOfLoader  
  3.     sub ax, FatOffset   ;分配5k内存(512*10/1024)  
  4.     mov es, ax  
  5.     mov bx, 0       ;将Fat读到es:bx  
  6.     mov ax, [BPB_RsvdSecCnt];Fat开始扇区 1  
  7.     mov cl, [BPB_FATSz16]  
  8.     call ReadSector  

 然后查找下一个簇号,由于可能多次调用写成了一个函数GetFATEntry():

  参数ax:簇号

  返回值ax:下一个簇号,需要注意簇号大于等于0xFF8表示文件的最后一个簇,为0xFF7表示坏簇。

Nasm代码  收藏代码
  1. ;----------------------------------------------------------------------------  
  2. ; 函数名: GetFATEntry  
  3. ;----------------------------------------------------------------------------  
  4. ; 作用: ax  
  5. ;     
  6. GetFATEntry:  
  7.     push bp  
  8.     mov bp, sp  
  9.     sub esp, 2  
  10.     mov word [bp-2], 0  
  11.     push es   
  12.     push bx  
  13.     push dx  
  14.     mov bx, 3  
  15.     mul bx              ;ax = ax * 3  
  16.     mov bx, 2  
  17.     div bx              ;商在ax 余数在dx  
  18.     cmp dx, 0  
  19.     jz LABEL_EVEN           ;偶数  
  20.     mov word [bp-2], 1      ;奇数  
  21. LABEL_EVEN:  
  22.     mov bx, ax  
  23.     mov ax, BaseOfLoader  
  24.     sub ax, FatOffset  
  25.     mov es, ax  
  26.     mov word ax, [es:bx]  
  27.     cmp word [bp-2], 0  
  28.     jz LABEL_EVEN_2  
  29.     shr ax, 4  
  30. LABEL_EVEN_2:  
  31.     and ax, 0FFFh           ;低十二位  
  32.     pop dx  
  33.     pop bx  
  34.     pop es  
  35.     add esp, 2  
  36.     pop bp  
  37.     ret  

 最后给出整个的程序:

 

Nasm代码  收藏代码
  1. ;*******************************************************************************80  
  2. ;**boot.asm 软盘引导分区  
  3. ;*******************************************************************************  
  4. org 07c00h              ;BIOS将把Boot Sector开机加载到   
  5.                     ;地址0000:7c00 并执行  
  6.   
  7. ;==宏===========================================================================  
  8. BaseOfStack equ 07c00h      ;boot状态下的堆栈基地址(注意堆栈是向下  
  9.                     ;生长的)  
  10. ;Loader address  
  11. BaseOfLoader        equ  09000h ;TEST.TXT 被加载到的位置 ----  段地址  
  12. OffsetOfLoader      equ   0100h ;TEST.TXT 被加载到的位置 ---- 偏移地  
  13.                     ;址 (共63K的大小)  
  14. ;FAT12  
  15. SectorNoOfRootDirectory equ 19  ;Root Directory 的第一个扇区号=   
  16.                     ;BPB_RsvdSecCnt + (BPB_NumFATs * FATSz)  
  17. RootDirSectors      equ 14  ; 根目录占用空间: RootDirSectors =   
  18.                     ;((BPB_RootEntCnt * 32) + (BPB_BytsPerSec  
  19.                     ; – 1)) / BPB_BytsPerSec; 但如果按照此公  
  20.                     ;式代码过长  
  21. FatOffset       equ     120h    ;FAT表缓冲偏移  
  22. ;===============================================================================  
  23. jmp short LABEL_START  
  24. nop                 ;必不可少的  
  25. ;**以下是FAT12的磁盘头的定义************  
  26. BS_OEMName  DB 'SongYang'       ;OEM String, 必须 8 个字节  
  27.   
  28. BSB_BytePerSec  DW 512          ;每扇区字节数  
  29. BSP_SecPerClus  Db 1            ;每簇多少扇区  
  30. BPB_RsvdSecCnt  DW 1            ;Boot 记录占用多少扇区  
  31. BSP_NumFats DB 2            ;共有多少 FAT 表  
  32. BSP_RootEntCnt  DW 224          ;根目录文件数最大值  
  33. BSP_TotSec16    DW 2880         ;逻辑扇区总数  
  34. BPB_Media   DB 0xF0         ;媒体描述符  
  35. BPB_FATSz16 DW 9            ;每FAT扇区数  
  36. BPB_SecPerTrk   DW 18           ;每磁道扇区数  
  37. BPB_NumHeads    DW 2            ;磁头数(面数)  
  38. BPB_HiddSec DD 0            ;隐藏扇区数  
  39. BPB_TotSec32    DD 0            ;如果 wTotalSectorCount 是0由这个值记录  
  40.                     ;扇区数  
  41.   
  42. BS_DrvNum   DB 0            ;中断 13 的驱动器号  
  43. BS_Reserved1    DB 0            ;未使用  
  44. BS_BootSig  DB 29h          ;扩展引导标记 (29h)  
  45. BS_VolID    DD 0            ;卷序列号  
  46. BS_Volab    DB 'Tinix0.01  '    ;卷标, 必须 11 个字节  
  47. BS_FileSysType  DB 'FAT12   '       ;文件系统类型, 必须 8个字节  
  48.   
  49. LABEL_START:  
  50. ;**初始化寄存器*************************  
  51.     mov ax, cs  
  52.     mov ds, ax  
  53.     mov es, ax  
  54.     mov ss, ax  
  55.     mov sp, BaseOfStack  
  56. ;**清屏*********************************  
  57.     mov ax, 0600h       ; AH = 6,  AL = 0h  
  58.     mov bx, 0700h       ; 黑底白字(BL = 07h)  
  59.     mov cx, 0           ; 左上角: (00)  
  60.     mov dx, 0184fh      ; 右下角: (8050)  
  61.     int 10h         ; int 10h  
  62. ;**软驱复位*****************************  
  63.     xor ah, ah  ; ┓  
  64.     xor dl, dl  ; ┣ 软驱复位  
  65.     int 13h     ; ┛  
  66. ;**在软盘中寻找Loader.bin(FAT12)********  
  67.     ;**将根目录整个的都读到es:bx处  
  68.     mov ax, BaseOfLoader  
  69.     mov es, ax          ; es <- BaseOfLoader  
  70.     mov bx, OffsetOfLoader  ; bx <- OffsetOfLoader   于是, es:bx =   
  71.   
  72. BaseOfLoader:OffsetOfLoader  
  73.     mov ax, SectorNoOfRootDirectory ; ax <- Root Directory 中的某 Sector 号  
  74.     mov cl, RootDirSectors      ;将根目录都读出来  
  75.     call    ReadSector  
  76.       
  77.     ;mov    ax, BaseOfLoader  
  78.     ;mov    es, ax  
  79.     ;mov    ax, cs  
  80.     ;mov    ds, ax  
  81.   
  82.     mov si, LoaderFileName  ; ds:si -> "TEST    TXT"  
  83.     mov di, OffsetOfLoader  ; es:di -> BaseOfLoader:0100 =   
  84.   
  85. BaseOfLoader*10h+100  
  86.     cld             ;设置方向标志  
  87.       
  88.     mov cx, [BSP_RootEntCnt]  
  89.     mov [RootDirectItemNum], cx ;根目录的条数  
  90.   
  91. LABEL_SEARCH_FOR_LOADERBIN:  
  92.     cmp word [RootDirectItemNum], 0  
  93.     jz NOLOADER  
  94.     dec word [RootDirectItemNum]  
  95.       
  96.     mov cx, 11  
  97. LABEL_CMP_FILENAME:  
  98.     cmp cx, 0  
  99.     jz  LABEL_FILENAME_FOUND    ; 如果比较了 11 个字符都相等, 表示找到  
  100.     dec cx  
  101.     lodsb               ; ds:si -> al  
  102.     cmp al, byte [es:di]  
  103.     jz  LABEL_GO_ON  
  104.     jmp LABEL_DIFFERENT     ; 只要发现不一样的字符就表明本 DirectoryEntry 不是  
  105.   
  106. LABEL_GO_ON:  
  107.     inc di  
  108.     jmp LABEL_CMP_FILENAME  
  109. LABEL_DIFFERENT:  
  110.     and di, 0FFE0h  
  111.     add di, 020h  
  112.     and si, LoaderFileName  
  113.     jmp LABEL_SEARCH_FOR_LOADERBIN  
  114. NOLOADER:  
  115.     mov dh, 0  
  116.     call DispStr  
  117.     jmp $  
  118. LABEL_FILENAME_FOUND:  
  119.     and di, 0FFE0h  
  120.       
  121.     mov eax, [es:di+0x1C]  
  122.     mov [FileLength], eax  
  123.   
  124.     add di, 01Ah        ; di -> 首 Sector(偏移)   
  125.       
  126.         mov cx, word [es:di]  
  127.     push cx         ;cx里面放的是Loader的第一个扇区号(数据区的从2开始)  
  128. ;******************************  
  129. ;**读取Fat表   
  130.     mov ax, BaseOfLoader  
  131.     sub ax, FatOffset   ;分配5k内存(512*10/1024)  
  132.     mov es, ax  
  133.     mov bx, 0       ;将Fat读到es:bx  
  134.     mov ax, [BPB_RsvdSecCnt];Fat开始扇区 1  
  135.     mov cl, [BPB_FATSz16]  
  136.     call ReadSector  
  137. ;**读取Loader到内存*******  
  138.       
  139.     mov ax, BaseOfLoader  
  140.     mov es, ax  
  141.     mov bx, OffsetOfLoader  
  142.     pop ax  
  143. LABEL_GOON_LOADING_FILE:  
  144.     push    ax      ;保存ax  
  145.     add     ax, 17 + 14  
  146.     mov     cl, 1   ;一个扇区  
  147.     call    ReadSector  
  148.     ;  
  149.     pop     ax      ;回复ax  
  150.     call    GetFATEntry;获得下个号码    
  151.       
  152.     cmp     ax, 0FFFh  
  153.     jz  LABEL_FILE_LOADED  
  154.     add     bx, [BSB_BytePerSec]  
  155.     jmp     LABEL_GOON_LOADING_FILE  
  156. LABEL_FILE_LOADED:  
  157.     ;打印文件内容  
  158.     mov ax, BaseOfLoader  
  159.     mov es, ax  
  160.     mov bp, OffsetOfLoader  
  161.   
  162.     mov cx, [FileLength]    ; CX = 串长度  
  163.     mov ax, 01301h      ; AH = 13,  AL = 01h  
  164.     mov bx, 0006h       ; 页号为0(BH = 0) 黑底白字(BL = 07h)  
  165.     mov dl, 0  
  166.     mov     dh, 0  
  167.     int 10h         ; int 10h  
  168.   
  169.     jmp $  
  170.   
  171.   
  172. ;============================================================================  
  173. ;字符串  
  174. ;----------------------------------------------------------------------------  
  175. LoaderFileName      db  "TEST    TXT"0    ; LOADER.BIN 之文件名  
  176. RootDirectItemNum   DW  0  
  177. FileLength      DW  0  
  178. ; 为简化代码, 下面每个字符串的长度均为 MessageLength  
  179. MessageLength       equ 9  
  180. BootMessage:        db "No FILE  "      ; 7字节  
  181. ;============================================================================  
  182.   
  183. ;----------------------------------------------------------------------------  
  184. ; 函数名: DispStr  
  185. ;----------------------------------------------------------------------------  
  186. ; 作用:  
  187. ;   显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)  
  188. DispStr:  
  189.     push ax  
  190.     push bx  
  191.     push cx  
  192.     push dx  
  193.     push es  
  194.     mov ax, MessageLength  
  195.     mul dh  
  196.     add ax, BootMessage  
  197.     mov bp, ax          ; ┓  
  198.     mov ax, ds          ; ┣ ES:BP = 串地址  
  199.     mov es, ax          ; ┛  
  200.     mov cx, MessageLength   ; CX = 串长度  
  201.     mov ax, 01301h      ; AH = 13,  AL = 01h  
  202.     mov bx, 0006h       ; 页号为0(BH = 0) 黑底白字(BL = 07h)  
  203.     mov dl, 0  
  204.     int 10h         ; int 10h  
  205.     pop es  
  206.     pop dx  
  207.     pop cx  
  208.     pop bx  
  209.     pop ax  
  210.     ret  
  211. ;----------------------------------------------------------------------------  
  212. ; 函数名: ReadSector  
  213. ;----------------------------------------------------------------------------  
  214. ; 作用:  
  215. ;   从第 ax 个 Sector 开始, 将 cl 个 Sector 读入 es:bx 中  
  216. ReadSector:  
  217.     ; -----------------------------------------------------------------------  
  218.     ; 怎样由扇区号求扇区在磁盘中的位置 (扇区号 -> 柱面号, 起始扇区, 磁头号)  
  219.     ; -----------------------------------------------------------------------  
  220.     ; 设扇区号为 x  
  221.     ;                           ┌ 柱面号 = y >> 1  
  222.     ;       x           ┌ 商 y ┤  
  223.     ; -------------- => ┤      └ 磁头号 = y & 1  
  224.     ;  每磁道扇区数     │  
  225.     ;                   └ 余 z => 起始扇区号 = z + 1  
  226.     push    bp  
  227.     mov bp, sp  
  228.     sub esp, 2          ; 辟出两个字节的堆栈区域保存要读的扇区数: byte   
  229.   
  230. [bp-2]  
  231.   
  232.     mov byte [bp-2], cl  
  233.     push    bx          ; 保存 bx  
  234.     mov bl, [BPB_SecPerTrk] ; bl: 除数  
  235.     div bl          ; y 在 al 中, z 在 ah 中  
  236.     inc ah          ; z ++  
  237.     mov cl, ah          ; cl <- 起始扇区号  
  238.     mov dh, al          ; dh <- y  
  239.     shr al, 1           ; y >> 1 (其实是 y/BPB_NumHeads, 这里  
  240.   
  241. BPB_NumHeads=2)  
  242.     mov ch, al          ; ch <- 柱面号  
  243.     and dh, 1           ; dh & 1 = 磁头号  
  244.     pop bx          ; 恢复 bx  
  245.     ; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 ^^^^^^^^^^^^^^^^^^^^^^^^  
  246.     mov dl, [BS_DrvNum]     ; 驱动器号 (0 表示 A 盘)  
  247. .GoOnReading:  
  248.     mov ah, 2           ; 读  
  249.     mov al, byte [bp-2]     ; 读 al 个扇区  
  250.     int 13h  
  251.     jc  .GoOnReading        ; 如果读取错误 CF 会被置为 1, 这时就不停地读, 直到  
  252.   
  253. 正确为止  
  254.   
  255.     add esp, 2  
  256.     pop bp  
  257.   
  258.     ret  
  259.       
  260. ;----------------------------------------------------------------------------  
  261. ; 函数名: GetFATEntry  
  262. ;----------------------------------------------------------------------------  
  263. ; 作用: ax  
  264. ;     
  265. GetFATEntry:  
  266.     push bp  
  267.     mov bp, sp  
  268.     sub esp, 2  
  269.     mov word [bp-2], 0  
  270.     push es   
  271.     push bx  
  272.     push dx  
  273.     mov bx, 3  
  274.     mul bx              ;ax = ax * 3  
  275.     mov bx, 2  
  276.     div bx              ;商在ax 余数在dx  
  277.     cmp dx, 0  
  278.     jz LABEL_EVEN           ;偶数  
  279.     mov word [bp-2], 1      ;奇数  
  280. LABEL_EVEN:  
  281.     mov bx, ax  
  282.     mov ax, BaseOfLoader  
  283.     sub ax, FatOffset  
  284.     mov es, ax  
  285.     mov word ax, [es:bx]  
  286.     cmp word [bp-2], 0  
  287.     jz LABEL_EVEN_2  
  288.     shr ax, 4  
  289. LABEL_EVEN_2:  
  290.     and ax, 0FFFh           ;低十二位  
  291.     pop dx  
  292.     pop bx  
  293.     pop es  
  294.     add esp, 2  
  295.     pop bp  
  296.     ret  
  297. times 510-($-$$) db 0           ;填充剩余的空间,是生成的代码正好为512B  
  298. dw  0xaa55              ;结束标记     

 编译命令:

Java代码  收藏代码
  1. nasm -I .\include\ boot1.asm -o boot.bin  
  2. WriteFlopy.exe  

 WriteFlopy.exe的功能是见生成的boot.bin文件写道虚拟软盘TINIX.IMG的引导扇区中,然后可以将该虚拟软盘挂载,向里面新建一个文件TEST.TXT然后写一些东西测试。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多