Skip to content

day7FIFO与鼠标控制

获取按键编码

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

在告诉收到信号的时候, 实际输出的是'0x60 + IRQ号码', 执行之后PIC会继续进行检测

把处理函数放在中断函数之外

c
struct KEYBUF {
	unsigned char data, flag;
};

struct KEYBUF keybuf;

void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61);	/* 清空中断标志位 */
	data = io_in8(PORT_KEYDAT);
	if (keybuf.flag == 0) {
		keybuf.data = data;
		keybuf.flag = 1;
	}
	return;
}
c
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);
    }
}

修改main函数

io_stihlt();这里使用这个函数是因为如果在sti函数之后发生中断数据就会存入, 但是没有被察觉到, 如果两个指令在一起CPU会屏蔽中间的中断

这时候的键盘返回的数据如果有两个会被吞掉, 因为一次按键只会产生两个中断, 但是直接收了每一次中断输出的第一个数据, 第二个数据在第二次中断的时候被舍弃了, 因为第二个数据到来的时候第一个数据没有处理完, 这时候会进入睡眠而不是进入再次处理

引入数据结构FIFO

c
/* 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;
}

鼠标

在初期的时候大多数的操作系统不支持鼠标, 所以鼠标的电路在不被激活的时候

在不激活的时候, 鼠标是不会产生中断的, 需要让鼠标的控制电路以及鼠标本身有效, 鼠标的控制电路包含在键盘的控制电路里面, 键盘的控制电路初始化的时候鼠标的初始化也就完成了

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

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

在确认可以发送信息之后设置模式的命令是0x60, 使用鼠标的模式是0x47

c
#define KEYCMD_SENDTO_MOUSE		0xd4
#define MOUSECMD_ENABLE			0xf4

void enable_mouse(void)
{
	/* 激活鼠标,先对键盘发送0xd4,之后的数据就会发送给鼠标 */
	wait_KBC_sendready();
	io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
	wait_KBC_sendready();
	io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
	return; /* 顺利的话的返回ACK(0xfa) */
}

这时候因为会返回一个数值, 所以会直接产生一个中断

对从鼠标获取的数据进行处理

c
struct FIFO8 mousefifo;

void inthandler2c(int *esp)
/* 来自鼠标的中断 */
{
	unsigned char data;
	io_out8(PIC1_OCW2, 0x64);	/* 通知PIC1 IRQ12的中断已经响应 */
	io_out8(PIC0_OCW2, 0x62);	/* 通知PIC0 IRQ12的中断已经响应 */
	data = io_in8(PORT_KEYDAT);
	fifo8_put(&mousefifo, data);
	return;
}
c
void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	char s[40], mcursor[256], keybuf[32], mousebuf[128];
	int mx, my, i;

	init_gdtidt();
	init_pic();
	io_sti(); /* 初始化结束之后打开中断 */
    //初始化两个FIFO
	fifo8_init(&keyfifo, 32, keybuf);
	fifo8_init(&mousefifo, 128, mousebuf);
	io_out8(PIC0_IMR, 0xf9); /* 许可中断 */
	io_out8(PIC1_IMR, 0xef); /* 许可中断 */
	//初始化键盘
	init_keyboard();
	//初始化花瓣
	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);
	//初始化鼠标
	enable_mouse();

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