既然我们能够通过根目录条目找到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 $ ; 到此停住
运行效果:
相关推荐
自己动手写操作系统(于渊-含代码).rar
于渊的自己动手写操作系统真正高清版,原来csdn上有一个号称高清的版本,但是没有part1,太坑人了,后来自己花了好长时间才下到,不敢独享,特发出来与大家共同学习!
《自己动手写操作系统》是一本深受程序员喜爱的书籍,它以实践为导向,引导读者深入了解操作系统的工作原理,并通过编写操作系统来学习相关知识。源码的提供为读者提供了宝贵的实践机会,帮助他们将理论与实践相结合...
非常清晰 非常清晰 非常清晰 非常清晰 非常清晰 非常清晰!
书名:自己动手写操作系统 作者:于渊 编著 来源:电子工业出版社 出版时间:2005年08月 ISBN:7-121-01577-3 定价:48元 内容介绍: 本书在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用...
在《自己动手写操作系统第二版》中,于渊详细解释了这些概念并提供了实际的代码示例。通过分析和实践这些源代码,读者可以深入了解操作系统的内部运作机制,提升对计算机系统的理解,这对于想要深入学习操作系统原理...
书名:自己动手写操作系统 作者:于渊 编著 来源:电子工业出版社 出版时间:2005年08月 ISBN:7-121-01577-3 定价:48元 内容介绍: 本书在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用...
《orange's一个操作系统的实现-【自己动手写操作系统第二版】》是一本深入浅出的操作系统构建指南,作者为于渊。这本书旨在帮助读者通过实际操作来理解操作系统的原理和构造,适合对操作系统感兴趣的DIY爱好者和...
书名:自己动手写操作系统 作者:于渊 编著 来源:电子工业出版社 出版时间:2005年08月 ISBN:7-121-01577-3 定价:48元 内容介绍: 本书在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用...
书名:自己动手写操作系统 作者:于渊 编著 来源:电子工业出版社 出版时间:2005年08月 ISBN:7-121-01577-3 定价:48元 内容介绍: 本书在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用...
自己动手写操作系统 于渊 随书光盘。 优秀国内原创图书!本书用丰富的实例代码,一步一步地指导读者用C语言和汇编语言编写出一个具备操作系统基本功能的操作系统框架,不同于其他的理论型书籍,本书提供给读者一个...
《于渊_自己动手写操作系统原代码》可能是这样一本教程或书籍,旨在引导读者深入理解操作系统的原理,通过编写操作系统源码来实现这一目标。在这样的学习过程中,读者可以了解操作系统的主要组成部分和工作流程。 1...
书名:自己动手写操作系统 作者:于渊 编著 来源:电子工业出版社 出版时间:2005年08月 ISBN:7-121-01577-3 定价:48元 内容介绍: 本书在详细分析操作系统原理的基础上,用丰富的实例代码,一步一步地指导读者用...
自己动手写操作系统 于渊 无水印 影印版
在本文中,我们将深入探讨“自己动手写操作系统”的主题,这通常被看作是理解和学习操作系统原理的一种实践方法。这一项目由作者于渊发起,旨在帮助读者通过实际操作来理解操作系统的构建过程。 首先,我们要明白...
自己动手写操作系统 于渊 第一版 影印版
《自己动手写操作系统》是于渊先生撰写的一本经典教程,旨在帮助读者深入理解操作系统的原理和实现过程。这本书不仅包含了理论知识,还提供了实际的源代码和开发工具,使得学习更加直观和实践性强。 首先,我们要...
《自己动手编写操作系统》这本书由于渊所著,旨在帮助读者深入理解操作系统的原理,并通过实际编写代码来构建一个基本的操作系统框架。下面将详细探讨书中涉及的知识点。 首先,操作系统的基本概念和组成是学习的...
2. **环境设置**:在动手写操作系统之前,需要搭建开发环境,这可能包括选择合适的编程语言(如汇编语言或C语言),设置交叉编译工具链,以及理解虚拟机或模拟器的使用。 3. **引导加载器**:操作系统的启动是从...
于渊写的自己动手写操作系统,一本操作系统入门学习不错的书