30天自制操作系统Day7

本文详细介绍了如何在操作系统中处理键盘和鼠标中断,包括获取按键编码并在屏幕上显示,通过循环队列缓冲区优化中断处理速度,以及激活鼠标并从鼠标接收数据的过程。同时,展示了中断处理函数的实现和主程序中对缓冲区的使用,以及鼠标控制电路的初始化和激活方法。
摘要由CSDN通过智能技术生成

第六天已经能够处理键盘中断了,但是还不能区分不同的按键,所以今天的第一个任务是把所按键的编码在画面上显示出来,并通过队列缓冲区来加快中断速度,然后激活鼠标,最后从鼠标接收数据

一、获取按键编码

bootpack.h节选

struct BOOTINFO { /* 0x0ff0-0x0fff */
	char cyls; /* ブートセクタはどこまでディスクを読んだのか */
	char leds; /* ブート時のキーボードのLEDの状態 */
	char vmode; /* ビデオモード  何ビットカラーか */
	char reserve;
	short scrnx, scrny; /* 画面解像度 */
	char *vram;
};
#define ADR_BOOTINFO	0x00000ff0

int.c节选

#define PORT_KEYDAT		0x0060

void inthandler21(int *esp)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;	//声明画面参数结构体指针
	unsigned char data, s[4];	//data按键编码,s显示到画面上的字符串
	io_out8(PIC0_OCW2, 0x61);	//bootpack.h中定义了(#define PIC0_OCW2		0x0020)将“0x60+IRQ号码”输出给OCW2,通知PIC0“IPQ-01已经受理完毕”,执行完这句话后,PIC继续监视IRQ1中断是否发生,否则PIC不再监视IRQ中断
	data = io_in8(PORT_KEYDAT);	//从键盘(0x0060)获取按键编码

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

二、优化中断并使用缓冲区

中断处理就是打断CPU本来的工作,这就要求中断要快速,一中在中断的过程中进行了字符显示,将字符显示从中断中分离会加快中断的处理速度,我们可以在中断处理(inthandler21)中将获取的按键编码放入缓冲区,由主函数(在bootpack.c中)偶尔去查看缓冲区,如果缓冲区中有数据,就将其显示。
缓冲区为一个循环队列,在键盘中断处理程序中加入按键编码,在显示程序中取出按键编码,我们将关于缓冲区的实现和操作单独放在一个文件fifo.c中,类似于面向对象语言中的封装,其他文件中再用到缓冲区,只需要调用缓冲区的函数就好,这样结构会更加清晰。

bootpack.h节选

struct FIFO8 {
	unsigned char *buf;
	int p, q, size, free, flags;
};
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf);
int fifo8_put(struct FIFO8 *fifo, unsigned char data);
int fifo8_get(struct FIFO8 *fifo);
int fifo8_status(struct FIFO8 *fifo);

fifo.c

#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;	//将flags置1
		return -1;
	}
	fifo->buf[fifo->p] = data;	//如果容量未满,将按键编码放入缓冲区
	fifo->p++;
	if (fifo->p == fifo->size) {	//写入的next已经到达缓冲区容量,则返回0,等同于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;
}

int.c节选

#define PORT_KEYDAT		0x0060

struct FIFO8 keyfifo;

void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61);	//通知PIC0“IPQ-01已经受理完毕”
	data = io_in8(PORT_KEYDAT);	//从键盘(0x0060)获取按键编码
	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) {	//缓冲区为空,执行STI和HLT
			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);
		}
	}

三、激活鼠标

鼠标属于新兴的外部输入设备,这一点从分配给鼠标的中断编号上就可以看出,键盘为IRQ1,鼠标为IRQ12,刚刚出现鼠标时,几乎所有的操作系统都不支持鼠标,所以如果不执行激活鼠标的指令,就不会产生鼠标的中断信号,如果想要CPU接收鼠标的中断信号,需要执行指令让鼠标控制电路鼠标本身有效。
鼠标控制电路包含在键盘控制电路里,如果键盘控制电路的初始化正常,鼠标电路控制器也就激活完成了。
bootpack.c节选

void enable_mouse(void);
void init_keyboard(void);

void HariMain(void)
{
	init_keyboard();

	enable_mouse();
}

#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) {	//CPU的电路比键盘控制电路快,所以要先不断询问键盘控制电路是否准备好了,如果键盘控制电路可以接受CPU指令了,CPU从键盘控制电路(0x0064)中所读取的数据的倒数第二位应该是0,否则CPU不断循环查询
			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);	//向键盘(转发给鼠标)发送0xf4,激活鼠标
	return; /* 键盘控制会返回ACK(0xfa) */
}

四、从鼠标接收数据

鼠标和键盘原理几乎相同,所以程序也非常相似。
int.c节选

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);	//从键盘(0x0060)获取按键编码
	fifo8_put(&mousefifo, data);//将按键编码放入鼠标缓冲区,激活鼠标后发送的第一个编码为0xfa
	return;
}

bootpack.c节选

	char s[40], mcursor[256], keybuf[32], mousebuf[128];
	
	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、付费专栏及课程。

余额充值