一.概述
根据以前写的一篇文章:【操作系统】MBR主引导目录结构以及作用,我们了解到BIOS在检测完内存、显卡,把硬盘等一系列外设简单检测之后,下一步将和主引导程序MBR进行交接,将主控权交付给下一位嘉宾,至此BIOS的任务就完成了继续睡去,本篇文章我们来开始编写一个简单MBR程序,并且让其完成从BIOS过渡到MBR这个过程,至于MBR的本质工作(启动引导程序等)我们将在之后逐步完善其内容。
二.实现的功能
本次文章需要用Bochs模拟器,详细可请参考之前写过的一篇文章:【操作系统】Bochs安装和配置,本次MBR实现的功能有:
- 实现BIOS与MBR的过渡
- MBR启动后,让其简单显示一些字符(效果为在屏幕上面打印出“1 MBR”字符),以便验证是否成功启动
三.源码和逐段解析
话不多说,直接先把源码全部贴上,后面再逐段解释:
- 其中第一行:
SECTION MBR vstart=0x7c00
该段代码表示本程序在编译时将其实地址编译为0x7c00。
- 第二行到第六行:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov fs,ax
该段代码进行了寄存器的初始化工作,因为ds、es、fs、ss等这类的寄存器不能通过赋值进行初始化,所以需要使用其他寄存器进行间接初始化,因为BIOS是通过jmp 0x7c00跳转到MBR的,此时段寄存器cs:ip的值分别为0和0x7c00,所以将cs的值赋予给力ax,通过通用寄存器ax来中转。Ps:cs不是通用寄存器,属于段寄存器,在程序运行中会被频繁使用,所以不能进行初始化寄存器使用喔。
- 第七行:
mov sp,0x7c00
该代码为初始化栈指针,将栈指针指向0x7c00的位置
- 第九行到第十四行:
mov ax,0x0600
mov bx,0x0700
mov cx,0
mov dx,0x184f
int 0x10
该段代码为清屏函数,因为BIOS在检测硬件时会显示一些硬件信息,为了看清楚我们自己的输出字符,所以使用了BIOS的0x10中断,0x06号上卷全部行的功能,其中ax的高位表示调用的功能号(AH=0x06),低位表示上卷的行数(AX=0X00,如果为0,表示全部)。bx表示上卷行属性。cx的高低位置(CL,CH)分别表示窗口左上角(X,Y)位置,dx的高低位置(DL,DH)分别表示窗口右下角(X,Y)位置,在VGA文件模式中,一行只能容纳80个字符,共25行,所以右下角坐标最大可以为(79,24),最后执行以下BIOS的10号中断。
- 第十六到第十九行:
mov ah,3
mov bh,0
int 0x10
该段代码为获取光标位置函数,调用3号子功能获取光标位置并且将位置存入ah寄存器中,使用bh来存储带获取的光标的页号,最后再次执行下BIOS的10号中断。
- 第二十一行到第二十八行:
mov ax,message
mov bp,ax
mov cx,5
mov ax,0x1301
mov bx,0x2
int 0x10
该段代码实现了打印字符串的功能,将需要打印的字符数据message存入到通用寄存器ax进行中转,将数据存入到bp寄存器中,es:bp为串首地址,此时es和cs保持一致,message在下面的第三十一行就进行了定义:
message db "1 MBR"
cx寄存器中保存字符串的长度,本文为5(包含空格)。紧接着调用13号显示字符以及属性的子功能,此时ah传入0x13,而al则为设置写入字符的方式,方式共有以下几种:
本文中使用了al=0x01的方式,而bh中存储的是要显示的页号,本文中bx=0x2,所以此处为第0页,bl为字符属性,bl=0x2为黑底绿字的意思,最后再次执行以下BIOS的10号中断。
- 第三十行到第三十三行:
jmp $
message db "1 MBR"
times 510-($-$$) db 0
db 0x55,0xaa
该段代码进行了收尾,也是本篇文章的精华之处,使用jmp $使得程序进入死循环,作用相当于which(1),其中“$”表示本行指令的地址,而“$$”则表示本section的起始地址,所以“$-$$”则是指本行到本section的偏移量。由于MBR的最后两个字节是固定的0x55和0xaa,所以要预留两个字节,故本扇区剩余的空间需要使用“0”来进行填充,通过510字节减去上面所算出来的偏移量得出的就是剩余的空间,将剩余的空间填充满“0”,并在最后的两个字节中固定为0x55和0xaa,使其变成MBR程序。
四.运行
代码编写完成后,我们保存一下代码(本文保存文件名为A.S),并且使用nasm对代码进行编译:
nasm –o A.bin A.S
此时,我们就可以开始运行调试了,我们需要用到之前写过的一篇文章(【操作系统】Bochs安装和配置)里面的两个东西:
- 使用Bximage工具生成的空白镜像(本文文件名为test.img),大小随意,大于512个字节即可
- Bochs模拟硬件环境的配置文件(文章中文件名为boch.disk)
我们首先将生成的A.bin文件写入到空白镜像中:
dd if=./A.bin of=./test.img bs=512 count=1 conv=notrunc
上述代码的具体参数可以自行百度,不再次赘述,将代码写入到镜像后,我们再将镜像文件加入到Bochs的模拟硬件环境配置文件中,根据文章中所描述的,我们直接将新生成的镜像写入配置文件的以下位置即可:
保存一下修改后的配置文件,我们开始运行Bochs模拟器进行模拟操作:
./Bochs –f boch.disk(你自己的硬件配置文件名)
运行成功后,会显示以下信息,并且默认为【6】:
此时我们再按一次回车,即可开始模拟:
我们在控制台中输入“c”(具体含义请查看上面所说的文章),继续往下运行,就能看到弹出的窗口中出现了我们所要的字符串: