30天自制操作系统(day10)

第10天:叠加处理

1、内容1:内存管理

因为bootpack.c太长了,所以创建一个memory.c,将bootpack里的一部分内容分到memory中,使bootpack.c的代码变短。
30天自制操作系统(day10)
在之前的内存管理函数中,memman_alloc和memman_free是以1字节为单位进行内存管理的,虽然这样没啥问题,但是分配过小的话,在反复进行内存分配和内存释放之后,内存中就会出现很多不连续的小段未使用空间,这样会把man->frees消耗殆尽。
因此,编写一些以0x1000(即4KB)字节为单位进行内存分配和释放的函数,这些函数还实现了把指定的内存大小按4KB为单位向上舍入。
增加的部分:7a中的memory文件
30天自制操作系统(day10)
这次的程序最关键的部分是向上舍入的部分,即size = (size + 0xfff) & 0xfffff000;
为什么要实现向上舍入?
因为在之前的内存管理中,将将 内存分成了0x1000字节大小的块,然后分配给程序使用,但并不是所有的程序用到的都是0x1000的整数倍字节,所以要设计一个取整的方法。在这里,作者采用的取整的方法就是向上舍入的取整。
size = (size + 0xfff) & 0xfffff000;首先将size加上0xfff,再将末尾三位置为0,若size形如abc000,那么结果就是abc000;若size形如abcxxx,也就是末三位不全为0,那么结果为ab(c+1)000,这个结果也符合向上取整的定义。
实验结果:
30天自制操作系统(day10)
一切正常。

2、内容2:叠加处理

之前由于鼠标的叠加处理没有设计好,导致状态栏会被鼠标所吃掉。因此,在这个内容中,设计了一个叠加程序,使得其不仅适用于鼠标的叠加处理,也能适用于窗口的叠加处理。
30天自制操作系统(day10)
在这个画面中,能清楚看到图层的代表意义,最上面的小图层用来描绘鼠标指针,它下面的几张图层是用来存放窗口的,而最下面的一张图层是用来存放存放和桌面壁纸。同时,还要通过移动图层的方法来实现鼠标指针的移动以及窗口的移动。
首先定义一个结构体,该结构体中,包含用于记录图层上所描画内容的地址buf,图层的整体大小,也就是长、宽,分别为bxsize和bysize。表示图层在画面上位置的坐标vx0和vy0,透明色色号col_inv,图层高度height和存放有关图层的各种设定信息的flag。
7b的bootpack.h文件
30天自制操作系统(day10)
当然,如果只有一个图层,是不能实现图层的叠加处理的,因此,便有了管理多重图层信息的结构:7b的bootpack.h文件
30天自制操作系统(day10)
创建一个SHTCTL结构体,MAX_SHEETS表示能够管理的最大图层数,暂时设为256。
VRAM里的地址和画面的大小由vram、xsize、ysize来表示。Top表示最上面一层的高度,sheets0用来存放准备的图层的信息,sheets是记忆地址变量的领域。
30天自制操作系统(day10)
在图层控制变量中,sheets0的部分大小就有32x256=8192,即8KB,如果再加上sheets的话,就超过了9KB。对于其的空间需求,使用memman_alloc_4k来分配内存空间。
首先使用memman_alloc_4k来分配记忆图层控制变量的内存空间,指定该变量所占空间的大小,通过sizeof,让编译器自动计算出该变量型所需的字节字节数,然后给控制变量赋值,给其下的所有图层变量都加上“未被使用的”标签,也就是flags=0。
7b中的sheet文件
30天自制操作系统(day10)
分配好内存空间后,还需要取得新生成的未使用图层的,即:7b中的sheet文件
30天自制操作系统(day10)
在之前所定义的sheets0[]中寻找未使用的图层,如果找到了,就将其标志为“正在使用”,即改变flags的值,使其变为1,并返回其地址。将高度设为1,即表示图层的高度还没有被设置,图层处于隐藏状态。
最重要的一步便是设定底板高度,因为高度决定着图层的显示。首先保存存储设置前的高度信息,如果设定的高度过高或者过低,都需要先矫正,比如height<-1时,就需要将高度设为-1。
当设定高度之后,就需要对sheets[]进行重新排列,这样才能在之后正确的显示出来。如果设定的高度比之前的高度低,并且,设定的高度是大于-1的话,那么就需要将之前的高度以上的图层向上移动一层,将所设定的高度插入。如果设定的高度是小于-1的,那么也就是要将该图层隐藏,所以将该图层以上的图层的高度向下一个,并矫正最高层的高度。
如果设定的高度比以前的高度高的话,并且以前的高度是大于0的话,那么就需要将以前高度到现在所设定高度的图层向下一层,为该高度的图层空出一层存放。如果以前的高度是-1,也就是处于隐藏状态的话,就将以上所有图层向上一层,即已显示的图层要增加一个,即最高层的高度增加1。修改好高度之后,便可以按新图层信息就行绘制画面。
修改的程序如下:7b中的sheet文件
30天自制操作系统(day10)
设计图层刷新函数,通过刷新函数,实现从下到上描绘所有的图层,对于已经设定好高度的多有图层来说,需要从下往上的,将透明色以外的所有像素都赋值到VRAM中,因为是从下往上开始复制的,所以最上面的内容就留在了画面上。
相关函数如下:7b中的sheet文件
30天自制操作系统(day10)
除此以外,还需要有可以上下左右移动指定图层的函数,并不改变图层的高度。也就是sheet.c中的slide函数。
相关函数:7b中的sheet文件
30天自制操作系统(day10)
释放已使用图层的内存的函数sheet_free。7b中的sheet文件
30天自制操作系统(day10)
首先准备两个图层,分别是sht_back和sht_mouse,两个缓冲区buf_back和buf_mouse,用于描绘图形。在每次修改缓冲区之后,都需要进行图层刷新。
7b中的bootpack文件
30天自制操作系统(day10)
运行结果:
30天自制操作系统(day10)
由于使用内存的增加,导致剩余内存相对减少。

3、内容3:提高叠加处理速度(1)

在内容二中,虽然最后结果显示是正确的,但是在这里操作时,会发现反应比较慢。因此就需要提高速度,首先看看在图层的移动中,有没有可以改进的地方。指针的移动,鼠标指针虽然最多只有16x16(即256)个像素,但是根据之前所设计的,只要鼠标指针发生移动,那么就需要重新描绘移动相关的部分,也就是256x2个像素,虽然这些占比比较少,但是还是可以通过修改其来实现提速的目的。7c中的sheet文件:
在图层刷新的函数中,我们增加一个if语句,来实现其试用vx0~vy1指定所要刷新的范围。
30天自制操作系统(day10)
实现指定刷新范围功能最关键的就是标红的一句,即if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) ,只有当vx0~vy1满足这其中所有的条件时,才进行刷新。
修改slide函数,来提高它的运行速度。7c中的sheet文件:
30天自制操作系统(day10)
首先记住图层移动前的显示位置,然后再设定新的显示位置,最后只需要重新描绘移动前和移动后的地方即可。
虽然这样改理论上能提高运行速度,但是在移动鼠标时,由于要在画面上显示坐标等信息,又执行了sheets_refresh函数,所以还是很慢,因此就需要解决图层文字显示上的问题。在图层上显示文字,实际上并不是改写图层的所有内容,只需要改写文字部分的像素的内容即可。
重新编写sheet_refresh函数:7c中的sheet文件:
30天自制操作系统(day10)
这里的指定范围,并不是直接指定画面内的坐标,而是指定缓冲区内的坐标,这样HariMain就可以不考虑图层在画面中的位置了。除此以外,只有每次想buf_back中写入信息时,才会进行sheet_refresh。

4、内容4:提高叠加处理速度(2)

虽然按照之前的做法,速度有了一定的改变,但如果还想要更加快一些的话,还是需要对efreshsub函数进行修改。在原函数中,即使不写入像素内容,也需要多次执行if语句。即便只刷新图层的一部分,也要对所有图层的全部像素执行if语句,来判断是否写入。而对于刷新范围以外的部分,即使执行了判断,最后也不会进行刷新,这样便造成了资源的浪费,减慢了刷新的速度。因此可以将for循环的范围限定在刷新范围之内。
30天自制操作系统(day10)
修改的代码如下:7d中的sheet文件
30天自制操作系统(day10)
改良的关键在于,bx在for语句中并使在0bxsize间循环,而是在bx0bx1之间循环,对于by也相同。而bx0和bx1均是通过刷新范围倒推得到。
30天自制操作系统(day10)
计算vx0的坐标相当于bx中的哪个位置,然后把它作为bx0,其它坐标的处理方法也一样。
30天自制操作系统(day10)
当处理刷新范围在图层外侧时,即sht_back中写入字符并进行刷新,并且刷新范围的一部分被鼠标覆盖时。
30天自制操作系统(day10)
在这里,需要进行重复描绘的只有与鼠标图层重叠的那一小块范围,而其他部分并没有被要求刷新,所以不能刷新,这样的话,可以把bx0和by0都置为0。
当有不同的重叠方式时,比如:
30天自制操作系统(day10)
在这种能够情况下,bx0和by0虽然都可以从vx0和vy0中顺利求取,但是bx1和by1就变得太大了,超出了图层的范围,所以要执行以下操作。
30天自制操作系统(day10)
第三种可能面临的情况是完全不重叠的情况,这是是不需要进行重复描绘的,此时利用倒推计算得出的bx0和bx1都是负值,而在第一种情况中,只有bx0被修正为0,而在第二种情况中国,bx1没有被修正,还是负值。这样for循环便不会执行,这样就不会重复描绘了。
实行之后,速度变得比之前都要快了。

二、遇到的问题及解决方法

1、问题1: 设计一个窗口,点击X可以将窗口关闭。
解决方法:在一开始的想法是,直接设计一个描画窗口的函数,在鼠标点击X之后,再用一个窗口进行覆盖,这样处理之后,虽然能够实现想要的功能,但是当鼠标再该区域移动时,又会刷新画面,导致又出现窗口。
后来参考了第11天的内容,选择用图层来做,首先也是设计一个描画窗口的函数。再graphic.c中添加一个window函数,该函数的功能是,描画窗口。
30天自制操作系统(day10)
需要注意的是,要注意在bootpack.h中声明。
30天自制操作系统(day10)
设计好窗口之后,要在HariMain中使用的话,模仿鼠标的显示,添加代码。
30天自制操作系统(day10)
为窗口图层分配内存:
30天自制操作系统(day10)
30天自制操作系统(day10)
调用函数,并且将窗口图层的高度设为1,背景的高度设为0,鼠标的高度设为2。
30天自制操作系统(day10)
当鼠标的左上角的坐标位于X的范围内时,并且鼠标发生了点击,即L,则将窗口图层的高度设为-1,将其进行隐藏。
30天自制操作系统(day10)
实验结果:
30天自制操作系统(day10)
点击X之后:
30天自制操作系统(day10)