2 提高窗口移动速度(2)(harib23b)
这点改善还不够,我们得想办法让速度变得更快才行。其实要想变快,方法还是很多的。
比如sheet_refreshmap中有这样一句:
map[vy * ctl->xsize + vx] = sid;
我们来琢磨一下这行代码。这个命令的功能是向内存中某个地址写入sid的值,它也位于for循环中,会被反复执行,而且这个地址的后面以及再后面的地址也要写入sid的值。
且慢,这样的话不是有个更好的方法吗?在汇编语言中,如果我们用16位寄存器代替8位寄存器来执行MOV指令的话,相邻的地址中也会同时写入数据,而如果用32位寄存器,仅1条指令就可以同时向相邻的4个地址写入值了。
更重要的是,即便是同时写入4个字节的值,只要指定地址是4的整数倍,指令的执行速度就和1个字节的MOV是相同的。也就是说,速度说不定能提高到原来的4倍!这简直是太强大了,我们一定得试试看。
修改后的代码如下。
本次的sheet.c节选
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{
int h, bx, by, vx, vy, bx0, by0, bx1, by1, sid4, *p; /*这里!*/
(中略)
for (h = h0; h <= ctl->top; h++) {
(中略)
if (sht->col_inv == -1) {
if ((sht->vx0 & 3) == 0 && (bx0 & 3) == 0 && (bx1 & 3) == 0) { /*从此开始*/
/*无透明色图层专用的高速版(4字节型)*/
bx1 = (bx1 - bx0) / 4; /* MOV次数*/
sid4 = sid | sid << 8 | sid << 16 | sid << 24;
for (by = by0; by < by1; by++) {
vy = sht->vy0 + by;
vx = sht->vx0 + bx0;
p = (int *) &map[vy * ctl->xsize + vx];
for (bx = 0; bx < bx1; bx++) {
p[bx] = sid4;
}
}
} else {
/*无透明色图层专用的高速版(1字节型)*/
for (by = by0; by < by1; by++) {
vy = sht->vy0 + by;
for (bx = bx0; bx < bx1; bx++) {
vx = sht->vx0 + bx;
map[vy * ctl->xsize + vx] = sid;
}
}
} /*到此结束*/
} else {
/*有透明色图层用的普通版*/
(中略)
}
}
return;
}
其实有透明色的情况我们也可以改成4字节型,不过这次我们先不改了。因为修改这里必须考虑透明色的处理,算法会比较复杂,而且现在用透明色的只有鼠标指针,鼠标的图层尺寸很小,两种方式的速度差异不大。
为了让这次的修改发挥最大的效果,我们需要使窗口在x方向上的大小为4的倍数,而且窗口的x坐标也要为4的倍数。目前所有的窗口大小都是4的倍数,所以不需要修改了,而对于窗口坐标,我们需要做AND运算来取整,使打开窗口时的显示位置为4的倍数。
本次的console.c节选
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
(中略)
} else if (edx == 5) {
sht = sheet_alloc(shtctl);
sht->task = task;
sht->flags |= 0x10;
sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2);/*这里!*/
sheet_updown(sht, shtctl->top); /*将窗口图层高度指定为当前鼠标所在图层的高度,鼠标移到上层*/
reg[7] = (int) sht;
} else if (edx == 6) {
(中略)
}
其中~3的意思是将3这个数的每个比特位进行取反,也就是等于0xfffffffc,之所以写成~3是因为这样写起来比较短,而且也不会由于f的个数太多而不小心写错。
还有一点,当用鼠标拖动窗口时如果目的地坐标不是4的倍数,我们这次的修改也就没有效果了,为了避免这种情况,我们必须保证目的地坐标为4的倍数才行。
本次的bootpack.c节选
void HariMain(void)
{
(中略)
int j, x, y, mmx = -1, mmy = -1, mmx2 = 0; /*这里!(添加mmx2)*/
(中略)
for (;;) {
(中略)
if (fifo32_status(&fifo) == 0) {
(中略)
} else {
(中略)
if (256 <= i && i <= 511) { /*键盘数据*/
(中略)
} else if (512 <= i && i <= 767) { /*鼠标数据*/
if (mouse_decode(&mdec, i - 512) != 0) {
(中略)
if ((mdec.btn & 0x01) != 0) {
/*按下左键*/
if (mmx < 0) {
(中略)
for (j = shtctl->top - 1; j > 0; j--) {
(中略)
if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
(中略)
if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
mmx = mx; /*切换到窗口移动模式*/
mmy = my;
mmx2 = sht->vx0; /*这里!*/
}
(中略)
}
}
}
} else {
/*如果处于窗口移动模式*/
x = mx - mmx; /*计算鼠标指针移动量*/
y = my - mmy;
sheet_slide(sht, (mmx2 + x + 2) & ~3, sht->vy0 + y); /*这里!*/
mmy = my; /*更新到移动后的坐标*/
}
} else {
(中略)
}
}
}
}
}
}
变量mmx2的功能是保存移动前的sht—>vx0的值。
sheet_slide的地方我们做了AND运算,之所以要先加2再做AND,是因为只做AND的话就像直接舍去小数一样,容易造成窗口往左移,而如果先加上2就相当于做了四舍五入,使往左和往右移的几率对等。
■■■■■
好了,我们来“make run”。哇哦!好快!如果说之前移动窗口是“唰唰唰”的感觉的话,现在比之前变得更加流畅了,差不多是“嗖嗖嗖”这样吧。大家可以在CPU特别慢的电脑上用QEMU来对比一下,一定能明显感觉到差距的。
截图上看不出来,不过速度快了不少