4 重新调整FIFO缓冲区(2)(harib10g)

我们已经可以确定性能真正得到了改善,所以下面把程序恢复到harib10c,沿着13.2节继续思考吧。

既然可以把3个定时器归纳到1个FIFO缓冲区里,那是不是可以把键盘和鼠标都归纳起来,只用1个FIFO缓冲区来管理呢?如果能够这样管理的话,程序就可以写成:

  1. if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) + fifo8_status(&timerfifo) == 0) {

冗长的if语句,也可以缩短了。那么或许harib10c中206行的bootpack.c也能简化。

■■■■■

在13.2节中,通过往FIFO内写入不同的数据,我们可以把3个定时器归入1个FIFO缓冲区里。同理,分别将从键盘和鼠标输入的数据也设定为其他值就可以了。那好,我们就这么办。

(写入FIFO的数值 中断类型)

0~ 1…………………光标闪烁用定时器

3…………………3秒定时器

10…………………10秒定时器

256~ 511…………………键盘输入(从键盘控制器读入的值再加上256)

512~ 767……鼠标输入(从键盘控制器读入的值再加上512)

这样,1个FIFO缓冲区就可以正常进行处理了。真是太好了!不过现在有一个问题,fifo8_put函数中的参数是char型,所以不能指定767那样的数值。哎,我们好不容易整理到1个缓存器中了,却又出现这种问题。

■■■■■

所以,我们想将写入FIFO缓冲区中的内容改成能够用int指定的形式。大家可不要担心哦。内容上与FIFO8完全相同。只是将char型变成了int型。

本次的bootpack.h节选

  1. struct FIFO32 {
  2. int *buf;
  3. int p, q, size, free, flags;
  4. };

本次的fifo.c节选

  1. void fifo32_init(struct FIFO32 *fifo, int size, int *buf)
  2. /* FIFO缓冲区的初始化*/
  3. {
  4. fifo->size = size;
  5. fifo->buf = buf;
  6. fifo->free = size; /*空*/
  7. fifo->flags = 0;
  8. fifo->p = 0; /*写入位置*/
  9. fifo->q = 0; /*读取位置*/
  10. return;
  11. }
  12. int fifo32_put(struct FIFO32 *fifo, int data)
  13. /*给FIFO发送数据并储存在FIFO中*/
  14. {
  15. if (fifo->free == 0) {
  16. /*没有空余空间,溢出*/
  17. fifo->flags |= FLAGS_OVERRUN;
  18. return -1;
  19. }
  20. fifo->buf[fifo->p] = data;
  21. fifo->p++;
  22. if (fifo->p == fifo->size) {
  23. fifo->p = 0;
  24. }
  25. fifo->free--;
  26. return 0;
  27. }
  28. int fifo32_get(struct FIFO32 *fifo)
  29. /*从FIFO取得一个数据*/
  30. {
  31. int data;
  32. if (fifo->free == fifo->size) {
  33. /*当缓冲区为空的情况下返回-1*/
  34. return -1;
  35. }
  36. data = fifo->buf[fifo->q];
  37. fifo->q++;
  38. if (fifo->q == fifo->size) {
  39. fifo->q = 0;
  40. }
  41. fifo->free++;
  42. return data;
  43. }
  44. int fifo32_status(struct FIFO32 *fifo)
  45. /*报告已经存储了多少数据*/
  46. {
  47. return fifo->size - fifo->free;
  48. }

■■■■■

下面我们就要写键盘和鼠标的相关程序了。我们不使用FIFO8,而是改为使用FIFO32。

本次的keyboard.c节选

  1. struct FIFO32 *keyfifo;
  2. int keydata0;
  3. void init_keyboard(struct FIFO32 *fifo, int data0)
  4. {
  5. /* 将FIFO缓冲区的信息保存到全局变量里 */
  6. keyfifo = fifo;
  7. keydata0 = data0;
  8. /* 键盘控制器的初始化 */
  9. wait_KBC_sendready();
  10. io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
  11. wait_KBC_sendready();
  12. io_out8(PORT_KEYDAT, KBC_MODE);
  13. return;
  14. }
  15. void inthandler21(int *esp)
  16. {
  17. int data;
  18. io_out8(PIC0_OCW2, 0x61); /* 把IRQ-01接收信号结束的信息通知给PIC */
  19. data = io_in8(PORT_KEYDAT);
  20. fifo32_put(keyfifo, data + keydata0);
  21. return;
  22. }

本次的mouse.c节选

  1. struct FIFO32 *mousefifo;
  2. int mousedata0;
  3. void enable_mouse(struct FIFO32 *fifo, int data0, struct MOUSE_DEC *mdec)
  4. {
  5. /* 将FIFO缓冲区的信息保存到全局变量里 */
  6. mousefifo = fifo;
  7. mousedata0 = data0;
  8. /* 鼠标有效 */
  9. wait_KBC_sendready();
  10. io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
  11. wait_KBC_sendready();
  12. io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
  13. /* 顺利的话,ACK(0xfa)会被发送*/
  14. mdec->phase = 0; /* 等待鼠标的0xfa的阶段*/
  15. return;
  16. }
  17. void inthandler2c(int *esp)
  18. /* 基于PS/2鼠标的中断 */
  19. {
  20. int data;
  21. io_out8(PIC1_OCW2, 0x64); /* 把IRQ-12接收信号结束的信息通知给PIC1 */
  22. io_out8(PIC0_OCW2, 0x62); /* 把IRQ-02接收信号结束的信息通知给PIC0 */
  23. data = io_in8(PORT_KEYDAT);
  24. fifo32_put(mousefifo, data + mousedata0);
  25. return;
  26. }

■■■■■

修改定时器结构体,让它也能使用FIFO32。

本次的bootpack.h节选

  1. struct TIMER {
  2. unsigned int timeout, flags;
  3. struct FIFO32 *fifo;
  4. int data;
  5. };

本次的timer.c节选

  1. void timer_init(struct TIMER *timer, struct FIFO32 *fifo, int data)
  2. {
  3. timer->fifo = fifo;
  4. timer->data = data;
  5. return;
  6. }
  7. void inthandler20(int *esp)
  8. {
  9. int i, j;
  10. io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00接收信号结束的信息通知给PIC */
  11. timerctl.count++;
  12. if (timerctl.next > timerctl.count) {
  13. return;
  14. }
  15. for (i = 0; i < timerctl.using; i++) {
  16. /* 因为timers的定时器都处于运行状态,所以不确认flags */
  17. if (timerctl.timers[i]->timeout > timerctl.count) {
  18. break;
  19. }
  20. /* 超时 */
  21. timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
  22. fifo32_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data); /* 这里! */
  23. }
  24. /* 正好是i个定时器超时了。移位其余的定时器。 */
  25. timerctl.using -= i;
  26. for (j = 0; j < timerctl.using; j++) {
  27. timerctl.timers[j] = timerctl.timers[i + j];
  28. }
  29. if (timerctl.using > 0) {
  30. timerctl.next = timerctl.timers[0]->timeout;
  31. } else {
  32. timerctl.next = 0xffffffff;
  33. }
  34. return;
  35. }

■■■■■

这样,我们的准备工作就完成了。最后我们来修改bootpack.c。

本次的HariMain节选

  1. struct FIFO32 fifo;
  2. char s[40];
  3. int fifobuf[128];
  4. (中略)
  5. fifo32_init(&fifo, 128, fifobuf);
  6. init_keyboard(&fifo, 256);
  7. enable_mouse(&fifo, 512, &mdec);
  8. io_out8(PIC0_IMR, 0xf8); /* 设定PIT和PIC1以及键盘为许可(11111000) */
  9. io_out8(PIC1_IMR, 0xef); /* 设定鼠标为许可(11101111) */
  10. timer = timer_alloc();
  11. timer_init(timer, &fifo, 10);
  12. timer_settime(timer, 1000);
  13. timer2 = timer_alloc();
  14. timer_init(timer2, &fifo, 3);
  15. timer_settime(timer2, 300);
  16. timer3 = timer_alloc();
  17. timer_init(timer3, &fifo, 1);
  18. timer_settime(timer3, 50);
  19. (中略)
  20. for (;;) {
  21. count++;
  22. io_cli();
  23. if (fifo32_status(&fifo) == 0) {
  24. io_sti();
  25. } else {
  26. i = fifo32_get(&fifo);
  27. io_sti();
  28. if (256 <= i && i <= 511) { /* 键盘数据*/
  29. sprintf(s, "%02X", i - 256);
  30. putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
  31. } else if (512 <= i && i <= 767) { /* 鼠标数据*/
  32. if (mouse_decode(&mdec, i - 512) != 0) {
  33. /* 已经收集了3字节的数据,所以显示出来 */
  34. sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
  35. if ((mdec.btn & 0x01) != 0) {
  36. s[1] = 'L';
  37. }
  38. if ((mdec.btn & 0x02) != 0) {
  39. s[3] = 'R';
  40. }
  41. if ((mdec.btn & 0x04) != 0) {
  42. s[2] = 'C';
  43. }
  44. putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
  45. /* 鼠标指针的移动 */
  46. mx += mdec.x;
  47. my += mdec.y;
  48. if (mx < 0) {
  49. mx = 0;
  50. }
  51. if (my < 0) {
  52. my = 0;
  53. }
  54. if (mx > binfo->scrnx - 1) {
  55. mx = binfo->scrnx - 1;
  56. }
  57. if (my > binfo->scrny - 1) {
  58. my = binfo->scrny - 1;
  59. }
  60. sprintf(s, "(%3d, %3d)", mx, my);
  61. putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
  62. sheet_slide(sht_mouse, mx, my);
  63. }
  64. } else if (i == 10) { /* 10秒定时器 */
  65. putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
  66. sprintf(s, "%010d", count);
  67. putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 10);
  68. } else if (i == 3) { /* 3秒定时器 */
  69. putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
  70. count = 0; /* 开始测试 */
  71. } else if (i == 1) { /* 光标用定时器*/
  72. timer_init(timer3, &fifo, 0); /* 下面是设定0 */
  73. boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
  74. timer_settime(timer3, 50);
  75. sheet_refresh(sht_back, 8, 96, 16, 112);
  76. } else if (i == 0) { /* 光标用定时器 */
  77. timer_init(timer3, &fifo, 1); /* 下面是设定1 */
  78. boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
  79. timer_settime(timer3, 50);
  80. sheet_refresh(sht_back, 8, 96, 16, 112);
  81. }
  82. }
  83. }

哦,经过修正,bootpack.c简化成了198行,足足减少了8行呀。

■■■■■

下面我们执行“make run”。能不能顺利地运行呢?运行正常,太好了!

4 重新调整FIFO缓冲区(2)(harib10g) - 图1

数字也在增加呀?!

嗯,和harib10c相比,结果值好了很多,竟然达到了1.7倍。

在模拟器上进行比较

harib10c:0002638668

harib10g:0004587870

这个结果可靠吗?(或许这个差异是模拟器或Windows造成的)。我们还是在真机上运行“make install”看看吧。

在真机上进行比较

harib10c:0074643595

harib10g:0099969263

差距是1.3倍左右。虽然改善幅度没有模拟器上大,但的的确确是改善了。

我们来想想这是为什么吧。定时器处理并没有变快,所以应该还有其他原因。……此次我们改写最多的是HariMain。在HariMain里,执行“count++;”语句和查询FIFO缓冲区中是否有数据这两个操作,是多次交互进行的。这次修改以后,程序只需要看1个FIFO缓冲区就行了,而以前要看3个。也就是说,FIFO缓冲区的查询能够更快完成,从而使得“count++;”语句执行的次数更多。

程序精简了,速度还变快了,太好了。