[第5天] 字体和鼠标

       今天比较轻松愉快,作者介绍了如何显示字体和做了一个鼠标模型。当然是用显示的点阵原理做的。我觉得这个显示字体的原理应该跟真正操作系统差不多。为了规范,我没有像以前那样把内核直接拼接到第二扇区,我把他当作文件一样放进去。从img文件可以看出,这个文件的起始位置在0x4400,所以要读很多扇区,干脆就读了10个柱面。相应的内存位置也改了,从0x1000改到0xc400了。

数据读取:

       这本来是好几天前的事情,但是我现在才真正理解这个读取。作者采用的方法是1个扇区1个扇区的读,而int 13h这个中断,明明可以连续读好几个扇区,效率高啊。但是因为这个原因,我debug好久,才发现不能这么做,这样做会引起DMA boundary error,这个太坑了ES:BX越了64K的界就会悲剧,怪不得我不能连续读4个扇区。我没有找解决方法,直接按作者的来。代码类似就不放了,关键教训就是 DMA Boundary Error。。

显示字符:

        字符就是点阵啊。比如A可以表示成8*16,如下图所示。用二进制表示就是下面的代码。有了这些二进制之后,就可以在对应的1位把颜色改成白色,0位改成背景色。代码见github

 A

制作字体:

       256个ascii,一个个做过来很蛋碎,还好作者提供了友人的制作的字体。但那字体都是.*表示,要经过转换。python就上了,直接脚本处理,然后tr命令去掉引号就万事ok了。然后当作头文件引入即可。作者使用链接的方式,我感觉没必要。总之所以字符都能显示了。还遇到一个事,就是sprintf不能用,因为我是64位系统,先不管这事,自己写个转换函数就能输出数字了。以后再想如何优雅的处理32位的事情。

制作鼠标:

      原理与制作字体一致,这鼠标还不能动。作者由此挖了个大坑,这次我没有跳进去。后面几天讲中断处理,感觉很麻烦。下面是目前系统的样子。总体代码见github

SlefMouse

GDT和IDT:

        GDTIDT这东西我还是没有完全搞懂。反正跟32位保护模式密切相关,什么代码段数据段的。一不小心代码就执行不了。本来我也把代码复制到0x280000去了,但是有可能入口地址啊,段寄存器啊什么的原因,导致不能运行代码。我就先放着不管了。

真机操作:

        做了操作系统不能在真机上跑,怎么行。但这事折腾了我1个晚上。刚开始我以为内存位置不对搞了半天。后来靠我天才的测试方法发现,原来disk read之前就跪了。对比时候发现是sp寄存器的问题。谁说sp寄存器一定要初始化为0x7c00啊,我参考的那个告诉我0x9000,太坑。。反正寄存器初始化正确后,我就能在同学的机子上跑了。我自己的笔记本是disk read error。我原本以为是显示用的LVDS而不是VGA的关系,然后我陷入了沉思,其他grub是怎么做到的? 后来我发现那个A20 没有打开。这个A20是允许使用超过1M的内存空间,一般都是打开的啊。我这么做之后,发现行了!!

        后来我突然就发现一个严重的问题,TMD,这个A20是读了磁盘之后才启用的啊,根本没有关系啊!!我无语了,我用特殊的调试技巧,发现真没关系。我把那段代码改掉,随便放点其他的代码,一样可以!!妹的,难道是跟引导长度有关,还是我BIOS的BUG? 还是更深层的原因,我还不知道。。反正成功了!上图!(事实证明是我电脑问题,BIOS Bug的可能性大,别的电脑都行)

Real Laptop

链接:

  1. Understanding the PC Boot Proces and Writing a Bootloader

[第4天]c语言指针和画面设定

       第4天,明显就简单了很多。作者讲了C语言指针的知识,然后用指针操作内存来设定画面。简单方便。不过今天也学到很多东西。

VGA设定:

       进入保护模式后,画面模式也改变了,采用VGA。查询之后发现BIOS的0x10号向量是用来设定VGA模式的。其中AH为0,代表Video模式,就是非text。AL为0x13就是所谓的Mode 13h, 即标准的VGA模式。还有很多AL设置选项,可以参考Ralf Brown’s Interrupt List.  以下就是设定VGA的汇编代码,顺便把设定数据存在内存里。可以看到操作VGA的内存地址从0xa0000开始,到0x000BFFFF,一共640kb,实际我们用的是320*200 = 64 kb。为什么从0xa0000开始, 有很多资料都有说明。0xa0000是mode 13h的地址, 0xb8000是text mode的地址。

C指针:

       作者先用了汇编来操作内存,控制显示,就是上一天的样子。今天就直接用C指针操作。比如下面那样。神奇的是,无论 2[p] 还是i[p], 还是p[i],编译器都会解释为 *(p+i)。以前还没有注意过。又可以装b了,不知道是不是所有编译器都支持这么写。。

 色号设定:

      VGA默认是256色,8位。但是作者认为我们只要用16种颜色,然后就用一段比较蛋疼的程序修改了默认颜色。不重要,我就先不管了。下图分别是默认的256种颜色,和修改后的16种颜色。

VGA_Palette_-_Big  modifycolor

绘制矩形:

      能操作内存,那么绘制图形也不再话下。用指针操作简直方便。代码见github,下图为效果。分别为测试矩形和假的任务栏。

shape  taskbar

链接:

  1. Interrupt List
  2. Operating System Development Series
  3. Memory Map And VGA

[第3天]32位模式和c语言

       终于到了第3天,这次作者太坑。搞的我差点放弃。这次是真的一天,一整天,拼起来24小时。今天我也终于实现了第一个内核,不再是简单的引导了。而且也进入CPU保护模式,这个就吊了。我还要吐槽下,为何其他很多中文博客TMD都是直接复制粘帖代码?坑啊。

IPL:

       IPL(Initial Program Load),就是把系统导入内存然后跳入系统的入口。跟Booting是一个意思我才知道。这个阶段BIOS读入磁盘上的第一个扇区,一共512Bytes。在这个阶段,就是所谓的实模式。在这个阶段,只有16位,但是借助段寄存器使用超过16位的内存。主要任务,是读取操作系统所在扇区的内存并载入到相应内存位置。我后来用的是0x1000部分的内存(Memory Map)。

读取磁盘:

      读取磁盘只要知道INT 0x13就行了。CH是柱面,DH是磁头,CL是要读的扇区位置, AL是要读的扇区数, AH=02表示读取。关键这里有的DL很坑,代表哪个磁盘,当然是0x00,但是我就傻逼了。我是直接生成img然后当作磁盘来跑,然后死也读不了。原因这时候要设成0x80。还有寄存器地址,主要跟ES和BX有关,具体地址是    ES:BX = ES*16 + BX, 一共fffff位,131072Bytes。还是不够用,这就是BIOS实模式的一个缺点。具体代码都在github

 载入内核:

        这里的内核只是让CPU空转。具体步骤是,读入磁盘内容到内存,找到函数入口的地址(编译后找img中相应字段),然后再修改读完磁盘内容后jump的地址。我的地址是0x4400,作者的是0x4200,然后开始的位置是对应内存0x8000,加起来就是入口地址就是0xc400。jump即可。具体代码github。这个不是重点,重点是我学会了怎么挂载镜像,就像虚拟光驱一样的。并可以写入文件到这个img。

entry

32位保护模式:

      进入保护模式。这个是最复杂的部分,作者挖了个大坑,GDT,这个太复杂了。今天也不想说明了。然后作者的引导方式很奇怪,是要根据编译后的函数入口地址来调节的,很蛋疼,而且好像跟COFF和ELF的格式有关,我试了很久都没办法不用作者的工具成功实验。(我做这个系统的原则是不使用作者的工具)。 后来实在没有办法,我是看了这个指导,重新写了个逻辑结构好的内核。直接调用,编译后直接拼接,免去了上一步,挂载磁盘的麻烦,而且还要读那么多扇区麻烦。这个版本只要读第二个扇区。代码我新建了一个Scratch,说是Scratch,但我感觉要在这个基础上进行下一步了。看看结果。我超前了一点点,把图像显示了一下。

 

video

链接:

  1. boot sector code, LILO
  2. Write a 16-bit os, Tiny bootloader