Day07
一些问题和感想
FIFO缓冲区的实现
因为中断请求(比如鼠标、键盘)可能短时间内会发送很多次,为了不要太影响当前正在执行的程序,我们可以对中断做一个缓冲区。当系统空闲,或者隔一段时间,再去看这个缓冲区里有没有东西。
作者最后用一个数组做的循环队列来存储这些缓冲。
struct FIFO8
{
unsigned char *buf;
int p, q, size, free, flags;
};
//初始化FIFO缓冲区
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; //下一个数据读出位置
}
#define FLAGS_OVERRUN 0x0001
//向FIFO传送数据并保存
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
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取得一个数据
int fifo8_get(struct FIFO8 *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;
}
鼠标中断以及键盘中断接受数据
鼠标和键盘中断会从io接受数据,我们可以用对应的io_in8()函数来接收。
此外,如果想要计算机接受鼠标的中断,要先激活鼠标控制电路(控制电路——CPU),然后激活鼠标(鼠标——控制电路)。
各种参数的设置
接收各种中断,当然少不了各种IO地址。这些东西貌似作者直接给在代码里了。如果要真的自己做操作系统,恐怕要在各种参考资料里查询地址吧。。
进入正题
获取按键编码
harib04a
我们现在让中断程序接收键盘发来的信息,比如按键编码、以及按下/释放。
int.c
void inthandler21(int *esp)
// 来自PS/2键盘的中断
{
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);
sprintf(s, "%02X", data);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfont8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
io_out一句,当中断结束后,我们需要重新让PC监视中断程序,不然执行一次中断后就不再执行了。
加快中断处理
harib04b
当然,每按一下键盘就中断、显示字符,也太频繁调用操作系统了(其实感觉鼠标会更麻烦)。所以我们可以设置一个缓冲区,用来暂时存储中断信息。
struct KEYBUF
{
unsigned char data, flag;
};
struct KEYBUF keybuf;
void inthandler21(int *esp)
// 来自PS/2键盘的中断
{
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;
}
}“
”
这个缓冲区还比较简陋,只能存储一个数据(还不能弹出),不过先这么用着吧。
Harimain部分
unsigned char i;
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);
putfont8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
cli先屏蔽中断,然后如果缓冲区没有东西,stihlt是恢复并暂停的意思。至于为什么要写一起,这样做是可以忽略sti和hlt之间的中断,否则突然有个数据进来就不好了。
其余的代码逻辑还是挺好懂的。
制作FIFO缓冲区
harib04c
一个很暴力的队列,弹出靠的是把数组里所有元素向前移。。这里就不放代码了,毕竟直接看最终版本就行了。
改善FIFO缓冲区
harib04d
用循环队列就好了。。很简单。。
整理FIFO缓冲区
harib04e
我们把队列的各种操作封装成方法,这样就可以比较方便、而且容易理解地进行各种队列操作了。
FIFO的各种方法写在了前面,现在看一下最终版本的中断程序。
int.c
struct FIFO8 keyfifo;
void inthandler21(int *esp)
// 来自PS/2键盘的中断
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61); // 通知PIC IRQ-01已经受理完毕
data = io_in8(PORT_KEYDAT);
fifo8_put(&keyfifo, data);
}
这样就简洁多了,然后是HariMain函数
char s[40], mcursor[256], keybuf[32];
fifo8_init(&keyfifo, 32, keybuf);
unsigned char i;
int j;
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);
putfont8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
}
}
总算讲到鼠标了
harib04f
鼠标中断号为IRQ12,然后是鼠标的一系列激活操作:先让键盘控制电路准备好,然后激活鼠标
#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)
{
return;
}
}
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)
}
如果我们先io_out KEYCMD_SENDTO_MOUSE,接下来的io_out就会将数据写入鼠标。
这样我们就能接收到鼠标中断了。
从鼠标接受数据
harib04g
接下来看一下鼠标的中断程序,还是比较相似的。
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);
}
首先要通知从PIC受理完成,然后再通知主PIC受理完成,不然主PIC会忽视从PIC的下一个中断请求。
然后鼠标数据的获取方法:
bootpack.c
fifo8_init(&mousefifo, 256, mcursor);
enable_mouse();
unsigned char i;
int j;
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);
putfont8_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);
putfont8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
}
}
}
这样就可以获得来自鼠标的数据了。。。
明天继续。。