自制操作系统日志——第十三天
今天续上昨天未完成的定时器的任务,再接再厉,争取今天将这部分弄完!!
一、简化主程序的字符串显示部分
由于在主程序中,每次显示字符都需要做如下三个步骤:先涂上背景色,然后写上字符,最后完成刷新这三个操作,因此为了简化程序,我们可以直接将这三个或者由前两个组成的语句进行浓缩成一个函数来使用:
//这里主要完成的任务是:先涂上背景色,然后在这之上描绘字符,然后刷新图层!! 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;
}
然后进行替换即可!!!
二、调整fifo缓冲区
1.简化定时器的缓冲区
由于在前所述中,我们对于每一个计时器都给予一个fifo缓冲区,这在只有3个时候还好,倘若有100个的话,这种方法不仅占用内存,会让判断的的那一句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);
略
else if (fifo8_status(&timerfifo) != 0)
{
i = fifo8_get(&timerfifo);//首先读入,为了设定起始点
io_sti();
if(i == 10)
{
putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
}else if (i == 3)
{
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
}else{
if (i != 0) {
timer_init(timer3, &timerfifo, 0); //然后设置为0
boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
} else {
timer_init(timer3, &timerfifo, 1); //然后设置为1
boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
}
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
}
}
然后,make run即可!!应该是没啥问题可以运行的! 注意顺便将之前声明的那些变量删了。。。
2、简化所有的fifo缓冲区
这里,如图前面所述,我们继续进一步的将所有的FIFO缓冲区全部浓缩在一个缓冲区当中!!!
具体的传送数据如下:
/*缓冲区读入的数据:
0~1 表示光标闪烁
3 表示3秒定时器
10 表示10s定时器
256~511 表示键盘输入(键盘输入值+256)
512~767 表示鼠标输入(鼠标输入值+512)
*/
因此,我们需要将之前的fifo缓冲区修改一下,将之前buf的char类型变为int型:
/* fifo.c */
struct FIFO32{
int *buf;//缓冲区地址
int p, q, size, free, flags;//p 下一写入地址;q 下一读出地址; size是总字节数,free是缓冲区中没有数据的字节数;flag判断溢出
};
void fifo32_init(struct FIFO32 *fifo, int size, int *buf);
int fifo32_put(struct FIFO32 *fifo, unsigned char data);
int fifo32_get(struct FIFO32 *fifo);
int fifo32_status(struct FIFO32 *fifo);
然后,对应的修改一下fifo.c的内容:
void fifo32_init(struct FIFO32 *fifo, int size, int *buf)
//初始化fifo地址
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size; //空
fifo->flags = 0;
fifo->p = 0 ; //写入位置
fifo->q = 0 ; //读取位置
return;
}
int fifo32_put(struct FIFO32 *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;
}
int fifo32_get(struct FIFO32 *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;
}
int fifo32_status(struct FIFO32 *fifo)
{
return fifo->size - fifo->free;
}
继续对应的修改一下,mouse
struct FIFO32 *mousefifo;
int mousedata0;
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
void enable_mouse(struct FIFO32 *fifo, int data0, struct MOUSE_DEC *mdec)
{
//将FIFO缓冲区内容保存到全局
mousefifo = fifo;
mousedata0 = data0;
//鼠标有效,且激活
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);//往键盘控制电路发送0xd4,则代表下一个数据发给鼠标
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);//激活鼠标数据 0xfa,键盘控制其返回ACK(0xfa) <== 答复信息
//如果激活了,就会传送0xfa过来
mdec->phase = 0;//等待0xfa阶段
return;
}
void inthandler2c(int *esp)
/* 来自PS/2鼠标的中断 */
{
int data;
io_out8(PIC1_OCW2,0X64); //通知PIC1 已经完成IRQ-12
io_out8(PIC0_OCW2,0X62);//通知PIC0 已经完成IRQ-2
data = io_in8(PORT_KEYDAT);
fifo32_put(mousefifo, data + mousedata0);
return;
}
修改keyboard:
struct FIFO32 *keyfifo;
int keydata0;
void inthandler21(int *esp)
{
int data;
/*通知PIC “IRQ-01”已经受理完毕;这里的计算公式是0x60+IRQ号码
只有输出到ocw2后,PIC才会继续监视IRQ1中断是否发生,如果不写这句话则后面不会再产生中断了
*/
io_out8(PIC0_OCW2,0X61);
data = io_in8(PORT_KEYDAT);
fifo32_put(keyfifo, data + keydata0);
return;
}
#define PORT_KEYSTA 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
void wait_KBC_sendready(void)
{
for(;;)
{
//等待键盘控制电路准备完毕,检测到设备号0x0064处数据的第2位是0时,就跳出等待
if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) {
break;
}
}
return;
}
void init_keyboard(struct FIFO32 *fifo, int data0)
{
keyfifo = fifo;
keydata0 = data0;
/* 初始化键盘控制电路 */
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);//向键盘控制电路发送模式设定指令,模式设定指令是0x60
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);//向键盘控制电路发送模式设定指令,鼠标的模式号码是0x47
return;
}
然后,也继续修改一下定时器的结构:
struct TIMER
{
unsigned int timeout, flags;//flags表示各个定时器的状态; timeout的含义,这里指予定时刻,通过settime中赋予的超时时间+当前时刻来计算,当到达多少时间后算超时
struct FIFO32 *fifo;//用于将超时的信息传给缓冲区。
int data;
};
最后改一下主程序就行了:
void HariMain(void)
{
//鼠标键盘,bootinfo等等的定义
struct BOOTINFO *binfo = ( struct BOOTINFO *) ADR_BOOTINFO;
struct FIFO32 fifo;
int fifobuf[128];
char s[40];
略
//初始化的部分
init_gdtidt();//初始化gdt、idt表
init_pic(); //初始化pic控制器
io_sti();
fifo32_init(&fifo, 128, fifobuf);
init_pit();
init_keyboard(&fifo, 256);
enable_mouse(&fifo, 512, &mdec);
io_out8(PIC0_IMR, 0xf8); /* 开放PIC1和键盘中断(11111000),键盘是IRQ1 */
io_out8(PIC1_IMR, 0xef); /* 开放鼠标中断(11101111) ,鼠标是IRQ12*/
timer = timer_alloc();
timer_init(timer, &fifo, 10);
timer_settime(timer, 1000);
timer2 = timer_alloc();
timer_init(timer2, &fifo, 3);
timer_settime(timer2, 300);
timer3 = timer_alloc();
timer_init(timer3, &fifo, 1);
timer_settime(timer3, 50);
略
for(;;)
{
count++;
io_cli(); //IF=0
if (fifo32_status(&fifo) == 0)
{
io_sti();
}else
{
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) { /*键盘数据 */
sprintf(s, "%02X", i - 256);
putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
} else if (512 <= i && i <= 767) { /* 鼠标数据 */
if (mouse_decode(&mdec, i - 512) != 0) {
/* 已经收集3字节数据,显示出来 */
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
/* 鼠标的移动 */
mx += mdec.x;
my += mdec.y;
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > binfo->scrnx - 1) {
mx = binfo->scrnx - 1;
}
if (my > binfo->scrny - 1) {
my = binfo->scrny - 1;
}
sprintf(s, "(%3d, %3d)", mx, my);
putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
sheet_slide(sht_mouse, mx, my);
}
} else if (i == 10) { /*10秒计时器 */
putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
sprintf(s, "%010d", count);
putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 10);
} else if (i == 3) { /* 3秒计时器 */
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
count = 0; /* 测定开始 */
} else if (i == 1) { /* 光标用计时器 */
timer_init(timer3, &fifo, 0); /* 设定为0 */
boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
} else if (i == 0) { /* 光标用计时器 */
timer_init(timer3, &fifo, 1); /* 设定为1 */
boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
}
}
}
}
至此,修改完毕!!!这里我们改进了代码中断交互操作,从以前每次循环都要查询三次的缓冲区,改成了一个。这样子就可以使得count中执行语句执行的更多次了!!! 具体的可以必交一下之前的程序,会发现差距还蛮大的。
加快中断处理
由于前面的过程中,我们采用的都是利用移位的操作来进行将timers中的计时器进行排序的。这种方法可能会使得再拥有多个计时器的情况下,在这一步的中断过程中时间明显会变长很多!!!
所以,我们计划利用链表的形式进行进行链接管理整个计时器的表,这样子就可以不用移位了!!而是直接更改链表指针的指向就可以实现移位了:
更改结构体:
#define MAX_TIMER 500
struct TIMER
{
struct TIMER *next_timer;//指下一个定时器的地址
unsigned int timeout, flags;//flags表示各个定时器的状态; timeout的含义,这里指予定时刻,通过settime中赋予的超时时间+当前时刻来计算,当到达多少时间后算超时
struct FIFO32 *fifo;//用于将超时的信息传给缓冲区。
int data;
};
struct TIMERCTL
{
unsigned int count, next_time, using;//using用于记录现在有几个定时器处于活动中,这里的next是指下一个超时时刻
struct TIMER *t0;
struct TIMER timers0[MAX_TIMER];
};
然后更改timer.c的部分函数即可:
/主程序中对于pit计时器是先注册并设置完一个之后再弄另一个的!!
//t用于进行遍历的,s保存t的前一个值
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.t0 = timer;
timer->next_timer = 0; //没有下一个
timerctl.next_time = timer->timeout;
io_store_eflags(e);
return;
}
t = timerctl.t0;
if(timer->timeout <= t->timeout)//插入最前面
{
timerctl.t0 = timer;
timer->next_timer = t;//接下来是t
timerctl.next_time = timer->timeout;
io_store_eflags(e);
return;
}
//搜索注册位置
for(;;) //找到比当前计时器拟到达时刻还要晚的计时器,然后插入在此前面
{
s = t;
t = t->next_timer;
if(t == 0)
{
break;
}
if(timer->timeout <= t->timeout)
{//插入到s和t中间
s->next_timer = timer;//s的下一个是timer
timer->next_timer = t;//timer的下一个是t
io_store_eflags(e);
return;
}
//插入的是最后一位
s->next_timer = timer;
timer->next_timer = 0;
io_store_eflags(e);
return;
}
}
void inthandler20(int *esp)
{
int i;
struct TIMER *timer;
io_out8(PIC0_OCW2, 0x60);//IRQ-0信号接收完后告知PIC
timerctl.count++;
if(timerctl.next_time > timerctl.count)
{
return; //还不到下一个时刻,因此返回
}
timer = timerctl.t0;//先把最前面的赋予给timer
for (i = 0; i < timerctl.using; i++) {// timers的定时器都是活动中的因此不需要确认flags
if(timer->timeout > timerctl.count )
{
break;
}
//超时
timer->flags = TIMER_FLAGS_ALLOC;
fifo32_put(timer->fifo, timer->data);
timer = timer->next_timer;//将下一定时器地址赋予给timer
}
//正好有i个计时器所以移位
timerctl.using -= i;
//新移位
timerctl.t0 = timer;
//timerctl.next的设定
if (timerctl.using > 0) {
timerctl.next_time = timerctl.t0->timeout;
} else {
timerctl.next_time = 0xffffffff;
}
return;
}
然后,makerun运行:
使用哨兵化进行处理
所谓“哨兵”,就是程序技巧中的专门用于,其目的是在某一串链接中,设置一个留下了进行"看门、巡逻”等等的,其目的不在于使用,而是可以减少我们程序的语句判断的逻辑!!
在前面的逻辑中,settime里,我们必须做如下四种情况:
- 运行中的定时器只有一种情况;
- 插入到最前面;
- 插入到t和s之间;
- 插入到最后面。
试想,这里我们建立起一个时刻为0xfffffff的定时器,置于整个计时器表中的最后一位(一般来说是到达不了的,就算可能要到达这个时刻,我们也可以进行修改时刻!)。那么这一个定时器,就会一直处于最后一个,像是留下来看家的一个,因此也可以称之为哨兵。 那么这个哨兵有什么用? 他的好处在于,我们可以极大的减少我们的判断语句,也就是说此时上面的四个条件我们已经可以去除掉两个了!!
- 运行中的定时器只有一种情况;×(因为有哨兵所以至少两个)
- 插入到最前面;√
- 插入到t和s之间;√
- 插入到最后面。× (永远有哨兵存在)
而且,由于我们使用的是next这种格式进行连接的,因此using也没有用了。
然后具体修改的代码如下:
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_timer = t;//接下来是t
timerctl.next_time = timer->timeout;
io_store_eflags(e);
return;
}
//搜索注册位置
for(;;) //找到比当前计时器拟到达时刻还要晚的计时器,然后插入在此前面
{
s = t;
t = t->next_timer;
if(t == 0)
{
break;
}
if(timer->timeout <= t->timeout)
{//插入到s和t中间
s->next_timer = timer;//s的下一个是timer
timer->next_timer = t;//timer的下一个是t
io_store_eflags(e);
return;
}
}
}
void inthandler20(int *esp)
{
int i;
struct TIMER *timer;
io_out8(PIC0_OCW2, 0x60);//IRQ-0信号接收完后告知PIC
timerctl.count++;
if(timerctl.next_time > timerctl.count)
{
return; //还不到下一个时刻,因此返回
}
timer = timerctl.t0;//先把最前面的赋予给timer
for (;;) {// timers的定时器都是活动中的因此不需要确认flags
if(timer->timeout > timerctl.count )
{
break;
}
//超时
timer->flags = TIMER_FLAGS_ALLOC;
fifo32_put(timer->fifo, timer->data);
timer = timer->next_timer;//将下一定时器地址赋予给timer
}
//新移位
timerctl.t0 = timer;
//timerctl.next的设定
timerctl.next_time = timerctl.t0->timeout;
return;
}
运行结果如下:
可以对比一下,明显又快了许多!!!!!可喜可贺啊
总结
以上就算所有的定时器操作,明天开始我们将开启提高屏幕的分辨率了!!!!