30天自制操作系统(第26天)

26.1   提高窗口移动速度(1

导致窗口移动速度慢的原因有很多,其中之一就是 sheet_refreshmap的速度太慢。这个函数在 sheet_slide中被调用了两次,如果能提高它的速度效果应该会很明显。思路:由于col_inv表示透明色,且在bootpack.c文件中将背景图层的透明色号设置为-1,像窗口都是有大量透明色,所以没有透明色图层需要全部刷新,有透明色的图层只需要刷新部分。
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0){
	(中略)	
	for(h=h0;h<=ctl->top;h++){
		(中略)
		if(sht->col_inv == -1){// 无透明色图层
			for (by = by0; by < by1; by++) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
					map[vy * ctl->xsize + vx] = sid;
				}
			}
		}else{// 有透明色的图层
			for (by = by0; by < by1; by++) {
				vy = sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
					if (buf[by * sht->bxsize + bx] != sht->col_inv) {
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		}
	}
	return;
}

26.2   提高窗口移动速度(2

需要实现的功能:原代码只能每次写入一个字节,若能每次向map中写入4个字节的数据,将会极大提高速率。思路:根据 map[vy * ctl->xsize + vx] = sid,且map的数据类型为char,当向内部传入32位数据时,它将自动向后移位赋值(参考了汇编中的MOV操作)。
为确保被创建的sht图层的显示地址(即对应结构体中的vx0和vy0)为4的倍数,所以修改创建时的坐标(hrb_api函数中sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2);)。还有主函数中的鼠标移动时,也会用到图层数据,所以也需要进行修改。(注:为何要删掉代码mmx=mx呢?是因为mmx、mmy与mmx2都是鼠标按下时才被确定的数据,而窗口移动时调用sheet_slide函数会修改图层的窗口坐标,其当前窗口的x坐标仅和mmx、mmx2有关系,而这两个数据是不随着鼠标变化的,后者y坐标用的是sht->vy0+y进行更新的,每次都会随着鼠标my坐标更新sht->vy0数值。)
/*                console.c                */
if(sht->col_inv == -1){// 无透明色图层
	if((sht->vx0&3) == 0 && (bx0&3) == 0 && (bx1&3) == 0){//4字节
		bx1 = (bx1-bx0)/4;//MOV次数
		sid4 = sid | sid << 8 | sid << 16 | sid << 24;//每次写入4字节
		for (by = by0; by < by1; by++) {
			vy = sht->vy0 + by;
			vx = sht->vx0 + bx0;
			p = (int *) &map[vy * ctl->xsize + vx];
			for(bx = 0; bx < bx1; bx++)
				p[bx] = sid4;
		}	
	}else{//1字节
		for (by = by0; by < by1; by++) {
			vy = sht->vy0 + by;
			for (bx = bx0; bx < bx1; bx++) {
				vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
				map[vy * ctl->xsize + vx] = sid;
			}
		}
	}
}else{// 有透明色的图层
	for (by = by0; by < by1; by++) {
		vy = sht->vy0 + by;
		for (bx = bx0; bx < bx1; bx++) {
		    vx = sht->vx0 + bx;// 不是透明色表明该位置有图形像素,更新对应map中的数据
			if (buf[by * sht->bxsize + bx] != sht->col_inv) {
				map[vy * ctl->xsize + vx] = sid;
			}
		}
	}
}

/*            bootpack.c文件的HariMain函数代码片段            */
if(mmx < 0){
    /*如果处于通常模式,窗口不移动*/
	/*按照从上到下的顺序寻找鼠标所指向的图层*/
	for(j = shtctl->top-1; j > 0; j--){
		(中略)
		if(0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize){
			if(sht->buf[y*sht->bxsize+x] != sht->col_inv){
				(中略)
				if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
					mmx = mx;
					mmy = my;
/*这里*/			mmx2 = sht->vx0;
				}
				(中略)
}else{/*窗口移动模式*/
	y = my - mmy;
	x = mx - mmx;
/*这里*/sheet_slide(sht, (mmx2+x+2)&~3, sht->vy0+y);
    mmy = my;
}

26.3   提高窗口移动速度(3

思路:由于函数 sheet_refreshsub与函数sheet_refreshmap的代码基本上相似,所以能否借鉴26.2节中的思路,每次写入4字节数据呢?有以下几种情况:1.图层的vx0(图层显示窗口的x轴坐标)是4的倍数 :①先写入前面非4倍数的显示数据②接着写入中间4的倍数部分③最后写入后面非4倍数的显示数据;2.否则就逐个写入显示数据。
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1){
	int h, bx, by, vx, vy, bx0, by0, bx1, by1, bx2, sid4, i, i1, *p, *q, *r;
	(中略)
	for(h=h0;h<=h1;h++){
		(中略)
		if((sht->vx0&3)==0){//图层的x坐标是4的倍数
			i = (bx0+3)/4;
			i1= bx1/4;
			i1= i1-i;
			sid4 = sid|sid<<8|sid<<16|sid<<24;
			for(by = by0; by < by1; by++){
				vy = sht->vy0+by;
				for (bx = bx0; bx < bx1 && (bx&3) != 0; bx++){//先写入前面非4倍数的地址数据
					vx = sht->vx0+bx;
					if(map[vy*ctl->xsize+vx] == sid)
						vram[vy*ctl->xsize+vx] = buf[by*sht->bxsize+bx];
				}
				vx = sht->vx0+bx;
				p = (int*)&map[vy*ctl->xsize+vx];
				q = (int*)&vram[vy*ctl->xsize+vx];
				r = (int*)&buf[by*sht->bxsize+bx];
				for(i = 0; i < i1; i++){//中间4的倍数部分
					if(p[i] == sid4)q[i] = r[i];
					else{
						bx2 = bx+i*4;
						vx = sht->vx0+bx2;
						if(map[vy*ctl->xsize+vx+0] == sid)
							vram[vy*ctl->xsize+vx+0] = buf[by*sht->bxsize+bx+0];
						if(map[vy*ctl->xsize+vx+1] == sid)
							vram[vy*ctl->xsize+vx+1] = buf[by*sht->bxsize+bx+1];
						if(map[vy*ctl->xsize+vx+2] == sid)
							vram[vy*ctl->xsize+vx+2] = buf[by*sht->bxsize+bx+2];
						if(map[vy*ctl->xsize+vx+3] == sid)
							vram[vy*ctl->xsize+vx+3] = buf[by*sht->bxsize+bx+3];
					}
				}
				for(bx += i1; bx < bx1; bx++){//最后写入后面非4倍数的地址数据
					vx = sht->vx0+bx;
					if(map[vy*ctl->xsize+vx] == sid)
						vram[vy*ctl->xsize+vx] = buf[by*sht->bxsize+bx];
				}
			}
		}else{
			for (by = by0; by < by1; by++) {
				vy = sht->vy0+by;
				for (bx = bx0; bx < bx1; bx++) {
					vx = sht->vx0+bx;
					if (map[vy*ctl->xsize+vx] == sid) {
						vram[vy*ctl->xsize+vx] = buf[by*sht->bxsize+bx];
					}
				}
			}
		}
	}
	return;
}

26.4   提高窗口移动速度(4

为什么明明已经放开了鼠标键,窗口却还在挪动呢?这是因为伴随图层移动所进行的绘图操作非常消耗时间,导致系统来不及处理FIFO中的鼠标移动数据。 那么可以在接收到鼠标移动数据后不立即进行绘图操作,可以等 FIFO 为空时再进行绘图操作。
增加了 new_mx new_wy 两个变量,并将原来的 sheet_slide (sht_mouse, mx, my ; 改成了 new_mx = mx; new_my = my;,也就是说,我们并不真的移动鼠标图层的位置,而是将移动后的坐标暂且保存起来,当 FIFO为空时,再执行sheet_slide sht_mouse, new_mx, new_my ;
窗口移动我们也采用相同的方法,只不过有一点小小的区别,代表 FIFO为空时不需要执行 sheet_slide 的值从 -1 变成了 0x7fffffff。
if (fifo32_status(&fifo) == 0) {
	/* FIFO为空,当存在搁置的绘图操作时立即执行*/
	if(new_mx >= 0){
		io_sti();
		sheet_slide(sht_mouse, new_mx, new_my);
		new_mx = -1;
	}else if(new_wx != 0x7fffffff){
		io_sti();
		sheet_slide(sht, new_wx, new_wy);
		new_wx = 0x7fffffff;
	}else{
		task_sleep(task_a);
		io_sti();
	}
} else {
    (中略)
else if (512 <= i && i <= 767) {/* 鼠标数据 */
	if (mouse_decode(&mdec, i - 512) != 0) {
	    /* 更新鼠标的位置 */
		mx += mdec.x;
		my += mdec.y;
		new_mx = mx;
		new_my = my;
		(中略)
		if((mdec.btn&0x01) != 0){// 如果左键按下,则移动窗口
			if(mmx < 0){
				for(j = shtctl->top-1; j > 0; j--){
					sht = shtctl->sheets[j];
					y = my - sht->vy0;
					x = mx - sht->vx0;
					if(0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize){
						if(sht->buf[y*sht->bxsize+x] != sht->col_inv){
							(中略)
							if(3 <= x && x < sht->bxsize-3 && 3<= y && y < 21){//判断是否在窗口的标题栏
								mmx = mx;
								mmy = my;
								mmx2 = sht->vx0;
								new_wy = sht->vy0;
							}
							(中略)
						}
					}
				}
			}else{/*窗口移动模式,更新图层的位置*/
				y = my - mmy;
				x = mx - mmx;
				new_wx = (mmx2 + x + 2) & ~3;
				new_wy = new_wy + y;
				mmy = my;
			}
		}else{/*没有按下左键*/
			mmx = -1;/*返回通常模式*/
			if(new_wx != 0x7fffffff){
				sheet_slide(sht, new_wx, new_wy);/*刷新图层位置*/
				new_wx = 0x7fffffff;
			}
		}
	}
}

26.5   启动时只打开一个命令行窗口

需要实现的功能:按下"shift+F2"组合键时,弹出新的一个命令窗口。思路:原代码已经实现了按下"shift+F1"组合键的功能,所以在接收键盘数据时增加一个判断即可,只有满足新创建的命令窗口不存在且同时按下组合键的要求才可以。open_console函数创建命令窗口,原程序在25.4节中,在本节中进行了封装。
if(i == 256 + 0x3c && key_shift != 0 && sht_cons[1] == 0){/* Shift+F2 开启新命令窗口 */
	sht_cons[1] = open_console(shtctl, memtotal);
	sheet_slide(sht_cons[1], 32, 4);
	sheet_updown(sht_cons[1], shtctl->top);
	/*自动将输入焦点切换到新打开的命令行窗口*/
	keywin_off(key_win);
	key_win = sht_cons[1];
	keywin_on(key_win);
}

26.6   增加更多的命令行窗口

需要实现的功能:按下"shift+F2"组合键时,弹出新的窗口,可以一直创建。思路:特地定义sht_cons[]这个变量保存了一个值,实际上却根本没有用到!按照之前的设定,key_win是指向当前窗口,与sht_cons[]功能相似,所以干脆直接用key_win变量。
void HariMain(void)
{
    (中略)
    struct SHEET *sht_back, *sht_mouse; /*删掉了sht_cons[2]*/
    (中略)
    /* sht_cons */
    key_win = open_console(shtctl, memtotal);
    (中略)
    sheet_slide(sht_back, 0, 0);
    sheet_slide(key_win, 32, 4); /*这里!*/
    sheet_slide(sht_mouse, mx, my);
    sheet_updown(sht_back, 0);
    sheet_updown(key_win, 1); /*这里!*/
    sheet_updown(sht_mouse, 2);
    keywin_on(key_win);
    (中略)
    for (;;) {
        (中略)
        if (fifo32_status(&fifo) == 0) {
            (中略)
        } else {
            (中略)
            if (256 <= i && i <= 511) { /*键盘数据*/
                (中略)
                if (i == 256 + 0x3c && key_shift != 0) { /* Shift+F2 */
                    /*自动将输入焦点切换到新打开的命令行窗口*/
/*从此开始*/         keywin_off(key_win);
                    key_win = open_console(shtctl, memtotal);
                    sheet_slide(key_win, 32, 4);
/*到此结束*/         sheet_updown(key_win, shtctl->top);
                    keywin_on(key_win);
                }
                (中略)
            } else if (512 <= i && i <= 767) { /*鼠标数据*/
                (中略)
            }
        }
    }
}

26.7   关闭命令行窗口(1

需要实现的功能:在命令行窗口中,输入“exit”命令就可以关闭当前窗口思路:首先在bootpack.c文件中需要将创建该窗口时所占用的内存空间全部释放出来,然后还需要释放窗口的图层和任务结构。 console.c文件中增加对输入字符的判断,首先需要取消控制光标闪烁的定时器,然后将FAT用的内存空间释放,最后调用close_console关闭命令行窗口和自身的任务( 需要让 task_a来替执行关闭任务这个操作,从命令行窗口任务向task_a任务发送一个数据,请task_a帮忙关闭命令行窗口任务, task_a FIFO 地址保存在 0x0fec 这个地址)。
还需要修改 HariMain 使其能够处理来自命令行窗口的 768 1023的数据,另外,从现在开始可能会出现画面上一个窗口都没有的情况,该处的代码就不做展示了。
/*                bootpack.c                */
struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal){
    (中略)
    task->cons_stack = memman_alloc_4k(memman, 64 * 1024); /*从此开始*/
    task->tss.esp = task->cons_stack + 64 * 1024 - 12; /*到此结束*/
    (中略)
}

void close_constask(struct TASK *task){
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    task_sleep(task);
    memman_free_4k(memman, task->cons_stack, 64 * 1024);
    memman_free_4k(memman, (int) task->fifo.buf, 128 * 4);
    task->flags = 0; /*用来替代task_free(task); */
    return;
}

void close_console(struct SHEET *sht){
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = sht->task;
    memman_free_4k(memman, (int) sht->buf, 256 * 165);
    sheet_free(sht);
    close_constask(task);
    return;
}

/*                console.c                */
void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, int memtotal){
    (中略)
    } else if (strcmp(cmdline, "exit") == 0) {
        cmd_exit(cons, fat);
    } else if (cmdline[0] != 0) {
        (中略)
}

void cmd_exit(struct CONSOLE *cons, int *fat){
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct TASK *task = task_now();
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
    timer_cancel(cons->timer);
    memman_free_4k(memman, (int) fat, 4 * 2880);
    io_cli();
    fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768); /* 768〜1023 */
    io_sti();
    for (;;) {
        task_sleep(task);
    }
}

26.8   关闭命令行窗口(2

需要实现的功能:点击"x",就可以关闭当前窗口思路:在bootpack.c函数中有判断没有点击应用程序窗口的"x"的程序,只需要添加else部分。 鼠标的点击是在 task_a 中处理的,如果直接调用 close_console,由窗口自身所管理的释放定时器及 FAT内存空间的部分就难以实现了。所以,选择向命令行窗口发送通知数据这种方式。
/*                bootpack.c                */
if(sht->bxsize-21 <= x && x < sht->bxsize-5 && 5<= y && y < 19){//点击"x"关闭窗口
	if((sht->flags & 0x10) != 0){/*该窗口不是应用程序窗口*/
		task = sht->task;
		cons_putstr0(task->cons, "\nBreak(mouse) :\n");
		io_cli();
		task->tss.eax = (int)&(task->tss.esp0);
		task->tss.eip = (int) asm_end_app;
		io_sti();
	}else{//是命令行窗口 /*从这里开始*/
		task = sht->task;
		io_cli();
		fifo32_put(&task->fifo, 4);
		io_sti(); /*到这里结束*/
	}
}

/*                console.c                */
void console_task(struct SHEET *sheet, int memtotal)
{
    (中略)
    for (;;) {
        (中略)
        if (fifo32_status(&task->fifo) == 0) {
            (中略)
        } else {
            (中略)
            if (i == 4) { /*点击命令行窗口的“×”按钮*/ /*从此开始*/
                cmd_exit(&cons, fat);
            } /*到此结束*/
            (中略)
        }
    }
}

26.9   start命令

需要实现的功能:输入"start color2",就可以运行color2程序,弹出对应窗口思路:可以新创建一个命令窗口,并部分写入"color2"数据。
void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, int memtotal){
    (省略)
    } else if (strncmp(cmdline, "start ", 6) == 0) {
    cmd_start(cons, cmdline, memtotal);
    } else if (cmdline[0] != 0) {
    (省略)
}

void cmd_start(struct CONSOLE *cons, char *cmdline, int memtotal){
    struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
    struct SHEET *sht = open_console(shtctl, memtotal);
    struct FIFO32 *fifo = &sht->task->fifo;
    int i;
    sheet_slide(sht, 32, 4);
    sheet_updown(sht, shtctl->top);
    /*将命令行输入的字符串逐字复制到新的命令行窗口中*/
    for (i = 6; cmdline[i] != 0; i++) {
        fifo32_put(fifo, cmdline[i] + 256);
    }
    fifo32_put(fifo, 10 + 256); /*回车键*/
    cons_newline(cons);
    return;
}
  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
第1篇 Java编程基础   第1章 Java开发环境的搭建(教学视频:9分钟) 2   1.1 理解Java 2   1.2 搭建Java所需环境 3   1.2.1 下载JDK 3   1.2.2 安装JDK 4   1.2.3 配置环境 5   1.2.4 测试JDK配置是否成功 7   实例1 开发第一个Java程序 7   第2章 Java基础类型与运算符(教学视频:39分钟) 9   2.1 基础类型 9   实例2 自动提升 9   实例3 自动转换 10   实例4 常用基础类型之强制转换 11   2.2 运算符 12   实例5 算术运算符 12   实例6 关系运算符 13   实例7 逻辑运算符 14   实例8 位运算符 15   实例9 移位运算符 16   实例10 转型运算符 17   2.3 其他形式 18   实例11 常量与变量 18   实例12 各种进制的转换 19   实例13 Java中的进制与移位运算符 22   第3章 条件控制语句(教学视频:75分钟) 26   3.1 if控制语句 26   实例14 判断输入的年份是否为闰年 26   实例15 抽奖活动 27   3.2 for语句 28   实例16 小九九乘法表 28   实例17 如何列出素数 29   实例18 Java中的递归 31   实例19 男生女生各多少人 32   实例20 求水仙花数 34   实例21 求任意一个正数的阶乘 35   实例22 求n的n次方 35   实例23 利用for循环输出几何图形 36   实例24 杨辉三角 38   3.3 while语句 39   实例25 求1到100之间的和 39   实例26 存上100元需要多少天 40   实例27 输出100之间的所有偶数 41   实例28 如何判断回文数字 42   3.4 do…while语句 43   实例29 输出100之间的所有奇数 44   实例30 求最大的随机数 44   3.5 switch语句 45   实例31 判断字母分类 46   实例32 优良及差 47   实例33 打印任意一年日历 48   实例34 一年四季的划分 51   第2篇 Java数据处理   第4章 异常处理(教学视频:62分钟) 54   4.1 编译时异常 54   实例35 除0发生的算术异常(ArithmeticException) 54   实例36 数组下标越界异常(ArrayIndexOutOfBoundsException) 55   实例37 数组元素类型不匹配异常(ArrayStoreException) 56   实例38 强制类型转换异常(ClassCastException) 56   实例39 索引越界异常(IndexOutOfBoundsException) 57   实例40 空指针异常(NullPointerException) 58   实例41 数字格式转换异常(NumberFornatException) 59   实例42 字符串索引越界异常(StringIndexOutBounds) 60   实例43 操作错误(UnsupportedOperationException) 60   4.2 运行时异常 61   实例44 找不到指定类时发生的异常(ClassNotFoundException) 62   实例45 请求的方法不存在(NoSuchMethodException) 63   4.3 try…catch捕获异常 65   实例46 try…catch捕获异常的实例 66   实例47 try…catch…finally捕获异常的实例 67   实例48 try…catch嵌套捕获异常的实例 68   4.4 throws声明异常 69   实例49 throws声明异常实例一 69   实例50 throws声明异常实例二 70   4.5 throw抛出异常 72   实例51 throw抛出异常实例一 72   实例52 throw抛出异常实例二 73   4.6 自定义异常 74   实例53 自定义异常实例一 74   实例54 自定义异常实例二 75   第5章 数组(教学视频:98分钟) 78   5.1 一维数组 78   实例55 一

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值