今天的内容相比前几天多了很多,主要是一些东西用代码写出来更难理解,需要更多的时间去琢磨,因此对于一些较为基础的内容不会进行详细的描述。
1.接收启动信息(harib02a)
之前的程序大都是直接使用0xa0000、320、200等数值,而这些值应该从asmhead.nas程序先前保存下来的值中取,如果不这样做,当画面模式改变时,系统就不能正确运行。
这里使用指针来取得这些值。
binfo是bootinfo的缩写,scrn是screen(画面)的缩写。
这里的0x0ff4之类的数据是从哪里来的呢,其实这些地址仅仅是为了与asmhead.nas保持一致才出现的。
另外这里把显示画面背景的部分独立出来,单独做成了一个函数 init_screen。增强了程序的可读性。
执行效果:
2.使用结构体(harib02b)
将一些常用在一块的数据定义成结构体,方便程序的书写,在后续的函数传参中也会释放出应有的威力。
与之前不同的就是将 cyls、leds、vmode 变量做成了一个结构体。
这里结构体中有一个变量reserve引起了笔者的注意。单词译为:保留,储备。比较这两个程序(上一节与本节),不难发现,reserve这个属性是多余的,应该是为了显现出字节对齐的存在。
↑结构体内存分布(字节对其)
3.试用箭头记号(harib02c)
本章节内容较为基础,介绍了指针的一种取值方式。
(*p).value = p - > value;
4.显示字符(harib02d)
这里为了显示一个字符,因为已经进入到32位模式,不能再调用BIOS函数往显卡中写入数据,但是换一种方式,使用8x16的长方形像素点阵来表示一个字符,例如:
一共占用16个字节(8x16)。写成16进制形式:
用for循环将画8个像素的程序循环16遍:
其中参数解释:
- char* vram,指针变量,保存着Video RAM的地址
- xize,VRAM每一行的像素点总数,这里是320
- x,y:写入像素点的起始位置(程序中完成了后面7个位置的填写)
- c:写入的颜色(这里传入了 COL8_FFFFFF 即 7,将这个像素点设置为白色)
- font数组,即前面用于描述A字符像素点的数字矩阵,用于判断像素点是否需要设置成c颜色(c:我们设置的颜色)
不难发现上述的程序中含有大量冗余的乘法运算,我们可以优化来减少这些无用功,进而提升程序的运行速度。
运行效果:
5.增加字体(harib02e)
在第4节中使用了数字矩阵表示出来字母A,这很麻烦,因此第五节中作者引入了一种字体文档:
文档是这么安排内容的:含有256个字符,A的字符编码是0x41,所以A的字体数据,放在"hankaku + 0x41 * 16"开始的16字节中。
将程序改造成一下就可以输出不一样的内容了:
运行效果:
6.显示字符串(harib02f)
第五节中,光显示几个字符就用了这么多行代码,看起来不是个滋味:
因为是一样的内容,所以可以封装成函数:
参数与之前的类似,函数中的s表示一个我们传入其中的字符串,C语言中,字符串都是以0x00结尾的,所以函数的结束就是以读取到的内容为0时。
函数调用:
整理后的HariMain函数
这里为什么对同意字符串调用了两次 putfonts8_asc函数?
不着急解释,我们将其中的一个调用去掉后
观察运行效果:
是这样的,再把注释掉的代码还原,运行:
这下子应该就清楚很多了,原来这个是为了调出黑色阴影而特意调用两次的呀。(注意调用的传入参数有所不同,是为了构造出黑色像素点“多写一个像素点”的效果)
7.显示变量值(harib02g)
这里是为了完成类似debugger显示出一些变量的值,
↑有关sprintf函数
sprintf函数的使用跟C语言中的printf函数非常相似,如:
内容较为基础,这里截自原书的解释:
运行效果:(变量成功显示在了界面上)
8.显示鼠标指针(harib02h)
对于程序中的for循环做一点解释:
应为显卡中描述像素点的方式为:
如果将坐标系类比成一个二维矩阵,那么y轴对应的就是数组的行方向,x轴对应的就是数组的列方向。这里是逐行遍历。对了,bc指的是back-color,背景色。
函数参数解释:vram和vxsize是关于VRAM的信息,它们的值分别是0xa0000和320;
pxsize和pysize是想要显示的图形(picture)的大小,鼠标指针的大小为16x16,所以这两个值都是16.
px0和py0指定图形在画面上的显示位置。
最后的buf和bxsize分别指定图形的存放地址和每一行含有的像素数。
运行效果:
9.GDT与IDT的初始化(harib02i)
这里为了实现鼠标的可移动作者对GDT和IDT做了详细的介绍。
GDT也好,IDT也好,它们都是与CPU有关的设定。为了让操作系统能够使用32位模式,需要对CPU做各种设定。不过,asmhead.nas里写的程序有点偷工减料,只是随意进行了一些设定。如果这样原封不动的话,就无法做出使用鼠标指针所需要的设定,所以我们要好好重新设置一下。
这里的内容如果没有操作系统的基础会变得较为复杂,主要讲解了为什么成需要分段。
按照上述的分段方法,为了表示一个段,需要有以下信息:
- 段的大小是多少
- 段的起始地址在哪里
- 段的管理属性(禁止写入,禁止执行,系统专用等)
后续的内容还是以作者的解释为主:
程序部分:
结构体定义
struct SEGMENT_DESCRIPTOR {
short limit_low, base_low;
char base_mid, access_right;
char limit_high, base_high;
};
struct GATE_DESCRIPTOR {
short offset_low, selector;
char dw_count, access_right;
short offset_high;
};
程序解释起来还是很麻烦的,这里牵涉到的移位与运算需要一定的C语言基础,段基址base被分为了三个部分:base_low、base_mid、base_high,其中base_low为字型数据,base_mid、base_high均为字节型数据,在set_segmdesc程序中:
获取base低位通过base & 0xffff 运算,取得低16位数据:sd->limit_low = limit & 0xffff;
获取base中位需要先对base进行右移运算:(base >> 16),右移去除掉低16位数据,然后再位与上 0xff ,就获得了中间8位数据,同理,base高8位通过右移24位,剩下的8位数据也是通过位与0xff获得。
注意程序中指针变量的+1是以指针数据类型大小作为步长的,举个例子:
char* p;
p ++; 相当于 p += sizeof(char);
double* p;
p++; 相当于 p += sizeof(double);
感受
今天由于课程的缘故,没有时间仔细完成博客, 因此需要抽出时间对本章内容再次深入阅读,本篇博客也需要补充一些笔者的理解。今天就先这样了,晚安