自制操作系统日志——第二十六天

自制操作系统日志——第二十六天

今天,我们主要的目标就是进一步的优化命令行窗口,以及我们改善一下窗口的移动速度。



一、提高窗口移动速度

首先呢,我们看看窗口移动速度较慢的原因有可能是由于我们的sheet_refreshmap函数的运行速度过慢:
在这里插入图片描述

从图中,我们可以看出如果每一个图层的刷新都要判断一次if语句的话,那么这个在三层for循环中的if语句很有可能执行上千万次,这应该就会影响移动速度了。可是,这里是判断有没有透明色的,很重要啊!!删掉鼠标就可能直接是个矩形了。

那么,我们仔细想想我们可以这样子做,将程序分为高速版和普通版,由于一般鼠标都具有透明色的,这样子每次的操作,都可以让鼠标或者有透明色的哪些图层速度变快许多,具体代码如下:

		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[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;
					if (buf[by * sht->bxsize + bx] != sht->col_inv) {
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		}

建议可以再qemu里试试看,可能会发现好像确实快了一点点? 不过可能透明色的就鼠标一个,所以可能感觉差距不大吧,不过没关系,要相信确实快了!!

那么继续,我们在上面的代码中可以看到这样一条语句,map[vy * ctl->xsize + vx] = sid也就是向内存地址中写入某个值。这里也和上面所说的if语句一样,处于三重for循环当中,如果每次都值读写一个数值,那将会极大的降低我们速度。

那么有没有什么方法可以加快呢? 我们想到在32位寄存器当中,我们可以一次性写入32位的数据,也就是向相邻的四地址进行写入,这样子的效率就远比我们单纯的写入一个地址要快了四倍。因此,这里我们也可以利用这种方法进行写入。也就是说,在这里我们一次写入四字节的执行速度,和执行1字节的mov是相同的。因此,下面我们来进行改造一下吧:

		if(sht->col_inv == -1){
			if((sht->vx0 & 3) == 0 && (bx0 & 3) == 0 && (bx1 & 3) ==0){//源地址和刷新范围的x地址都必须能够被4整除
				//无透明色的高速版(4字节型)
				bx1 = (bx1 - bx0) / 4; //计算mov次数,按四字节进行写入操作
				sid4 = sid | sid << 8 | sid << 16 | sid << 24;//将四字节数据存入sid4
				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[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;
					if (buf[by * sht->bxsize + bx] != sht->col_inv) {
						map[vy * ctl->xsize + vx] = sid;
					}
				}
			}
		}

当然除此之外,我们窗口图层的起始地址、大小等等最好也是4的倍数,因此我们继续进一步的做出如下设置:
console.c:
hrb_appi:

else if (edx == 5){
		sht = sheet_alloc(shtctl);
		sht->task = task;
		sht->flags |= 0x10;//启动应用程序自动关闭窗口的功能
		sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);//设置x、y轴和透明色
		make_window8((char *)ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
		sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, ((shtctl->ysize - edi) / 2) & ~3);//~3其实就是0xfffffffe,取4的倍数
		sheet_updown(sht, shtctl->top);//将窗口高度指定在当前鼠标的这层,然后鼠标再上移一层
		reg[7] = (int) sht;//方便后续向应用程序的返回值动手脚
	}

主函数:

	if(3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21){//标题栏
											//窗口移动模式
											mmx = mx;
											mmy = my;
											mmx2 = sht->vx0;
										}
										if(sht->bxsize -21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19){
											//点击X按钮
											if((sht->flags & 0x10) != 0){//是由应用程序生成的窗口吗?窗口的flags已经OR 0x10了
												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();
											}
										}
										break;
									}
								}
							}
				     	}else{
						    //处于窗口移动模式
					    	x = mx - mmx;//计算鼠标移动模式
					    	y = my - mmy;
						    sheet_slide(sht,(mmx2+ x + 2) & ~3, sht->vy0 + y);
						    mmy = my;//更新为移动后的位置
					    }

mmx2 是保留移动之前的sht->vx0的值。然后下面这里,之所以要加2是因为加上了后才会让最后取4的倍数时进行四舍五入,才能让图层最后向左移动和向右移动的概率对等。

oh,这里我们还需要修改一下窗口移动的这个刷新的函数,也是按照上面的思路分4字节和1字节进行分开刷入,当然由于刷新窗口的范围有可能会有余数,故这里也做了对应的调整:

//指定了vx0~vy1 的刷新范围,以及指定的图层之上的刷新
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;
	unsigned char *buf, *vram = ctl->vram, *map = ctl->map, sid;
	struct SHEET *sht;//创建一个临时图层
	//如果refresh的范围超出了画面则修正,ctl中的xsize与ysize中包含的是vram的大小
	if (vx0 < 0) { vx0 = 0; }
	if (vy0 < 0) { vy0 = 0; }
	if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }
	if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }
	for( h = h0; h <= h1; h++)//从下往上,所有图层都描绘一遍
	{
		sht = ctl->sheets[h];//记录该层的图层信息
		buf = sht->buf;//记录图层上描绘图画的地址
		sid = sht - ctl->sheets0;
		//使用vx0~vy1,对比bx0~by1进行倒推
		bx0 = vx0 - sht->vx0;
		by0 = vy0 - sht->vy0;
		bx1 = vx1 - sht->vx0;
		by1 = vy1 - sht->vy0;
		if(bx0 < 0) {bx0 = 0;} //这里的bx0、1都是相对距离,即相对于sht->vx0,也就是刷新的部分=图层在画面上的的起始地址+相对于起始地址的距离
		if(by0 < 0) {by0 = 0;}
		if(bx1 > sht->bxsize) {bx1 = sht->bxsize;}
		if(by1 > sht->bysize) {by1 = sht->bysize;}
		if((sht->vx0 & 3) == 0){
			//四字节型
			i = (bx0 + 3) / 4;//bx0除以4(小数进位)
			i1 = bx1      / 4;//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 + bx2 + 0];
						}
						if (map[vy * ctl->xsize + vx + 1] == sid) {
							vram[vy * ctl->xsize + vx + 1] = buf[by * sht->bxsize + bx2 + 1];
						}
						if (map[vy * ctl->xsize + vx + 2] == sid) {
							vram[vy * ctl->xsize + vx + 2] = buf[by * sht->bxsize + bx2 + 2];
						}
						if (map[vy * ctl->xsize + vx + 3] == sid) {
							vram[vy * ctl->xsize + vx + 3] = buf[by * sht->bxsize + bx2 + 3];
						}
					}
				}
				for (bx += i1 * 4; 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{
			//1字节型
			for(by = by0; by < by1; by++)
			{
				vy =sht->vy0 + by;
				for (bx = bx0; bx < bx1; bx++)
				{
					vx = sht->vx0 + bx;//vy0 vx0代表图层在VRAM上画面的相对地址,用这个来让图层进行移动
					if (map[vy * ctl->xsize + vx] == sid) //按照map中的内容判断是否写入刷新
					{
						vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];//写入vram中
					}
				}	
			}
		}
	}
	return;
}

此时,在移动看看,就会发现块很多了!!!

继续,优化一下! 可能在qemu上,大家会发现图层的速度跟不上鼠标的。这是因为由于我们之前每移动一次所进行的绘图操作极其耗时,就有可能导致系统来不及处理FIFO中的鼠标移动数据。

那么我们接下里设置一些,新的变量,例如new_mx和new_my两个变量,我们先将移动后的坐标暂时保存起来,等待FIFO缓冲区为空时候,我们就进行描绘,这样子就可以解决上述出现的问题,且不会影响我们整体的速度,还能让我们的移动看起来更加丝滑。当然,窗口也是同理的:
主函数:

for(;;)   
	{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 if (512 <= i && i <= 767) { /* 鼠标数据 */
			    if (mouse_decode(&mdec, i - 512) != 0) {
    				/* 鼠标的移动 */
					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;
					}
					new_mx = mx;
					new_my = my;if(3 <= x && x < sht->bxsize - 3 && 3 <= y && y < 21){//标题栏
											//窗口移动模式
											mmx = mx;
											mmy = my;
											mmx2 = sht->vx0;
											new_wy = sht->vy0;
										}
										if(sht->bxsize -21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19){
											//点击X按钮
											if((sht->flags & 0x10) != 0){//是由应用程序生成的窗口吗?窗口的flags已经OR 0x10了
												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();
											}
										}
										break;
									}
								}
							}
				     	}else{
						    //处于窗口移动模式
					    	x = mx - mmx;//计算鼠标移动模式
					    	y = my - mmy;
							new_wx = (mmx2 + x + 2) & ~3;
							new_wy = new_wy + y;
						    mmy = my;//更新为移动后的位置,因为x只需要计算最后移动的位置处是否是4的倍数,其余的不用管先。
					    }
					}else{
						//没有按下左键
						mmx = -1;
						if (new_wx != 0x7fffffff) {
							sheet_slide(sht, new_wx, new_wy);	/* g固定图层位置 */
							new_wx = 0x7fffffff;
						}
					}
				}
			}
		}
	}
}

至此,运行看看!!至此,我们就可以看到,当鼠标停止后窗口也会跟着停止,而不是像之前那样子,可能鼠标停了图层还在继续移动!

二、命令行窗口优化

1.利用快捷键打开窗口

我们利用按下shift + F2按钮来打开新的命令行窗口,除此之外我们可能不仅仅只想要两个命令行窗口而已。我们需要的是能够打开足够多个命令行窗口供我们使用! 但是吧,总不好写出con[100]吧? 这样子会造成极大的浪费呀! 那么由于我们之前在图层里已经增加了tak等变量,因此啊,我们可以直接用图层进行分配命令行窗口即可,这样子就不要预先设定命令行窗口致使浪费或者不够的现象出现。用此种方法的话,只要我们的内存足够我们就可以运行多个命令行窗口了!
具体改动如下:

void HariMain(void)
{
	//进行声明
	struct BOOTINFO *binfo = ( struct BOOTINFO *) ADR_BOOTINFO;
	struct FIFO32 fifo, keycmd;
	struct SHTCTL *shtctl;//用于管理的
	int fifobuf[128], keycmd_buf[32];
	char s[40];
	int mx, my, i, new_mx = -1, new_my =0, new_wx = 0x7fffffff, new_wy = 0;
	unsigned int memtotal;
	struct MOUSE_DEC mdec;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;//设定了管理表位于内存的0x003c0000地址,预估今后不会用到
	unsigned char *buf_back, buf_mouse[256];//缓冲区,用于在其中描绘需要的图形
	struct SHEET *sht_back, *sht_mouse;//准备图层区
	struct TASK *task_a, *task;/* 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 (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);
				}}

struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct SHEET *sht = sheet_alloc(shtctl);
	unsigned char *buf = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
	struct TASK *task = task_alloc();
	int *cons_fifo = (int *) memman_alloc_4k(memman, 128 * 4);
	sheet_setbuf(sht, buf, 256, 165, -1); /* 无透明色*/
	make_window8(buf, 256, 165, "console-yuan-os", 0);
	make_textbox8(sht, 8, 28, 240, 128, COL8_000000);
	task->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12;
	task->tss.eip = (int) &console_task;
	task->tss.es = 1 * 8;
	task->tss.cs = 2 * 8;
	task->tss.ss = 1 * 8;
	task->tss.ds = 1 * 8;
	task->tss.fs = 1 * 8;
	task->tss.gs = 1 * 8;
	*((int *) (task->tss.esp + 4)) = (int) sht;
	*((int *) (task->tss.esp + 8)) = memtotal;
	task_run(task, 2, 2); /* level=2, priority=2 */
	sht->task = task;
	sht->flags |= 0x20;	/* 有光标 */
	fifo32_init(&task->fifo, 128, cons_fifo, task);
	return sht;
}

这里,我们创建了新的函数open_console用于创建一个新的命令行窗口,至此我们就可以利用图层和这个函数打开n个命令行窗口了,让我们试试看:

在这里插入图片描述

看,可以运行多个命令行窗口和应用程序喔!

2.关闭命令行窗口

关闭一个命令行窗口需要做以下几件事: 首先,就是将创建该窗口时所占用的内存空间全部释放出来;然后还需要释放窗口的图层结果和任务结构。

当然,目前我们没有保存命令行窗口使用的栈地址,因此我们需要用一个变量将这个值存储起来:

struct TASK
{
	int sel, flags; //sel用于存放GDT编号
	int level, priority; //设置优先级
	struct FIFO32 fifo;
	struct TSS32 tss;
	struct CONSOLE *cons;
	int ds_base, cons_stack;//数据段地址;栈的地址
};

并保存:

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;}

然后,让我们梳理一下大致的关闭操作。以下呢,我们将建立两个函数:close_constask 用于关闭命令行窗口任务,close_console 用于关闭该命令行窗口的图层。 我们先执行关闭图层的函数,然后再在该函数中调用关闭任务的函数,即可完成将命令行窗口的完全释放。

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
	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;
}

在close_constask中,我们必须先让任务休眠,以防止我们操作过程中,突然按下tab键切换了! 然后就可以安全释放内存,最后我们不能使用free这个对任务进行释放,而是一个将flags置为0,然后task_alloc就可以继续利用该闲置任务了。

接下来我们先写一个exit命令,用于在命令行直接退出!

由于我们的close_constask中有当前任务休眠这个功能,因此我们不能直接在exit这个函数里调用(不然可能之后无法执行了),因此我们可以向task_a(目前用于处理鼠标,键盘等中断数据 的处理)发送一个请求数据,请求task_a帮我们进行关闭任务!

void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, unsigned int memtotal)
{else if (strcmp(cmdline, "exit") == 0){
		cmd_exit(cons,fat);
	}}
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 (这里还要减去768,因为鼠标数据的范围是到768,避免混淆)*/
	io_sti();
	for (;;) {
		task_sleep(task);
	}
}

然后这里我们还需要修改一下主函数,以便支持传递过来的数据后我们可以调用命令进行关闭,以及利用鼠标进行点击关闭命令行窗口:
bootpack.c:主函数

//初始化的部分
	init_gdtidt();//初始化gdt、idt表
	init_pic();  //初始化pic控制器
	io_sti();
    fifo32_init(&fifo, 128, fifobuf, 0);//先传入0,禁止启动自动唤醒功能
	*((int *) 0x0fec) = (int) &fifo;for(;;){if(key_win->flags == 0 && key_win != 0){//输入窗口被关闭
				if(shtctl->top == 1){//当画面只剩鼠标和背景时候
					key_win = 0;
				}else{
					key_win = shtctl->sheets[shtctl->top - 1];
					keywin_on(key_win);
				}
			}if( s[0] != 0 && key_win != 0){//一般字符,退格键,回车键if(i == 256 + 0x0f && key_win != 0){//tab键if(i == 256 + 0x3b && key_shift != 0 && key_win != 0){//shift + F1强制关闭if (i == 256 + 0x3c && key_shift != 0) {//shift + F2 打开命令行
					if (key_win != 0) {
						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);
				}if(sht->bxsize -21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19){
											//点击X按钮
											if((sht->flags & 0x10) != 0){//是由应用程序生成的窗口吗?窗口的flags已经OR 0x10了
												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);//向命令行发送数据,以此调用命令行的exit功能
												io_sti();
											}
										}else if (768 <= i && i <= 1023){//命令行窗口关闭处理
				close_console(shtctl->sheets0 + (i - 768));
			}
		}
	}
}

console_task:

			if(i == 4){//鼠标点击关闭
				cmd_exit(&cons, fat);
			}

然后我们运行看看:
输入exit:
在这里插入图片描述
成功关闭:
在这里插入图片描述
再点击X按钮,也发现可以成功关闭!!

不过这里仍然有个小bug,就是当运行程序时候是关闭不了的。这个我们后续再继续解决吧!

3、命令

start

start是Windows中用于开启一个新的命令行窗口,并运行其中命令的,下面我们也来实现:
void cons_runcmd:

else if(strncmp(cmdline, "start", 6) == 0){
		cmd_start(cons,cmdline, memtotal);
	}
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;
}

在这里插入图片描述

ncst

ncst 是不打开新命令行窗口的start命令!

console:
首先,我们将没有窗口的命令设置位cons->sht = 0 。这样子我们就可以进行分类了,即有一部分命令是强依赖于命令行窗口的,比如dir命令等等:

void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, unsigned int memtotal)
{
	if (strcmp(cmdline, "mem") == 0 && cons->sht != 0) {
		cmd_mem(cons, memtotal);
	} else if (strcmp(cmdline, "cls") == 0 && cons->sht != 0) {
		cmd_cls(cons);
	} else if (strcmp(cmdline, "dir") == 0 || strcmp(cmdline, "ls") == 0 && cons->sht != 0) {
		cmd_dir(cons);
	} else if (strncmp(cmdline, "type ", 5) == 0 && cons->sht != 0) {
		cmd_type(cons, fat, cmdline);else if (strncmp(cmdline, "ncst ", 5) == 0) {
		cmd_ncst(cons, cmdline, memtotal);
	} else if (cmdline[0] != 0) {
		if (cmd_app(cons, fat, cmdline) == 0) {
			/*不是命令,不是应用程序,也不是空行*/
			cons_putstr0(cons, "Bad command.\n\n");
		}
	}
	return;
}

进一步的,我们模仿start命令,来写ncst:

void cmd_ncst(struct CONSOLE *cons, char *cmdline, int memtotal)
{
	struct TASK *task = open_constask(0, memtotal);
	struct FIFO32 *fifo = &task->fifo;
	int i;
	//将命令行输入的字符串逐个赋值到新的命令行窗口
	for (i = 5; cmdline[i] != 0; i++) {
		fifo32_put(fifo, cmdline[i] + 256);
	}
	fifo32_put(fifo, 10 + 256);	/* Enter */
	cons_newline(cons);
	return;
}

当cons->sht = 0 时,要禁用命令行窗口的所有字符显示等操作:

void cons_putchar(struct CONSOLE *cons, int chr, char move)
{for (;;) {
			if (cons->sht != 0) {
				putfonts8_asc_sht(cons->sht, cons->cur_x, cons->cur_y, COL8_FFFFFF, COL8_000000, " ", 1);
			}else { /*一般字符*/
		if (cons->sht != 0) {
			putfonts8_asc_sht(cons->sht, cons->cur_x, cons->cur_y, COL8_FFFFFF, COL8_000000, s, 1);
		}}
void cons_newline(struct CONSOLE *cons)
{else {
		/*滚动*/
		if(sheet != 0){
			for (y = 28; y < 28 + 112; y++) {}
	}
	cons->cur_x = 8;
	return;
}

接下来我们修改以下console_task。这里主要是为了当我们不显示命令行窗口时候,就禁用一些不必要的处理,并且当命令执行完毕后,立即结束命令行窗口。

if (sheet != 0) {
		cons.timer = timer_alloc();
		timer_init(cons.timer, &task->fifo, 1);
		timer_settime(cons.timer, 50);
	}
	else if (i == 10 + 256) {
					/*回车键*/
					/*将光标用空格擦除后换行 */
					cons_putchar(&cons, ' ', 0);
					cmdline[cons.cur_x / 8 - 2] = 0;
					cons_newline(&cons);
					cons_runcmd(cmdline, &cons, fat, memtotal); /*运行命令*/
					if (sheet == 0) {
						cmd_exit(&cons, fat);
					}
					/*显示提示符*/
					cons_putchar(&cons, '>', 1);
				} else {
					/*一般字符*/
					if (cons.cur_x < 240) {
						/*显示一个字符之后将光标后移一位*/
						cmdline[cons.cur_x / 8 - 2] = i - 256;
						cons_putchar(&cons, i - 256, 1);
					}
				}
			}
			/*重新显示光标*/
			if (sheet != 0) {
				if (cons.cur_c >= 0) {
					boxfill8(sheet->buf, sheet->bxsize, cons.cur_c, 
						cons.cur_x, cons.cur_y, cons.cur_x + 7, cons.cur_y + 15);
				}
				sheet_refresh(sheet, cons.cur_x, cons.cur_y, cons.cur_x + 8, cons.cur_y + 16);
			}		
		}
	}
}

cmd_exit这里我们也要改成能够无命令行窗口下退出:

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);
	if (cons->sht != 0) {
		timer_cancel(cons->timer);
	}
	memman_free_4k(memman, (int) fat, 4 * 2880);
	io_cli();
	if (cons->sht != 0) {
		fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768);	/* 有窗口的情况下,返回768~1023 */
	} else {
		fifo32_put(fifo, task - taskctl->tasks0 + 1024);	/* 无窗口的情况下返回1024~2023 */
	}	
	io_sti();
	for (;;) {
		task_sleep(task);
	}
}

最后,修改bootpack.c

struct TASK *open_constask(struct SHEET *sht, unsigned int memtotal)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct TASK *task = task_alloc();
	int *cons_fifo = (int *) memman_alloc_4k(memman, 128 * 4);
	task->cons_stack = memman_alloc_4k(memman, 64 * 1024);
	task->tss.esp = task->cons_stack + 64 * 1024 - 12;
	task->tss.eip = (int) &console_task;
	task->tss.es = 1 * 8;
	task->tss.cs = 2 * 8;
	task->tss.ss = 1 * 8;
	task->tss.ds = 1 * 8;
	task->tss.fs = 1 * 8;
	task->tss.gs = 1 * 8;
	*((int *) (task->tss.esp + 4)) = (int) sht;
	*((int *) (task->tss.esp + 8)) = memtotal;
	task_run(task, 2, 2); /* level=2, priority=2 */
	fifo32_init(&task->fifo, 128, cons_fifo, task);
	return task;
}

struct SHEET *open_console(struct SHTCTL *shtctl, unsigned int memtotal)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct SHEET *sht = sheet_alloc(shtctl);
	unsigned char *buf = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
	sheet_setbuf(sht, buf, 256, 165, -1); /* 透明色 */
	make_window8(buf, 256, 165, "console", 0);
	make_textbox8(sht, 8, 28, 240, 128, COL8_000000);
	sht->task = open_constask(sht, memtotal);
	sht->flags |= 0x20;	/* 显示光标 */
	return sht;
}

主函数:

else if (768 <= i && i <= 1023){//命令行窗口关闭处理
				close_console(shtctl->sheets0 + (i - 768));
			}else if (1024 <= i && i <= 2023) {//无命令行窗口下,直接关闭该任务
				close_constask(taskctl->tasks0 + (i - 1024));
			}

注意,我们还需要再bootpack.h里添加:

extern struct TASKCTL *taskctl;

然后,运行看看:
在这里插入图片描述
喔喔,成功了!不过这里我们有点点小bug,就是点击窗口的X是无法进行关闭的。。。。让我们看看怎么修改:
主函数:

				if(i == 256 + 0x3b && key_shift != 0 && key_win != 0){//shift + F1强制关闭
				    task = key_win->task; //获取当前处于的图层
					if(task != 0 && task->tss.ss0 != 0)
					{
					    cons_putstr0(task->cons, "\nBreak(key):\n");
					    io_cli();//不能再改变寄存器的值时候进行切换
						task->tss.eax = (int) &(task->tss.esp0);
						task->tss.eip = (int) asm_end_app;
						io_sti();
						task_run(task, -1, 0);//为了确实执行结束处理,需要把处于休眠状态的这个任务唤醒
					}
				}if(sht->bxsize -21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19){
											//点击X按钮
		if((sht->flags & 0x10) != 0){//是由应用程序生成的窗口吗?窗口的flags已经OR 0x10了
					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();
					task_run(task, -1, 0);
					}

这里,是由于如果任务一直处于休眠状态的话那么结束任务的处理是不会执行的!!

但是有可能有人会问,为什么之前又可以?当然,这是之前就存在的小bug了!我们之前能关闭完全是由于我们处在当前任务窗口时,会隔一定时间就产生光标的中断,因此就会被自动唤醒的!


总结

以上,就是今天有关于命令行窗口的所有内容。明天我们将继续LDT与库的制作了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值