平台
处理器:Intel Celeron(R) Dual-Core CPU
操作系统:Windows7 专业版 x86
阅读书籍:《30天自制操作系统》—川合秀实[2015.03.22 ]
将《30天自制操作系统》简称为“书”。对于书中的工具,可以专门对其笔记学习。
工具:../toolset/
1 缓冲区(FIFO)
缓冲区用来存储鼠标发送过来的数据,鼠标中断一产生就会发送3个数据给鼠标控制电路。鼠标操作也比较频繁,故而需要为鼠标中断发送的数据准备一个缓冲区。
1.1 描述缓冲区的数据结构
用以下结构体实现:
struct FIFO8 {
unsigned char *buf; //一块内存的首地址
int p, q; //下一个数据写入地址,下一个数据读出地址
int size; //缓冲区的总字节数
int free, flags; //缓冲区内无数据的字节数,记录缓冲区是否溢出
};
1.2 用缓冲区初始化描述缓冲区的数据结构
初始化缓冲区的函数:
/*初始化缓冲区结构体
*fifo描述循环队列的结构体
*size表真个结构体的大小
*buf表一段缓冲区的首地址
*/
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;
return;
}
1.3 通过缓冲区数据结构往缓冲区写数据
往缓冲区存储1字节信息的函数:
#define FLAGS_OVERRUN 0x0001
/*往缓冲区循环队列存储1字节信息
*fifo为描述缓冲区的结构体
*data为要往缓冲区写入的数据
*若溢出则返回-1,写入数据成功则返回0
*/
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;
}
1.4 通过缓冲区数据结构从缓冲区中读数据
从缓冲区读取一个字节的函数:
/*从缓冲区读取一个字节信息
*fifo是描述缓冲区的结构体
*如果无数据可读则返回-1,如果还有数据可读则返回读取到的数据
*/
int fifo8_get(struct FIFO8 *fifo)
{
int data;
if (fifo->free == fifo->size) {
return -1;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size) {
fifo->q = 0;
}
fifo->free++;
return data;
}
1.5 通过缓冲区数据结构获取缓冲区存储的数据量
获取缓冲区内的数据的函数:
/*获取缓冲区内存有多少数据
*fifo为描述缓冲区的结构体
*函数返回存有数据的个数
*/
int fifo8_status(struct FIFO8 *fifo)
{
return fifo->size - fifo->free;
}
将这些函数存放在buf.c文件中。
2 利用缓冲区显示鼠标数据
2.1 HariMain()主函数鼠标缓冲区部分
int data;
char str[30];
struct FIFO8 mousefifo;
char mousebuf[128]; //接收鼠标数据缓冲区
fifo8_init(&mousefifo, 32, mousebuf);//将keybuf缓冲区的信息初始化给结构体keyfifo
unsigned int sx, sy = 0;
unsigned char mouse_dbuf[3], mouse_phase = 0;
//不断查询缓冲区,如果有数据,将缓冲区内的数据显示完为止
for (;;) {
io_cli(); //查看缓冲区时,屏蔽中断
if (fifo8_status(&mousefifo) == 0) {
io_stihlt(); //如果缓冲区内无数据则开启中断并让CPU休眠,直到中断唤醒
} else {
data = fifo8_get(&mousefifo);
io_sti(); //开启中断
if (mouse_phase == 0) {
//激活鼠标时鼠标回复的0xfa
if (i == 0xfa) {
mouse_phase = 1;
}
} else if (mouse_phase == 1) {
//鼠标数据的第一字节
mouse_dbuf[0] = data;
mouse_phase = 2;
} else if (mouse_phase == 2) {
//鼠标数据的第二字节
mouse_dbuf[1] = data;
mouse_phase = 3;
} else if (mouse_phase == 3) {
//鼠标数据的第3字节
mouse_dbuf[2] =data;
mouse_phase = 1;
//将鼠标数据显示出来
sprintf(str, "%02X %02X %02X", mouse_dbuf[0], mouse_dbuf[1], mouse_dbuf[2]);
putfonts8_asc(binfo->vram, binfo->scrnx, sx, sy, COL8_FFFFFF, str);
//满屏显示,屏满后再重新满屏
if (sx > binfo->scrnx && sy >= binfo->scrny - 30){
sy = 0;
sx = 0;
}
if (sy >= binfo->scrny - 28){
sy = 0;
sx += 96;
}else{
sy += 16;
}
}
}
}
激活鼠标成功时,鼠标会回复0xfa数据,舍掉0xfa数据。取一次鼠标中断发生时,鼠标会回复3字节数据,并将它们显示出来。
io_stihlt();函数使开通所有中断并执行HLT功能的汇编函数,在naskfunc.nas中定义:
|
1. _io_stihlt: ; void io_stihlt(void);
2. STI
3. HLT
4. RET
|
此函数开通所有中断并让CPU进入休眠状态。
2.2 inthandler2c()鼠标中断函数
extern struct FIFO8 mousefifo;
#define PORT_KEYDAT 0x0060
void inthandler2c(int *esp)
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64); //告知从PIC已经接受了IRQ12,让从PIC继续监视IRQ12
io_out8(PIC0_OCW2, 0x62); //让主PIC继续监视IRQ2
data = io_in8(PORT_KEYDAT); //接收鼠标发来的数据
fifo8_put(&mousefifo, data); //将鼠标发送来的数据缓存起来
return;
}
2.3 运行结果
将缓冲区的函数保存到buf.c文件,并将新增的函数声明添加到bootpack.h文件中,修改bootpack.c和鼠标中断函数,修改Makefile。编译通过后运行“!cons_nt.bat”,在命令窗口中运行“makerun”命令,得到以下结果:
Figure1. 接收鼠标数据的操作系统的运行结果
每次鼠标中断发生时,鼠标会发出3字节数据。根据这3个数据可以指挥鼠标的移动。
3 总结
[1] 在访问一个数据结构没有完成时,不应该有其它中断程序进来访问这个数据结构。不然就有可能影响正确性。如到达缓冲区末尾正准备回到缓冲区开始处时发生了中断将数据保存到了缓冲区外。所以,for()循环中查询缓冲区数据时,应该屏蔽中断,将数据读出来后才开启中断,在访问缓冲区的过程中防止访问缓冲区的中断发生。
[2] 中断程序中不宜处理复杂的过程。像鼠标中断这种频繁的中断(且一次中断就会连续引发3次中断,回复3字节数据),如果在鼠标中断程序中进行复杂的处理,有可能处理未完毕下一个鼠标中断又来临。
[3] 不断查询缓冲区及时处理掉缓冲区中的数据缓冲区(循环FIFO)就不那么容易满(即数据被覆盖的情况)。只有在访问缓冲区程序之前的程序的处理过程十分复杂,发生多次中断把缓冲区都存满了都还没有执行到访问缓冲区的情况下缓冲区内的数据才有可能被覆盖。
[x86OS] Note Over.
[2015.04.17]