05年底就卖了第一版《自己动手写操作系统》,也将其陆陆续续读完。09年第二版《Orange’s一个操作系统的实现》出版后,也将其购入。随手翻来,发现云里雾里,几年前的理解早就抛光,重读已是困难重重。既然写代码要有注释,读书亦要有注释。
1搭建OS开发环境
第一步:安装Ubuntu10.04。
具体安装过程可参考《鸟哥的Linux私房菜基础学习篇》第四章。
第二步:安装GCC和NASM。
在Ubuntu终端命令窗口输入:sudoapt-get install build-essential nasm
在build-essential中包含了GCC和GNUMaker,关于GNUmaker可打开http://www.linuxsir.org/main/doc/gnumake/GNUmake_v3.80-zh_CN_html/index.html了解。
第三步:安装虚拟机Bochs。
在bochs官网http://sourceforge.net/projects/bochs/files/bochs/2.4.5/下载
。终端命令切换到bochs2.4.5目录下,输入:rpm-ivh bochs-2.4.5-1.i586.rpm
到目前为止,我在Ubuntu系统下安装了GCC和NASM编译器,以及虚拟机Bochs。目前,自己动手写操作系统的具体的流程如下:
-
在emacs下编写asm文件
-
使用NASM编译器编译asm文件为bin文件
-
将bin文件写入软盘
-
编写bochs配置文件
-
启动bochs
下面我将遵循流程编写第一个在bochs中启动的程序。
2编写boot.asm
在终端命令窗口输入:emacsboot.asm,编写如代码清单1所示代码。
代码清单1boot.asm
org 07c00h ;告诉编译器程序加载到7c00处
movax,cs
movds,ax
moves,ax
call DispStr ;调用显示字符串例程
jmp $ ;无限循环
DispStr: ;本段代码为10h中断设置寄存器
movax,BootMessage
movbp,ax ; es:bp = 串地址
movcx,16 ; cx = 串长度
movax,01301h ; ah = 13, al = 01h
movbx,000ch ; 页号为0(bh = 0)黑底红字(bl = 0ch,高亮)
movdl,0
int 10h ; 10h号中断,在显示器上显示字符
ret
BootMessage:
db "Hello,OS world!"
times510-($-$$) db 0 ;填充剩下的空间,使生成的二进制代码为512字节
dw0xaa55 ;结束标志
下面我们来分析代码清单。
2.1寄存器
在代码清单中,使用了寄存器ax、bx、cx、dx、cs、ds、es、bp,在代码清单1中用红色加粗字体表示。将这8个寄存器分类如下:
2.1.1数据寄存器
数据寄存器主要用来存放源操作数、目的操作数或运算结果等信息,从而节省读取操作数占用总线和访问存储器的时间。数据寄存器分别命名为:ax、bx、cx和dx,4个16位寄存器又可分割成8个独立的8位寄存器(ax:ah-al、bx:bh-bl、cx:cx-cl、dx:dh-dl),每个寄存器都有自己的名称,可独立存取。
(1)寄存器ax和al通常称为累加器(Accumulator),用累加器进行的操作可能需要更少时间。累加器可用于存放数据、算数运算、输入/输出等操作,它们的使用频率很高。代码清单1中使用到ax的代码有:
……
;将当前代码段的地址赋值给ax
movax,cs
;将当前ax中的地址(代码段地址)赋值给数据段寄存器ds
movds,ax
;将当前ax中的地址(代码段地址)赋值给附加段寄存器ex
moves,ax
……
DispStr:
;将bootMessage指向的地址赋值给ax
movax,BootMessage
;在int10h中断中,es:bp表示要显示字符串的地址
movbp,ax
……
;当调用int10h中断时,使用ax设置功能
;ah= 13表示在Teletype模式下显示字符串(什么时Teletype模式?没搞懂)
;al= 01表示字符串中只含显示字符,其显示属性在bl中
;显示后,光标位置改变
movax,01301h
……
(2)寄存器bx称为基址寄存器(BaseRegister)。用来存储地址和访问地址。
;bh= 0表示页号为0
;bl= 0ch,当al = 00h或01h时,使用bl属性
;0ch表示黑底红字
movbx,000ch
(3)寄存器cx称为计数寄存器(CountRegister)。在循环和字符串操作时,要用它来控制循环次数。
;在int10h中断中表示显示字符串长度
movcx,16
(4)寄存器dx称为数据寄存器(DataRegister)。在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。
;在int10h中断中dh表示字符串要显示的行,dl表示字符串要显示的列
movdl,0
2.1.2段寄存器
段寄存器是根据内存分段的管理模式而设置的。内存单元的物理地址由段寄存器的值和一个偏移量组合而成的,这样可用两个较少位数的值组合成一个可访问较大物理空间的内存地址。CPU内部的段寄存器:
cs:代码段寄存器(CodeSegment Register),其值为代码段的段值;
ds:数据段寄存器(DataSegment Register),其值为数据段的段值; es:附加段寄存器(ExtraSegment Register),其值为附加数据段的段值;3编译asm文件
在终端命令窗口中输入:nasmboot.asm -o boot.bin
4 写入软盘
在终端命令窗口输入:ddif=boot.bin of=a.img bs=512 conv=notrunc
if =输入文件(或设备名称)
Of =输出文件(或设备名称)
bs =bytes同时设置读/写缓冲区的字节数
conv =notrunc不截断输出文件
5编写bochs配置文件
在a.img同目录下,编写bochsrc配置文件。
###############################################################
#Configuration file for Bochs
###############################################################
#how much memory the emulated machine will have
megs:32
#filename of ROM images
#安装bochs时,将会在share目录下生成(红色标注文件)
romimage:file=/usr/share/bochs/BIOS-bochs-latest
vgaromimage:file=/usr/share/vgabios/vgabios.bin
#what disk images will be used
floppya:1_44=a.img, status=inserted
#choose the boot disk.
boot:floppy
#where do we send log messages?
#log: bochsout.txt
#disable the mouse
mouse:enabled=0
#enable key mapping, using US layout as default.
keyboard_mapping:enabled=1, map=/usr/share/bochs/keymaps/x11-pc-us.map
5 启动OS
在终端命令窗口输入:bochs-f bochsrc,将会在终端看到如下过程。
Compiledat Aug 5 2011, 23:33:08
======================================================================
00000000000i[ ] reading configuration from bochsrc
------------------------------
BochsConfiguration: Main Menu
------------------------------
Thisis the Bochs Configuration Interface, where you can describe the
machinethat you want to simulate. Bochs has already searched for a
configurationfile (typically called bochsrc.txt) and loaded it if it
couldbe found. When you are satisfied with the configuration, go
aheadand start the simulation.
Youcan also start bochs with the -q option to skip these menus.
1.Restore factory default configuration
2.Read options from...
3.Edit options
4.Save options to...
5.Restore the Bochs state from...
6.Begin simulation
7.Quit now
Pleasechoose one: [6]
选择6,回车。将会看到bochs启动界面。
00000000000i[CPU0] CPUID[0x00000003]: 00000000 00000000 00000000 00000000
00000000000i[CPU0] CPUID[0x00000004]: 00000000 00000000 00000000 00000000
00000000000i[CPU0] CPUID[0x00000007]: 00000000 00000000 00000000 00000000
00000000000i[CPU0] CPUID[0x80000000]: 80000004 00000000 00000000 00000000
00000000000i[CPU0] CPUID[0x80000001]: 00000000 00000000 00000000 00000000
00000000000i[CPU0] CPUID[0x80000002]: 20202020 20202020 20202020 6e492020
00000000000i[CPU0] CPUID[0x80000003]: 286c6574 50202952 69746e65 52286d75
00000000000i[CPU0] CPUID[0x80000004]: 20342029 20555043 20202020 00202020
00000000000i[ ] reset of 'unmapped' plugin device by virtual method
00000000000i[ ] reset of 'biosdev' plugin device by virtual method
00000000000i[ ] reset of 'speaker' plugin device by virtual method
00000000000i[ ] reset of 'extfpuirq' plugin device by virtual method
00000000000i[ ] reset of 'iodebug' plugin device by virtual method
00000000000i[ ] reset of 'ioapic' plugin device by virtual method
00000000000i[ ] reset of 'keyboard' plugin device by virtual method
00000000000i[ ] reset of 'harddrv' plugin device by virtual method
00000000000i[ ] reset of 'serial' plugin device by virtual method
00000000000i[ ] reset of 'parallel' plugin device by virtual method
00000000000i[XGUI] [x] Mouse off
00000000000i[ ] set SIGINT handler to bx_debug_ctrlc_handler
Nextat t=0
(0)[0x00000000fffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b ; ea5be000f0
<bochs:1>
在终端命令窗口输入:<bochs:1>c,加载OS。到目前为止,我成功使用bochs加载自己编写的OS。虽然很简单,但是毕竟迈出了第一步。