于渊-动手写操作系统2

既然我们能够通过根目录条目找到DIR_FstClus了,这个字段告诉了我们文件开始的簇号,它告诉我们文件存放在磁盘的什么位置,从而让我们可以找到它。其实准确来说,这里应该是它告诉了我们文件存放在磁盘数据区的什么位置。需要注意的是,数据区的第一个簇的簇号是2,而不是0或者1.也就是说,如果我们在根目录条目中发现了一个文件,该文件的开始簇号是2,那么就是说,该文件的数据开始于数据区的第一个簇。

那么既然我们可以通过根目录区找到改文件它在磁盘的位置,那么我们还要FAT做什么,原因是对于小于512字节的文件来说,FAT表用处并不大,但如果大于512字节,那么我们就需要依靠FAT表来找到所有的簇,即找到该文件分布在磁盘中的扇区。

FAT表是什么?它有点像一个位图,其中,每12位称为一个FAT项(FATEntry),代表一个簇。第0个和第1个FAT项始终不使用,从第2个FAT项开始表示数据区的每一个簇,也就是说,第2个FAT项表示数据区的第一个簇,这与数据区的第一个簇的簇号为2相呼应的。

由于每个FAT项占12位,包含一个字节和另一个字节的一半,所以显得有点别扭,假设连续3个字节分别如下,那么灰色框表示的是前一个FAT项(FATEntry1),BYTE1是FATEntry的低8位,BYTE2是FATEntry的高4位,白色框表示的是后一个FAT项(FATEntry2),BYTE2的高4位是FATEntry2的低4位,BYTE3是FATEntry2的高8位。



 

那么FAT项的值代表的是什么呢?因为我们上面说过,我们要依靠FAT表来找到这个文件分布的位置,那么很容易想到FAT项的值就是代表文件的一个簇号,有点类似链表的感觉,如果FAT项的值大于0xFF8,则表示当前簇号已经是文件的最后一个簇了,如果值是0xFF7,表示它是一个坏簇。

那么我们把之前的boot.asm的LABEL_FILENAME_FOUND:jmp做下面的修改

;; 既然发现有LOADER.BIN了,那我们就需要将它加载到内存来了
LABEL_FILENAME_FOUND:
	mov	ax, RootDirSectors ;ax=14个扇区
	and	di, 0FFE0h	  ;di -> 当前条目的开始
	add	di, 01Ah	  ;di -> 首Sector根目录条目的前26个字节放着开始的簇号
	mov	cx, word [es:di]
	push	cx		;保存此Sector在FAT中的序号
	add	cx, ax
	add	cx, DeltaSectorNo ;这句完成时cl里面编程LOADER.BIN的其实扇区号(从0开始数的序号)
	mov	ax, BaseOfLoader  ;es -> BaseOfLoader
	mov	es, ax
	mov	bx, OffsetOfLoader ;bx -> OffsetOfLoader 于是,es:bx=BaseOfLoader:OffsetOfLoader=BaseOfLoader*10h+OffsetOfLoader
	mov	ax, cx		   ;ax <- Sector号

;; 将LOADER.BIN的内容从磁盘拷贝到内存BaseOfLoader:OffsetOfLoader的位置
LABEL_GOON_LOADING_FILE:
	push	ax
	push	bx
	mov	ah, 0Eh
	mov	bl, 0Fh
	int	10h
	pop	bx
	pop	ax

	mov	cl, 1
	call	ReadSector
	pop	ax		;取出此Sector在FAT中的序号,这里存放的是cx的值
	call	GetFATEntry
	cmp	ax, 0FFFh
	jz	LABEL_FILE_LOADED
	push	ax		;保存Sector在FAT中的序号
	mov	dx, RootDirSectors
	add	ax, dx
	add	ax, DeltaSectorNo
	add	bx, [BPB_BytsPerSec] ;往后加上512个字节
	jmp	LABEL_GOON_LOADING_FILE

我们的任务就是取出每个在FAT表中的簇号,然后转换为实际的扇区号,然后把实际扇区号中的内容load到内存进来。 

GetFATEntry的实现:

;----------------------------------------------------------------------------

;----------------------------------------------------------------------------
; 函数名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;	找到序号为 ax 的 Sector 在 FAT 中的条目, 结果放在 ax 中
;	需要注意的是, 中间需要读 FAT 的扇区到 es:bx 处, 所以函数一开始保存了 es 和 bx
;       以上部分是整个函数的难点所在,用户计算簇号在FAT表中所对应的FATENTRY相对于FAT首地址的偏移。
;       从书上可以得知,FAT12中每个FATENTRY是12位的。所以如下:
;       7654 | 3210(byte1)    7654|3210(byte2)    7654|3210(byte3)
;       byte1和byte2的低4位表示一个Entry;根据Big-Endian,Entry内容为:3210(byte2)76543210(byte1)
;       byte3和byte2的高4位表示一个Entry;根据Big-Endian,Entry内容为:76543210(byte3)7654(byte2)
;       所以这里存在一个奇偶数的问题,以字节为单位。以上为例,Entry0偏移为0,Entry1偏移为1,Entry2偏移为3。
;       以INT[“簇号”*1.5]的方式增加。这也就是为什么上面先乘3再除2来计算。
;       根据DIV指令规定,商保存在ax中,余数在dx中。所以此时ax就是FATENTRY在FAT中以字节为边界的偏移量。
GetFATEntry:
	push	es		;BaseOfLoader
	push	bx		;OffsetOfLoader
	push	ax		;存进此Sector在FAT中的序号
	mov	ax, BaseOfLoader
	sub	ax, 0100h	;在BaseOfLoader后面留出4K空间用于存放FAT
	mov	es, ax
	pop	ax
	mov	byte [bOdd], 0
	mov	bx, 3
	mul	bx		;dx:ax = ax * 2
	mov	bx, 2
	div	bx		;dx:ax / 2 ==> ax <- 商,dx <- 余数
	cmp	dx, 0
	jz	LABEL_EVEN
	mov	byte [bOdd], 1

;; 偶数处理
LABEL_EVEN:
	xor	dx, dx		;现在ax中是FATEntry在FAT中的偏移量,下面来计算FATEntry在哪个扇区中(FAT占用不止一个扇区)
	mov	bx, [BPB_BytsPerSec] ; dx:ax / BPB_BytsPerSec==>ax<-商(FATEntry 所在的扇区相对于 FAT 来说的扇区号)
	div	bx
	push	dx
	mov	bx, 0		;bx <- 0,于是es:bx=(BaseOfloader-100):00=(BaseOfloader-100)*10h
	add	ax, SectorNoOfFAT1 ;此句执行之后的ax就是FATEntry所在的扇区号
	mov	cl, 2
	call	ReadSector	;读取FATEntry所在的扇区,一次读两个,避免在边界发生错误,因为一个FATEntry可能跨越两个扇区
	pop	dx
	add	bx, dx
	mov	ax, [es:bx]
	cmp	byte [bOdd], 1
	jnz	LABEL_EVEN_2
	shr	ax, 4		;ax的值在此被更新

;7654 | 3210(byte1)    7654|3210(byte2)    7654|3210(byte3)
;根据上面这个例子,奇数的簇号,1,偏移为1,所以读要ax中的时候,因为ax是16位,所以根据Big-Endian,ax的内容为7654|3210(byte3)|7654|3210(byte2),所以右移4位就可以了。
;偶数簇号,0,偏移为0,读入ax后,ax的内容为7654|3210(byte2)7654 | 3210(byte1),我们只需要低12位即可,
;所以and ax,0FFFh
LABEL_EVEN_2:
	and	ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:
	pop	bx
	pop	es
	ret

最后再来一个跳转:

; *****************************************************************************************************
	jmp	BaseOfLoader:OffsetOfLoader	; 这一句正式跳转到已加载到内存中的 LOADER.BIN 的开始处
						; 开始执行 LOADER.BIN 的代码
						; Boot Sector 的使命到此结束
; *****************************************************************************************************

 

 loader.asm内容如下:

org	0100h

	mov	ax, 0B800h
	mov	gs, ax
	mov	ah, 0Fh				; 0000: 黑底    1111: 白字
	mov	al, 'L'
	mov	[gs:((80 * 0 + 39) * 2)], ax	; 屏幕第 0 行, 第 39 列

	jmp	$				; 到此停住

 运行效果:




 

猜你喜欢

转载自jjchen-lian201205235512.iteye.com/blog/1924398