FAT12文件格式

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)的大小。

FAT12文件格式

 

     要注意的是,由于每个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然后写一些东西测试。

 

 

你可能感兴趣的:(dos,单元测试,F#,Go)