5 整理FIFO缓冲区(harib04e)
本来正说着键盘中断的话题,中间却插进来一大段关于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”测试一下(当然,信的话更要试试啦)。看,运行正常!