MacOS环境-手写操作系统-12-键盘中断机制

为键盘建立中断机制

1.简介

我们实现了键盘中断的响应 但响应的处理比较简单 只是向界面打印一条字符串而已

本节 当键盘上的一个按键按下时 键盘会发送一个中断信号给CPU

与此同时 键盘会在指定端口(0x60) 输出一个数值

这个数值对应按键的扫描码(make code) 当按键弹起时 键盘又给端口输出一个数值 这个数值叫断码(break code).我们以按键按键’A’为例 当按键’A’按下时 键盘给端口0x60发出的扫描码是0X1E 当按键’A’弹起时 键盘会给端口0x60发送断码0x9E

键盘的扫描码和断码列表:

在这里插入图片描述

2.代码

从上图可以看到

当按键 ‘A’按下时 键盘向端口发送数值0x1E 弹起时发送数值0x9e

同理按键’B’按下时,键盘向端口0x60发送数值0x30,弹起时向端口发送0xB0,

我们更改上一节的中断处理代码,使得键盘按键按下和弹起时,在界面上显示出按键的make code 和 break code:

void intHandlerFromC(char* esp) {
    char*vram = bootInfo.vgaRam;
    int xsize = bootInfo.screenX, ysize = bootInfo.screenY;
    io_out8(PIC_OCW2, 0x21);
    unsigned char data = 0;
    data = io_in8(PORT_KEYDAT);
    char* pStr = charToHexStr(data);
    static int showPos = 0;
    showString(vram, xsize, showPos, 0, COL8_FFFFFF, pStr);
    showPos += 32;
}

char   charToHexVal(char c) {
    if (c >= 10) {
        return 'A' + c - 10;
    } 

    return '0' + c;
}

char*  charToHexStr(unsigned char c) {
    int i = 0;
    char mod = c % 16;
    keyval[3] = charToHexVal(mod);
    c = c / 16;
    keyval[2] = charToHexVal(c);

    return keyval;
}

intHandlerFromC 是C语言处理中断的函数

我们注意看语句:
io_out8(PIC_OCW2, 0x21);
其中PIC_OCW2 的值是0x20 也就是主PIC芯片的控制端口

上一节我们解释过 0x21对应的是键盘的中断向量

当键盘中断被CPU执行后 下次键盘再向CPU发送信号时 CPU就不会接收

要想让CPU再次接收信号 必须向主PIC的端口再次发送键盘中断的中断向量号

PORT_KEYDAT 的值是0x60 io_in8 是内核汇编部分提供的函数 它从指定端口读入数据 并返回

charToHexStr 作用是将键盘输出的数值转换为16进制的字符串。

当一个按键被按下然后弹起时 上面的intHandlerFromC会调用两次

从而一次按键使得界面上会连续打印两个16进制数值

下面我们对代码进行编译:

编译C文件

i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga_desktop.o write_vga_desktop.c

反汇编o文件

./objconv -fnasm write_vga_desktop.o write_vga_desktop.asm

删除无用部分

修改kernel

%include "write_vga_desktop.asm"

修改boot(直接放大一点)直接读了18个扇区 肯定够用了

    mov          AL,  18        ; AL 表示要练习读取几个扇区

编译boot

nasm -o boot.bat boot.asm

编译kernel

nasm -o kernel.bat kernel.asm

运行java 生成system.img

然后按下按键结果显示如下:

(出现乱的是因为我多按了 导致覆盖了)

在这里插入图片描述

3.优化

中断 实际上是将CPU当前正在执行的任务给打断 让CPU先处理中断任务 然后再返回处理原先的任务

这时会有一个问题 如果中断处理过久 就会对CPU原来的任务造成负面影响

就以键盘中断为例 我们处理键盘中断时 要获取按键的扫描码和断码的数值

同时将数值转换为字符串 最后再将字符串的每一个字符绘制到界面上

这一系列其实是很耗时的计算 假设这时候有网络数据抵达系统 但是CPU忙于处理键盘中断 不能及时接收网络数据 便会丢失网络数据

所以 对于中断 我们要尽可能快的处理 然后把控制器交还给原来的任务

对于键盘中断 我们可以把键盘发送的扫描码和断码数值缓存起来 然后把控制器交换给原来任务 等到CPU稍微空闲时再处理键盘事件

因此我们为键盘中断设置一个缓冲区:

struct KEYBUF {
    unsigned char key_buf[32];
    int next_r, next_w, len;
};

struct KEYBUF keybuf;

我们设置了长为32字节的缓冲区 当键盘中断接收到数据时 从next_w指向的位置开始写入 len用来表示当前缓冲区中的有效数据长度

例如 当我们按下’A’和’B’两个按键时 我们会向缓冲区写入4个字节 于是len就等于4

void intHandlerFromC(char* esp) {
    char*vram = bootInfo.vgaRam;
    int xsize = bootInfo.screenX, ysize = bootInfo.screenY;
    io_out8(PIC_OCW2, 0x21);
    unsigned char data = 0;
    data = io_in8(PORT_KEYDAT);
    if (keybuf.len < 32) {
       keybuf.key_buf[keybuf.next_w] = data;
       keybuf.len++;
       keybuf.next_w = (keybuf.next_w+1) % 32;
    }

}

每次键盘中断 代码都将相应的扫描码和断码写入缓冲区

如果缓冲区写满后 也就是next_w的值达到32 那么通过一次求余 next_w会重新设置为0

也就是说一旦缓冲区写满后 下次写入将从头开始

键盘数据的输出转移到内核的主函数CMain中

void CMain(void) {
   ...

   int data = 0;
    for(;;) {
       io_cli();
       if (keybuf.len == 0) {
           io_stihlt();
       } else {
           data = keybuf.key_buf[keybuf.next_r];
           keybuf.next_r = (keybuf.next_r + 1) % 32;
           io_sti();

           char* pStr = charToHexStr(data);
           static int showPos = 0;
           showString(vram, xsize, showPos, 0, COL8_FFFFFF, pStr);
           showPos += 32;           
       }
    }
}

主函数不再单纯的死循环 而是每次循环的时候查看键盘缓冲区是否有数据 有数据的话就把缓冲区中的数据显示到屏幕上 同时next_r增加 以指向下一个要输出的数据

4.编译运行

编译C文件

i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga_desktop_keyboard_buf.o write_vga_desktop_keyboard_buf.c

反汇编o文件

./objconv -fnasm write_vga_desktop_keyboard_buf.o write_vga_desktop_keyboard_buf.asm

删除无用部分

修改kernel

%include "write_vga_desktop_keyboard_buf.asm"

修改boot(直接放大一点)直接读了18个扇区 肯定够用了

    mov          AL,  18        ; AL 表示要练习读取几个扇区

编译boot

nasm -o boot.bat boot.asm

编译kernel

nasm -o kernel.bat kernel.asm

运行java 生成system.img

最后在编译的时候 遇到了 kernel 中报错(提示找不到 keybuf)

原博客老师写的是:

struct KEYBUF keybuf;

我改成了

static struct KEYBUF keybuf;

在C中 改成了这样 就可以了(也是个坑呀!)

运行后看不出区别来 所以我就不放图了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值