分享

30天自制操作系统

 liluvu 2015-12-09

/**********第0天 着手开发之前**********/
所谓开发操作系统,就是想办法制作一张“含有操作系统的,能够自启动的磁盘”。

主要的学习内容:
第一周:
1. 写一个"一通电就能运行的程序“
2. 写一个从磁盘读取操作系统的程序
3. c语言来开发显示画面的程序
4. 实现”移动鼠标“,对cpu进行细致的设定,掌握中断处理程序的写法

第二周:
5. 算法的学习

第三周:
6. 支持多任务
7. 开发命令行窗口
8. 开发应用程序

第四周:
9. 集中精力在编程上
10. 操作系统支持文字显示

第五周:
11. 润色加工

/**********第1天 从计算机结构到汇编程序入门**********/
利用二进制编辑工具编辑二进制文件,二进制文件包含cpu执行的机器指令,文件中是裸的机器指令。
功能是用来启动计算机,并在屏幕上显示hello world。

通过汇编语言编写上述程序,编译后生成以上功能的二进制文件,即启动区的程序。
启动区大小为512字节,并以55 AA结束,以标志是启动区。

/**********第2天 汇编语言学习和Makefile入门**********/
在PC机加电ROM BIOS自检后,引导扇区由BIOS加载到内存0x7C00处.
因此引导扇区的代码要用org伪指令:
ORG 0x7C00 伪指令说明其后面程序的目标代码在内存中存放的起始地址是0x7C00

ORG伪指令:
ORG是Origin的缩写:起始地址,源。
在汇编语言源程序的开始通常都用一条ORG伪指令来实现规定程序的起始地址。
如果不用ORG规定则汇编得到的目标程序将从0000H开始。例如:
 ORG 2000H   
 START:MOV  AX,#00H

汇编语言源程序中若没有ORG伪指令,则程序执行时,指令代码被放到自由内存空间的CS:0处;
若有ORG伪指令,编译器则把其后的指令代码放到ORG伪指令指定的偏移地址。
两个ORG伪指令之间,除了指令代码,若有自由空间,则用0填充。

预处理-->编译-->汇编-->链接,这是高级语言的编译全过程。对于纯汇编,就只有汇编和链接两个步骤。
org指令是链接时使用的,不是汇编那一步使用的。即不是cpu的一条指令,而是给编译器看的伪指令。

BIOS中断服务:
如INT 0x10用来显示文字。
BIOS的功能非常多,可以看成是为操作系统开发人员准备的各种函数集合。

/**********第3天 进入32位模式并导入c语言**********/
为启动区添加读取软盘数据的功能代码,利用INT 0x13中断来读取软盘10个柱面的数据,并放到内存中。
软盘的数据将被加载到内存中0x8200到0x83ff的地方。

有以上功能后,编写汇编代码文件haribote.nas(操作系统的功能),通过nask编译成haribote.sys,
这个文件保存到磁盘映象haribote.img中,最后保存到软盘中。

用二进制编辑器查看haribote.img,可以发现,一般向一个空软盘保存文件时:
1. haribote.sys的文件名会写在0x002600以后的地方
2. haribote.sys的内容会写在0x004200以后的地方
因此,我们将操作系统本身的内容写到名为haribote.sys文件中,再把它保存到磁盘映象里,然后我们从启动区执行这个haribote.sys就行了。

现在的程序是从启动区开始,把磁盘上的内容装载到内存0x8000号地址(第二扇区装载在0x8200,除去第一个扇区的大小0x200,即0x8000,实际上,第一扇区启动区装载在0x7c00地址),
所以磁盘0x4200处的内容就应该位于内存0x8000+0x4200=0xc200号地址。
这样的话,往haribote.nas里加上ORG 0xc200。

切换到32位模式:
笔者准备的c编译器只能生成32位模式的机器语言,16位模式和32位模式中,机器语言的命令代码不一样。
32位模式的两个优点:1.内存使用容量大于1MB 2.CPU的自我保护功能。
如果用32位模式就不能调用BIOS功能,因为BIOS是用16位机器语言写的。

haribote.sys分拆成两个文件:
1. asmhead.nas 切换到32位模式的准备工作,后面详解
 - 设置显示模式
 - 用BIOS取得键盘上各种LED指示灯的状态
 - BOOT_INFO相关的内容保存(画面模式、分辨率、VRAM缓冲区地址)
 - 进入32位模式的设置(GDT/IDT初始化)
2. bootpack.c 操作系统的功能

/**********第4天 C语言与画面显示的练习**********/
往VRAM(video RAM)内存中写入像素数据,可以显示不同的画面。
在asmhead.asm中,通过INT 0x13设置显卡的显示模式VGA 320x240x8位彩色。
此模式的VRAM的内存地址空间为0xa0000~0xaffff的64KB。

为了显示不同的颜色,需要设定色号,如何设定色号?
设定色号的步骤:
1. 记录中断许可标志的值 eflags
2. 禁止中断 io_cli
3. io_out8根据设备号PORT往设备发送命令或数据,设置对应的色号
3. 恢复中断许可标志

有了不同的颜色后,使用不同的色号往vram中写不同颜色像素绘制矩形。

/**********第5天 结构体、文字显示与GDT/IDT初始化**********/
用结构体来封装BOOT_INFO相关的内容(画面模式、分辨率、VRAM缓冲区地址)

如何显示字符?
1. 定义字体数据(像素点矩阵的方式)
2. 解析像素点矩阵
3. 绘制各个像素点

如何在屏幕上显示变量值?
使用sprintf。
由于printf不管如何精心设计,都不可避免地要使用操作系统的功能。
而sprintf不同,它只对内存进行操作,所以可以应用与所有操作系统。

如何显示鼠标指针?
准备鼠标指针的数据(像素点矩阵),往vram里绘制。

如果鼠标需要移动的话,由于涉及到硬件的控制,需要利用中断,所以前提是要初始化GDT、IDT。

GDT和IDT都是与CPU的设定有关,为了让操作系统能够使用32位模式,需要对CPU做各种设定。

分段:
将4GB的内存分成很多块,每一块一个段,每一块的起始地址都看做0来处理。

分段在16位和32位中的区别:
16位中的段寄存器,在计算地址时,要将段地址乘以16;而在32位中,段寄存器表示的是段选择符。

32位中的分段,为了表示一个段,需要有以下信息
为了表示一个段,需要有以下信息:
 - 段的大小是多少
 - 段的起始地址在哪里
 - 段的管理属性(禁止写入,禁止执行,系统专用等)
 
CPU用8个字节的数据来表示这些信息,而段寄存器只有16位,如何处理?
解决方法:
段寄存器作为段的索引,段的信息保存在内存中,由于CPU设计上的原因,段寄存器的低3位不能使用。
因此能够使用的段号只有13位,能够处理的段号就只有0~8191,即可以定义8192个段。
内存中需要8192x8 = 64KB的空间来保存每个段的信息,而这64KB的数据就称为GDT(Global (segment) descriptor table)。
而GDTR中会保存GDT的内存的起始地址和有效设定个数。
GDTR是个48位的寄存器,低16位是段上限,即GDT总共大小;剩下32位代表GDT的开始地址。
段信息的8个字节中:其中32位表示表示该段的基址,分为low(2个字节),mid(1个字节),high(1个字节),分为3段的目的是为了兼容80286的程序。
 - 其中32位表示表示该段的基址,分为low(2个字节),mid(1个字节),high(1个字节),分为3段的目的是为了兼容80286的程序。
 - 其中段上限只能使用20位,这样只能使用1MB内存,但是通过段属性的标志位Gbit设置成1的时候,limit的单位不解释成byte,而解释成page(4KB).
   这样,就能使用4GB的内存空间。
 - 剩下的12位段属性称为段的访问权属性

IDT记录了0~255的中断号码与调用函数的对应关系。

/**********第6天 分割编译与中断处理**********/
鼠标指针的移动需要使用到中断,而要使用中断必须将GDT和IDT正确无误的初始化。

使用中断前,需要初始化PIC。
从CPU角度来看,PIC是外部设备,CPU使用OUT指令进行操作。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多