zl程序教程

您现在的位置是:首页 >  系统

当前栏目

操作系统键盘---17

操作系统 --- 17 键盘
2023-09-14 09:13:34 时间


终端设备包括显示器和键盘


让外设工作起来

在这里插入图片描述


关于键盘的故事从哪里开始?

如何使用键盘?

  • 对于使用者(人): 敲键盘、看结果
  • 对于操作系统: “等着”你敲键盘,敲了就中断

所以故事该从键盘中断开始,从中断初始化开始…

void con_init(void) //应为键盘也是console的一部分
{ 
//键盘中断对应的是21号中断---21号中断对应的处理程序为keyboard_interrupt
set_trap_gate(0x21, &keyboard_interrupt); }

汇编语言中断及外部设备操作篇–06

在kernel/chr_drv/keyboard.S.globl _keyboard_interrupt
_keyboard_interrupt:
//通过inb指令从外设端口读取数据
inb $0x60,%al //从端口0x60读扫描码
call key_table(,%eax,4) //调用key_table+eax*4
... push $0 call _do_tty_interrupt

处理扫描码key_table+eax*4

  • key_table是一个函数数组
在kernel/chr_drv/keyboard.S中
key_table:
                   //do_self:显示字符通常都 用此函数处理!
.long none,do_self,do_self,do_self //扫描码00-03
.long do_self, ...,func, scroll, cursor 等等
  • 扫描码02对应按键1;01对应ESC;12对应E等等
mode: .byte 0
do_self:
    lea alt_map,%ebx 
    testb $0x20,mode //alt键是否同时按下 jne 1f
    lea shift_map,%ebx testb $0x03,mode jne 1f
     //找到映射表,如a的key_map映射为a,而shift_map映射为A
     //将key_map起始地址赋值给ebx
    lea key_map,%ebx
1:

从key_map中取出ASCII码

#if defined(KBD_US)
   key_map: .byte 0,27 .ascii “1234567890-=...
   //同时按了shift键,需要转换成大写
   shift_map: .byte 0,27 .ascii “!@#$%^&*()_+...
#elif defined(KBD_GR) ...
  • 继续do_self,从1:开始,ebx放的是map起始地址
1: movb (%ebx,%eax),%al //扫描码索引,ASCII码àal
    orb %al,%al je none //没有对应的ASCII码
    testb $0x4c,mode //看caps是否亮
    je 2f cmpb $’a,%al jb 2f
    cmpb $’},%al ja 2f subb $32,%al //变大写
2:testb $??,mode //处理其他模式,如ctrl同时按下
                 //得到ascii码后,会将得到的ascii码加入put_queue中
3:andl $0xff,%eax call put_queue
none:ret

put_queue将ASCII码放到? con.read_q

//中端设备分为输入和输出设备,因此每个外设都对应两个队列,一个输入队列,一个输出队列
//我们这里读取键盘输入,显然应该是read_q队列
struct tty_queue *table_list[]={
&tty_table[0].read_q, 
&tty_table[0].write_q;
...
};
put_queue:
   //拿到对应的队列---这里是输入队列read_q
   movl _table_list,%edx 
   //拿到对应队列头部地址
   movl head(%edx),%ecx
   //将ascii码输出到队列的头部
1:movb %al,buf(%edx,%ecx)
  • 到目前为止还差什么? ---->一般读取完键盘输入后,还需要回显字符
void do_tty_interrupt(int tty) //上面传来的是0
{ 
//回显到什么设备上去---这里根据设备号索引,去tty_table中确定具体的设备
copy_to_cooked(tty_table+tty); }
void copy_to_cooked(struct tty_struct *tty)
{ 
//从当前设备的输入队列中,获取需要回显的字符
GETCH(tty->read_q,c);
if(L_ECHO(tty)){ //回显,也可以不回显--有些字符不能显示,就不进行回显
//将从输入队列读取的字符再放入当前设备对应的输出队列中去
PUTCH(c,tty->write_q);
tty->write(tty); } //立刻显示到屏幕上
PUTCH(c,tty->secondary); //完成copy_to_cooked---对字符进行一些处理后放入tty->secondary队列中去
... wake_up(&tty->secondary.proc_list);}

向屏幕输出 tty->write(tty) ,请看前一节内容


键盘处理…

在这里插入图片描述

  • 键盘按下一个按键后,对应触发键盘中断处理程序
  • 通过扫描码得到对应的ascii码,然后将ascii码加入read_q队列中去
  • do_tty_interrupt函数负责读取read_q队列
  • 判断是否需要显示当前字符到屏幕上去,如果需要,则从read_q队列读取对应的字符,加入write_q队列中,然后调用tty_write输出到屏幕
  • 对字符进行一些处理后,加入tty->secondary队列中去(具体是什么处理,感兴趣可以自行查询资料)

如果将键盘输入和显示器输出结合起来,就如下图所示:

在这里插入图片描述