您的位置:首页 > 其它

用软盘引导扇区加载.bin文件到内存…

2017-06-21 22:51 246 查看
在x86平台,如果我们的汇编程序比较小,例如只显示一个字符串,则我们用软盘的引导扇区(占一个扇区)就够了;但是我们的程序以后会越写越大,会超出一个扇区的范围(512字节),这时我们就需要改变策略,我们先把程序分成两个部分,第一部分是引导程序,第二部分是我们要真正操作的核心程序。我们把第二部分存到软盘的某个地方,然后通过第一部分的引导程序把第二部分程序加载到内存中运行。这样我们的核心程序就可以写得很大。

我们先把简单的部分说了,也就是第二部分程序,为了方便起见,我们还是现实一个字符串,但是在这里我们开辟1024*2个字节的堆栈空间,这样第二部分程序的大小就远远超出了512个字节。
代码如下

%include "pm.inc"

org 0x7c00
jmp LABEL_BEGIN

;----------------------------------------------------------------------------------------
;GDT table
[SECTION .gdt]
LABEL_GDT: Descriptor 0,    
0,      
   0
LABEL_DESC_DATA: Descriptor 0,  
    DATA_LEN,  
DA_DRW
LABEL_DESC_STACK: Descriptor 0,  
    STACK_LEN,
 DA_DRWA + DA_32
LABEL_DESC_VIDEO: Descriptor  
   0xB8000, 0xFFFF,
   
DA_DRW 

GDT_LEN equ $ - LABEL_GDT
GDT_PTR dw GDT_LEN - 1
dd LABEL_GDT

;----------------------------------------------------------------------------------------
;Selector
SelectorData equ LABEL_DESC_DATA - LABEL_GDT
SelectorStack equ LABEL_DESC_STACK - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT

;----------------------------------------------------------------------------------------
[SECTION .data]
ALIGN 32
[BITS 32]
LABEL_DATA:
SayHelloLily: db "Hello, Lily!"
LilyStringOffset equ SayHelloLily - $$
SayHelloKaito: db "Hello, kaito!"
KaitoStringOffset equ SayHelloKaito - $$
DATA_LEN equ $ - LABEL_DATA - 1

[SECTION .stack]
ALIGN 32
[BITS 32]
LABEL_STACK:
times 1024*2 db 0
STACK_LEN equ $ - LABEL_STACK - 1

;----------------------------------------------------------------------------------------
[SECTION .s16]
[BITS 16]
LABEL_BEGIN:
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0100h
mov ax, 0xB800
mov gs, ax
mov ah, 0x0F
mov di, (80 * 3 + 1) * 2
mov al, 'L'
mov [gs:di], ax
mov di, (80 * 3 + 2) * 2
mov al, 'i'
mov [gs:di], ax
mov di, (80 * 3 + 3) * 2
mov al, 'l'
mov [gs:di], ax

mov di, (80 * 3 + 4) * 2
mov al, 'y'
4000

mov [gs:di], ax

jmp $

虽然没有进入保护模式的代码,但是堆栈还是开辟了, 1024*2个字节
编译 nasm loader.asm -o loader.bin

接下来就是要写引导扇区的程序,要在软盘中找到loader.bin,再加载到内存运行

首先要学习一下软盘的存储结构,我在虚拟机上添加了一个虚拟软盘,FAT12的
其结构如下图




1.最开始的一个扇区(一个扇区512字节)是引导扇区,x86处理器开机会把这个扇区的内容加载到内存的0x7c00的位置运行;这里整个扇区的结构是固定的




我们的引导代码就放在62偏移位置。

2.然后是两个FAT区,每一个FAT区占9个扇区,一般FAT2是FAT1的复制,不用;
FAT区是由一个个FAT项构成的,每一个FAT项有3个字节(24位)。

3.再后面是根目录区,其大小不一定,但是可以设置(在引导扇区开头有一个结构,可以设置根目录区有多少个扇区);
根目录区存放的文件的条目,每一个条目代表一个文件,占用32位



4.最后是数据区,存的是文件的内容,以簇为单位(这里一个簇占一个扇区)

【注】在数据区一般不用第0和1个簇号,也就是说数据区的第一个扇区的簇号是2;又因为FAT区的每一个FAT项对应数据区的一个簇,所以图形理解为




利用簇号*3/2可以得到FAT项

所以总的步骤是这样的
1.我们在引导程序中需要给出要加载的文件的文件名
2.根据这个文件名,在根目录区找到这个文件的条目
3.从条目的0x1A偏移处获取簇号,跳转到数据区文件的起始位置(簇号),并读取该扇区的内容到内存
4.根据簇号,计算其在FAT中对应的FAT项
5.判断该项是0xFFF(文件的结束),还是0x002~0xFEF(下一个簇号)
6.如果是下一个簇号,则跳转到数据区对应簇号位置,又读取一个扇区,再回到步骤4

mov word [wSectorNo], SectorNoOfRootDirectory
;表示当前要读取的扇区号
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmp word [wRootDirSizeForLoop], 0
;根目录总的扇区数,判断是否把整个根目录区读完了,若为0则表示读完了整个根目录区都没有找到lodaer.bin
jz LABEL_NO_LOADERBIN
dec word [wRootDirSizeForLoop]

mov ax, BaseOfLoader ;填写读取扇区函数的参数,读取扇区到内存处的基地址
mov es, ax
mov bx, OffsetOfLoader ;填写读取扇区函数的参数,读取扇区到内存处的偏移地址
mov ax, [wSectorNo] ;当前要读取的起始扇区号
mov cl, 1 ;要读取的扇区数
call ReadSector ;读取一个扇区到内存的[es:bx]位置
mov si, LoaderFileName
;读取完一个扇区后开始判断这个扇区里面是否有loader.bin这个文件名
mov di, OffsetOfLoader
cld
mov dx, 0x10
;根目录区中一个条目占用32个字节,一个扇区共512个字节,所以根目录区中,一共扇区共有16个条目
LABEL_SEARCH_FOR_LOADERBIN:
cmp dx, 0
jz LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR
dec dx
mov cx, 11
LABEL_CMP_FILENAME: ;文件名占11位
cmp cx, 0
jz LABEL_FILENAME_FOUND
dec cx
lodsb
cmp al, byte [es:di]
jz LABEL_GO_ON
jmp LABEL_DIFFERENT
LABEL_GO_ON: ;判断文件名的下一个字节
inc di
jmp LABEL_CMP_FILENAME
LABEL_DIFFERENT:
and di, 0xFFE0
add di, 0x20
mov si, LoaderFileName
jmp LABEL_SEARCH_FOR_LOADERBIN

LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
add word [wSectorNo], 1
jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN

在这其中,有一个函数:ReadSector

;读取第ax开始的扇区,读取cl个扇区,存到es:bx的位置
ReadSector:
mov cl, [BPB_SecPerTrk]
div cl
inc ah
mov cl, ah ;cl is ready
mov dh, al
mov ch, al
shr ch, 1 ;ch is ready
and dh, 1 ;dh is ready
mov dl, 0 ;dl is ready
.GoOnReading:
mov ah, 2
mov al, 1
int 13h
jc .GoOnReading

ret

用的是BIOS调用,读取cl个扇区(填写好下表中各个寄存器,调用int 0x13软中断即可)




LABEL_FILENAME_FOUND:
.........
这样我们就找到loader.bin文件在根目录的条目,来到了LABEL_FILENAME_FOUND
既然我们找到了这个文件,那接下来就要把loader.bin加载到内存中

LABEL_FILENAME_FOUND:
and di, 0xFFE0 ;指向该条目的DIR_Name的位置,即该条目的起始位置
add di, 0x1A ;指向该条目的DIR_FstClus位置,为该条目在数据区中的簇号
mov ax, word [es:di] ;ax为数据区中对应的簇号
push ax ;保存这个簇号,为GetFATEntry输入参数

;DeltaSectorNo equ 17
;文件的起始扇区号   =   [es:di]
     
   + RootDirSectors
     
 +  DeltaSectorNo
;      
 DirEntry中的簇号 + 根目录区占用的扇区数  +
 17(因为数据区0、1簇号不使用,所以减2)

add ax, RootDirSectors ;根目录区占用14个扇区
add ax, DeltaSectorNo ;此时ax为loader.bin的起始扇区号
mov dx, BaseOfLoader
mov es, dx
mov dx, OffsetOfLoader
mov bx, dx ;准备好ReadSector的[es:bx]
LABEL_GOON_LOADING_FILE:
mov cl, 1
call ReadSector ;读取在数据区loader.bin的起始扇区
pop ax
call GetFATEntry ;根据数据区的簇号,在FAT中找对应的条目,即找下一个簇号
cmp ax, 0xFFF
jz LABEL_FILE_LOADED
push ax
add ax, RootDirSectors
add ax, DeltaSectorNo
add bx, [BPB_BytesPerSec]
jmp LABEL_GOON_LOADING_FILE

;此处loader.bin已经全部加载到内存了BaseOfLoader:OffsetOfLoader这个位置,所以跳转到这里执行,也就是开始执行我们的loader.bin的程序了
LABEL_FILE_LOADED:
jmp BaseOfLoader:OffsetOfLoader

到此我们已经得到了boot.bin 和 loader.bin
我们要在虚拟机里面做好软盘,再运行

在Linux下制作软盘映像用bximage命令,选择fd,后面全默认
然后dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc
mount -o loop /mnt/floppy (要事先在mnt目录下建立floppy目录)
cp loader.bin /mnt/floppy
umount /mnt/floppy

这样就得到了一个a.img映像,里面还包含loader.bin文件,我们把这个a.img作为新的虚拟机的启动软盘映像就可以启动了

下面是两个文件的代码:
boot&loader

虽然能够加载文件了,但是这个好像进不了保护模式,在切换到保护模式后,对段寄存器操作就会出错,我知道是GDT的首地址变了,可是改了也没用,这是个问题,回头再看了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐