自制操作系统日志——第七天

30天自制操作系统——第七天

今天,继续加油,进一步的利用器昨天的中断,实现键盘的任意输入吧!以及我们要进一步的完成让鼠标能够顺利的接受数据!



一、获取从键盘上输入的字符编码

昨天的内容当中,我们已经顺利的初始化了PIC,并且已经开放了外部中断的权限。那么,我们接下来就利用键盘中断来接受我们输入的字符编码吧:

首先,需要对int.c进行修改:

struct KEYBUF {
	unsigned char data, flag;
};
#define PORT_KEYDAT		0x0060

struct KEYBUF keybuf;

void inthandler21(int *esp)
{
	unsigned char data;
 /*通知PIC “IRQ-01”已经受理完毕;这里的计算公式是0x60+IRQ号码
 只有输出到ocw2后,PIC才会继续监视IRQ1中断是否发生,如果不写这句话则后面不会再产生中断了
  */
	io_out8(PIC0_OCW2, 0x61);
	data = io_in8(PORT_KEYDAT);
	if (keybuf.flag == 0) {
		keybuf.data = data;
		keybuf.flag = 1;
	}
	return;
}

我们进行定义一个结构体,用于充当一个小型的缓冲区,用于接收数据和判断缓冲区是否为空。

⇒ flag 表示缓冲区是否为空;若flag=0 则为空。
⇒ 至于io_out8 那一句话,参考代码里的注释
⇒ in8 代表从键盘的端口处读取对应的数据

然后,我们修改一下主函数的内容:

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

将for循环里的内容进行更改。 我们利用起io_cli 先进行屏蔽其他中断。这是因为,由于计算机只能同时处理一个中断响应,因此,若我们在处理键盘产生中断的过程中,由其他中断进来打扰的话,可能就会产生错误了,这是绝对不允许的!

其次,flag=0 代表缓冲区为空,则我们执行 stihlt()这个函数,用于设置IF=1 表明此时计算机可以接受其他中断,然后再让cpu停止工作,等待下个中断的到来。

至此,我们完成了键盘数据的接收,然后让我们make run一下看看:
在这里插入图片描述

当然,这里可能是因为qemu这个虚拟机的问题,他无法显示我按下的第二个字符。然后,我就使用了vmwar虚拟机来模拟真实系统打开的情况,然后发现在vmware中,你按下字符,它是会对应的显示的!!!!

二、制作FIFO缓冲区

在前面我们制作的能够缓冲区大小仅能接受一个字符,因此倘若我们在键盘上按下alt 或者 回车符 时候,你会看到屏幕先一闪而过一个字符,然后又跳到另一个字符上去。 这就是因为前面制作的缓冲区大小有问题。

那么这里,由于我们最终要实现的功能应该是这种: 即我们随意按下键盘后等待cpu处理完其他事情后,然后再读入一个字符(最好一个,因为如果太多字符的话,可能会占用cpu时间太久,导致cpu不能完成其他事情,比如下载东西),然后发现缓冲区还要数据,继续读入,重复该操作。

为了实现我们上述的功能,我们引入一个技巧操作,即:FIFO缓冲区。
该缓冲区会使用一片连续的地址空间,然后用两个指针分别代表这写入与读出。 当有数据写入时,写指针就会往前移动;当有数据取出时,读指针就会往前移动,就好像读指针一直在追着写指针一样!!
在这里插入图片描述

大致如图所示,也就是当读与写指针位置相同时,就有可能是缓冲区为空或者是满的情况。然后,读与写指针会一直在这段空间里往复循环,具体代码修改如下:
bootpack.h:

struct FIFO8{
	unsigned char *buf;//缓冲区地址
	int p, q, size, free, flags;//p 下一写入地址;q 下一读出地址; size是总字节数,free是缓冲区中没有数据的字节数;flag判断溢出
};

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

int.c:

struct FIFO8 keyfifo;

void inthandler21(int *esp)
{
	unsigned char data;

 /*通知PIC “IRQ-01”已经受理完毕;这里的计算公式是0x60+IRQ号码
  只有输出到ocw2后,PIC才会继续监视IRQ1中断是否发生,如果不写这句话则后面不会再产生中断了
  */
	io_out8(PIC0_OCW2,0X61);
	data = io_in8(PORT_KEYDAT);
	fifo8_put(&keyfifo, data);
	return;
}

主函数:

	for(;;)   
	{
		io_cli(); //IF=0
		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);
		    }

然后,make run 一下就可以成功了。。

三、鼠标接收数据

首先,这里讲一下为什么昨天那部分,鼠标无法接受数据进入!

在我们的主板上,其实是存在这鼠标使用的电路的。但是由于早期,IBM的大叔们认为如果只要移动一下鼠标就发生中断的话,对于他们来说很不方便(那时的计算机性能远没有现在的强),因此他们再设计之处,是关闭了鼠标的控制电路的。因此,我们必须先激活起鼠标的控制电路,才能产生鼠标的中断信号!!

想激活鼠标,主要先让两个装置激活,一个是鼠标的控制电路,一个是鼠标本身。我们先激活控制电路,再唤起鼠标本身。
然后,由于鼠标的控制电路是包含在键盘的控制电路里的,因此,只要我们把键盘的控制电路顺利的初始化后,再发送相应的指令即可:
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(;;)
	{
	//等待键盘控制电路准备完毕,检测到设备号0x0064处数据的第2位是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);//激活鼠标数据 0xfa
	return; //顺利的话,键盘控制其返回ACK(0xfa) <== 答复信息
}

具体的解释代码中已经有详细的注释了。这里我再解释一下,为什么要使用wait这个函数。这是因为cpu与键盘的速度上差距过大,如果不适用wait的话,cpu可能发送过多的指令到键盘上,导致键盘处理不过来,从而发送未知错误。

好的,这里我们make run一下,就可以看见我们已经成功的接受到鼠标的信息了!

进一步的,做出如下修改:
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-2
		data = io_in8(PORT_KEYDAT);
		fifo8_put(&mousefifo, data);
  	return;
}

因为鼠标的中断号是12,位于从pic,因此我们需要先通知从PIC,然后再通知主PIC 说鼠标已经完成指令的信息。

bootpack.c:

for(;;)   
	{
		io_cli(); //IF=0
		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);
		    }
	   }
	}

然后,make run 一下:

在这里插入图片描述


总结

很好,今天已经完成了鼠标对于数据的接受的这个任务了,那么我们明天进行鼠标移动的测试,鼠标的移动是需要接受鼠标对应的数据,然后再更换未知信息,并进行显示的,因此,我们明天的目标就是了解鼠标接收到的数据信息有哪些,然后针对性的进行更改。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值