一般的键按下只产生1个Make Code,弹起也只产生1个Break Code;而有些键按下或弹起却产生不止1个Code,而是2个Code,例如右边的Ctrl键,如图所示:
我们看到,右边的Ctrl的Make Code为0xE0,0x1D,而Break Code为0xE0,0x9D,而8042的输入缓冲区只有1个字节,当0xE0传进来时无法得知到底按下的是哪一个键,说不定是这个键又被释放了呢。所以我们有必要扩展缓冲区,让键盘中断程序把从8042接收到的扫描码放到这个缓冲区中。
我们用一个数据结构来描述这个缓冲区,在include文件夹新建一个Keyboard.h头文件,写一个KB_Input_Buffer结构,缓冲区大小为256个字节:
- /*====================================================================
- keyboard.h
- ====================================================================*/
- typedef struct s_kb_inputz_buffer
- {
- char *p_head;
- char *p_tail;
- int count;
- char buffer[256];
- }KB_Input_Buffer;
先定义一个这个结构类型的变量,由于我们只想在本文件使用这个变量,所以加上static进行保护,还要写一些初始化这个结构的代码,写在Init_Keyboard函数里:
- /*====================================================================
- keyboard.c
- ====================================================================*/
- #include "type.h"
- #include "proto.h"
- #include "keyboard.h"
- static KB_Input_Buffer kb_input_buffer;
- void Init_Keyboard()
- {
- kb_input_buffer.count = 0;
- kb_input_buffer.p_head = kb_input_buffer.p_tail = kb_input_buffer.buffer;
- Put_IRQ_Handler(1,Keyboard_Handler);
- Enable_IRQ(1);
- }
下面再来在Keyboard_Handler中写把键盘输入的扫描码丢到缓冲区的代码:
- void Keyboard_Handler(u32 irq_no)
- {
- u8 scan_code = In_Byte(0x60);
- if(kb_input_buffer.count < 256)
- {
- *(kb_input_buffer.p_tail) = scan_code;
- kb_input_buffer.p_tail++;
- kb_input_buffer.count++;
- if(kb_input_buffer.p_tail >= kb_input_buffer.buffer + 256)
- {
- kb_input_buffer.p_tail = kb_input_buffer.buffer;
- }
- }
- }
代码很简单,首先判断一下缓冲区够不够地方装这个扫描码,如果不够则忽略掉,如果够则把缓冲区的尾指针指向的地儿装这个扫描码,然后尾指针前移,缓冲区的当前扫描码数增1,如果尾指针到了buffer[256]了,就是说刚好越界了,就把尾指针移回指向buffer[0],这实际上是个线性循环队列,像我刚学数据结构时那纳闷了,这么枯燥的东西到底有啥用,哦哦,敢情在这用上了。
光把扫描码丢进缓冲区还不行,还得把它显示出来,而为了避免键盘中断处理程序过于庞大,我们新建一个终端进程,这个进程是用来做一些显示和打印工作的。
我们在kernel文件夹里新建一个tty.c文件,写这个进程的执行体:
- /*====================================================================
- tty.c
- ====================================================================*/
- #include "type.h"
- #include "proto.h"
- void tty()
- {
- while(1)
- {
- Keyboard_Read();
- }
- }
我们只做一件事,反复的调用Keyboard_Read函数,这个函数负责从缓冲区中取出一个扫描码并打印出来,这个函数写在keyboard.c中:
- void Keyboard_Read()
- {
- u8 scan_code;
- if(kb_input_buffer.count > 0)
- {
- Disable_Int();
- scan_code = *(kb_input_buffer.p_head);
- kb_input_buffer.p_head++;
- kb_input_buffer.count--;
- if(kb_input_buffer.p_head >= kb_input_buffer.buffer + 256)
- {
- kb_input_buffer.p_head = kb_input_buffer.buffer;
- }
- Enable_Int();
- Disp_Int(scan_code);
- }
- }
逻辑也很简单,如果缓冲区还有扫描码,就拿出来打印一下,头指针前移,如果越界,则移回来。我们发现了两个新函数:Disable_Int,Enable_Int,它们的作用分别是关中断和开中断,为什么要这样做呢,因为我们想保证这两个函数之间的代码具有原子性,就是一气呵成的完成,不想被别人打断。如果不这样做的话,那么我们在从缓冲区取扫描码的时候有可能会被键盘中断打断,而键盘中断处理程序也会对缓冲区进行操作,这样就可能造成错误。
在kliab.asm中添加这两个函数:
global Disable_Int
global Enable_Int
...
;Disable_Int==========================================================
Disable_Int:
cli
ret
;end of Disable_Int===================================================
;Enable_Int===========================================================
Enable_Int:
sti
ret
;end of Enable_Int====================================================
别忘了在proto.h中添加函数的声明:
- void Keyboard_Read();
- void Disable_Int();
- void Enable_Int();
- void tty();
哦哦,对了,还有一点差点忘了,还没分配中断进程的PCB和栈空间呢,下面来做这件事。
首先进程个数改为4,再为该进程分配栈空间:
- #define PROC_NUM 4
- #define PROC_TTY_STACK_SIZE 0x8000
- #define PROC_STACK_SIZE_TOTAL (PROC_A_STACK_SIZE + /
- PROC_B_STACK_SIZE + /
- PROC_C_STACK_SIZE + /
- PROC_TTY_STACK_SIZE)
接着修改:
- TASK Task_Tables[PROC_NUM] = { {Proc_A,PROC_A_STACK_SIZE},
- {Proc_B,PROC_B_STACK_SIZE},
- {Proc_C,PROC_C_STACK_SIZE},
- {tty,PROC_TTY_STACK_SIZE}
- };
再把此进程的优先级改为最高:
- PCB_Tables[3].ticks = PCB_Tables[3].priority = 300;
新添加了2个头文件,所以要修改MAKEFILE,运行结果如以前一样,按一个键打印出相应的扫描码,但是有时候会有些延迟,因为此时系统中共有4个进程在运行,而且前3个进程每个都要延迟200毫秒,当我们按下一个键后返回到的进程不一定是终端进程,所以不能确保马上能打印出来。
下一步就该是如何把无意义的扫描码转化为我们熟知的对应的字符了,休息下再说。