终端设备包括显示器和键盘
如何使用键盘?
所以故事该从键盘中断开始,从中断初始化开始…
void con_init(void) //应为键盘也是console的一部分
{
//键盘中断对应的是21号中断---21号中断对应的处理程序为keyboard_interrupt
set_trap_gate(0x21, &keyboard_interrupt); }
在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
在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 等等
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:
#if defined(KBD_US)
key_map: .byte 0,27 .ascii “1234567890-=“ ...
//同时按了shift键,需要转换成大写
shift_map: .byte 0,27 .ascii “!@#$%^&*()_+” ...
#elif defined(KBD_GR) ...
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
//中端设备分为输入和输出设备,因此每个外设都对应两个队列,一个输入队列,一个输出队列
//我们这里读取键盘输入,显然应该是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) ,请看前一节内容
如果将键盘输入和显示器输出结合起来,就如下图所示: