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

3 制作FIFO缓冲区(harib04c)

问题到底出在哪儿呢?在于笔者所创建的缓冲区,它只能存储一个字节。如果做一个能够存储多字节的缓冲区,那么它就不会马上存满,这个问题也就解决了。

最简单的解决方案是像下面这样增加变量。

struct KEYBUF {
    unsigned char data1, data2, data3, data4, ...
};

但这样一来,程序就变长了,所以将它写成下面这样:

struct KEYBUF {
    unsigned char data[4];
};

当我们使用这些缓冲区的时候,可以写成data[0]、data[1]等。至于创建得是否正常,那就是后话了。

说起缓冲,我们在讲栈的时候,曾讲过FIFO、FILO等,这次我们需要的是FIFO型。为什么呢?如果输入的是ABC,输出的时候,却把顺序搞反了,写成CBA,那可就麻烦了。所以需要按照输入数据的顺序输出数据。

根据这种思路,我们制作了以下程序:

int.c节选

struct KEYBUF {
    unsigned char data[32];
    int next;
};

void inthandler21(int *esp)
{
    unsigned char data;
    io_out8(PIC0_OCW2, 0x61);   /* 通知PIC IRQ-01已经受理完毕 */
    data = io_in8(PORT_KEYDAT);
    if (keybuf.next < 32) {
        keybuf.data[keybuf.next] = data;
        keybuf.next++;
    }
    return;
}

keybuf.next的起始点是“0”,所以最初存储的数据是keybuf.data[0]。下一个数据是keybuf.data[1],接着是[2],依此类推,一共有32个存储位置。

下一个存储位置用变量next来管理。next,就是“下一个”的意思。这样就可以记住32个数据,而不会溢出。但是为了保险起见,next的值变成32之后,就舍去不要了。

■■■■■

取得数据的程序如下所示。

for (; ; ) {
    io_cli();
    if (keybuf.next == 0) {
        io_stihlt();
    } else {
        i = keybuf.data[0];
        keybuf.next——;
        for (j = 0; j < keybuf.next; j++) {
            keybuf.data[j] = keybuf.data[j + 1];
        }
        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);
    }
}

如果next不是0,则说明至少有一个数据。最开始的一个数据肯定是放在data[0]中的,将这个数存入到变量i中去。这样,数就减少了一个,所以将next减去1。

接下来的for语句,我们用下图来说明它所完成的工作。

像上面这样,数据的存放位置全部都向前移送了一个位置。如果不移送的话,下一次就不能从data[0]读入数据了。

■■■■■

那好,我们赶紧测试一下,看看能不能正常运行。“make run”,按下右Ctrl键,哦,运行正常!

虽然现在想说这个程序已经OK了,但实际上还是有问题。还有些地方还不尽如人意。inthandler21可以了,完全没有问题。有问题的是HariMain。说得具体一点,是从data[0]取得数据后有关数据移送的处理不能让人满意。

像这种移送数据的处理,一般说来也就不超过3个,基本上没有什么问题。但运气不好的时候,我们可能需要移送多达32个数据。虽然这远比显示字符所需的128个像素要少,但要是有办法避免这种操作的话,当然是最好不过了。

数据移送处理本身并没有什么不好,只是在禁止中断的期间里做数据移送处理有问题。但如果在数据移送处理前就允许中断的话,会搞乱要处理的数据,这当然不行。那该怎么办才好呢?接下来的harib04d章节就要讲述这个问题了。