13.1、简化字符串显示
boxfill8(buf_back, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
putfonts8_asc(buf_back, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
sheet_refresh(sht_back, 32, 16, 32 + 15 * 8, 32);
// sht:图层地址;x,y:绘制的开始坐标;c:绘制的颜色;b:背景颜色;s:字符串;l:字符串长度
void putfonts8_asc_sht(struct SHEET *sht, int x, int y, int c, int b, char *s, int l)
{
boxfill8(sht->buf, sht->bxsize, b, x, y, x + l * 8 - 1, y + 15);
putfonts8_asc(sht->buf, sht->bxsize, x, y, c, s);
sheet_refresh(sht, x, y, x + l * 8, y + 16);
return;
}
13.2、重新调整FIFO缓冲区
13.2.1、合并定时器的缓冲区
现在是一个定时器一个缓冲区,每次判断缓冲区为空时,if条件都要写的特别长,每次都要去不同的地址空间去查询写入的数据,这就拖慢了速度。
如果能够把所有定时器的数据都写入一个缓冲区就好了。问题就是一个缓冲区怎么区分数据的,写入的数据不同不就行了。
// 三个定时器数据都写入同一个缓冲区,不同定时器写入数据不同
fifo8_init(&timerfifo, 8, timerbuf);
timer = timer_alloc();
timer_init(timer, &timerfifo, 10);
timer_settime(timer, 1000);
timer2 = timer_alloc();
timer_init(timer2, &timerfifo, 3);
timer_settime(timer2, 300);
timer3 = timer_alloc();
timer_init(timer3, &timerfifo, 1);
timer_settime(timer3, 50);
13.2.2、合并所有的缓冲区
尝到甜头了,尝试把所有缓冲区都给统一到一个缓冲区里,利用写入数据来区分不同的作用:
data | 用途 |
---|---|
0~1 | 光标闪烁用定时器 |
3 | 3秒定时器 |
10 | 10秒定时器 |
256~512 | 键盘输入(从键盘控制器读入的值再加上256) |
512~767 | 鼠标输入(从键盘控制器读入的值再加上512) |
之前使用的缓冲区都是无符号的char类型,不能支持256以上的定时器,所以把缓冲区也要修改成int类型,后面读入缓冲区数据都修改成int类型:
struct FIFO32 {
int *buf; // 修改成int
int p, q, size, free, flags;
};
13.3、加快中断处理
在前一天,排序号的定时器,前面的超时之后,要把后面的定时器都往前移动一下,这个移动时很耗时的,来修改一下,提高效率。
在每个定时器结构体中加一个struct timer 类型的next指针。
#define MAX_TIMER 500
struct TIMER {
struct TIMER *next; // 添加next指针
unsigned int timeout, flags;
struct FIFO32 *fifo;
int data;
};
struct TIMERCTL {
unsigned int count, next, using;
struct TIMER *timers[MAX_TIMER];
struct TIMER timers0[MAX_TIMER];
};
通过next把所有的定时器串起来,就是线性链表。
中断处理函数:
void inthandler20(int *esp)
{
int i;
struct TIMER *timer;
io_out8(PIC0_OCW2, 0x60);
timerctl.count++;
if (timerctl.next > timerctl.count) {
return;
}
timer = timerctl.timers[0]; /* 最前面的定时器 */
for (i = 0; i < timerctl.using; i++) {
if (timer->timeout > timerctl.count) {
break;
}
/* 超时 */
timer->flags = TIMER_FLAGS_ALLOC;
fifo32_put(timer->fifo, timer->data);
timer = timer->next; /* 指向下一个定时器 */
}
timerctl.using -= i;
/* 新移位 */
timerctl.timers[0] = timer;
/* timerctl.next最近超时的定时器 */
if (timerctl.using > 0) {
timerctl.next = timerctl.t0->timeout;
} else {
timerctl.next = 0xffffffff;
}
return;
}
主要逻辑是:当timer指向定时器超时后,就响应,然后调整timer指向下一个定时器,调整后的如果没有超时,就把该定时器放在排序后数组的首位。
设定定时器顺序的代码修改:
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
int e;
struct TIMER *t, *s;
timer->timeout = timeout + timerctl.count;
timer->flags = TIMER_FLAGS_USING;
e = io_load_eflags();
io_cli();
timerctl.using++;
if (timerctl.using == 1) {
/* 只有一个定时器 */
timerctl.timers[0] = timer;
timer->next = 0; /* 指向空 */
timerctl.next = timer->timeout; // 最近超时的定时器
io_store_eflags(e);
return;
}
t = timerctl.timers[0]; // 最前面的定时器
if (timer->timeout <= t->timeout) {
/* 插入最前面 */
timerctl.timers[0] = timer;
timer->next = t; /* 下一个是t */
timerctl.next = timer->timeout;
io_store_eflags(e);
return;
}
/* 寻找插入位置 */
for (;;) {
s = t; // 第一个定时器
t = t->next; // 第二个定时器
if (t == 0) {
break; /* 到最后面了(s指的就是最后一个定时器) */
}
if (timer->timeout <= t->timeout) {
/* 插入到s和t位置中间 */
s->next = timer; /* s链接到timer */
timer->next = t; /* timer指向t */
io_store_eflags(e);
return;
}
}
/* 插入到最后 */
s->next = timer;
timer->next = 0;
io_store_eflags(e);
return;
}
// 以上整个排序过程就是线性链表的插入操作
修改之后发现啊,原来用于排序的timers已经没啥用了,就用来指出第一个,那我们还定义这么多变量干啥,直接把timers数组换成t0。
struct TIMERCTL {
unsigned int count, next, using;
struct TIMER *t0;
struct TIMER timers0[MAX_TIMER];
};
13.4、使用“哨兵”简化程序
刚设置定时器时,排序有以下几种情况:
1、是第一个
2、插入最前面
3、插入到中间
4、插入到末尾
这里给简化以下:
方法:设置一个哨兵 = 0xffffffff
那么1、3情况将不存在
修改过程:
// 初始化
void init_pit(void)
{
int i;
struct TIMER *t;
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
timerctl.count = 0;
for (i = 0; i < MAX_TIMER; i++) {
timerctl.timers0[i].flags = 0; /* 未使用 */
}
t = timer_alloc(); /* 先获取一个当哨兵 */
t->timeout = 0xffffffff; // 哨兵设置的非常大
t->flags = TIMER_FLAGS_USING;
t->next = 0; /* 下一个指向空 */
timerctl.t0 = t; /* 现在只有哨兵,所以排最前面 */
timerctl.next = 0xffffffff; /* 只有哨兵在等待超时 */
return;
}
// 设置定时器排序
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
int e;
struct TIMER *t, *s;
timer->timeout = timeout + timerctl.count;
timer->flags = TIMER_FLAGS_USING;
e = io_load_eflags();
io_cli();
t = timerctl.t0;
if (timer->timeout <= t->timeout) {
/* 插入最前面 */
timerctl.t0 = timer;
timer->next = t; /* 后面跟着t */
timerctl.next = timer->timeout;
io_store_eflags(e);
return;
}
/* 插入s、t中间 */
for (;;) {
s = t;
t = t->next;
if (timer->timeout <= t->timeout) {
/* 插入s、t之间 */
s->next = timer; /* s后面跟着要插入的timer */
timer->next = t; /* timer后面跟着t */
io_store_eflags(e);
return;
}
}
}
// 发现少了两种情况
中断处理函数:
void inthandler20(int *esp)
{
struct TIMER *timer;
io_out8(PIC0_OCW2, 0x60);
timerctl.count++;
if (timerctl.next > timerctl.count) {
return;
}
timer = timerctl.t0; /* 第一个赋值给timer */
for (;;) { // 这里不需要using进行循环判断了,因为定时器不会超过哨兵的大小,通过下面if判断就可以了
if (timer->timeout > timerctl.count) {
break;
}
/* 超时 */
timer->flags = TIMER_FLAGS_ALLOC;
fifo32_put(timer->fifo, timer->data);
timer = timer->next; /* 超时响应后,timer指向下一个定时器,这就省略了之前using -= i */
}
timerctl.t0 = timer; //t0 指向第一个没有超时的定时器
timerctl.next = timer->timeout; // 设置最近的超时时间
return;
}
这一节没有太大变化,主要是简化了代码