出于兴趣,开始研究自制操作系统,具体的学习路线可以在知乎查一下:“如何自制操作系统”之类的。在被安利了一些书之后兴冲冲跑去图书馆借了川合秀实的《30天自制操作系统》,开始入了这个巨坑
启动分区格式
经过几天的学习,大致明白了一些关键点。首先是系统的入口。操作系统的启动前计算机会先启动BIOS。BIOS会读取磁盘的第一个扇区(前512字节),如果最后两个字节的值是 0x55, 0xAA。那么就认为这个是启动分区(引导分区)。这个时候就会开始运行这个磁盘上的程序。
因为《30天》中作者用的是自制的编译器(这是真的强),并且用了很多他自制的工具。虽然方便但是不利于我们掌握更常见的工具(gcc、nasm等)。所以我打算自己写一个引导程序。很快就踩了无数坑。
首先,这一段引导程序无法用C语言完成,所以用的是汇编,但是需要注意的是不同文件格式的开头要求不同。
其次,另一个难处是如何将自己的程序做成镜像运行在虚拟机中,我参考了
自制操作系统:引导扇区的实现 - JollyWing - 博客园www.cnblogs.com不过很可惜,这篇的作者没有考虑到文件格式的问题(可能他的环境跟我不一样?)。
因为没有理解程序运行的原理,直接照搬(因为比较简单所以偷个懒)了上个链接里的一段程序以为可以跑,折腾了一天才通过对比作者的代码发现了端倪,FAT12格式的开头有一个JMP语句,跳到汇编程序的开头。具体可以参考这篇
FAT和NTFS启动扇区结构blog.csdn.net这里贴出代码分享一下
;; BIOS会把512字节的引导扇区加载到 0000:7c00 处,
;; 然后跳转到0000:7c00处,将控制权交给引导代码。
org 07c00h ;这一行告诉编译器,我们的代码将被加载到7c00处。
jmp start
DB 0x90
DB "HELLOIPL" ; 启动扇区名称(8字节)
DW 512 ; 每个扇区(sector)大小(必须512字节)
DB 1 ; 簇(cluster)大小(必须为1个扇区)
DW 1 ; FAT起始位置(一般为第一个扇区)
DB 2 ; FAT个数(必须为2)
DW 224 ; 根目录大小(一般为224项)
DW 2880 ; 该磁盘大小(必须为2880扇区1440*1024/512)
DB 0xf0 ; 磁盘类型(必须为0xf0)
DW 9 ; FAT的长度(必??9扇区)
DW 18 ; 一个磁道(track)有几个扇区(必须为18)
DW 2 ; 磁头数(必??2)
DD 0 ; 不使用分区,必须是0
DD 2880 ; 重写一次磁盘大小
DB 0,0,0x29 ; 意义不明(固定)
DD 0xffffffff ; (可能是)卷标号码
DB "HELLO-OS " ; 磁盘的名称(必须为11字?,不足填空格)
DB "FAT12 " ; 磁盘格式名称(必??8字?,不足填空格)
start:
mov ax, cs ;将代码段寄存器的值送入AX
mov ds, ax ;将数据段的地址置为代码段的地址?
mov es, ax ;将附加段的地址置为代码段的地址?
mov sp, 0x7c00
call DispStr ;调用显示字符串例程
jmp $ ;无限循环, $表示当前行编译后的地址
DispStr:
mov ax, BootMessage ;将字符串首地址传给寄存器ax
mov bp, ax ;CPU将用ES:BP来寻址字符串
mov cx, 16 ;通过CX,CPU知道字符串的长度
mov ax, 01301h ;AH=13表示13号中断, AL=01H,表示目标字符串仅仅包含字符,属性在BL中包含,移动光标
mov bx, 000ch ;黑底红字, BL=0CH,高亮
mov dl, 0 ;dh表示在第几行显示,dl表示第几列显示
int 10h ;BIOS的10H中断的13号中断用于显示字符串
ret
BootMessage: db "Hello, OS world!" ;对NASM来讲,标号和变量的作用一样, db表示define byte
;; $当前行被汇编后的地址,$$表示一个section开始处的地址,本程序只有一个section,所以指0x7c00
times 510-($-$$) db 0 ;填充剩下空间,使生成的二进制恰好为512字节
db 0x55, 0xaa ;结束标志,如果发现扇区以0xAA55结束,则BIOS认为它是一个引导扇区,dw表示define word
当然,上面的代码并没有实现跳着到kernel的操作(待补充)。