获取按键编码
修改int.c中的inthandle21
#define PORT_KEYDAT 0x0060 //从编号为0x0060设备输入的8位信息是按键编码
/* 来自 PS/2 键盘的中断 */
void inthandler21(int *esp) //将接收到的按键编码显示在画面上
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
unsigned char data, s[4];
//将“0x60+IRQ号码”输出给OCW2,用来通知PIC已经发生了“IRQ号码”中断,然后PIC继续监视“IRQ号码”中断是否发生
io_out8(PIC0_OCW2, 0x61);
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;
}
按下键后可以显示按键编码
加快中断
先将按键编码接收下来,保存在变量里,然后由HariMain偶尔去查看变量
int.c节选
struct KEYBUF
{
unsigned char data, //存储按键编码
flag; //flag用于表示缓冲区是否为空,0为空
};
#define PORT_KEYDAT 0x0060 //从编号为0x0060设备输入的8位信息是按键编码
struct KEYBUF keybuf;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* 通知PIC IRQ1已结受理完毕 */
data = io_in8(PORT_KEYDAT);
if (keybuf.flag == 0)
{
keybuf.data = data;
keybuf.flag = 1;
}
return;
}
bootpack.c中的HariMain节选
for (;;)
{
io_cli(); //屏蔽中断
if (keybuf.flag == 0) //没有键按下
{
io_stihlt(); //恢复中断,CPU休眠
}
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); //显示键码
}
}
缺陷:
当按下右键Ctrl时,一次产生两个字节键码值,因为键盘内部电路一次只能发送一个字节,所以一次按键会产生2次中断,本程序第二次产生的键码值被丢弃了。
制作FIFO缓冲区
问题出现在之前所创建的缓冲区只能存储一个字节,现在做一个能存储多个字节的缓冲区。
int.c节选
struct KEYBUF
{
unsigned char data[32], //存储按键编码
next; //next的起点为0,接着是1,依次类推,一共32个存储位置
};
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* 通知PIC IRQ1已结受理完毕 */
data = io_in8(PORT_KEYDAT);
if (keybuf.next < 32)
{
keybuf.data[keybuf.next] = data;
keybuf.next ++;
}
return;
}
bootpack.c节选
for (;;)
{
io_cli(); //屏蔽中断
if (keybuf.next == 0) //没有键按下
{
io_stihlt(); //恢复中断,CPU休眠
}
else
{
i = keybuf.data[0]; //保存按键编码
keybuf.next--; //将键码清空
for(j=0; j<keybuf.next; j++)
keybuf.data[j] = keybuf.data[j+1];
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); //显示键码
}
}
改善FIFO缓冲区
去掉数据移送操作
int.c节选
struct KEYBUF
{
unsigned char data[32]; //存储按键编码
int next_r, next_w, len; //len指缓冲区能记录多少个字节的数据
};
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* 通知PIC IRQ1已结受理完毕 */
data = io_in8(PORT_KEYDAT);
if (keybuf.len < 32)
{
keybuf.data[keybuf.next_w] = data;
keybuf.len++;
keybuf.next_w++;
if (keybuf.next_w == 32) //如果到了32后,再次从头开始写入
keybuf.next_w = 0;
}
return;
}
bootpack.c节选
for (;;)
{
io_cli(); //屏蔽中断
if (keybuf.len == 0) //没有键按下
{
io_stihlt(); //恢复中断,CPU休眠
}
else
{
i = keybuf.data[keybuf.next_r];
keybuf.len--;
keybuf.next_r++;
if (keybuf.next_r == 32) { //如果到达32后,再次从头开始读
keybuf.next_r = 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); //显示键码
}
}
整理FIFO缓冲区
将结构体改为:
struct FIFO8
{
unsigned char *buf; //大小可变的缓冲区,buf为缓冲区的地址
int next_w, next_r, size, free; //size保存缓冲区的总字节数,free保存没有数据的字节
int flags; //flags记录缓冲区是否溢出,溢出为1
};
fifo.c
#define FLAGS_OVERRUN 0x0001
/* 初始化FIFO缓冲区 */
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size; /* 缓冲区的大小 */
fifo->flags = 0;
fifo->next_w = 0; /* 下一个数据写入位置 */
fifo->next_r = 0; /* 下一个数据读出位置 */
return;
}
/* 向FIFO缓冲区传送数据并保存 */
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
if (fifo->free == 0) /* 空余没有了,溢出 */
{
fifo->flags |= FLAGS_OVERRUN;
return -1;
}
fifo->buf[fifo->next_w] = data;
fifo->next_w++;
if (fifo->next_w == fifo->size) {
fifo->next_w = 0;
}
fifo->free--;
return 0;
}
/* 从FIFO取得一个数据 */
int fifo8_get(struct FIFO8 *fifo)
{
int data;
if (fifo->free == fifo->size) //如果缓冲区为空,则返回-1
{
return -1;
}
data = fifo->buf[fifo->next_r];
fifo->next_r++;
if (fifo->next_r == fifo->size) {
fifo->next_r = 0;
}
fifo->free++;
return data;
}
/* 报告一下到底积攒了多少数据 */
int fifo8_status(struct FIFO8 *fifo)
{
return fifo->size - fifo->free;
}
init.c节选
#define PORT_KEYDAT 0x0060 //从编号为0x0060设备输入的8位信息是按键编码
struct FIFO8 keyfifo;
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); /* 通知PIC IRQ1已结受理完毕 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&keyfifo, data); //第一个参数为内存地址
return;
}
bootpack.c节选
char s[40], mcursor[256], keybuf[32];
fifo8_init(&keyfifo, 32, keybuf);
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);
}
}
激活鼠标控制器
bootpack.c节选
#define PORT_KEYDAT 0x0060
#define PORT_KEYSTA 0x0064
#define PORT_KEYCMD 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
//让键盘控制电路KBC(keyboard controller)做好准备,等待控制指令的到来
void wait_KBC_sendready(void)
{
for (;;) //一直进行查询
//如果KBC可以接受CPU指令了,CPU从设备码0x0064处所读取的数据的倒数第二位应该是0
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); //模式设定指令是0x60
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE); //鼠标模式的模式号码是0x47
return;
}
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
/* 激活鼠标 */
void enable_mouse(void)
{
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE); //如果往键盘控制电路发送指令0xd4,下一个数据会自动发送给鼠标
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
return; /* 顺利的话,键盘控制器会返送回ACK(0xfa) */
}
然后在主函数中调用init_keyboard();和enable_mouse();
make run后产生了鼠标中断
从鼠标接收数据
int.c节选
struct FIFO8 mousefifo;
/* 来自PS/2鼠标的中断 */
void inthandler2c(int *esp)
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64); /* 通知PIC1 IRQ-12的受理已经完成 */
io_out8(PIC0_OCW2, 0x62); /* 通知PIC0 IRQ-02的受理已经完成 */
data = io_in8(PORT_KEYDAT);
fifo8_put(&mousefifo, data);
return;
}
bootpack.c节选
char s[40], mcursor[256], keybuf[32], mousebuf[128];
fifo8_init(&keyfifo, 32, keybuf);
fifo8_init(&mousefifo, 128, mousebuf);
for (;;)
{
io_cli();
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0)
io_stihlt();
else
{
if (fifo8_status(&keyfifo) != 0)
{
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);
}
else if (fifo8_status(&mousefifo) != 0)
{
i = fifo8_get(&mousefifo);
io_sti();
sprintf(s, "%02X", i);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 47, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
总结一下从鼠标或者键盘获取数据并显示:
1、初始化鼠标控制电路。鼠标控制电路包含在键盘控制电路中,如果键盘控制电路初始完成,鼠标控制器的激活也就完成了。循环检查键盘电路是否准备好,准备好后发送模式设定指令。
2、发送激活鼠标指令。归根到底还是要想键盘控制器发送指令。这一步的函数与上一步很像,不同点在于写入数据不同,如果往键盘控制电路写入0xd4,下一个数据就会自动发送给鼠标。
3、在中断处理函数中编写中断处理,如要将显示的内容存入缓冲区。
4、在主函数显示中断处理,如将缓冲区中的内容显示处来。