自己动手写操作系统系列之----------加载Loader
本帖最后由 冰琥珀 于 2015-10-25 03:01 编辑上一个帖子:http://www.ihonker.org/thread-6657-1-1.html
上个帖子我们编写了引导扇区的代码,也说过引导扇区就是为了加载Loader的,但我们只写到在根目录中找到Loader的目录项,并没有把Loader加载到内存中,这次我们来完成把Loader加载到内存的工作。在此之前,我们先写个简单的Loader,代码如下
org 0x100
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
call DispMsg
jmp $
DispMsg:
mov ax, Message
mov bp, ax
mov cx, 0x0a
mov dx, 0x1500
mov bx, 0x000c
mov ax, 0x1301
int 0x10
ret
Message: db "In the Loader.bin"
这段代码很简单,就是显示一串字符串,然后程序就停在那,具体的Loader里面该干些啥活,我们下个帖子再写,现在只有把它加载到内存就行。接下来该来完善我们的引导扇区了。上个帖子我们在目录区找到Loader之后,就调用了个jmp $,让程序停在那了,现在我们来接着往下写。
我们先来理下思路,在找到Loader的目录项之后,要取出Loader在FAT中的起始簇号,然后根据起始簇号找出Loader在FAT表中的偏移,从而找出下一个簇号(如果有的话,没有就是0xfff),同时我们还要根据这个起始簇号来计算处Loader数据的第一个扇区的偏移,然后将该扇区数据读取进内存,再根据下一个簇号计算出下一个扇区的偏移,再将其读进内存,直到所有的数据都读取完毕。思路基本上理清了,现在有两个问题要处理:
一、怎么根据原始簇号得出Loader在FAT中的偏移。
我们先把上面的Loader代码编译成bin文件,然后将其拷贝到软盘中,命令如下
$sudo nasm Loader.asm-oLoader.bin
$sudo mount -o loop boot.img/mnt/floppy
$sudo cp Loader.bin /mnt/floppy
$sudo umount /mnt/floppy
然后我们来看看根目录的数据,如下图
上图中,根目录区的偏移 = 19 * 512,即两个FAT表的扇区数加上引导记录,结果为19个扇区,每个扇区512个字节。通过上图我们可以知道,Loader.bin的初始簇号为0x0003,下面我们先来计算Loader.bin在FAT表中的偏移。因为在FAT12文件系统中,每个簇号在FAT表中占一个半字节,即12位,所以3个字节可以存两个簇号,因此簇号偏移 = 簇号 * 3 / 2,这里得到的偏移是字节为单位,我们还要计算出簇号所在扇区相对于FAT的偏移,这时我们就用上面计算得到的簇号偏移/512,得到的商是相对于FAT的扇区偏移,余数是该簇号在当前扇区的偏移。
我们把初始簇号0x0003带入上面的公式,得到簇号偏移=0x0003 * 3 / 2 = 4,然后用4 / 512得到商为0,余数为4,也就是说,初始簇号在FAT中的偏移是FAT的第一个扇区,偏移为4字节的地方,我们到FAT表中去看看,如下图
因为FAT表在引导扇区之后,所以偏移为512个字节,通过上面的FAT表数据我们可知,从偏移为4开始,接下去的一个半字节为0xfff,在FAT表中,如果一个文件大小大于512字节(一个扇区),那个当前簇号的位置存放的是下一个簇号,以0xfff表示文件结束,因为我们的Loader.bin文件很小,一个扇区可以装下所有的数据,所以这里就填了0xfff。
根据初始簇号得出Loader在FAT中的偏移这个问题,我们已经解决了,接下来就是要根据初始簇号来计算Loader.bin所在数据区的偏移了。因为数据区在根目录区之后,而我们要的Loader.bin文件的数据是存放在数据区的,所以数据偏移 = (19 + 14) * ((簇号 - 2) * 每簇扇区数),因为簇号是从2号簇开始算起的,所以我们在计算偏移的时候要用当前簇号-2,这里我们的每簇扇区数为1,所以上面的公式计算出来的数据偏移为33(单位是扇区),转换为字节就是0x4400,下面我们看看在a.img中,偏移为0x4400处的数据,如下图
我们可以对比Loader.bin,看数据是不是一致,下面是Loader.bin的数据
我们可以看出,在0x4400开始处的数据正是Loader.bin。
好了,现在主要的问题都解决了,接下来就是写代码来加载数据到内存中了,代码如下
LABEL_FOUND_LOADER: ;找到LOADER.BIN文件
xor cx, cx
and di, 0xffe0 ;这里把di设置为指向目录项的首地址
add di, 0x1a ;目录项中偏移0x1a处为初始簇号
mov cx, word ;将初始簇号存入cx
mov word , OffsetOfLoader
LABEL_GO_ON_LOADING:
push ax
push bx
mov ah, 0x0e
mov al, '.'
mov bl, 0x0f
int 0x10
pop bx
pop ax
push cx
mov ax, cx
sub ax, 2
xor cx, cx
mov cl,
mul cl
mov bx, DataOffsetSec
add ax, bx ;得到当前簇号的数据偏移(单位:扇区)
push ax
mov ax, BaseOfLoader
mov es, ax ;存放数据的段地址
pop ax
mov bx, word ;存放数据的偏移
mov cl, 1
call ReadSector ;读取数据
pop ax ;取出当前簇号
mov bx, 3
mul bx ;簇号 * 3
mov bx, 2
div bx ;再除2,这里得到簇号的偏移(单位:字节)
test dx, dx
jz LABEL_IN_HIGH_BYTE
mov byte , 1 ;该簇号的在当前字节的高4位
LABEL_IN_HIGH_BYTE:
xor dx, dx
mov bx,
add word , bx
div bx
inc ax ;FAT
push dx
push ax
mov ax, BaseOfLoader
sub ax, 0x100
mov es, ax ;设置读的位置的段地址
pop ax ;ax中存放要读取的扇区号
xor bx, bx
mov cl, 2 ;一次读取2个扇区
call ReadSector
pop dx
add bx, dx
mov ax, word
cmp byte , 0 ;看簇号是不是在当前字节的高4位
jz LABEL_EVEN
shr ax, 4 ;如果是则ax右移4位
LABEL_EVEN:
and ax, 0xfff
cmp ax, 0xfff ;如果ax的值为0xfff表示文件结束
jz LABEL_FILE_LOADED
mov cx, ax
jmp LABEL_GO_ON_LOADING
LABEL_FILE_LOADED:
mov dh, 1
call DispStr
jmp BaseOfLoader:OffsetOfLoader ;跳转到Loader.bin
代码看着有点长,我们一段一段来分析,在开始处,则需要先从目录项中取出初始簇号,代码如下
xor cx, cx
and di, 0xffe0 ;这里把di设置为指向目录项的首地址
add di, 0x1a ;目录项中偏移0x1a处为初始簇号
mov cx, word ;将初始簇号存入cx
mov word , OffsetOfLoader
得到了初始簇号之后,我们要通过初始簇号来计算出数据所在的扇区号,代码如下
LABEL_GO_ON_LOADING:
push ax
push bx
mov ah, 0x0e
mov al, '.'
mov bl, 0x0f
int 0x10 ;在屏幕上”Booting”之后显示'.'
pop bx
pop ax
push cx
mov ax, cx
sub ax, 2
xor cx, cx
mov cl,
mul cl
mov bx, DataOffsetSec
add ax, bx ;得到当前簇号的数据偏移(单位:扇区)
这段代码主要就是通过簇号来计算出数据所在的扇区号,因为簇号是从2开始算起的,所以我们要先用簇号减2,得到的结果乘以每簇扇区数,就可以得出数据所在扇区相对于数据区的偏移,然后再加上数据区的起始扇区号,就可以得出要加载数据的扇区号,得到扇区号之后,我们就要将数据读入内存了,代码如下
push ax
mov ax, BaseOfLoader
mov es, ax ;存放数据的段地址
pop ax
mov bx, word ;存放数据的偏移
mov cl, 1
call ReadSector ;读取数据
这里设置了加载数据的内存地址,和读取的扇区数,以及扇区号,然后开始读取数据,接下来,我们要判断该文件在其他扇区是否还有数据,这就要通过FAT表来判断,我们先根据初始扇区号来计算出它在FAT表中的偏移,然后读取出该偏移的数据,如果文件还有数据,则这个值是下个簇号,否则这个值为0xfff,我们来看代码
pop ax ;取出当前簇号
mov bx, 3
mul bx ;簇号 * 3
mov bx, 2
div bx ;再除2,这里得到簇号的偏移(单位:字节)
test dx, dx
jz LABEL_IN_HIGH_BYTE
mov byte , 1 ;该簇号的在当前字节的高4位
这里因为一个簇号占一个半字节,两个簇号占3个字节,所以我们要用簇号乘以3,然后除以2,就可以得出簇号在该扇区的偏移。因为我们无法从扇区中读取出一个半字节,因此我们每次读取出2个字节数据,然后根据具体情况来获取出这一个半字节。因为我们要从FAT表中读取出簇号,所以接下来我们要读取FAT表,代码如下
LABEL_IN_HIGH_BYTE:
xor dx, dx
mov bx,
add word , bx
div bx
inc ax ;ax存放的是相对扇区号,dx中存放的是在扇区中的偏移
push dx
push ax
mov ax, BaseOfLoader
sub ax, 0x100
mov es, ax ;设置读的位置的段地址
pop ax ;ax中存放要读取的扇区号
xor bx, bx
mov cl, 2 ;一次读取2个扇区
call ReadSector
我们已经得出簇号在FAT表中的偏移(字节),所以我们就要根据这个偏移来计算处它所在的扇区号,然后我们将该扇区读出(磁盘的读写都是按照扇区来进行的),然后再根据偏移来从该扇区中取出簇号。得到簇号之后,我们要判断是不是已经结束,如果不是则要继续读取下一个扇区,否则就跳转到加载结束的位置,代码如下
pop dx
add bx, dx
mov ax, word
cmp byte , 0 ;看簇号是不是在当前字节的高4位
jz LABEL_EVEN
shr ax, 4 ;如果是则ax右移4位
LABEL_EVEN:
and ax, 0xfff
cmp ax, 0xfff ;如果ax的值为0xfff表示文件结束
jz LABEL_FILE_LOADED
mov cx, ax
jmp LABEL_GO_ON_LOADING
接下来我们来看文件加载完成之后怎么处理,代码如下
LABEL_FILE_LOADED:
mov dh, 1
call DispStr
jmp BaseOfLoader:OffsetOfLoader ;跳转到Loader.bin
这个就很简单了,在屏幕上显示”Ready.”,然后通过jmp指令跳转到Loader.bin中去执行,这样我们的Loader就完成了,Loader.bin加载完成并将控制前转交给它之后,运行的结果如下图
感谢楼主的分享~ 还是不错的哦,顶了 冰大哥太流弊了666666666666666666666666666666 还是不错的哦,顶了 支持中国红客联盟(ihonker.org) 还是不错的哦,顶了 支持中国红客联盟(ihonker.org) 学习学习技术,加油!