一、内存管理(续)
Menmman_free函数不足:出现不连续的小段未使用空间,把man->frees耗尽
解决:以0x1000(4KB)字节为单位分配内存和释放内存
主要添加的部分是向上舍入。
在二进制中想把某位变0,只需要进行“与运算”,而十六进制是二进制的组合,同意进行与运算就可以。
向下舍入:0x12345678 & 0xfffff000=0x12345000 -> i = i & 0xfffff000
向上舍入:i= (i & 0xfffff000) + 0x1000 -> 相当于向下舍入再加了一个单位,实际上是错的,比如以100为单位对300向上舍入变成400.
向上舍入正确做法是先判断后舍入:
if ( ( i & 0xfff) != 0 ) { i=( i& 0xfffff000) + 0x1000; }
向上舍入改进版:i= ( i + 0xfff ) & 0xfffff000 ;
转为十进制理解:若当前为300(单位为100),加上99之后变成399,向下舍入还是300,若是301,加99变成400,向下舍入变成400结果正确,可以这样类比。
十进制的向下舍入:i=i-(i%100)
二、叠加处理
为深入解决鼠标移动问题 -> 做一段适用于鼠标和窗口叠加处理的程序
- 最上面的小图层用来描绘鼠标指针,最下面是桌面壁纸,中间是窗口的图层。
图层结构体:
管理多重图层信息结构体(最大图层数256):
- Sheets的部分有32x256=8192大小(8KB),如果还要加其他就很大了,所以使用mamman_alloc_4k来分配内存。
- Shtctl_init()函数是对内存的初始化,用sizeof(变量)函数来指定所占空间大小,里面每一个图层都标记未使用。下一个函数sheet_alloc是获取新生成的图层,可以理解为对256个图层的初始化(标记为使用),但是都没有显示在界面中(height= -1)。
sht = &ctl->sheets0[i];表示ctl->sheets0[i]的地址
设定图层的缓冲区大小和透明色的函数:
设定底板高度的函数:void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height)
刷新函数:根据高度显示图层,最高的放在最上面(用户视角)
Bx:对应显示图层的横坐标(0开始)
By:对应显示图层的纵坐标(0开始)
滑动函数:重新显示就好了
释放函数:全部设置为隐藏
最后的主函数:
void HariMain(void)
{
……
struct SHTCTL *shtctl;
struct SHEET *sht_back, *sht_mouse;
unsigned char *buf_back, buf_mouse[256];
……
init_palette();
shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny); /*初始化屏幕内存*/
sht_back = sheet_alloc(shtctl);
sht_mouse = sheet_alloc(shtctl[两个图层,一个是sht_back和sht_mouse分别表示大背景和鼠标的背景(后面定义为透明色)]);
buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny); /*内存分配*/
sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); /* 屏幕的图层,没有透明色 */
sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);
init_screen8(buf_back, binfo->scrnx, binfo->scrny);
init_mouse_cursor8(buf_mouse, 99);/*背景色号 99*/
sheet_slide(shtctl, sht_back, 0, 0); /*绘制背景滑动*/
mx = (binfo->scrnx - 16) / 2; /* 画面中央的坐标计算 */
my = (binfo->scrny - 28 - 16) / 2;
sheet_slide(shtctl, sht_mouse, mx, my); /*绘制鼠标滑动*/
sheet_updown(shtctl, sht_back, 0);
sheet_updown(shtctl, sht_mouse, 1);/*设置为背景图层,1为鼠标图层*/
(显示函数)
sheet_refresh(shtctl);
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
io_stihlt();
} else {
if (fifo8_status(&keyfifo) != 0) {
(获取鼠标并显示)
sheet_refresh(shtctl);
} else if (fifo8_status(&mousefifo) != 0) {
(显示鼠标)
sheet_slide(shtctl, sht_mouse, mx, my);
/* sheet_refresh滑动显示 */
}
}
}
}
}
显示结果:
三、提高叠加处理速度
第二点已经实现鼠标图层的移动,但是速度太慢。根据上面的原理,只要鼠标一动就会对整个画面重新刷新,相当于重写64000个像素。
- 第一次优化:重写鼠标移动前和移动后的像素2x256=512,sheet.c其他函数保持不变,只修改刷新函数:
增加一个sheet_refreshsub函数
函数与sheet_refresh相比唯一不同在于使用vx0-vy1指定的刷新范围。加了一个判断条件 if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1),意思是在传入参数的指定范围内才能绘制,在sheet_refresh中实现调用:
先记住移动前的位置,再设置新的显示位置,重新绘制这两个地方。
修改之后,作者还解决了一个文字显示问题:每次显示文字都要重写64000个像素,这里可以调用refreshsub函数,重写sheet_refresh函数就可以了:刷新指定范围(缓冲区坐标)
改造updown:
sheet_refresh(ctl);变成下面形式:
sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize);
最后改写bootpack.c:
变更点四个:每次往buf_back(屏幕)中写入信息才进行sheet_refresh
sheet_refresh(shtctl, sht_back, 0, 0, binfo->scrnx, 48); //初始化屏幕
sheet_refresh(shtctl, sht_back, 0, 16, 16, 32); //刷新键盘
sheet_refresh(shtctl, sht_back, 32, 16, 32 + 15 * 8, 32); //显示鼠标信息
sheet_refresh(shtctl, sht_back, 0, 0, 80, 16); //显示坐标
最后显示同上,速度提高!
四、提高叠加处理速度
- 按照refreshsub的思路,会有bysize*bxsize次进入判断语句。这种写法每次刷新都要重新判断一次全部的显示图层(sht_back),所以作者修改了for语句的循环范围,如图:
- 第二次优化:sheet_refreshsub函数
第一层for循环在于遍历图层,从低到高显示,理解了这一步,这样下面的判断就很好理解了: for (h = 0; h <= ctl->top; h++)
改良关键在于,bx在for语句中并不是在0到bxsize之间循环,而是在bx0到bx1之间循环,bx0,bx1都是通过刷新范围倒推出来的一个相对坐标值。Vx0的坐标相当于bx中的哪个位置,把它当作bx0。
根据
得出公式:vx= sht->vx0+bx; -> bx=vx-sht->vx0;
代码解析:
bx0 = vx0 - sht->vx0;
by0 = vy0 - sht->vy0;
bx1 = vx1 - sht->vx0;
by1 = vy1 - sht->vy0;
通过上面公式推导出的四个坐标值,对应图(例子)为:
这样就基本理解书本上的那些图了
当h=1时,想要重复刷新鼠标的图层,就变成下面:
这个判断说明的是在左上角叠加的情况:bx1,by1变大要修正。
只有满足叠加范围内的才会重新绘制,只绘制这一部分(BX0,BY0)->(BX1,BY1),其他的不用绘制。运行结果不变,速度变快。