上次的文章结束的时候把一大段我的Boot Loader贴出来了,可能会让人迷惑.这里我又写了个小小的微缩版的16位实模式下的Boot Loader,大家看看吧.
这里我再次说明一下,这个文章只是记录我自己做16位OS的经验,绝不是什么标准的教程.但是我也不会像其它的文章讲个boot loader就结束了.后面我还会做到kernel以及文件系统等很多东西
BITS 16
org 0x7c00
entry:
mov ax, 0
mov ds, ax
mov si, WelcomeMsg
call putstr
hang:
jmp hang
WelcomeMsg db 'Welcome to My Operating System',0
putstr:
lodsb
or al, al
jz putstrd
mov ah, 0x0e
mov bx, 0x0007
int 0x10
jmp putstr
putstrd:
retn
size equ $ - entry
%if size+2 > 512
%error "code is too large for boot sector"
%endif
times (512 - size - 2) db 0
db 0x55, 0xAA
其中显示为深红色的才是真正的汇编代码,而显示为蓝色的是nasm的宏或一些控制语句.
首先是BITS 16,这个说明是要把我们这个程序编译成16位的代码,org 0x7c00则是告诉编译器我们的这段代码将从0x7c00这个地址开始执行(由于汇编编译器对于静太变量的引用都是使用绝对地址,所以一定要设置好你程序运行开始的地址.比如DOS下.com文件就要设置org 0x100也是一个道理.否则你的数据地址会出现错误)
entry:
mov ax, 0
mov ds, ax
mov si, WelcomeMsg
call putstr
程序中entry开始进入执行.下面的汇编代码就用不着我解释了吧.首先把0x0000传到ds去.因为我们这个程序是在0x0000:0x7c00执行,那么段地址应该是0x0000,否则后面我们的WelcomeMsg就显示不出来了.
然后调用一个子程序putstr.它的功能就是显示一个字符串.参数是放在SI寄存器中的,我把WelcomeMsg的地址传给SI,然后调用call putstr,就可以显示出来Welcome to My Operating System.看到这里,很多人都会兴奋不已的.毕竟看到自己的"操作系统"为自己打印出来一句话,确实让人高兴!但是没有完,这个Boot Loader程序连个真正的Boot Loader程序都算不上,怎么能说是操作系统.
putstr:
lodsb
or al, al
jz putstrd
mov ah, 0x0e
mov bx, 0x0007
int 0x10
jmp putstr
putstrd:
retn
这个子程序就不讲了吧,很简单,就是调用BIOS 10h显示字符的中断.需要注意的是,不要使用21h DOS中断来显示字符哦!因为那是DOS的东西,这里是在启动另外一个"操作系统",怎么用得了DOS的中断,是不是?
size equ $ - entry
%if size+2 > 512
%error "code is too large for boot sector"
%endif
times (512 - size - 2) db 0
这段蓝色的代码不是我们程序的,只是为了控制我们的这个程序大小为512个字节而显示的.因为这个程序是放在软盘的第一个扇区里,是为软盘的第一个扇区量身打造的,所以大小一定要是512字节,也既是一个扇区的大小.
db 0x55, 0xAA
这里有点奇怪,为什么要在程序的最后加上0x55,0xAA?这个也是计算机BIOS规定的东西.前面不是说了,BIOS会自动读取软盘的第一个扇区并执行吗?当BIOS读到这里0x55,0xAA的时候,它就知道该读的扇区的信息已经结束了,然后自动去执行刚读取的代码.所以我们必须把这个0x55,0xAA加上去.
好了,这个微型的Boot Loader的介绍算完毕了.但是这个Boot Loader Program功能还不齐全呢.因为Boot Loader Program最重要的功能就是把OS的kernel导出来运行.我们总不可能把OS最到boot loader program里嘛,boot loader program最大只能有512,怎么写?
把kernel导出来的方法应是很简单的.特别是我们选择的软盘作为我们OS的存储介质,那么读取数据是轻松加愉快的事情了.BIOS中有个13h的磁盘管理方面的中断程序为我们提供了很多方面.这里我不再仔细介绍这个中断了,你可以去参考一下汇编语言方面的书籍,上面应该给出详细的介绍
我的OS很小,而且是16位实模式下的,所以kernel(内核)也很小.我规定为8K,恰好16个扇区的大小.1.44MB软盘一个磁道(柱面)共18个扇区.我的kernel加上我的boot loader共17个扇区的大小.好!刚好能够在一个盘面的一个磁道装下.
对了,我还忘了介绍怎么把我们boot loader program写到磁盘上.
很多方法,特别对于软盘来说.你去找个已经注册了的WinHex的软件,上面支持对磁盘扇区的读写,而且很方便.另一种方法就是到前面我介绍的那个网站去下载一个PartCopy的程序,不过我从来没有用过这个东西.因为我的WinHex功能已经很全面了而且使用很方面很直观,用剪贴板就就可以把文件上数据复制到磁盘上任何一个扇区中了.对于老dos下的debug工具也可以来写磁盘.
比如你的boot loader program名字叫boot.asm.然后用nasm来编译
nasmw boot.asm -o boot.bin
debug
-n boot.bin
-l 0
-w 0 0 0 1
-q
在你的dos下打入上面黄色的命令就可以把boot.asm编译并写入磁盘了.虽然看起来用debug十分方便,但是我觉得还是WinHex这个软件好.
前面不是说了我们这个boot loader 功能不全吗?现在我们把它功能加全吧.对于16实模式下的O很简单,我们用BIOS 13h中断把磁盘后面16个扇区的数据读出来就是了.至于读到内存中什么位置就由你来决定了.有的人喜欢读到0x800:0x0000,我的这个OS是读到0x500:0x0000.可能你把kernel读到其它地方会出现无法预料的错误出现,那么你就读到0x500:0x0000吧.我的OS的kernel就是在那里运行的,而且运行正常.
前一篇文章中的Boot Loader中已经给出来读kernel的代码.你也可以去看看<<关于16位OS的尝试(1)>>.
这里给出的只是调用13h读kernel的代码
mov ax, 0x500 ; 先将扇区01的数据存放的缓冲段地址传递给AX
mov es, ax ; 通过AX,再缓冲段地址传递给ES
mov bx, 0 ; 缓冲偏移地址为0
mov dl, 0 ; 要读取的驱动器号为0h,为A软驱
mov dh, 0 ; 要读取的磁头号为0
mov ch, 0 ; 要读取的磁道号为0
mov cl, 2 ; 要读取的扇区号为2
mov al, 16 ; 要读取的扇区数为16,因为我的kernel共16个扇区大
mov ah, 2 ; 调用读磁盘的中断程序
int 13h
好了.读完kernel到0x500:0x0000后,就应该去执行kernel.
这个在汇编语言中很容易实现,用个jmp指令不就OK了吗?
但是除了在用jmp的时候需要注意一些细节问题
mov ax, 0x500 ; 跳转指令到0x500:0000,并把es,ds都改到0x500,但是注意,在jmp指令前不能改cs
mov es, ax
mov ds, ax
jmp 0x0500:0x0000
上面红色的就是boot loader的跳转到kernel的代码.可以看出,我们除了jmp 0x0500:0x0000外,还需要把es,ds等寄存器设置对.上面绿色的注释记录了我曾犯过的一个小错误.当时我除了把es,ds设置成0x500外,还把cs都该成0x500了. cs是记录我当前代码的段地址的,我们用jmp 0x500:0x0000的时候,cs会自动变成0x500.所以我们在jmp之前绝不能改变cs.一旦改变了cs,那么计算机执行的代码就会跳到0x500的段地址去执行.这些话说起来就点绕,不知道我说清楚了没有.我们计算机的CPU执行代码是按CS:IP中指到的顺序来执行的,每执行一条指令,IP就移动到下一条.所以一旦CS或者IP变了,那么我们的执行顺序也就变了.所以一般我们是不能碰CS和IP的.
好了,当你的boot loader编译成功后,写到磁盘上去,放入计算机,重新启动计算机,并以软盘启动,你就可以看到你的OS的boot loader的运行效果了.
如果我们每次都要重新启动计算机才能看到我们的OS运行结果,这样会很麻烦的.还好,老外们写了很多很好的模拟器,可以来模拟计算机启动运行我们的OS的情景.
bochs-2.0就是一个很好的80x86的模拟器.同样的,你可以到http://osdev.neopages.net/index.php去下载这个软件.它的功能很多,除了模拟软盘启动计算,还可以模拟硬盘和光盘启动计算机呢.最开始我拿到它的时候我不知道怎么用呢.它的使用需要设置一些参数.我现在就直接把它的使用方法说了吧.
首先解压后,进入bochs-2.0,里面已经有一个dlxlinux的使用范例.我直接拿它来修改一下就可以用了.打开start.bat,加入
SET BXSHARE=F:\bochs-2.0
..\bochs
不知道怎么的,它的环境目录BXSHARE没有设置,所以我们需要在start.bat设置一下,我的bochs-2.0是安装在F:\bochs-2.0中的,所以设置成SET BXSHARE=F:\bochs-2.0
然后打开bochstr.txt,找到
# choose the boot disk.
boot: a
把boot修改成a:,这样bochs-2.0就会模拟从A:启动
# what disk images will be used
floppya: 1_44=floppya.img, status=inserted
floppyb: 1_44=floppyb.img, status=inserted
floppya: 1_44=floppya.img说明了我们的A盘景象文件是floppya.img
这里又涉及到了一个如何创建floppya.img景象文件的问题.boch的景象文件很简单,就是一个完全的1.44MB的文件.你把软盘上的每个扇区的数据按顺序把复制到这个文件里去就可以了.
在boch中有个程序bximage.exe可以帮你创建floppya.img景象文件
然后把你的boot loader编译后的boot.bin复制到floppya.img头512字节中去就可以了.你可以用WinHex来复制,很方便,直接用剪贴板复制都可以.所以我一直说WinHex真的是个很好的东西呢!
好了,一切工作都做好了.运行start.bat,出现了个选择,不用管它,我们的参数设置都设置好了,直接"模拟运行"就可以了.紧接着boch弹出个窗口,那个就是我们boch模拟运行OS的画面了.
好累,讲了这么多.连kernel都还没有开始呢.下一节我们就开始进入kernel的部分吧!