写在前面的话:
一直以来就想要自己动手写一个简单的操作系统,可惜水平有限,在linux下编译安装一个程序几天都没有成功,更不用说研究Linux的内核代码了。后来买来minix的书籍,看了半个月,也不知道如何开始写自己的第一行代码……
无意中发现于源写的《自己动手写操作系统》,终于让我真正踏上了编写操作系统的第一步。这里要非常感谢于源大大为广大学习编写操作系统的初学者作出的巨大贡献。
可惜好景不长,因为对书本中第二章配置实验环境这一关没有完全通过,在第五章就卡壳了。之后因为电脑资源吃紧,将实验用的虚拟机都删除了。学习之路也就沉默了下来。
最近,在逛CSDN时,无疑发现了杨晓兵大大的博客以及所编写的编译器YC09还有上面作为范例之一的tinix操作系统,才知道,原来编写测试操作系统代码可以这么简单——只需一个yc09编译器与虚拟机Bochs就可以了,再也不许要花费大力气来配置实验环境安装Virtual PC、VMware以及DOS和Linux,又是使用nasm又是gcc,以及一次又一次启动虚拟机里的DOS或Linux。于是重新燃起了编写操作系统的热情。
实验环境:windows XP
编译器:YC09
运行调试工具:Bochs
实验代码结构说明:
所有实验都是由run.c以及操作系统实际实验代码组成。run.c的作用是生成run.exe程序,由run.exe程序自动编译实验代码,生成img以及调用运行Bochs。只要一次编译好run.c,此后修改微调实验代码后,直接运行run.exe即可。我特地设置run.c为一个无限循环。于是修改测试实验代码就变得非常简单:
(1)运行run.exe
(2)查看虚拟机运行效果
(3)关闭虚拟机
(4)修改实验代码
(5)在run.exe的控制台里点击回车(第一轮一次,以后要两次点击回车)
(6)回到第二步……
下面是本次实验代码:
一个写在引导扇区的简单程序——在屏幕上显示一个字符串。
尽管网上已经有了无数个版本的例子,在这里,我还是厚脸皮的拿出来与大家分享。
code:run.c
//文件:run.c //功能:编译操作系统的代码并创建img,生成bochs配置文件,运行bochs //运行:请使用yc09编译器编译运行。 //作者:miao //时间:2010-1-12 //虚拟机设置 char *bootSrc = "megs: 32 /n" "romimage: file=BIOS-bochs-latest, address=0xf0000 /n" "vgaromimage: VGABIOS-elpin-2.40 /n" "floppya: 1_44=boot.img, status=inserted /n" "boot: a /n" "log: log.out /n" "mouse: enabled=0 /n" "keyboard_mapping: enabled=1, map=x11-pc-us.map /n"; //获取当前文件夹路径 char * GetFilePath() { static char filePath[MAX_PATH];//文件路径,全局有效 //获取可执行文件的完整路径 int fileNameLength = GetModuleFileName(NULL,filePath,MAX_PATH); if(!fileNameLength) { printf("获取当前运行程序文件路径失败!/n"); return filePath; } //去除程序名称:run.exe,只保留文件夹路径 for( int i = fileNameLength;filePath[i] != '//';i--) filePath[i] = '/0'; return filePath; } void main() { _start: #define FDISK_SIZE 1474560 //镜像大小:1.4mb byte *imgBuffer = new byte[FDISK_SIZE]; //镜像缓冲区 char * filePath = GetFilePath(); //当前文件夹路径 char fileName[MAX_PATH]; //用于缓存各个文件名 //获取可执行文件的路径,加上引导文件名 strcat(strcpy(fileName,filePath),"boot.c"); printf("引导文件名:%s/t/n",fileName); char *bootBuffer; //保存引导程序的缓冲区 //编译引导程序,结果放到bootBuffer中 int bootLength = YC_CompileCpp(&bootBuffer,fileName,0,0); if(bootLength <= 0) { printf("文件: %s 中存在一些错误/n", fileName); return; } printf("文件: %s 编译成功,大小为:%d字节。/n", fileName,bootLength); //将引导程序放到镜像缓冲区 memcpy(imgBuffer,bootBuffer,bootLength); free(bootBuffer); //0000H-01FFH为FAT引导信息 以55AA标志结束 长度为200H(512)字节 [第0扇区] imgBuffer[510] = 0x55; imgBuffer[511] = 0xaa;//标记软盘引导结尾 //创建操作系统镜像boot.img //获取可执行文件的路径,加上镜像名 strcat(strcpy(fileName,filePath),"boot.img"); if(YC_writefile(fileName,imgBuffer,FDISK_SIZE) != FDISK_SIZE) { printf("写: %s 文件过程中出现错误。/r/n", fileName); return; } delete imgBuffer; printf("/n%s 创建成功。/n", fileName); //生成操作系统虚拟机配置文件boot.src YC_writefile(strcat(strcpy(fileName,filePath),"boot.src"),bootSrc,strlen(bootSrc)); //运行虚拟机 YC_WinExec(strcat(strcpy(fileName,filePath),"bochs.exe"), "-q -f boot.src"); printf("/n点击回车重新编译运行!/n/n/n"); getchar(); goto _start; }
code:boot.c
//文件:boot.c //功能:处于引导扇区的一个显示一个字符串的简单程序 //运行:run.exe自动会编译boot.c与生成img并调用Bochs运行此程序 //提示:请先用yc09编译run.c文件,生成run.exe程序 // 之后修改boot.c代码,可直接运行run.exe查看效果 #define YCBIT 16 //告诉编译器,以16位格式编译程序 #define YCORG 7c00h //告诉编译器,在7c00处加载程序 #define MsgLngth 12 //串长度 char Msg[] = "Hello, Miao!"; asm void main() { mov ax, cs mov ds, ax mov es, ax ; 清屏 mov ah, 06h ; 屏幕初始化或上卷 mov aL, 00h ; AH = 6, AL = 0h mov bx, 01111h ; 设置底色为蓝色 mov cx, 0 ; 左上角开始: (0, 0) mov dl, 4fh ; 到第x列 mov dh, 1fh ; 到第x行 int 10h ; 显示中断 ; 显示字符串 mov ax,&Msg mov bp,ax ;es:bp=串地址 mov ah, 13h ; AH:13显示字符串 mov al, 1h ; AH = 13, AL = 01h mov bx, 0014h ; 页号为0(BH = 0) 蓝底红字(BL = 14h) mov cx, MsgLngth ; CX = 串长度 mov dl, 00h ; 起始列 mov dh, 00h ; 起始行 int 10h ; 显示中断 }
想要实验以上代码的朋友请注意:
1.到杨晓兵大大的博客上下载安装yc09,安装只需一分钟左右。
2.将以上run.c、boot.c代码拷贝到某个实验用的文件夹内。
3.在YC09/example文件夹内找到bochs.exe、BIOS-bochs-latest、VGABIOS-elpin-2.40、x11-pc-us.map四个文件拷贝到试验用的文件夹内。
4.使用yc09编译运行run.c
ps1:YC09/example文件夹内有tinix操作系统的示例,有兴趣的朋友可以直接研究它。另外还有许多有趣开源的程序代码作为yc09的示例,值得一看。
ps2:最近逛新华书店,发现于源大大的《自己动手写操作系统》出了第二版,可怜我第一版都还没摸热……