自制操作系统13:移动鼠标 - 中断机制探秘,键盘中断识别具体键值,键盘缓冲区

参考:
https://blog.csdn.net/tyler_download/article/details/52770376
https://www.bilibili.com/video/BV1VJ41157wq?p=13
https://weread.qq.com/web/reader/38732220718ff5cf3877215k9f6326602389f61408e3715
上节只是捕获了按键,然后打印出来事件,但并不知道按了哪个键。
今天我们继续加油吧。鼠标不动的原因已经大体弄清楚了,主要是由于设定不到位。但是,在解决鼠标问题之前,还是先利用键盘多练练手,这样更易于鼠标问题的理解。
现在,只要在键盘上按一个键,就会在屏幕上显示出信息,其他的我们什么都做不了。我们将程序改善一下,让程序在按下一个键后不结束,而是把所按键的编码在画面上显示出来,这样就可以切实完成中断处理程序了。

write_vga_desktop.c
#define PORT_KEYDAT 0x0060
#define PIC_OCW2 0x20
......
static char keyval[5] = {'0', 'X', 0, 0, 0};
char charToHexVal(char c);
char* charToHexStr(unsigned char c);
......
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;
}
然后就是老套路,编译反编译,制作启动盘
gcc -m32 -fno-pie -s -c -o write_vga_desktop.o write_vga_desktop.c
../objconv-new/objconv/objconv -fnasm write_vga_desktop.o -o write_vga_desktop.asm
delete:
global
extern
section
endbr32 
kernel.asm
%include "write_vga_desktop.asm"
nasm -o kernel.bat kernel.asm

实验发现比演示程序多两个字节,不明原因。
0x21,改为0x61好像一样


大家可以做各种尝试,比如按下“B”键,按下回车键等。键按下去之后,随即就会显示出一个数字(十六进制)来,键松开之后也会显示出一个数字。所以,计算机不光知道什么时候按下了键,还知道什么时候把键松开了。这种特性最适合于开发游戏了。不错不错,心满意足。

程序做出来了,大家心情肯定很好,但其实这个程序里有一个问题,那就是字符显示的内容被放在了中断处理程序中。所谓中断处理,基本上就是打断CPU本来的工作,加塞要求进行处理,所以必须完成得干净利索。而且中断处理进行期间,不再接受别的中断。所以如果处理键盘的中断速度太慢,就会出现鼠标的运动不连贯、不能从网上接收数据等情况,这都是我们不希望看到的。另一方面,字符显示是要花大块时间来进行的处理。仅仅画一个字符,就要执行8×16=128次if语句,来判定是否要往VRAM里描画该像素。如果判定为描画该像素,还要执行内存写入指令。而且为确定具体往内存的哪个地方写,还要做很多地址计算。这些事情,在我们看来,或许只是一瞬间的事,但在计算机看来,可不是这样。谁也不知道其他中断会在哪个瞬间到来。事实上,很可能在键盘输入的同时,就有数据正在从网上下载,而PIC正在等待键盘中断处理的结束。

那该如何是好呢?结论很简单,就是先将按键的编码接收下来,保存到变量里,然后由HariMain偶尔去查看这个变量。如果发现有了数据,就把它显示出来。我们就这样试试吧。这就是键盘缓冲区。第一次听到键盘缓冲区竟然是几十年前在大学里面。
打个比喻:前面的程序键盘控制器(设备号码0x0060)是在“想去厕所,快要忍不住了”(=屏蔽中断的状态)的情况下,等待着程序的处理,才勉强得到这样看起来还不错的结果。但这对于硬件来讲,实在有点太勉为其难了。在新程序中,硬件没有负担,不会憋得肚子疼。
struct KEYBUF {
    unsigned char key_buf[32];
    int next_r, next_w, len;
};

struct KEYBUF keybuf;

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;
    }

}

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;           
       }
    }
}
write_vga_desktop_keyboard_buf.c

然后就是老套路,编译反编译,制作启动盘
gcc -m32 -fno-pie -s -c -o write_vga_desktop_keyboard_buf.o write_vga_desktop_keyboard_buf.c
../objconv-new/objconv/objconv -fnasm write_vga_desktop_keyboard_buf.o -o write_vga_desktop_keyboard_buf.asm
delete:
global
extern
section
endbr32 
kernel.asm
%include "write_vga_desktop_keyboard_buf.asm"
nasm -o kernel.bat kernel.asm

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值