• 操作系统键盘---17



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


    让外设工作起来

    在这里插入图片描述


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

    如何使用键盘?

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

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

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

    汇编语言中断及外部设备操作篇–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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    处理扫描码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 等等
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 扫描码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:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    从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) ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 继续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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    put_queue将ASCII码放到? con.read_q

    //中端设备分为输入和输出设备,因此每个外设都对应两个队列,一个输入队列,一个输出队列
    //我们这里读取键盘输入,显然应该是read_q队列
    struct tty_queue *table_list[]={
    &tty_table[0].read_q, 
    &tty_table[0].write_q;
    ...
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    put_queue:
       //拿到对应的队列---这里是输入队列read_q
       movl _table_list,%edx 
       //拿到对应队列头部地址
       movl head(%edx),%ecx
       //将ascii码输出到队列的头部
    1:movb %al,buf(%edx,%ecx)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 到目前为止还差什么? ---->一般读取完键盘输入后,还需要回显字符
    void do_tty_interrupt(int tty) //上面传来的是0
    { 
    //回显到什么设备上去---这里根据设备号索引,去tty_table中确定具体的设备
    copy_to_cooked(tty_table+tty); }
    
    • 1
    • 2
    • 3
    • 4
    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);}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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


    键盘处理…

    在这里插入图片描述

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

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

    在这里插入图片描述

  • 相关阅读:
    Django-(5)
    c# 学习教程
    Debezium系列之:Debezium采集Mysql数据库,数据库时间字段值的变化
    【计算机网络】网络层IP协议
    Java实现SFTP传输
    【Python笔记-设计模式】命令模式
    吉林大学高级程序设计(红皮书例题)(1~7章)
    中国500米分辨率年最大LAI数据集(2000-2020)
    【RapidAI】P1 中文文本切割程序
    day31 代码随想录 分发饼干&摆动序列&最大子序和
  • 原文地址:https://blog.csdn.net/m0_53157173/article/details/126172816