《30day自制操作系统》学习笔记07

(本章主要完善见键盘中断函数,通过FIFO缓存区存储多个中断值,并分别作出反应;之后添加鼠标控制)

键盘中断控制

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);	/* 通知PIC“IRQ-01已经受理完成” */
	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;
}

注意:编号0x0060的设备就是键盘。从0x0060设备输入的8位信息是按键编码。

2.加快中断编码

        1中的程序中断时间过长,因为中断处理程序中包含字符显示程序。(1.中断处理时,CPU不在接收其他中断;2.字符显示程序执行时间太长,在执行完之前,不会接收其他中断)。

        将字符显示程序从int.c的21号中断处理程序中移出到bootpack.c中:

;int.c
#define PORT_KEYDAT		0x0060

struct KEYBUF keybuf;

void inthandler21(int *esp)
{
	unsigned char data;
	io_out8(PIC0_OCW2, 0x61);	/* 通知PIC“IRQ-01已经受理完成” */
	data = io_in8(PORT_KEYDAT);
	if (keybuf.flag == 0) {
		keybuf.data = data;
		keybuf.flag = 1;
	}
	return;
}

        注意:构造了一个结构体用于KEYBUF用于存储数据(data)、缓冲区的状态(flag,是否为空)。

;bootpack.c
extern struct KEYBUF keybuf;

void HariMain(void)
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	char s[40], mcursor[256];

	for (;;) {
		io_cli(); /*屏蔽中断*/
		if (keybuf.flag == 0) {
			io_stihlt(); /*如果flag==0,释放屏蔽并hlt*/
		} else {
			i = keybuf.data;
			keybuf.flag = 0; /*如果flag==1,先把data存入i,后将flag置0、释放屏蔽并hlt*/
			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缓冲区

        2中的程序只能显示E0。问题出在缓冲区只能存储一个字节,那么就创造个大缓冲区(通过在结构体里加数据数组)、使用FIFO型缓冲区(通过将全体数据下移):

        创造大缓冲区:

;int.c

struct KEYBUF {
	unsigned char data[32];
	int next;
};

#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.next < 32) {
		keybuf.data[keybuf.next] = data;   /*keybuf.next起始是0*/
		keybuf.next++;
	}
	return;
}

        取得数据,同时将所有数据下移覆盖被取掉的数据,营造FIFO的结构:

;bootpack.c
for (;;) {
		io_cli();
		if (keybuf.next == 0) {
			io_stihlt();
		} 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);
		}

注意:该章节中仍然在中断过程内,对数据进行了转移:

        i = keybuf.data[0];
        keybuf.next--;
        for (j = 0; j < keybuf.next; j++) {
                keybuf.data[j] = keybuf.data[j + 1];
        }

4.开发一个不需要数据转移的FIFO型缓冲区

        新增一个用于读取的next,并在到达尽头的时候重置为0:

         新增读取得next:

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

        写入数据得程序;

;int.c

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

        读取数据得程序:

;bootpack.c

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.扩容缓冲区

        4中缓冲区大小固定了,将缓冲区大小设置为可以更改:

;bootpack.h

struct FIFO8 {
	unsigned char *buf;
	int p, q, size, free, flags;
};    

/*buf表示缓冲区地址,p表示next_w,q表示next_r*/
/*size表示缓冲区总字节数,free表示缓冲区没有数据得字节数*/
/*flags记录是否溢出*/

        初始化缓冲区结构体:

;fifo.c

void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)

{
	fifo->size = size;
	fifo->buf = buf;
	fifo->free = size; 
	fifo->flags = 0;
	fifo->p = 0; 
	fifo->q = 0; 
	return;
}

        向缓冲区写入1字节信息的函数:

;fifo.c

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

        从缓冲区读取数据的函数:

;fifo.c

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

        报告以下缓冲区里有多少数据的函数:

;fifo.c

int fifo8_status(struct FIFO8 *fifo)
/* 报告缓冲区积攒了多少数据 */
{
	return fifo->size - fifo->free;
}

        键盘中断执行程序:

;int.c

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

        

;bootpack.c

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

鼠标中断控制

1.激活鼠标控制电路

        鼠标要想实现中断控制,需要:1.先让鼠标控制电路有效;2.让鼠标本身有效。

        鼠标控制电路包含在键盘控制电路里,键盘控制电路激活完成,鼠标控制电路也就激活完成了。

        键盘控制电路没有CPU那么快,需要CPU等待——wait_KBC_sendready(直到从PORT_KEYSTA接收到的数据倒数第二位为0,表示可以发送数据了)。初始化键盘电路时,需要一边等待确认可否向控制电路发送信息,然后向控制电路发送模式设定指令。(模式设定指令是0x60,利用鼠标模式的模式号码是0x47)。

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

        然后初始化鼠标,发送激活鼠标的指令:

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

2.从鼠标接受数据

        鼠标接收数据和键盘操作非常相似。

        创建鼠标的缓冲区结构体,并设置中断函数(向其中存入一个数据):

;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);  /* 接收数据*/
	fifo8_put(&mousefifo, data);  /* 向缓冲区结构体中存入一个数据*/
	return;
}

        取得数据并显示画面的程序:

;bootpack.c

fifo8_init(&mousefifo, 128, mousebuf);

for (;;) {
         
        /*键盘和鼠标缓冲区都为空,释放屏蔽中断并hlt*/
		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);
			}
		}
	}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值