《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[];
  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, , , , );
  putfonts8_asc(binfo->vram, binfo->scrnx, , , 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表示缓冲区空,可以放键值
  keybuf.data = data;
  keybuf.flag = ;
  }
  return;
}
//加快中断处理第二步:修改io_halt的无限循环
for (;;) {
  io_cli(); //io_cli指令屏蔽中断,因为在后面的处理,防止后面的处理中有中断进来,发生不可预料的结果
  if (keybuf.flag == ) {
    io_stihlt(); //缓冲区空闲,执行STI和HLT指令;这时,PIC准备好接受中断唤醒CPU
  } else { //缓冲区不空闲,将获取的键值输出来,接着标志位置零。
    i = keybuf.data;
    keybuf.flag = ;
    io_sti();
    sprintf(s, "%02X", i);
    boxfill8(binfo->vram, binfo->scrnx, COL8_008484, , , , );
    putfonts8_asc(binfo->vram, binfo->scrnx, , , COL8_FFFFFF, s);
  }
}

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

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

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

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

   《30天自制操作系统》07_day_学习笔记

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

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

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

for (;;) {
  io_cli();
  if (keybuf.len == ) {
    io_stihlt(); //这样每一次屏蔽中断的时间有1/2降低为1/32
  } else {
    i = keybuf.data[keybuf.next_r];
    keybuf.len--;
    keybuf.next_r++;
    if (keybuf.next_r == ) {
    keybuf.next_r = ;
    }
    io_sti();
    sprintf(s, "%02X", i);
    boxfill8(binfo->vram, binfo->scrnx, COL8_008484, , , , );
    putfonts8_asc(binfo->vram, binfo->scrnx, , , 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 = ;
fifo->p = ; /* 下一个数据写入的位置 */
fifo->q = ; /* 下一个读数据的位置 */
return;
} int fifo8_put(struct FIFO8 *fifo, unsigned char data)
/* 向FIFO缓冲区存储一个字节的数据 */
{
if (fifo->free == ) { /* 溢出了 */
fifo->flags |= FLAGS_OVERRUN;
return -; //返回-1 溢出了
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size) {
fifo->p = ;
}
fifo->free--;
return ; //返回0,没有溢出
} int fifo8_get(struct FIFO8 *fifo)
/* 从缓冲区取一个字节的函数 */
{
int data;
if (fifo->free == fifo->size) {
/* 如果缓冲区为空返回-1 */
return -;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size) {
fifo->q = ;
}
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) == ) {
    io_stihlt();
  } else {
  i = fifo8_get(&keyfifo);
  io_sti();
  sprintf(s, "%02X", i);
  boxfill8(binfo->vram, binfo->scrnx, COL8_008484, , , , );
  putfonts8_asc(binfo->vram, binfo->scrnx, , , 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) == ) { 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, , mousebuf){
  if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == ) {
  io_stihlt();
  } else {
    if (fifo8_status(&keyfifo) != ) {
    i = fifo8_get(&keyfifo);
    io_sti();
    sprintf(s, "%02X", i);
    boxfill8(binfo->vram, binfo->scrnx, COL8_008484, , , , );
    putfonts8_asc(binfo->vram, binfo->scrnx, , , COL8_FFFFFF, s);
  } else if (fifo8_status(&mousefifo) != ) {
    i = fifo8_get(&mousefifo);
    io_sti();
    sprintf(s, "%02X", i);
    boxfill8(binfo->vram, binfo->scrnx, COL8_008484, , , , );
    putfonts8_asc(binfo->vram, binfo->scrnx, , , COL8_FFFFFF, s);
  }
}
上一篇:oracle net manager 数据传输安全


下一篇:php服务器探针