定时器:每隔一段时间就发送一个中断信号给CPU。
1.控制定时器
要在电脑中管理定时器,需要设定PIT(可编程的间隔型定时器),在电脑上PIT连接着IRQ0的中断。
IRQ0的中断周期变更程序:见书。实际代码:
timer.c
void init_pit(void)
{
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
return;
}
;bootpack.c
init_gdtidt();
init_pic();
io_sti(); /* IDT/PIC初始化已经完成,可以解除CPU中断了 */
fifo8_init(&keyfifo, 32, keybuf);
fifo8_init(&mousefifo, 128, mousebuf);
init_pit(); /*这里*/
io_out8(PIC0_IMR, 0xf8); /* PITとPIC1とキーボードを許可(11111000) */
io_out8(PIC1_IMR, 0xef); /* マウスを許可(11101111) */
添加IRQ0号中断对应的函数:
;timer.c
void inthandler20(int *esp)
{
io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接受完了的信息通知给PIC */
/* 暂时什么也不做 */
return;
}
;naskfunc.nas
_asm_inthandler20:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler20
POP EAX
POPAD
POP DS
POP ES
IRETD
将中断处理程序注册到IDT,init_gdtidt函数也要加上几行:
void init_gdtidt(void)
{
set_gatedesc(idt + 0x20, (int) asm_inthandler20, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);
return;
}
2.使用定时器计数
添加变量count
struct TIMERCTL {
unsigned int count;
};
struct TIMERCTL timerctl;
void init_pit(void)
{
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
timerctl.count = 0; /*这里*/
return;
}
void inthandler20(int *esp)
{
io_out8(PIC0_OCW2, 0x60);
timerctl.count++; /*这里*/
return;
}
3.超时功能
添加变量timeout,表示距离超时还有多久:
添加变量*fifo、data,一旦超时就向fifo发送data:
;bootpack.h
struct TIMERCTL {
unsigned int count;
unsigned int timeout;
struct FIFO8 *fifo;
unsigned char data;
};
;timer.c
void init_pit(void)
{
io_out8(PIT_CTRL, 0x34);
io_out8(PIT_CNT0, 0x9c);
io_out8(PIT_CNT0, 0x2e);
timerctl.count = 0;
timerctl.timeout = 0;
return;
}
void inthandler20(int *esp)
{
io_out8(PIC0_OCW2, 0x60); /* IRQ-00受付完了をPICに通知 */
timerctl.count++;
if (timerctl.timeout > 0) { /* タイムアウトが設定されている */
timerctl.timeout--;
if (timerctl.timeout == 0) {
fifo8_put(timerctl.fifo, timerctl.data);
}
}
return;
}
void settimer(unsigned int timeout, struct FIFO8 *fifo, unsigned char data)
{
int eflags;
eflags = io_load_eflags();
io_cli();
timerctl.timeout = timeout;
timerctl.fifo = fifo;
timerctl.data = data;
io_store_eflags(eflags);
return;
}
4.设定多个定时器
在timerctl中添加timer数组:
#define MAX_TIMER 500
struct TIMER {
unsigned int timeout, flags;
struct FIFO8 *fifo;
unsigned char data;
};
struct TIMERCTL {
unsigned int count;
struct TIMER timer[MAX_TIMER];
};
#define TIMER_FLAGS_ALLOC 1 /* 初始化状态 */
#define TIMER_FLAGS_USING 2 /* 使用状态 */
void init_pit(void)
{
int i;
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.timer[i].flags = 0; /* 未使用 */
}
return;
}
struct TIMER *timer_alloc(void)
{
int i;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == 0) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
return &timerctl.timer[i];
}
}
return 0; /* 見つからなかった */
}
void timer_free(struct TIMER *timer)
{
timer->flags = 0; /* 未使用 */
return;
}
void timer_init(struct TIMER *timer, struct FIFO8 *fifo, unsigned char data)
{
timer->fifo = fifo;
timer->data = data;
return;
}
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
timer->timeout = timeout;
timer->flags = TIMER_FLAGS_USING;
return;
}
void inthandler20(int *esp)
{
int i;
io_out8(PIC0_OCW2, 0x60); /* IRQ-00受付完了をPICに通知 */
timerctl.count++;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
timerctl.timer[i].timeout--;
if (timerctl.timer[i].timeout == 0) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
}
}
}
return;
}
5.加快中断20号的处理速度
5.1不再--
之前每发生一次中断,所有timer的timeout依次减一,浪费时间。现在改为当前timeout=设定timeout+cout0的形式,判断时只需判断当前count和各个timeout:
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
timer->timeout = timeout + timerctl.count;
timer->flags = TIMER_FLAGS_USING;
return;
}
void inthandler20(int *esp)
{
int i;
io_out8(PIC0_OCW2, 0x60); /* IRQ-00受付完了をPICに通知 */
timerctl.count++;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
if (timerctl.timer[i].timeout <= timerctl.count) {
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
}
}
}
return;
}
5.2添加next
添加变量next用来记录下一个超时时间:
没超时时就可以跳过for循环,也就不用判断500个定时器是否超时。
;bootpack.h
struct TIMERCTL {
unsigned int count, next;
struct TIMER timer[MAX_TIMER];
};
;timer.c
void inthandler20(int *esp)
{
int i;
io_out8(PIC0_OCW2, 0x60); /* IRQ-00受付完了をPICに通知 */
timerctl.count++;
if (timerctl.next > timerctl.count) {
return; /* まだ次の時刻になってないので、もうおしまい */
}
timerctl.next = 0xffffffff;
for (i = 0; i < MAX_TIMER; i++) {
if (timerctl.timer[i].flags == TIMER_FLAGS_USING) {
if (timerctl.timer[i].timeout <= timerctl.count) {
/* タイムアウト */
timerctl.timer[i].flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timer[i].fifo, timerctl.timer[i].data);
} else {
/* まだタイムアウトではない */
if (timerctl.next > timerctl.timer[i].timeout) {
timerctl.next = timerctl.timer[i].timeout;
}
}
}
}
return;
}
5.3定时器排序
在达到next时,程序需要多次判断,所以很慢。模仿SHTCTL中,定义一个*sheet[],用来存放排序好的图层地址。
struct TIMERCTL {
unsigned int count, next, using;
struct TIMER *timers[MAX_TIMER];
struct TIMER timers0[MAX_TIMER];
};
void inthandler20(int *esp)
{
int i, j;
io_out8(PIC0_OCW2, 0x60); /* 把IRQ-00信号接收结束的信息通知给PIC */
timerctl.count++;
if (timerctl.next > timerctl.count) {
return;
}
for (i = 0; i < timerctl.using; i++) {
/* timers的定时器都处于动作中、所以不能确认flags */
if (timerctl.timers[i]->timeout > timerctl.count) {
break;
}
/* 超时 */
timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
}
/* 正好有i个定时器超时。其余的进行位移。 */
timerctl.using -= i;
for (j = 0; j < timerctl.using; j++) {
timerctl.timers[j] = timerctl.timers[i + j];
}
if (timerctl.using > 0) {
timerctl.next = timerctl.timers[0]->timeout;
} else {
timerctl.next = 0xffffffff;
}
return;
}
设定定时器值的时候,进行排序:
void timer_settime(struct TIMER *timer, unsigned int timeout)
{
int e, i, j;
timer->timeout = timeout + timerctl.count;
timer->flags = TIMER_FLAGS_USING;
e = io_load_eflags();
io_cli();
/* どこに入れればいいかを探す */
for (i = 0; i < timerctl.using; i++) {
if (timerctl.timers[i]->timeout >= timer->timeout) {
break;
}
}
/* うしろをずらす */
for (j = timerctl.using; j > i; j--) {
timerctl.timers[j] = timerctl.timers[j - 1];
}
timerctl.using++;
/* あいたすきまに入れる */
timerctl.timers[i] = timer;
timerctl.next = timerctl.timers[0]->timeout;
io_store_eflags(e);
return;
}
void inthandler20(int *esp)
{
int i, j;
io_out8(PIC0_OCW2, 0x60); /* IRQ-00受付完了をPICに通知 */
timerctl.count++;
if (timerctl.next > timerctl.count) {
return;
}
for (i = 0; i < timerctl.using; i++) {
/* timersのタイマは全て動作中のものなので、flagsを確認しない */
if (timerctl.timers[i]->timeout > timerctl.count) {
break;
}
/* タイムアウト */
timerctl.timers[i]->flags = TIMER_FLAGS_ALLOC;
fifo8_put(timerctl.timers[i]->fifo, timerctl.timers[i]->data);
}
/* ちょうどi個のタイマがタイムアウトした。残りをずらす。 */
timerctl.using -= i;
for (j = 0; j < timerctl.using; j++) {
timerctl.timers[j] = timerctl.timers[i + j];
}
if (timerctl.using > 0) {
timerctl.next = timerctl.timers[0]->timeout;
} else {
timerctl.next = 0xffffffff;
}
return;
}