《30天自制操作系统》读书笔记(6) 鼠标键盘

  • 总览

    从现在开始我把这些代码都放在了GitHub上, 欢迎围观 地址是:

      https://github.com/LastAvenger

  上一篇笔记介绍的是GDT,IDT,PIC等各种我也不太懂的东西, 但是这些了解这些东西对启用鼠标键盘是必须的.

  我们在键盘上按下一个键之后, 键盘产生一个信号, PIC接收到信号后, 会通过IRQ1向CPU发送一个中断信号,而这个中断信号是我们自己设定的, 这里的是int 21, CPU接收到int 21 中断后, 会停下现在的工作, 在IDT中寻找int 21 对应的ISR, 并执行它, 我们就通过这个ISR函数来处理键盘事件, (这对于USB键盘竟然也是有效的).

  对于鼠标, 大概由于鼠标是后来才有的, PS/2鼠标的中断是通过IRQ12发送给CPU的, 而USB鼠标的中断不然, OSDev 上面这样写:

  A USB mouse generally emulates a PS2 mouse, except that it generates IRQs from the USB bus, and not IRQ 12.

所以我们的系统暂时不能支持USB鼠标, 而且鼠标的控制电路一开始是不被启用的, 我们需要激活这个控制电路, 之后的步骤和键盘的相差无几, 这里鼠标的中断号设置为int 2c.

  综上我们要先 设定GDT, IDT(装入ISR)->设定PIC->初始化键盘鼠标. 而为了防止我们在处理当前键盘中断的时候又有中断要处理, 所以处理的机制是: ISR不停接收中断, 将数据填入循环队列, ISR是回调的(可以这么说吧?), 有中断的时候会自动执行, 而主函数则不断地检查队列是否空, 非空的话就处理数据(很有趣的样子), 原理和Windows的消息队列是一样的.

  以上其实还包括了很多繁琐细节, 略去不表, 详见原书, 这只是笔记.

设定GDT是为了进入32位模式, 这个书的作者已经在asmhead.nas里面先实现了, 具体细节由于能力限制无法了解.

  • 队列缓冲区

  队列的实现采用了静态链表做循环队列的方式, 数据结构课上有教过, 代码如下:

 1 /* FIFO*/

 2 

 3 #include "bootpack.h"

 4 

 5 #define FLAGS_OVERRUN        0x0001

 6 

 7 void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)

 8 /* FIFO缓冲区初始化*/

 9 {

10     fifo->size = size;

11     fifo->buf = buf;

12     fifo->free = size; /* 缓冲区大小*/

13     fifo->flags = 0;

14     fifo->p = 0; /* 队列尾*/

15     fifo->q = 0; /* 队列头*/

16     return;

17 }

18 

19 int fifo8_put(struct FIFO8 *fifo, unsigned char data)

20 /* 入队*/

21 {

22     if (fifo->free == 0) {

23         /* 队列已满*/

24         fifo->flags |= FLAGS_OVERRUN;

25         return -1;

26     }

27     fifo->buf[fifo->p] = data;

28     fifo->p++;

29     if (fifo->p == fifo->size) {

30         fifo->p = 0;

31     }

32     fifo->free--;

33     return 0;

34 }

35 

36 int fifo8_get(struct FIFO8 *fifo)

37 /* 出队*/

38 {

39     int data;

40     if (fifo->free == fifo->size) {

41         /* 队列空 */

42         return -1;

43     }

44     data = fifo->buf[fifo->q];

45     fifo->q++;

46     if (fifo->q == fifo->size) {

47         fifo->q = 0;

48     }

49     fifo->free++;

50     return data;

51 }

52 

53 int fifo8_status(struct FIFO8 *fifo)

54 /* 取得状态*/

55 {

56     return fifo->size - fifo->free;

57 }

 

  • ISR(不停将数据写入缓冲区)

设定IDT的代码之前已经给出, 现在需要知道的是ISR怎么写, ISR 如下:

 1 struct FIFO8 keyfifo;

 2 void inthandler21(int *esp)

 3 {/* 来自PS/2键盘的中断*/

 4     unsigned char data;

 5     io_out8(PIC0_OCW2, 0x61);    /* 通知PIC, IRQ-01的受理已经完成*/

 6     data = io_in8(PORT_KEYDAT);

 7     fifo8_put(&keyfifo, data);

 8     return;

 9 }

10 

11 struct FIFO8 mousefifo;

12 void inthandler2c(int *esp)

13 /*来自PS/2 鼠标的中断 */

14 {

15     unsigned char data;

16     io_out8(PIC1_OCW2, 0x64);    /* 通知PIC IRQ-12 的受理已经完成*/

17     io_out8(PIC0_OCW2, 0x62);    /* 通知PIC IRQ-02 的受理已经完成*/

18     data = io_in8(PORT_KEYDAT);

19     fifo8_put(&mousefifo, data);

20     return;

21 }

 

  • 设定PIC的代码见上文
  • 主函数中处理数据的部分

键盘传来的数据容易理解, 都是键盘的字节码, 一个键的一个状态(Press/Up) 对应一个字节码(size =byte), 将其显示出来即可.

鼠标发生一次中断传回来3个byte 的数据:

byte 1:

Y overflow

X overflow

Y sign bit

X sign bit

Always 1

Middle Btn

Right Btn

Left Btn

byte 2 :

X movement

byte 3:

Y movement

 

我们主要需要的是第一个byte的后三位, 分别代表中键, 左键和右键, byte2 和 byte3 则是在X,Y轴上的位移, 主函数处理的代码如下:

 1 for (;;)

 2 {

 3         io_cli();

 4         if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) 

 5         {

 6             io_stihlt();

 7         } 

 8         else

 9         {

10             if (fifo8_status(&keyfifo) != 0) 

11             {

12                 i = fifo8_get(&keyfifo);

13                 io_sti();

14                 sprintf(s, "%02X", i);

15                 boxfill8(binfo->vram, binfo->scrnx, COL8_008484,  0, 16, 15, 31);

16                 putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);

17             }

18             else if (fifo8_status(&mousefifo) != 0) 

19             {

20                 i = fifo8_get(&mousefifo);

21                 io_sti();

22                 if (mouse_decode(&mdec, i) != 0) {

23                     /* 凑齐三个字节后显示 */

24                     sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);

25                     if ((mdec.btn & 0x01) != 0) {

26                         s[1] = 'L';

27                     }

28                     if ((mdec.btn & 0x02) != 0) {

29                         s[3] = 'R';

30                     }

31                     if ((mdec.btn & 0x04) != 0) {

32                         s[2] = 'C';

33                     }

34                     boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);

35                     putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);

36                     

37                        /* 鼠标指针的移动*/

38                     boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15); /* 隐藏鼠标*/

39                     mx += mdec.x;

40                     my += mdec.y;

41                     if (mx < 0) {

42                         mx = 0;

43                     }

44                     if (my < 0) {

45                         my = 0;

46                     }

47                     if (mx > binfo->scrnx - 16) {

48                         mx = binfo->scrnx - 16;

49                     }

50                     if (my > binfo->scrny - 16) {

51                         my = binfo->scrny - 16;

52                     }

53                     sprintf(s, "(%3d, %3d)", mx, my);

54                     boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15); /* 隐藏坐标*/

55                     putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); /* 显示坐标*/

56                     putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16); /* 绘制鼠标*/

57                 }

58             }

59         }

60     }

 

最后上图:

《30天自制操作系统》读书笔记(6) 鼠标键盘

你可能感兴趣的:(操作系统)