30天自制操作系统
上QQ阅读APP看书,第一时间看更新

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”测试一下(当然,信的话更要试试啦)。看,运行正常!