5 整理FIFO缓冲区(hiarib04e)
本来正说着键盘中断的话题,中间却插进来一大段关于FIFO缓冲区基本结构的介绍,不过既然这样,我们就来整理一下,让它具有一些通用性,在别的地方也能发挥作用。所谓“别的地方”,是指什么地方呢?当然是鼠标啦。鼠标只要稍微动一动,就会连续发送3个字节的数据。……事实上这次我们之所以先做键盘的程序,就是想拿键盘来练习一下FIFO和中断。因为如果一上来就做鼠标程序,数据来得太多,又要取出来进行处理,会让人手忙脚乱,不知所措。
首先我们将结构做成以下这样。
struct FIFO8 {
unsigned char *buf;
int p, q, size, free, flags;
};
如果我们将缓冲区大小固定为32字节的话,以后改起来就不方便了,所以把它定义成可变的,几个字节都行。缓冲区的总字节数保存在变量size里。变量free用于保存缓冲区里没有数据的字节数。缓冲区的地址当然也必须保存下来,我们把它保存在变量buf里。p代表下一个数据写入地址(next_w),q代表下一个数据读出地址(next_r)。
fifo.c的fifo8_init函数
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;
}
fifo8_init是结构的初始化函数,用来设定各种初始值,也就是设定FIFO8结构的地址以及与结构有关的各种参数。更具体的说明就不用了吧。
fifo.c的fifo8_put函数
#define FLAGS_OVERRUN 0x0001
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;
}
fifo8_put是往FIFO缓冲区存储1字节信息的函数。以前如果溢出了,就什么也不做。但这次,笔者想:“如果能够事后确认是否发生了溢出,不是更好吗?”所以就用flags这一变量来记录是否溢出。至于其他内容,只是写法上稍有变化而已。
啊,要说这里出现的新东西,可能就是return语句后面跟着数字的写法了吧。如果有人调用以上函数,写出类似于“i = fifo8_put(fifo, data);”这种语句时,我们就可以通过这种方式指定赋给i的值。为了能够简单明了地确认到底有没有发生溢出,笔者将它设定为-1或0,分别表示有溢出和没有溢出这两种情况。
fifo.c的fifo8_get函数
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;
}
fifo8_get是从FIFO缓冲区取出1字节的函数。这个应该不用再讲了吧。
fifo.c的fifo8_status函数
int fifo8_status(struct FIFO8 *fifo)
/* 报告一下到底积攒了多少数据 */
{
return fifo->size - fifo->free;
}
这是附赠的函数fifo8_status,它能够用来调查缓冲区的状态。status的意思是“状态”。
笔者把以上这几个函数总结后写在了程序fifo.c里。
■■■■■
使用以上函数写成了下面的程序段。
int.c节选
struct FIFO8 keyfifo;
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;
}
这段程序看起来非常清晰,12行变成了5行。在fifo8_put的参数里,有一个“&”符号,这可不是AND运算符,而是取地址运算符,用它可以取得结构体变量的地址值。变量名的前面加上&,就成了取地址运算符。这稍微有点复杂。fifo8_put接收的第一个参数是内存地址,与之匹配,这里调用时传递的第一个参数也要是内存地址。
MariMain函数内容如下所示:
char s[40], mcursor[256], keybuf[32];
fifo8_init(&keyfifo, 32, keybuf);
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);
}
}
这段程序简洁而清晰。for语句的内容被精简掉了5行呀。当然,程序运行肯定也没问题。不信的话,可以用“make run”测试一下(当然,信的话更要试试啦)。看,运行正常!