zoukankan      html  css  js  c++  java
  • 《30天自制操作系统》07_day_学习笔记

    harib04a:
      P126    获取按键编码;
      让程序在按下键盘的键之后,将键值编码显示出来
      修改的是前面编写的鼠标按键的处理键盘中断的函数inthandler21()
      这里笔者介绍了怎样把中断号告诉CPU:
        1、计算0x60+IRQ号码
        2、把结果输出给OCW2寄存器
        3、具体方法:调用io_out8(PIC0_OCW2, 0x60+IRQ);

    //int.c节选,修改键盘中断处理函数
    void inthandler21(int *esp)
    {
      struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
      unsigned char data, s[4];
      io_out8(PIC0_OCW2, 0x61);    /* 通知PIC已经发生了IRQ1中断 */
      data = io_in8(PORT_KEYDAT);  //获取键盘的按键键值,放到DATA中
      sprintf(s, "%02X", data);    //data写到S中;
      boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
      putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
      return;
    }

    harib04b:
      加快中断处理,上一步获取键盘键值的中断写在了中断处理程序中
      这样如果这时有其他的中断来了就佷尴尬了
      加速原理:利用缓冲区(笔者使用了一个变量)把读到的键值先保存在缓冲区中,需要时,再由HarMain去查看
      注     意:键盘中有的键是一个字节,有的键是两个字节;

    //加快中断处理第一步:增加键值缓冲区
    struct KEYBUF keybuf{          //键值缓冲区结构体,data:数据位,flag:标志位
      unsigned char data,flag; 
       };
    void inthandler21(int *esp) {   unsigned char data;   io_out8(PIC0_OCW2, 0x61); /* 通知PIC已经发生了IRQ1中断 */   data = io_in8(PORT_KEYDAT); //获取键值   if (keybuf.flag == 0) { //标志位为0表示缓冲区空,可以放键值   keybuf.data = data;   keybuf.flag = 1;   }   return; }
    //加快中断处理第二步:修改io_halt的无限循环
    for (;;) {
      io_cli();                     //io_cli指令屏蔽中断,因为在后面的处理,防止后面的处理中有中断进来,发生不可预料的结果
      if (keybuf.flag == 0) {
        io_stihlt();              //缓冲区空闲,执行STI和HLT指令;这时,PIC准备好接受中断唤醒CPU
      } else {                     //缓冲区不空闲,将获取的键值输出来,接着标志位置零。
        i = keybuf.data;
        keybuf.flag = 0;
        io_sti();
        sprintf(s, "%02X", i);
        boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
        putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
      }
    }

    harib04c:
      P131    制作FIFO缓冲区;
      思 考:为什么按下和松开都有键值,CTRL键又不一样;
      原 因:我们设定的缓冲区struct KEYBUF keybuf{ unsigned char data,flag};只有一个字节
      下面笔者加大了缓冲区的长度,并用FIFO的栈机制:

    struct KEYBUF keybuf{
      unsigned char data[32];     //缓冲区大小增加为32个字节
      int next;                   //指向缓冲区的下一个,因为是字符型数组
      };
    //缓冲区数据的程序用FIFO机制做了相应的调整
    for (;;) {
      io_cli();                   //io_cli指令屏蔽中断,
      if (keybuf.next == 0) {
        io_stihlt();            //缓冲区空闲,执行STI和HLT指令;
       } else {
        i = keybuf.data[0];
        keybuf.next--;          //不断的从缓冲区读和写FIFO
        for (j = 0; j < keybuf.next; j++) {
          keybuf.data[j] = keybuf.data[j + 1];
         }
        io_sti();
        sprintf(s, "%02X", i);
        boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
        putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
      }
    }

    harib04d:(这一部分请对照图片看!)
      到这里位置数据移送最多只能有32个字节(明显不够)
      到上面为止,数据移送都是在禁止中断的情况下:
        1、在FOR循环中首先进行的就是中断屏蔽: io_cli();//io_cli指令屏蔽中断;

        2、之后才能进行键盘的数据接收;
      思 考:如果每次读数据都要先屏蔽中断,这样也太。。。。(麻烦)。。怎么办?
      解 决:笔者接下来开发了一个不需要数据移送操作的FIFO缓冲区;大致运用了循环链表的思想:

       

      如上图所示:当写入的位置到达缓冲区末尾,缓冲区开头应该已经开始变空(如果没有变空,说明数据读跟不上数据写,那么只好把部分数据扔掉)。因此,如果下一个数据写入位置到了32以后,就强制性的将其置0;对下一个数据读出位置也做同样的处理,一旦到了32以后,就把它设置从0开始据徐读取数据。这样32字节的缓冲区就能一圈一圈的不断循环(其实就是循环链表;如果看不懂我的解释,请看书本P134内容

    //next_r :下一个读的位置
    //next_w :下一个写的位置
    //   len :缓冲区能记录多少字节的数据
    struct KEYBUF {         
      unsigned char data[32];
      int next_r, next_w, len;   };

      接下来做的事:修改中断处理程序(void inthandler21(int *esp))和io_halt的无限循环;下面是io_halt修改的部分:

    for (;;) {
      io_cli();
      if (keybuf.len == 0) {
        io_stihlt(); //这样每一次屏蔽中断的时间有1/2降低为1/32
      } else {
        i = keybuf.data[keybuf.next_r];
        keybuf.len--;
        keybuf.next_r++;
        if (keybuf.next_r == 32) {
        keybuf.next_r = 0;
        }
        io_sti();
        sprintf(s, "%02X", i);
        boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
        putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
      }
    }

    harib04e:
      我们知道,每次鼠标产生动作,会连续发送3个字节的数据(两个坐标和一个状态信息)
      接下来我们进一步修改缓冲区的内容,让他也能适应我们要做的鼠标的移动(重新定义缓冲区):

    //1、缓冲区大小改为可变,不再是32字节了
    //2、保存缓冲区的总字节数size
    //3、缓冲区空闲的字节数free
    //4、缓冲区的地址buf
    struct FIFO8 {
      unsigned char *buf;
      int p, q, size, free, flags;
    };

      作者其实是相当负责的,接着作者写了几个相关的操作函数以便于后续的调用;这些函数都被封装在FIFO.C中(FIFO.C代码较长,我们折叠起来吧!):

    /* FIFO.c */
    #include "bootpack.h"
    #define FLAGS_OVERRUN        0x0001
    
    void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
    /* FIFO缓冲区的初始化,用来设定FIFO8的结构地址以及有关的各种参数 */
    {
        fifo->size = size;
        fifo->buf = buf;
        fifo->free = size; /* 缓冲区大小 */
        fifo->flags = 0;
        fifo->p = 0;       /* 下一个数据写入的位置 */
        fifo->q = 0;       /* 下一个读数据的位置 */
        return;
    }
    
    int fifo8_put(struct FIFO8 *fifo, unsigned char data)
    /* 向FIFO缓冲区存储一个字节的数据 */
    {
        if (fifo->free == 0) {    /* 溢出了 */
            fifo->flags |= FLAGS_OVERRUN;
            return -1;           //返回-1 溢出了
        }
        fifo->buf[fifo->p] = data;
        fifo->p++;
        if (fifo->p == fifo->size) {
            fifo->p = 0;
        }
        fifo->free--;
        return 0;               //返回0,没有溢出
    }
    
    int fifo8_get(struct FIFO8 *fifo)
    /* 从缓冲区取一个字节的函数 */
    {
        int data;
        if (fifo->free == fifo->size) {
            /* 如果缓冲区为空返回-1 */
            return -1;
        }
        data = fifo->buf[fifo->q];
        fifo->q++;
        if (fifo->q == fifo->size) {
            fifo->q = 0;
        }
        fifo->free++;
        return data;
    }
    int fifo8_status(struct FIFO8 *fifo)
    /* 调出缓冲区的状态,报告到底积攒了多少数据 */
    {
        return fifo->size - fifo->free;
    }
    FIFO.C

      接下来做的事,和上面相同:修改中断处理程序(void inthandler21(int *esp));和io_halt的无限循环;

    void inthandler21(int *esp)     //修改中断处理程序(void inthandler21(int *esp))
    {
      unsigned char data;
      io_out8(PIC0_OCW2, 0x61);    /* PIC打开IRQ-1中断口,告诉CPU */
      data = io_in8(PORT_KEYDAT);
      fifo8_put(&keyfifo, data);  //看见木有,直接调用封装在fifo.c中的函数
      return;
    }
    for (;;) {                     //MariMain中也做相应调整;io_halt的循环早就没有啦
      io_cli();
      if (fifo8_status(&keyfifo) == 0) {
        io_stihlt();
      } else {
      i = fifo8_get(&keyfifo);
      io_sti();
      sprintf(s, "%02X", i);
      boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
      putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
      }
    }

    harib04f:
      好了,现在我们又要开始折腾鼠标了!
      还记得前面笔者给鼠标和键盘分配的PIC的中断号是多少吗?(键盘:IRQ01;鼠标:IRQ12
      笔者在书中这一部分首先给我们普及了一下鼠标是如何兴起的。接着普及了鼠标一些操作的一些变化(一句话:以前的鼠标操作和现在不同)
      我们先来看看控制电路是什么情况:
      注   意:鼠标控制器和键盘控制器实际上集成在同一个控制电路中。

    void wait_KBC_sendready(void)
    {     /* 等待键盘控制电路准备完成 */
        //如果键盘端口就绪PROT_KEYSTA & 处于准备发送数据的状态KEYSTA_SEND_NOTREADY
        //表示键盘控制电路已经准备完毕了。跳出去,返回,不再等待
      for (;;) {   if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) { break; }   }   
      return; 
    } 
    void init_keyboard(void) { /* 初始化键盘 */
       wait_KBC_sendready();   
       io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
       wait_KBC_sendready();   
       io_out8(PORT_KEYDAT, KBC_MODE);
       return; 
    }

      接下来向控制器发送指令,激活鼠标:

    //激活鼠标的相关程序,
    //这里和上一部中的void init_keyboard(void)初始化键盘很相似,发现了没有?
    //没错,就是因为这两个设备的控制器实际上是在同一个控制电路中的
    #define KEYCMD_SENDTO_MOUSE    0xd4    //键盘的使能信号
    #define MOUSECMD_ENABLE      0xf4     //鼠标的使能信号
    void enable_mouse(void)                //鼠标使能函数,想控制器发送激活指令
    {   /* 激活鼠标, */
      wait_KBC_sendready(); 
      io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
      wait_KBC_sendready();
      io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
      return; /* 激活成功,返回ACK(0xfa) */
    }

    harib04g:
      P142 从鼠标接收数据;原理和上面的从键盘接受数据相同
      在 harib04e 中,我们已经修改好了缓冲区,让他同时成为鼠标和键盘数据的缓冲(详见harib04e)
      那么我们要做什么事呢?
          没错,和键盘接受数据一样,修改两个东西:鼠标的中断程序(函数):inthandler2c() 和 io_halt的无限循环
      理解了上面键盘的做法,到这里理解起来就很简单了;直接上代码:

    //怎么样。鼠标的中断程序和键盘中断程序inthandler21(int *esp)神似,有木有
    void inthandler2c(int *esp)      /* PS/2的鼠标中断 */
    {
      unsigned char data;
      io_out8(PIC1_OCW2, 0x64);        /* IRQ-12已经受理完成 */
      io_out8(PIC0_OCW2, 0x62);        /* IRQ-02已经受理完成 */
      data = io_in8(PORT_KEYDAT);
      fifo8_put(&mousefifo, data);
      return;
    }
    //取数据程序(io_halt的无限循环)
    //这里和键盘的取数据程序也神似,不同的是,缓冲区开到了128字节;
    //因为鼠标的数据量更大
    fifo8_init(&mousefifo, 128, mousebuf){
      if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
      io_stihlt();
      } else {
        if (fifo8_status(&keyfifo) != 0) {
        i = fifo8_get(&keyfifo);
        io_sti();
        sprintf(s, "%02X", i);
        boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
        putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
      } else if (fifo8_status(&mousefifo) != 0) {
        i = fifo8_get(&mousefifo);
        io_sti();
        sprintf(s, "%02X", i);
        boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 47, 31);
        putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
      }
    }
  • 相关阅读:
    Code Review 五问五答
    JavaScript 10分钟入门
    swagger editor使用
    Tyk API网关介绍及安装说明
    Castle 多继承选择
    线程信息的获取和设置
    s3 api接口的调用
    在Hadoop集群上的HBase配置
    OpenStack 单元测试
    在Hadoop集群上的Hive配置
  • 原文地址:https://www.cnblogs.com/pengfeiz/p/5788964.html
Copyright © 2011-2022 走看看