《彖》曰:屯,刚柔始交而难生。《易经》的屯挂,讲述的是万物始生,充满艰难险阻,然而顺时应运,必欣欣向荣,故屯挂也叫做始生之挂。
任何事物,都必须经历一个艰难的开始,方有可能守得云开见月明,当然操作系统学习也不例外。
学编程逃不过的真香定律:一个程序永远是打印一行"Hello Word",那么这章节,我们也来编写一个可独立运行于x86架构的程序(注意,这里是脱离操作系统的)。
在上一篇我们说了,BIOS会加载启动介质中的主引导程序,主引导程序是一个由汇编语言完成的软件,并且存储在启动介质的前512字节处,入口地址为0x7C00,以0x55AA结束。主引导程序的入口地址0x7C00类比到C语言中,就是main函数入口,对应关系可参考下图:
本章节要实现的就是一个简单的主引导程序,功能为运行后在屏幕上打印字符串"Hello OS"。
编写这个汇编程序有如下几个点需要注意:
一、打印相关设置
1、bx寄存器固定设置为0x0F (mov bx, 0x0F)
2、ah寄存器固定设置为0x0E (mov ah, 0x0E)
3、al寄存器设置要打印的字符 (mov al, '要打印的字符')
4、使用 int 0x10指令触发0x10号中断,使得al寄存器的值在屏幕上显示
二、其他汇编贴士
1、hlt:程序进入睡眠状态的指令
2、标签:一段指令代码的起始地址,类似C语言的标签
3、&:表示当前指令的地址 && :表示当前汇编段的起始地址
有了以上两点的基础知识,下边就来编写这个主引导程序,文件名为boot.asm,启动时在屏幕上打印字符串"Hello OS"
org 0x7c00 ;指定起始地址为0x7c00
start: ;关键寄存器清零
mov ax, cs
mov ss, ax
mov ds, ax
mov es, ax
mov si, msg ;将msg地址放入si寄存器中
print:
mov al, [si] ;取si寄存器中的一个字节数据
add si, 1
cmp al, 0x00 ;比较al是否到达信息尾
je last ;al值等于0x00,则跳到last标签
mov ah, 0x0e ;设置打印参数
mov bx, 0x0f
int 0x10 ;调用中断,打印字符
jmp print
last:
hlt
jmp last
msg:
db 0x0a, 0x0a ;定义换行符
db "Hello OS!" ;定义要打印的字符串
db 0x0a, 0x0a
times 510 - ($-$$) db 0x00 ;times 510 - ($-$$)为0占位符,确保启动程序为512字节
db 0x55, 0xaa
上述代码就是一段简单的主引导程序,占用512字节,对于一些汇编指令,如定义数据指令 db、跳转指令jmp、比较指令cmp等,可自行查x86汇编,这里不再详解。
主引导程序boot.asm已经写好了,前边说了,主引导程序存储在启动介质的前512字节处,下边需要解决的问题就是:
1、使用什么启动介质?
答:启动介质这里通过bximage工具创建虚拟软盘 a.img
bximage a.img -q -fd -size=1.44
2、如何编译这段汇编代码,并将其存入启动介质的前512字节处?
答:A、使用nasm编译器将boot.asm汇编源码编译成二进制机器码
nasm指令:nasm boot.asm -o boot.bin
B、使用dd命令将编译生成的二进制机器码写入虚拟软盘a.img的起始位置
dd指令:dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc
参数 if:输入文件,这里即为编译后的二进制机器码文件boot.bin
of:输出文件,这里即为创建的虚拟软盘a.img
bs:每次操作的块大小,这里为512字节
count:每次操作的块数量,这里为1
conv:notrunc表示无间隙写入
C、在虚拟机中将虚拟软盘a.img作为启动盘执行
将a.img拷贝到windows路径下
这里选"稍后安装操作系统",因为我们要使用a.img作为启动盘
运行设置好的虚拟机,此时操作系统MyOS的启动介质为a.img
,
BIOS执行后加载运行启动介质a.img前512的主引导程序,我们的实现为打印字符串"Hello OS!"
到此为止,尝鲜版 “Hello OS” 编写并运行完成。
总结
1、主引导程序大小不可超过512字节
2、主引导程序需要使用汇编语言开发
3、主引导程序中可以通过BIOS中断使用硬件功能(文章中我们使用了0x10号中断打印字符串)
4、主引导程序运行于实模式(地址都是实际的物理地址)
学自 --《狄泰软件学院》- 门徒操作系统