在网上看到这篇文章的原文,这篇文章写的浅显易懂。即使看英文,大家也都能看懂。只是出于练练自己手的目的,也是因为太无聊的缘故,翻译了这篇文章。有些地方可能翻译的并不对,放到这儿的目的纯属为了自我安慰,感兴趣的话,建议看原版。
介绍:
本文档指导你如何去开始实现自己的操作系统,并给出一个实现操作系统的方法。本文档没有试图去解释所有有关操作系统理论方面的知识,也没有去使用最好或者最快的方法和技巧。所有的源码是为了更容易理解的目的而写,而且没有优化。
我假定读者使用一个标准的PC结构的计算机,使用Intel x86或者兼容x86的处理器。以后的章节,需要一个拥有IA-32体系结构(i386 到 Petuinum 4)的处理器。另外一些章节假定用户使用FAT 格式的文件系统;还有一些章节要求用户使用MS-DOS/Windows 的DEBUG命令,我假定这些命令会正常执行。我不会深入讲解BIOS,汇编语言程序,Intel或者PC的体系结构等,这些内容的知道可以在其它地方找到。在Intel的网站上可以找到很好的针对Intel 处理器编程的汇编语言资料。
我自己也在通过这些材料学习有关处理器的知识,学习这些可以提升我的经验,在学新的东西的时候可能会有新的发现。所以,这个课程的数量会继续增加,记得时常访问该站。如果你对未来的章节有任何建议,请告诉我。另外,你可以把你的建议,问题和评论发送到我的信箱。
声明:使用该文档,你要自己承担风险。对于使用这里的信息而造成的不良后果,我概不负责。
第一节: 引导
这一节我们要学习有关引导部分的知识,并且开始写我们自己的引导程序。
当计算机从软盘启动的时候,BIOS读取并加载第一个扇区的内容到内存地址:0000:7C00。第一个扇区的名字是:DOS引导区,简写为DBR。BIOS将跳转到内容地址0x7c00,并且开始在哪儿执行指令。此处的指令将会将操作系统载入内存,并开始操作系统的加载;所以这些指令被称为 系统加载(boot loader) 指令。
我们要做的第一步是去看一下引导扇区的内容。 DOS的DEBUG是一个用户很广的工具,可以帮助我们产看内存和磁盘中的内容。我们用它来查看软盘中的引导扇区。
在DOS或者Windows的命令行模式下键入 “debug”命令,这时候你可以看到一个连字符(-)。在连字符后,输入单词”d”,并且按回车,屏幕上将会显示内存中的一段内容。如果你在连字符后输入问号(?),屏幕上会显示DEBUG中可以使用的命令列表。(请谨慎使用DEBUG,它可能会覆盖掉磁盘中的内容而导致数据丢失)。
在A驱中放一张刚刚格式化过的软盘。载入软盘中的引导扇区内容的方法是,在DEBUG中输入一下内容:
l 0 0 0 1
(第一字符不是数字 1,而是L的小写 )。这个命令是把磁盘中的内容加载到内存中。L后的4个数字代表的意义是:第一个数字指定了数据被加载到内存时,在内存中的开始位置;第二个数字是驱动器的号码(0代表的是软驱);第三个数字是要被加载内容的开始扇区的号码;第四个数字表示有几个扇区要被加载。以上的命令是加载软驱中的第一个扇区的内容到内存,内存的开始位置是0。
现在,我们把引导扇区加载到了内存,我们来看它的内容。输入以下的命令:
d 0
你将看到8行内容,是软盘引导区开始的128(16进制是0x80)个字节。我软盘中的内容如下:
0AF6:0000 EB 3C 90 4D 53 44 4F 53-35 2E 30 00 02 01 01 00 .<.MSDOS5.0.....
0AF6:0010 02 E0 00 40 0B F0 09 00-12 00 02 00 00 00 00 00 ...@............
0AF6:0020 00 00 00 00 00 00 29 F6-63 30 88 4E 4F 20 4E 41 ......).c0.NO NA
0AF6:0030 4D 45 20 20 20 20 46 41-54 31 32 20 20 20 33 C9 ME FAT12 3.
0AF6:0040 8E D1 BC F0 7B 8E D9 B8-00 20 8E C0 FC BD 00 7C ....{.... .....|
0AF6:0050 38 4E 24 7D 24 8B C1 99-E8 3C 01 72 1C 83 EB 3A 8N$}$....<.r...:
0AF6:0060 66 A1 1C 7C 26 66 3B 07-26 8A 57 FC 75 06 80 CA f..|&f;.&.W.u...
0AF6:0070 02 88 56 02 80 C3 10 73-EB 33 C9 8A 46 10 98 F7 ..V....s.3..F...
刚开始看,它不会告诉我们太对事情。我能看到这是一个MS-DOS 5.0的盘,没有名字,使用着FAT12的文件系统。最左边的数字显示了它们在RAM中的内存地址,中间的十六进制数是这段内存中的所有数据,最右边的显示16进制数相应的ASCII码(如果16进制数不能转化为可显示的字符,用一点代替)。这些你看到的字节,有的是引导装载内容的指令,一些是有关磁盘的信息:如,每个扇区的字节数,每个磁道的扇区数等。
现在我们看一下引导装载系统的代码,在命令行下输入以下命令:
u,0
这是一个反汇编指令,这次,在屏幕上显示和上次相同的字节信息(从位置0开始),但这次显示的是这次反编译过来的Intel 指令。下面是我的软盘的显示结果:
0AF6:0000 EB3C JMP 003E
0AF6:0002 90 NOP
0AF6:0003 4D DEC BP
0AF6:0004 53 PUSH BX
0AF6:0005 44 INC SP
0AF6:0006 4F DEC DI
0AF6:0007 53 PUSH BX
0AF6:0008 352E30 XOR AX,302E
0AF6:000B 0002 ADD [BP+SI],AL
0AF6:000D 0101 ADD [BX+DI],AX
0AF6:000F 0002 ADD [BP+SI],AL
0AF6:0011 E000 LOOPNZ 0013
0AF6:0013 40 INC AX
0AF6:0014 0BF0 OR SI,AX
0AF6:0016 0900 OR [BX+SI],AX
0AF6:0018 1200 ADC AL,[BX+SI]
0AF6:001A 0200 ADD AL,[BX+SI]
0AF6:001C 0000 ADD [BX+SI],AL
0AF6:001E 0000 ADD [BX+SI],AL
第一条指令是跳到地址0x3E。下面的字节是我提到过的诸如磁道扇区数等磁盘信息,并不是相应的指令,但是DEBUG还是把它们转化成了指令。
跳过这些数据后,引导程序从地址0x3E开始,让我们来看下面的这些指令。输入
u 3E
你将看到装载DOS 或者Windows操作系统文件的代码。如果是DOS,这段代码将在磁盘上查找IO.SYS 和 MSDOS.SYS文件;这些文件包含操作系统的代码。引导装载代码会把这些文件载入内存,并开始执行它们。机器启动过程中,如果在磁盘上找不到这些文件,将会显示这个著名的错误信息:
Invalid system disk
Disk I/O error
Replace the disk, and then press any key
这段信息可以在DOS引导程序的最后部分找到。以下是在我的软盘上的情形:
-d 180
0AFC:0180 18 01 27 0D 0A 49 6E 76-61 6C 69 64 20 73 79 73 ..'..Invalid sys
0AFC:0190 74 65 6D 20 64 69 73 6B-FF 0D 0A 44 69 73 6B 20 tem disk...Disk
0AFC:01A0 49 2F 4F 20 65 72 72 6F-72 FF 0D 0A 52 65 70 6C I/O error...Repl
0AFC:01B0 61 63 65 20 74 68 65 20-64 69 73 6B 2C 20 61 6E ace the disk, an
0AFC:01C0 64 20 74 68 65 6E 20 70-72 65 73 73 20 61 6E 79 d then press any
0AFC:01D0 20 6B 65 79 0D 0A 00 00-49 4F 20 20 20 20 20 20 key....IO
0AFC:01E0 53 59 53 4D 53 44 4F 53-20 20 20 53 59 53 7F 01 SYSMSDOS SYS..
0AFC:01F0 00 41 BB 00 07 60 66 6A-00 E9 3B FF 00 00 55 AA .A...`fj..;...U.
这段正好显示了引导记录的结束部分。引导部分正好占用了磁盘上的一个扇区(512字节),如果它被加载到内存的开始地址是0,那么他的最后一个字节将是0x1FF。如果你看到引导记录最后两个字节(地址0x1FE 和0x1FF)的内容是0x55和0xAA。这两个字节必须被设置成这样的值,否则的话其它的BIOS就不能引导这个扇区并执行它们了。
总结一下这节的内容:DOS启动记录开始的一条指令是跳转过一段表示磁盘信息的数据,接着执行其它指令。从0x02开始,到0x3d结束的包含60个字节的磁盘信息;启动程序重新从0x3E开始执行,直到地址0x1FD,接着这段代码最后以0x55和0xAA结束。在下一节,我们将要通过这些内容定制我们自己的程序。
第二节:制作我们的第一个引导盘
这一节,我们来学习在软盘上制作引导程序。我们从修改DOS的引导记录开始。
我们的目的是修改启动装载部分的代码而没有改变引导扇区的其它数据。如果我们错误修改了一些数据,那么DOS或者Windows将不认这些磁盘。Windows将要给一个错误信息说这个磁盘没有格式话。这样你将不能操作这个磁盘上的任何文件。然而,我们以我们想要的方式修改引导程序中的内容,而且不丢失其它数据;并且DOS和Windows仍旧可以很好的读写这些数据。
我们保留第一条指令,因为我们要跳过引导记录的数据区。我们在0x3E处开始修改代码,执行DEBUG程序,从软盘的第一个扇区读取内容到内存地址0。输入命令
u 3E
观察这儿的指令。现在我们开始修改代码,输入命令
a 3E
开始组合指令。下划线提示符变成了我们所输入的内存地址位置。输入一下的指令并回车
jmp 3E
这条指令被转化成机器指令,并且被放回到内存中。下面的提示符是我们刚才输入的指令的下面的内存地址。再一次输入回车,返回到组合模式。整个过程如下所示:
-a 3E
0AFC:003E jmp 3E
0AFC:0040
-
这条指令的作用是跳转到0x3E,所以它将一直继续。试着用这个盘引导计算机,似乎什么也没有发生,计算机没做任何事。事实上你的新“操作系统”正在运行。
OK,我知道你想说:你希望去些一些标志证明你的程序正在运行而没有搞乱计算机。实现这个,我们需要调用BIOS(http://users.win.be/W0005997/GI/biosref.html)。你需要用这些功能来实现它们。
我们将要使用中断0x10和功能0x0E去些一个字符到屏幕上。寄存器必须按照以下的方式设置:
AH = 0x0E
AL = ASCII code of the character to be printed
BL = color/style of character
现在,重复上面的指令,用下面的指令更换上卖弄的指令。
-a 3E
0AF6:003E mov ah, 0e
0AF6:0040 mov al, 48
0AF6:0042 mov bl, 07
0AF6:0044 int 10
0AF6:0046 jmp 46
0AF6:0048
-
首先,我们设置AH为0,AL为0x48(ASCII字符”H”),BL为7(屏幕颜色:黑底白字),然后调用中断0x10,它处理视频显示。最后的指令实现无限的挑战,保存这些信息到磁盘的引导扇区(-w 0 0 0 1),用它重新引导机器。这次,在系统挂起的时候,你将在屏幕上看到大写的字符”H”。
你可以尝试打印不同的字符在屏幕上,或者尝试用不同的软件中断。接着,我们开始下一节,用成熟的汇编编译器代替DEBUG来完成程序。
第三节 NASM
这节,我们学习用汇编编译器来完成我们的程序。前面的章节,我们利用DEBUG来编译程序。玩一会DEBUG,你将发现这是一件痛苦的事:DEBUG只能用少数的指令,而且修改也痛苦。我们需要一个简单的方法,尝试使用NASM(Netwide Assembler)吧。你可以去它的官方网站http://nasm.sourceforge.net/index.php 下载一个版本。
现在,我们使用这个编译器去制作一个和第二节一样的”操作系统”。下载引导程序 h.asm ,打开它。第一条指令是我们熟悉的,这条指令是跳过引导记录的数据区。在这儿,它跳到了标号 begin 处。跳转指令之后是20字节的数据,这些数据是我曾用DEBUG从我的软盘上读到的数据。这些变量将很好的运行。你也可以把这些数据变成从你自己盘上读到的数据。大体上它们应该是相同的。
(注意:这里超过一个字节的数字和你在DEBUG中看到的结果是相反的,因为在Intel体系结构中,最前面的字节被保存在低位的内存地址中。)
这份代码从 begin 开始,和我们在第二节写的代码类似。它只是把大写H 打印到屏幕上并开始循环。在文件底部,你可以看到一个检测:确认这个文件在512字节以内(一个扇区的长度)。下面的一行以 times 开始,在文件后端的字节设置为0,直到第510字节。最后,0x55和0xAA被追加到文件的末尾。在命令行下,使用下面的语句编译这个文件
nasmw h.asm –0 b.bin
它将把这段代码编译成单纯的二进制文件 h.bin。检查这个二进制文件的长度,正常操作的话,它应该是512字节。这个正是软盘引导扇区所需要的长度。
现在我们需要把这个文件移到软盘上区。插入软驱,运行DEBUG,执行下面的命令:
debug
-n h.bin
-l 0
这样我们的文件被加载到内存中,开始地址是0。如果想确认我们的文件是否被正确加载,可以使用d和u命令。你可以看到我们写的很少几条指令。注意这个文件是否已经被正确的加载0在扇区结束的0x1FE 和0x1FF前面。也可以查看寄存器CX,它有从文件加载的字节数。(显示寄存器内容的命令是r)
写文件到磁盘的命令是:
w 0 0 0 1
使用软盘重新引导计算机,产看这个程序的效果。使用这份代码,尝试更多的事情。例如,尝试打印更多的字符。当你准备号的时候,继续下一节,我们将制作一个”Hello,World”操作系统。