一.什么是启动扇区
我们使用软盘来启动操作系统时,系统首先就是从软盘的第一个扇区中开始读取数据,也就是第0面,0磁道的第0个扇区,软盘的每个扇区为512个字节的大小,如果最后两个字节为0xaa55(当BIOS看到这两个字节时,就认为是引导扇区的结束标志),则代表该两个字节前的机器指令都是启动扇区的指令,以0x00开头一直到0xaa55这512个字节的扇区就称之为启动扇区。
二.引导扇区的程序编写
代码(程序名称为boot.S)以及注释如下:
.code16
.text
mov %cs,%ax
mov %ax,%ds
mov %ax,%es #将寄存器ds和es同时指向与cs相同的段,使得后续在进行数据操作时能定位到正确的位置
call DispStr #调用DispStr函数
jmp . #进入死循环
DispStr:
mov $BootMessage,%ax
mov %ax,%bp
mov $16,%cx
mov $0x1301,%ax
mov $0x00c,%bx
mov $0,%dl
int $0x10
ret
BootMessage:.ascii "Hello, OS world!"
.org 510
.word 0xaa55
DispStr函数的注释如下:
DispStr:
mov $BootMessage,%ax #将字符串变量“BootMessage”的首地址传给寄存器ax
mov %ax,%bp #使用寄存器ES:BP保存字符串的地址
mov $16,%cx #保存字符串的长度
mov $0x1301,%ax #寄存器ax高位为 0x13h,低位为0x01h
mov $0x00c,%bx #设置页号为0(高位0X0h),字符串颜色为红色高亮(低位0x0ch)
mov $0,%dl
int $0x10 #10h号中断
ret
剩下的代码注释如下:
BootMessage:.ascii "Hello, OS world!" #字符串函数
.org 510 #用0x00填满剩下的字节,一直到第510个字节
.word 0xaa55 #剩下的两个字节填0xaa55
三.连接脚本的编写
要想将一个程序编译成可执行文件,我们需要经过类似中间代码生成、连接等步骤(可以参考之前写过的一篇文章:【c语言】从高级语言到可以执行的EXE程序的编译过程),所以,在编译之前,我们还需要编写一个连接脚本(名称为solrex_X86.ld),代码如下:
SECTIONS
{
. = 0x7c00;
.text :
{
_ftext = .;
} = 0
}
当BIOS发现了引导分区(以0xaa55结尾的前512个字节的扇区)之后,就会把这512个字节的内容转载到内存的0000:7c00处(可以参考之前写过的一篇文章:【操作系统】操作系统的启动都干了些什么),然后跳转到0000:7c00处将控制权彻底交给这段引导代码中,所以该连接脚本的功能就是在进行连接时,将程序入口设置到内存0000:7c00的位置,而如果我们希望将代码放置到其他地址空间时,就可以直接修改连接脚本的地址即可。
四.Makefile脚本编写
下一步我们编写Makefile脚本,用于将高级语言(.S)编译成中间文件(.o),再将连接脚本(.ld)和中间文件(.o)连接编译成可执行文件(.elf),最后将可执行文件写入到软盘镜像中(.img),代码如下:
CC=gcc
LD=ld
LDFILE=solrex_X86.ld #连接脚本
OBJCOPY=objcopy
all: boot.img
boot.o: boot.S #生成中间文件
$(CC) -c boot.S
boot.elf: boot.o #连接脚本+中间文件=可执行文件
$(LD) boot.o -o boot.elf -e c -T$(LDFILE)
boot.bin: boot.elf #移除可执行文件中没有用到的块
@$(OBJCOPY) -R .pdr -R .comment -R.note -S -O binary boot.elf boot.bin
boot.img: boot.bin #生成软盘镜像
@dd if=boot.bin of=boot.img bs=512 count=1
@dd if=/dev/zero of=boot.img skip=1 seek=1 bs=512 count=2879
clean:
@rm -rf boot.o boot.elf boot.bin boot.img
使用指令:
make
进行编译,输出如下:
最后即生成一个软盘镜像(boot.img)和一些中间文件:
五.虚拟机VXBox开启镜像
我们在虚拟机上新建一个虚拟电脑,然后按照推荐一直点下一步:
创建完成后,我们选择虚拟电脑的“设置”项,添加一个虚拟软盘:
点击”添加虚拟软驱“,然后点击”注册“,将你先前编译的boot.img进行注册操作:
注册完成后选择对应的软盘镜像,即可保存退出:
为了让系统优先导入我们的软盘镜像,我们可以将控制器:IDE整个删除,使得我们的镜像处于最高优先级:
最后我们直接点击“启动”,即可看见先前编写的字符串黑底红字!!
恭喜!你已经踏入了编写操作系统的第一步!