1. 获取按键编码
这里需要特别注意的是,需要采用io_out8(PIC0_OCW2, 0x61);
来通知cpu IRQ-01上面的中断已经处理完毕。如果不写这句话的话, cpu会忽略后续IRQ-01 上传递过来的中断信号。其他 IRQ-n 对应的编码应该为: 0x60 + n
#define PORT_KEYDAT 0x0060
void inthandler21(int *esp)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
unsigned char data, s[4];
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
sprintf(s, "%02X", data);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
return;
}
2. 加快中断处理
- 所谓中断操作,基本就是打断CPU 本来的工作,加塞要求进行处理,所以应该简短,而上面,我们将屏幕显示灯操作放入到中断响应中来,可能会造成响应迟钝等一系列问题。
- 我们的处理方式是,定义一个缓冲区,将传入的数据写入到缓冲区中,在主程序中访问这个缓冲区进行相应的处理。
- int.c
/* int.c */
struct KEYBUF {
unsigned char data, flag;
};
#define PORT_KEYDAT 0x0060
struct KEYBUF keybuf;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
if (keybuf.flag == 0) {
keybuf.data = data;
keybuf.flag = 1;
}
return;
}
main
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
char s[40], mcursor[256];
int mx, my, i;
init_gdtidt();
init_pic();
io_sti(); /* IDT/PICの初期化が終わったのでCPUの割り込み禁止を解除 */
io_out8(PIC0_IMR, 0xf9); /* PIC1とキーボードを許可(11111001) */
io_out8(PIC1_IMR, 0xef); /* マウスを許可(11101111) */
init_palette();
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
mx = (binfo->scrnx - 16) / 2; /* 画面中央になるように座標計算 */
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%d, %d)", mx, my);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
for (;;) {
io_cli();
if (keybuf.flag == 0) {
io_stihlt();
} else {
i = keybuf.data;
keybuf.flag = 0;
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
}
3. 制作FIFO缓冲区
- 为了避免数据丢失,我们可以设定缓冲区大小,为了提高效率, 这里我们采用循环数组的方式处理
- fifo.c
其中, free 表示可以使用的缓存区空间大小, p 表示写入位置, q表示读取位置
/* fifo.c */
struct FIFO8 {
unsigned char *buf;
int p, q, size, free, flags;
};
/* FIFOライブラリ */
#include "bootpack.h"
#define FLAGS_OVERRUN 0x0001
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
/* FIFOバッファの初期化 */
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size; /* 空き */
fifo->flags = 0;
fifo->p = 0; /* 書き込み位置 */
fifo->q = 0; /* 読み込み位置 */
return;
}
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
/* FIFOへデータを送り込んで蓄える */
{
if (fifo->free == 0) {
/* 空きがなくてあふれた */
fifo->flags |= FLAGS_OVERRUN;
return -1;
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size) {
fifo->p = 0;
}
fifo->free--;
return 0;
}
int fifo8_get(struct FIFO8 *fifo)
/* FIFOからデータを一つとってくる */
{
int data;
if (fifo->free == fifo->size) {
/* バッファが空っぽのときは、とりあえず-1が返される */
return -1;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size) {
fifo->q = 0;
}
fifo->free++;
return data;
}
int fifo8_status(struct FIFO8 *fifo)
/* どのくらいデータが溜まっているかを報告する */
{
return fifo->size - fifo->free;
}
相应的中断处理函数
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&keyfifo, data);
return;
}
main
void HariMain(void)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
char s[40], mcursor[256], keybuf[32];
int mx, my, i;
init_gdtidt();
init_pic();
io_sti(); /* IDT/PICの初期化が終わったのでCPUの割り込み禁止を解除 */
fifo8_init(&keyfifo, 32, keybuf);
io_out8(PIC0_IMR, 0xf9); /* PIC1とキーボードを許可(11111001) */
io_out8(PIC1_IMR, 0xef); /* マウスを許可(11101111) */
init_palette();
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny);
mx = (binfo->scrnx - 16) / 2; /* 画面中央になるように座標計算 */
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%d, %d)", mx, my);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
for (;;) {
io_cli();
if (fifo8_status(&keyfifo) == 0) {
io_stihlt();
} else {
i = fifo8_get(&keyfifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
}
6. 鼠标
- 由于鼠标的控制电路包含在键盘的控制电路中, 所以,如果键盘控制电路初始化正常完成,鼠标电路控制器的激活也就完成了
- bootpack.c
这里,wait_KBC_sendready, 用来让键盘控制电路做好准备等待指令到来。只有当设备号码为0x0064处读取的数据倒数第二位为 0 的时候, 键盘控制电路是可以接受CPU 指令的
void wait_KBC_sendready(void)
{
/* キーボードコントローラがデータ送信可能になるのを待つ */
for (;;) {
if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {
break;
}
}
return;
}
void init_keyboard(void)
{
/* キーボードコントローラの初期化 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);
return;
}
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
void enable_mouse(void)
{
/* マウス有効 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
return; /* うまくいくとACK(0xfa)が送信されてくる */
}
7. 从鼠标接受数据
- int.c
鼠标接受数据和键盘中断的处理流程类似, 不过需要额外注意的是, 需要给 PIC1_OCW2 和 PIC0_OCW2 都发送一个cpu 响应中断处理的消息, 这是因为 鼠标使用的是 IRQ-12信号, 位于从PIC 上面。
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&keyfifo, data);
return;
}
struct FIFO8 mousefifo;
void inthandler2c(int *esp)
/* PS/2マウスからの割り込み */
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64); /* IRQ-12受付完了をPIC1に通知 */
io_out8(PIC0_OCW2, 0x62); /* IRQ-02受付完了をPIC0に通知 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&mousefifo, data);
return;
}