上一篇讲到如何在屏幕上显示一个Hello,world!
本篇要从软盘中读取文件——Loader.bin
由于起始扇区只有512字节,要在FAT12中读取文件Loader.bin,代码比较紧凑。下面是fat12的文件系统BPB,其中的值基本上都是固定的。
jmp LABEL_START /* 跳转指令 */
nop /* 这个nop指令是可选的,如果你用的是e9指令(长跳转),这个是不需要的
但是如果你用的eb指令,说明你只是指明的跳转的步长,nop是必需的 */
BS_OEMName: .ascii "MSWIN4.1" /* OEM生成厂商的名字,微软建议使用MSWIN4.1保持兼容行 */
BPB_BytsPerSec: .2byte 512 /*对面现代磁盘来说,这个数可能是4096,但是软盘这个是固定的*/
BPB_SecPerCluse: .byte 1 /*每簇的扇区数,可以取2的n次方*/
BPB_ResvdSecCnt: .2byte 1 /*保留扇区数,必须为1*/
BPB_NumFATs: .byte 2 /*FAT的数目,一般是2*/
BPB_RootEntCnt: .2byte 224 /*根目录可容纳的条目,FAT32此项为0*/
BPB_TotSec16: .2byte 2880 /*16bit扇区总数*/
BPB_Media: .byte 0xf0 /*媒体介质类型,f0表示可移动磁盘*/
BPB_FATSz16: .2byte 9 /*FAT表的大小,9个扇区*/
BPB_SecPerTrk: .2byte 18 /*每磁道的扇区数 18 */
BPB_NumHeads: .2byte 2 /*磁头数,对于1.44M磁盘来说,这个数固定为2*/
BPB_HiddSec: .4byte 0 /*FAT分区之前的隐藏扇区数,没有分区的磁盘来说此项为0*/
BPB_TotSec32: .4byte 0 /*总扇区数,和tolsec16含义相同,16位无法表示可用此项*/
BS_DrvNum: .byte 0 /*用于0x13中断,0为软盘,0x80为硬盘*/
BS_Reserved1: .byte 0 /*保留NT使用*/
BS_BootSig: .byte 0x29 /*扩展引导标记,表明此后的3个域可用*/
BS_VolID: .4byte 0 /*卷标序列号*/
BS_VolLab: .ascii "Solrex 0.01" /*卷标*/
BS_FileSysType: .ascii "FAT12 " /*文件类型,只是文本字符串,不可用来检测文件系统类型*/
/* Initial registers.初始化寄存器 */
LABEL_START:
mov %cs,%ax
mov %ax,%ds
mov %ax,%es
mov %ax,%ss
mov $BaseOfStack, %sp
/*清屏*/
/*mov $0x0600,%ax /*ah =6 ,al =0*/
/*mov $0x0700,%bx /* 白屏*/
/*mov $0,%cx /*左上角 0,0*/
/*mov $0x184,%dx /*右下角 80,50*/
/*int $0x10 /*10h号中断,ah=6初始化屏幕*/
/*原有清屏函数有bug新清屏函数*/
mov $3,%ax
int $0x10
/*显示引导信息*/
mov $0,%dh
call Dispstr
/*重置软盘*/
xor %ah,%ah
xor %dl,%dl
int $0x13
/*在根目录查找内核文件*/
movw $SecNoOfRootDir,(wSectorNo) /*第19扇区,固定值*/
/*读取根目录到内存*/
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
cmpw $0,(wRootDirSizeForLoop) /*如果搜寻完整个根目录找不到内核*/
jz LABEL_NO_LOADERBIN /*显示找不到信息*/
decw (wRootDirSizeForLoop) /*变量-1*/
mov $BaseOfLoader,%ax /*要载入的基址es*/
mov %ax,%es
mov $OffsetOfLoader,%bx /*偏移地址bx*/
mov (wSectorNo),%ax /*起始扇区为第19扇区*/
mov $1,%cl /*读取一个扇区*/
call ReadSector /*调用读取扇区子函数*/
mov $LoaderFileName,%si /*取文件名的基地址放入ds:si*/
mov $OffsetOfLoader,%di /*读取后的偏移地址->di,*/
cld /*请方向位,传送方向正向*/
mov $0x10,%dx /*先查找1个扇区,16*32 = 512byte*/
流程如下,先清屏,之后在根目录中查找LOARER.BIN。 找到后加载到ds:si中。
最后跳转到,jmp $BaseOfLoader,$OffsetOfLoader执行。
Loader.S中仅显示一个“L”
.code16
.text
mov $0xb800,%ax
movw %ax,%gs
mov $0xf,%ah
mov $'L',%al
mov %ax,%gs:(0)
jmp .
完
Makefile文件如下:
CC=gcc
LD=ld
LDFILE=foot.ld
LDFILE_LOAD=loader.ld
OBJCOPY=objcopy
all: boot.img LOADER.BIN
#Step 1:gcc 调用as将boot.S编译成目标文件boot.o
boot.o: boot.S
$(CC) -c boot.S
#Step 2:ld 调用脚本foot.ld将boot.o链接成可执行文件boot.elf
boot.elf: boot.o
$(LD) boot.o -o boot.elf -T$(LDFILE)
#Step3: objcopy 移除boot.elf 中无用的section(.pdr,.comment,.note),
# strip 掉所有符号信息,输出为二进制文件 boot.bin
boot.bin: boot.elf
@$(OBJCOPY) -R .pdr -R .comment -R .note -S -O binary boot.elf boot.bin
LOADER.BIN: loader.S
$(CC) -c loader.S
$(LD) loader.o -o loader.elf -T$(LDFILE_LOAD)
$(OBJCOPY) -R .pdr -R .comment -R .note -S -O binary loader.elf $@
#Step 4:生成可启动软盘镜像
boot.img: boot.bin
@dd if=boot.bin of=boot.img bs=512 count=1 #用boot.bin 生成镜像文件第一个扇区
#在bin生成镜像文件后补空白,最后成为合适大小的软盘镜像
@dd if=/dev/zero of=boot.img skip=1 seek=1 bs=512 count=2879
copy: boot.img LOADER.BIN
mkdir -p /tmp/floppy1;\
mount -o loop boot.img /tmp/floppy1/ -o fat=12;\
cp LOADER.BIN /tmp/floppy1/;
umount /tmp/floppy1/;
rm -rf /tmp/floppy1/;
clean:
@rm -rf *.o *.elf *.bin *.img