2 提高窗口移动速度(2)(harib23b)

这点改善还不够,我们得想办法让速度变得更快才行。其实要想变快,方法还是很多的。

比如sheet_refreshmap中有这样一句:

  1. map[vy * ctl->xsize + vx] = sid;

我们来琢磨一下这行代码。这个命令的功能是向内存中某个地址写入sid的值,它也位于for循环中,会被反复执行,而且这个地址的后面以及再后面的地址也要写入sid的值。

且慢,这样的话不是有个更好的方法吗?在汇编语言中,如果我们用16位寄存器代替8位寄存器来执行MOV指令的话,相邻的地址中也会同时写入数据,而如果用32位寄存器,仅1条指令就可以同时向相邻的4个地址写入值了。

更重要的是,即便是同时写入4个字节的值,只要指定地址是4的整数倍,指令的执行速度就和1个字节的MOV是相同的。也就是说,速度说不定能提高到原来的4倍!这简直是太强大了,我们一定得试试看。

修改后的代码如下。

本次的sheet.c节选

  1. void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
  2. {
  3. int h, bx, by, vx, vy, bx0, by0, bx1, by1, sid4, *p; /*这里!*/
  4. (中略)
  5. for (h = h0; h <= ctl->top; h++) {
  6. (中略)
  7. if (sht->col_inv == -1) {
  8. if ((sht->vx0 & 3) == 0 && (bx0 & 3) == 0 && (bx1 & 3) == 0) { /*从此开始*/
  9. /*无透明色图层专用的高速版(4字节型)*/
  10. bx1 = (bx1 - bx0) / 4; /* MOV次数*/
  11. sid4 = sid | sid << 8 | sid << 16 | sid << 24;
  12. for (by = by0; by < by1; by++) {
  13. vy = sht->vy0 + by;
  14. vx = sht->vx0 + bx0;
  15. p = (int *) &map[vy * ctl->xsize + vx];
  16. for (bx = 0; bx < bx1; bx++) {
  17. p[bx] = sid4;
  18. }
  19. }
  20. } else {
  21. /*无透明色图层专用的高速版(1字节型)*/
  22. for (by = by0; by < by1; by++) {
  23. vy = sht->vy0 + by;
  24. for (bx = bx0; bx < bx1; bx++) {
  25. vx = sht->vx0 + bx;
  26. map[vy * ctl->xsize + vx] = sid;
  27. }
  28. }
  29. } /*到此结束*/
  30. } else {
  31. /*有透明色图层用的普通版*/
  32. (中略)
  33. }
  34. }
  35. return;
  36. }

其实有透明色的情况我们也可以改成4字节型,不过这次我们先不改了。因为修改这里必须考虑透明色的处理,算法会比较复杂,而且现在用透明色的只有鼠标指针,鼠标的图层尺寸很小,两种方式的速度差异不大。

为了让这次的修改发挥最大的效果,我们需要使窗口在x方向上的大小为4的倍数,而且窗口的x坐标也要为4的倍数。目前所有的窗口大小都是4的倍数,所以不需要修改了,而对于窗口坐标,我们需要做AND运算来取整,使打开窗口时的显示位置为4的倍数。

本次的console.c节选

  1. int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
  2. {
  3. (中略)
  4. } else if (edx == 5) {
  5. sht = sheet_alloc(shtctl);
  6. sht->task = task;
  7. sht->flags |= 0x10;
  8. sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
  9. make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
  10. sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2);/*这里!*/
  11. sheet_updown(sht, shtctl->top); /*将窗口图层高度指定为当前鼠标所在图层的高度,鼠标移到上层*/
  12. reg[7] = (int) sht;
  13. } else if (edx == 6) {
  14. (中略)
  15. }

其中~3的意思是将3这个数的每个比特位进行取反,也就是等于0xfffffffc,之所以写成~3是因为这样写起来比较短,而且也不会由于f的个数太多而不小心写错。

还有一点,当用鼠标拖动窗口时如果目的地坐标不是4的倍数,我们这次的修改也就没有效果了,为了避免这种情况,我们必须保证目的地坐标为4的倍数才行。

本次的bootpack.c节选

  1. void HariMain(void)
  2. {
  3. (中略)
  4. int j, x, y, mmx = -1, mmy = -1, mmx2 = 0; /*这里!(添加mmx2)*/
  5. (中略)
  6. for (;;) {
  7. (中略)
  8. if (fifo32_status(&fifo) == 0) {
  9. (中略)
  10. } else {
  11. (中略)
  12. if (256 <= i && i <= 511) { /*键盘数据*/
  13. (中略)
  14. } else if (512 <= i && i <= 767) { /*鼠标数据*/
  15. if (mouse_decode(&mdec, i - 512) != 0) {
  16. (中略)
  17. if ((mdec.btn & 0x01) != 0) {
  18. /*按下左键*/
  19. if (mmx < 0) {
  20. (中略)
  21. for (j = shtctl->top - 1; j > 0; j--) {
  22. (中略)
  23. if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) {
  24. if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
  25. (中略)
  26. if (3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21) {
  27. mmx = mx; /*切换到窗口移动模式*/
  28. mmy = my;
  29. mmx2 = sht->vx0; /*这里!*/
  30. }
  31. (中略)
  32. }
  33. }
  34. }
  35. } else {
  36. /*如果处于窗口移动模式*/
  37. x = mx - mmx; /*计算鼠标指针移动量*/
  38. y = my - mmy;
  39. sheet_slide(sht, (mmx2 + x + 2) & ~3, sht->vy0 + y); /*这里!*/
  40. mmy = my; /*更新到移动后的坐标*/
  41. }
  42. } else {
  43. (中略)
  44. }
  45. }
  46. }
  47. }
  48. }
  49. }

变量mmx2的功能是保存移动前的sht—>vx0的值。

sheet_slide的地方我们做了AND运算,之所以要先加2再做AND,是因为只做AND的话就像直接舍去小数一样,容易造成窗口往左移,而如果先加上2就相当于做了四舍五入,使往左和往右移的几率对等。

■■■■■

好了,我们来“make run”。哇哦!好快!如果说之前移动窗口是“唰唰唰”的感觉的话,现在比之前变得更加流畅了,差不多是“嗖嗖嗖”这样吧。大家可以在CPU特别慢的电脑上用QEMU来对比一下,一定能明显感觉到差距的。

2 提高窗口移动速度(2)(harib23b) - 图1

截图上看不出来,不过速度快了不少