30天自制OS学习笔记 (七)FIFO与鼠标控制

本文详细介绍了在操作系统环境下,如何处理键盘和鼠标的中断信号,并使用FIFO缓冲区优化数据处理流程。从获取按键编码到加快中断处理速度,再到实现FIFO缓冲区的通用性和效率改进,最后探讨了鼠标控制电路的初始化和数据接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.获取按键编码
接着上次改善一下程序,让程序在按下一个键时,让程序把所按键的编码在画面上显示出来。

#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);	/* 通知PIC0  IRQ-01 已经受理完毕 */
	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;
}

io_out8(PIC0_OCW2, 0x61); 这句话用来通知PIC已经知道发生了IRQ1中断了。将“0x60+IRQ号码” 输出给OCW2就可以。执行这句话之后,PIC继续时刻监视着IRQ1中断是否发生。如果不执行PIC就不在监视IRQ1中断,下次不管由键盘输入什么信息,都感知不到。
这里说一下ICW和OCW:
(1) 初始化命令字ICW(initialization command word):ICW1~ICW4,它们必须在初始化时分别写入4个相应的寄存器。并且一旦写入,一般在系统运行过程中就不再改变。
(2) 工作方式命令字或操作命令字OCW(operation command word):OCW1~OCW3,它们必须在设置初始化命令后方能分别写入3个相应的寄存器。它们用来对中断处理过程进行动态的操作与控制。在一个系统运行过程中,操作命令字可以被多次设置。

这样,就可以运行了。不知道是不是我的电脑问题,运行书中的源文件,按下再松开按键之后并没有显示其他数字…太让人头大了…(所显示的数字是键盘扫描码)

2.加快中断处理
为了提高中断处理的速度,先将按键的编码接收下来,保存到变量里,然后由HariMain偶尔去查看这个变量。如果发现有了数据就显示出来。
int.c节选:

struct KEYBUF {
	unsigned char data, flag;  //flag=0 缓冲区为空,flag=1 缓冲区有数据 满了
};
struct KEYBUF keybuf;

void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61);	/* 通知PIC0  IRQ-01 已经受理完毕 */
	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();
		} 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缓冲区 & 4.改善FIFO缓冲区
制作FIFO缓冲区 是在上面的基础上做了一个存储多字节的缓冲区,但在读数据是却需要移动操作。所以下面是改善的FIFO缓冲区介绍。
在这里插入图片描述
int.c节选:
写数据

struct KEYBUF {
	unsigned char data[32];
	int next_r, next_w, len;
};

void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61);	/* IRQ-01受付完了をPICに通知 */
	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) {
			keybuf.next_w = 0;
		}
	}
	return;
}

读数据

for (;;) {
		io_cli();
		if (keybuf.len == 0) {
			io_stihlt();
		} else {
			i = keybuf.data[keybuf.next_r];
			keybuf.len--;
			keybuf.next_r++;
			if (keybuf.next_r == 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);
		}
	}

这样就没有数据移动的操作了。

5.整理FIFO缓冲区
为了让缓冲区具有通用性,可以将缓冲区大小设为可变的,几个字节都行。

struct FIFO8 {
	unsigned char *buf;
	int p, q, size, free, flags;  /*size为缓冲区总字节数,free为缓冲区中没有数据的字节数, 
  buf为缓冲区地址,p为下一个数据写入地址,q为下一个数据读出地址 */
};
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;  //flags记录是否溢出
		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) {
		/* 如果缓冲区为0 则返回-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);	/* 通知PIC IRQ-01的受理已经完成 */
	data = io_in8(PORT_KEYDAT);
	fifo8_put(&keyfifo, data);
	return;
}

读取数据显示:

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.总算讲到鼠标了 & 7.从鼠标接受数据
早期的时候,虽然在主板上做了鼠标用的电路,但只要不执行激活鼠标的指令。在这里插入图片描述
事实上鼠标控制电路包含在键盘控制电路里,如果键盘控制电路初始化正常完成,鼠标电路控制器的激活也就完成了。
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

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

在这里插入图片描述
init_keyboard的任务是,一边确认可否往键盘控制电路传送信息,一边发送模式控制指令,指令中包含着要设为何种模式,模式设定的指令是0x60,利用鼠标模式的模式号码是0x47。
在HariMain中调用init_keyboard,鼠标控制电路就准备好了。

所谓发送鼠标激活指令,归根到底还是要向键盘控制器发送指令。

#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) */
}

在这里插入图片描述
make run一下,鼠标就出来了。
在这里插入图片描述
鼠标中断已经正常了,下面来取出中断数据,其原理与键盘类似。
鼠标中断处理程序:

struct FIFO8 mousefifo;

void inthandler2c(int *esp) /* 来自PS/2鼠标的中断 */
{
	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;
}

IRQ12是从PIC的第4号,首先要通知IRQ12已经受理完成,然后再通知主PIC,这是因为主从PIC的协调不能自动完成,如果程序不教给主PIC该怎么做,它就会忽视从PIC的下一个中断请求。
在这里插入图片描述

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

这样,键盘和鼠标的中断及数据处理就完成了。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值