冰琥珀 发表于 2015-10-23 04:20:48

自己动手写操作系统系列之----------加载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加载完成并将控制前转交给它之后,运行的结果如下图
       

r00tc4 发表于 2015-10-24 09:30:22

感谢楼主的分享~

HUC-参谋长 发表于 2015-10-24 10:34:29

还是不错的哦,顶了

小圈圈 发表于 2015-10-24 10:52:14

冰大哥太流弊了666666666666666666666666666666

arctic 发表于 2015-10-24 12:31:35

还是不错的哦,顶了

fireworld 发表于 2015-10-24 20:22:40

支持中国红客联盟(ihonker.org)

H.U.C—Prince 发表于 2015-10-25 04:58:12

还是不错的哦,顶了

小路 发表于 2015-10-25 05:33:11

支持中国红客联盟(ihonker.org)

Jack-5 发表于 2015-10-25 15:07:24

学习学习技术,加油!

Lucifer 发表于 2015-10-25 18:59:32

页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 自己动手写操作系统系列之----------加载Loader