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

    最后上图:

  • 相关阅读:
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-13 实现用户信息在页面显示
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-12 回顾cookie与session
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-11 实现用户登录
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-10 设置跨域配置实现前后端联调
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-9 使用tomcat运行前端源码
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-8 优化Swagger2显示
    阶段一-01.万丈高楼,地基首要-第3章 用户登录注册模块开发-3-6 整合Swagger2文档api
    Swift实现Touch ID验证
    Mac上phantomjs装了不能用的解决
    after modifying system headers, please delete the module cache at
  • 原文地址:https://www.cnblogs.com/lastavengers/p/3983518.html
Copyright © 2020-2023  润新知