《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     }

 

最后上图:

posted @ 2014-09-20 17:55  谷月轩  阅读(1924)  评论(2编辑  收藏
Flag Counter