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

简介: 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中 改成了这样 就可以了(也是个坑呀!)


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


相关文章
|
12小时前
|
缓存 Java iOS开发
MacOS环境-手写操作系统-13-鼠标中断
MacOS环境-手写操作系统-13-鼠标中断
8 1
|
11小时前
|
iOS开发 MacOS
MacOS环境-手写操作系统-40-进程消息通讯 和 回车键处理
MacOS环境-手写操作系统-40-进程消息通讯 和 回车键处理
9 2
|
13小时前
|
存储 Java C语言
MacOS环境-手写操作系统-04-实模式进入保护模式
MacOS环境-手写操作系统-04-实模式进入保护模式
6 1
|
13小时前
|
存储 Java iOS开发
MacOS环境-手写操作系统-02-读写软盘
MacOS环境-手写操作系统-02-读写软盘
9 3
|
11小时前
|
存储 iOS开发 MacOS
MacOS环境-手写操作系统-33-多任务多窗口
MacOS环境-手写操作系统-33-多任务多窗口
7 0
|
11小时前
|
存储 C语言 iOS开发
MacOS环境-手写操作系统-25-实现定时器
MacOS环境-手写操作系统-25-实现定时器
8 0
|
11小时前
|
存储 算法 调度
MacOS环境-手写操作系统-34-进程优先级
MacOS环境-手写操作系统-34-进程优先级
6 0
|
12小时前
|
Java C语言 iOS开发
MacOS环境-手写操作系统-11-建立中断机制
本文详细介绍了如何为内核建立中断机制,涉及8259A中断控制器的初始化、中断信号的传递过程以及中断描述符表的设置。通过汇编和C语言代码展示了如何处理中断,特别是键盘和鼠标中断,最后给出了编译和运行的步骤。 摘要由CSDN通过智能技术生成
7 0
|
11小时前
|
存储 算法 调度
MacOS环境-手写操作系统-29-进程切换
MacOS环境-手写操作系统-29-进程切换
5 0
|
12小时前
|
存储 Java C语言
MacOS环境-手写操作系统-14-控制鼠标移动
MacOS环境-手写操作系统-14-控制鼠标移动
6 0