初始化PIC和IDT
- PIC ,prograrnmable interrupt controller ,可编程中断控制器,从书上的描述,这里我们可以把这种东西理解成事件与回调函数,上层的UI程序在处理键盘鼠标事件也是同样的方法,绑定一段代码给某个时间,在操作系统中,键盘和鼠标在敲击的时候,会产生一些列中断,PIC就是产生事件的地方,CPU会把事件和对应的汇编代码绑定。
- 书上介绍了一般电脑都有两个PIC,然后两个级联。然后各种概念啥的,在这里,我们大概理解一下就行了,不用太在意细节。特别是init_pic函数,不用理解其具体含义,因为和芯片初始化相关的,知道了也没啥作用。
- 主要注意调用过程中,需要加上以下代码去打开键盘的中断通知。
io_out8(PIC0_IMR, 0xf9);
io_out8(PIC1_IMR, 0xef);
- 做到PIC的初始化还是不够的,还需要加上IDT(Interrupt Descriptor Table),这部分是在 day5里面说的。在刚刚#1中,说到,PIC是产生中断的地方,那么绑定中断和对应的函数的地方叫做IDT,这里初始化的代码如下:
LABEL_IDT:
%rep 255
Gate SelectorCode32, SpuriousHandler,0, DA_386IGate //配置中断调用函数
%endrep
IdtLen equ $ - LABEL_IDT
IdtPtr dw IdtLen - 1
dd 0
xor eax, eax
mov ax, ds
shl eax, 4
add eax, LABEL_IDT
mov dword [IdtPtr + 2], eax
lidt [IdtPtr]
sti //这里有被坑到,打开终端的意思。
_SpuriousHandler: //加上IDT对应的函数实现,其实就是调用C实现的函数。
SpuriousHandler equ _SpuriousHandler - $$
push es
push ds
pushad
mov eax, esp
push eax
call intHandlerFromC
pop eax
mov esp, eax
popad
pop ds
pop es
sti
iretd
- C函数的实现。我们先实现一个简单的。
void intHandlerFromC() {
char * vram = (char *) 0xa0000;
showString(vram, 320, 0, 0, COL8_FFFFFF, "Hello");
}
- 运行一下,发现敲击任何键,屏幕都会打印Hello,说明,我们的系统已经接收到了键盘的时间消息了。
识别某个键
修改 intHandlerFromC如下:
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;
}
void intHandlerFromC() {
char * vram = (char *) 0xa0000;
io_out8(PIC0_OCW2, 0x21);
unsigned char data = 0;
data = io_in8(PORT_KEYDAT);
char* pStr = charToHexStr(data);
static int showPos = 0;
showString(vram, 320, showPos, 0, COL8_FFFFFF, pStr);
showPos += 32;
}
鼠标的支持
- 调试过程中发现,之前的代码中,并没有初始化栈的GDT,需要抄一下代码。
- 然后修改init_pic里的值,添加对鼠标的支持。
io_out8(PIC0_IMR, 0xfb );
io_out8(PIC1_IMR, 0xef );
- 添加init_keyboard和enable_mouse两个函数,还有一些io_sti和io_stihlt等函数,感觉不是很理解,先把程序跑起来吧。
- 在IDT里面添加鼠标中断的函数回调。
%rep 10
Gate SelectorCode32, SpuriousHandler,0, DA_386IGate
%endrep
.2CH:
Gate SelectorCode32, mouseHandler,0, DA_386IGate
实现一个FIFO的队列
这里可以理解成两个线程,一个是主程序的主线程,一个是事件线程,但实际上并没有分开线程,只是打个比方。在这里,主线程中读取队列中的数据,事件回掉负责写入数据。