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


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


相关文章
|
1天前
|
存储 C语言 iOS开发
MacOS环境-手写操作系统-48-让内核从错误中恢复
MacOS环境-手写操作系统-48-让内核从错误中恢复
9 0
|
1天前
|
存储 API C语言
MacOS环境-手写操作系统-46,47-C语言开发应用程序
MacOS环境-手写操作系统-46,47-C语言开发应用程序
8 0
|
1天前
|
编译器 API C语言
MacOS环境-手写操作系统-45-C语言开发应用程序
MacOS环境-手写操作系统-45-C语言开发应用程序
10 0
|
1天前
|
小程序 iOS开发 MacOS
MacOS环境-手写操作系统-44-运行简单的程序
MacOS环境-手写操作系统-44-运行简单的程序
7 0
|
1天前
|
存储 Java iOS开发
MacOS环境-手写操作系统-43-dir命令的实现 和 文件写入
MacOS环境-手写操作系统-43-dir命令的实现 和 文件写入
9 0
|
3月前
|
安全 Linux 网络安全
部署07--远程连接Linux系统,利用FinalShell可以远程连接到我们的操作系统上
部署07--远程连接Linux系统,利用FinalShell可以远程连接到我们的操作系统上
|
3月前
|
Linux 虚拟化 数据安全/隐私保护
部署05-VMwareWorkstation中安装CentOS7 Linux操作系统, VMware部署CentOS系统第一步,下载Linux系统,/不要忘, CentOS -7-x86_64-DVD
部署05-VMwareWorkstation中安装CentOS7 Linux操作系统, VMware部署CentOS系统第一步,下载Linux系统,/不要忘, CentOS -7-x86_64-DVD
|
2月前
|
编解码 安全 Linux
基于arm64架构国产操作系统|Linux下的RTMP|RTSP低延时直播播放器开发探究
这段内容讲述了国产操作系统背景下,大牛直播SDK针对国产操作系统与Linux平台发布的RTMP/RTSP直播播放SDK。此SDK支持arm64架构,基于X协议输出视频,采用PulseAudio和Alsa Lib处理音频,具备实时静音、快照、缓冲时间设定等功能,并支持H.265编码格式。此外,提供了示例代码展示如何实现多实例播放器的创建与管理,包括窗口布局调整、事件监听、视频分辨率变化和实时快照回调等关键功能。这一技术实现有助于提高直播服务的稳定性和响应速度,适应国产操作系统在各行业中的应用需求。
|
3月前
|
Linux 测试技术 数据安全/隐私保护
阿里云Linux os copilot 运维助手初体验
阿里云Linux os copilot 初体验
389 1
阿里云Linux os copilot 运维助手初体验
|
3月前
|
弹性计算 运维 自然语言处理
阿里云OS Copilot测评:重塑Linux运维与开发体验的智能革命
阿里云OS Copilot巧妙地将大语言模型的自然语言处理能力与操作系统团队的深厚经验相结合,支持自然语言问答、辅助命令执行等功能,为Linux用户带来了前所未有的智能运维与开发体验。