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

18.1 控制光标闪烁(1

按照使用window系统的习惯,只有当前在接受键盘输入的窗口才会有光标闪烁,其他窗口不显示。而在整个函数中,只有TASK_A和命令行窗口才有显示光标的功能。而TASK_A的显示光标的功能在 HariMain函数中,而命令行在console_task函数。所以,需要在HariMain中接收键盘数据中进行分类讨论:1、切换到TASK_A,需要正常显示光标;2、切换到命令行窗口时,TASK_A的光标不显示。思路为:切换到命令行窗口后,可将 cursor_c置为-1,并在所有显示TASK_A光标处增加一个判断(当 cursor_c>=0则显示,否则不显示
void HariMain(void)
{
	(中略)
	for (;;) {
		(中略)
		if (fifo32_status(&fifo) == 0) {
			task_sleep(task_a);
			io_sti();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (256 <= i && i <= 511) {/* 键盘数据 */
				(中略)
				if(i == 256 + 0x0f){/* Tab键*/
					if(key_to == 0){/*切换到命令窗口,其光标显示是在函数console_task中*/
						key_to = 1;
						make_wtitle8(buf_win,  sht_win->bxsize,  "task_a",  0);
						make_wtitle8(buf_cons, sht_cons->bxsize, "console", 1);
						cursor_c = -1;/*所以win窗口不显示光标*/
						boxfill8(sht_win->buf, sht_win->bxsize, COL8_FFFFFF, cursor_x, 28, cursor_x + 7, 43);/*用白色直接覆盖光标位置*/
					}else{/*切换到TASK_A窗口,则需要重新显示光标*/
						key_to = 0;
						make_wtitle8(buf_win,  sht_win->bxsize,  "task_a",  1);
						make_wtitle8(buf_cons, sht_cons->bxsize, "console", 0);
						cursor_c = COL8_000000; /*显示光标*/
					}
					sheet_refresh(sht_win,  0, 0, sht_win->bxsize,  21);
					sheet_refresh(sht_cons, 0, 0, sht_cons->bxsize, 21);
				}
				(中略)
				if(cursor_c >= 0)
					boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
				sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
			} else if (512 <= i && i <= 767) {/* 鼠标数据 */
				(中略)
			} else if (i <= 1) {/* 光标用计时器 */
				if( i != 0){
					timer_init(timer, &fifo, 0); /* 设定为0 */
					if(cursor_c >= 0)
						cursor_c = COL8_000000;
				}else{
					timer_init(timer, &fifo, 1); /* 设定为1 */
					if(cursor_c >= 0)
						cursor_c = COL8_FFFFFF;
				}
				timer_settime(timer, 50);
				if(cursor_c >= 0){
					boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
					sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
				}
			} 
		}
	}
}

18.2 控制光标闪烁(2

在18.1中已经实现了对TASK_A窗口光标是否需要闪烁的功能,现在实现命令窗口的光标问题。其中有个问题, HariMain函数该如何通知console_task函数开始或停止光标闪烁呢?FIFO中还有很多位置未使用,0/1是用来显示光标,可以定义2为光标开始闪烁和3为停止闪烁。那么什么时候向FIFO中写入数据呢?需要在HariMain函数的TAB键判断中增加该功能。当key_to=0,表示切换到命令窗口,增加一行:fifo32_put(&task_cons->fifo, 2); /*命令行窗口光标ON */;当key_to=1,表示切换到TASK_A 窗口,增加一行:fifo32_put(&task_cons->fifo, 3); /*命令行窗口光标 OFF */。
下面就是修改console_task函数,思路与上一节思路相似,增加两个取值(2和3)判断,废话不多说,直接上代码。
void console_task(struct SHEET *sheet){
	struct TIMER *timer;
	struct TASK *task = task_now();
	int i, fifobuf[128],  cursor_x = 16, cursor_c = -1;/*    这里    */
	char s[2];
	(中略)
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			i = fifo32_get(&task->fifo);
			io_sti();
			if(i <= 1){
				if(i != 0){
					timer_init(timer, &task->fifo, 0);
					if(cursor_c >= 0)/*    这里    */
						cursor_c = COL8_FFFFFF;
				}else{
					timer_init(timer, &task->fifo, 1);
					if(cursor_c >= 0)/*    这里    */
						cursor_c = COL8_000000;
				}
				timer_settime(timer, 50);/* 重新设定计时器 */
			}
			if(i == 2){/*    这里    */
				cursor_c = COL8_FFFFFF;
			}
			if(i == 3){/*    这里    */
				cursor_c = -1;
				boxfill8(sheet->buf, sheet->bxsize, COL8_000000, cursor_x, 28, cursor_x + 7, 43);/*用黑色直接覆盖光标位置*/
			}
				
			if(256 <= i && i <= 511){ /*键盘数据*/
				(中略)
			}
			if(cursor_c >= 0){/*    这里    */
				boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, 28,  cursor_x+7, 43);
			}
			sheet_refresh(sheet, cursor_x, 28, cursor_x + 8, 44);
		}
	}
}

18.3 对回车键的支持

因为TASK_A窗口只有一行,所以该功能只能在命令窗口实现。而键盘中ENTER键对应的ASCII码为10,所以在 HariMain函数中的切换到命令窗口时写入数值‘10+256’(该处代码就不展示了),并在 console_task函数增加对该值的判断。思路:当读到数值‘10+256’时,将光标向下移动一行(即 cursor_y +16),并将cursor_x置为16(前面需要一个‘>’字符的位置),以达到实现换行的功能。console_task中 cursor_y 表示光标所在行,所以该函数中进行光标或字符打印的均需要进行修改。
void console_task(struct SHEET *sheet){
	struct TIMER *timer;
	struct TASK *task = task_now();
	int i, fifobuf[128],  cursor_x = 16, cursor_y = 28, cursor_c = -1;/*这里*/
	char s[2];
	(中略)
	/*显示提示符*/
	putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);/*这里*/
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			i = fifo32_get(&task->fifo);
			io_sti();
			(中略)
			if(i == 3){
				cursor_c = -1;
				boxfill8(sheet->buf, sheet->bxsize, COL8_000000, cursor_x, cursor_y, cursor_x + 7, cursor_y+15);/*用黑色直接覆盖光标位置*/    /*这里*/
			}
				
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					if(cursor_x > 16){/*用空白擦除光标后将光标前移一位*/
						putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);    /*这里*/
						cursor_x -= 8;
					}
				}else if(i == 10+256){/*回车键*//*从这里开始*/
					if(cursor_y < 28+112){
						putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
						cursor_y += 16;
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
						cursor_x = 16;
					}/*到这里结束*/
				}else{/*一般字符*/
					if(cursor_x < 240){/*显示一个字符之后将光标后移一位 */
						s[0] = i - 256;
						s[1] = 0;
						putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);/*这里*/
						cursor_x += 8;
					}
				}
			}
			if(cursor_c >= 0){
				boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, cursor_y,  cursor_x+7, cursor_y+15);/*这里*/
			}
			sheet_refresh(sheet, cursor_x, cursor_y, cursor_x+8, cursor_y+16);/*这里*/
		}
	}
}

18.4 对窗口滚动的支持

由于命令窗口的高度为240*128,可写入8行,无法输入第9行。为解决这个问题,可将命令窗口中第1行数据改成第2行的,以此类推,第7行数据改成第8行的,并将第8行数据置空(置空也能看成将该行全部涂黑)。
void console_task(struct SHEET *sheet){
	(中略)
	int x, y;
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			i = fifo32_get(&task->fifo);
			io_sti();
			(中略)
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					(中略)
				}else if(i == 10+256){/*回车键*/
					putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
					if(cursor_y < 28+112){/*未到第9行*/
						cursor_y += 16;/*换行*/
					}else{/*滚动*/
						for(y = 28; y < 28+112; y++){//字符覆盖
							for(x = 8;x < 8+240; x++){
								sheet->buf[x+y*sheet->bxsize] = sheet->buf[x+(y+16)*sheet->bxsize];
							}
						}
						for(y = 28+112; y < 28+128; y++){//新增的一行涂黑
							for(x = 8;x < 8+240; x++){
								sheet->buf[x+y*sheet->bxsize] = COL8_000000;
							}
						}
						sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);
					}
					putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
					cursor_x = 16;
				}else{/*一般字符*/
					if(cursor_x < 240){/*显示一个字符之后将光标后移一位 */
						(中略)
					}
				}
			}
			(中略)
		}
	}
}

18.5 mem命令 - 打印内存信息

实现输入mem命令,出现内存总量和剩余内存的信息。首先,在写入字符时还需要另外存放至cmdline数组,以便在按下回车时判断cmdline数组中的字符串是否为“mem”,如果是则进行输出内存总量和剩余内存的信息,否则输出“Bad command.”。另外,将换行功能单独写成函数 cons_newline,与18.4节中的部分代码一样,这里就不展示了。console_task函数的输入参数增加了,需要在函数HariMain中设置sht_cons处增加一行代码:*((int *) (task_cons->tss.esp + 8)) = memtotal;可将HariMain的memtotal值传到console_task函数中。
void console_task(struct SHEET *sheet, unsigned int memtotal){
	(中略)
	char s[30], cmdline[30];
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	(中略)
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			(中略)				
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					(中略)
				}else if(i == 10+256){/*回车键*/
					putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);/*空白擦除光标*/
/*从这里开始*/		cmdline[cursor_x / 8 - 2] = 0;//-2的原因是cursor_x初始值为16
					cursor_y = cons_newline(cursor_y, sheet);//换行
					if(cmdline[0] == 'm' && cmdline[1] == 'e' && cmdline[2] == 'm' && cmdline[3] == 0){
						sprintf(s, "total %dMB", memtotal/(1024*1024));
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
						cursor_y = cons_newline(cursor_y, sheet);
						sprintf(s, "free %dKB", memman_total(memman)/1024);
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
						cursor_y = cons_newline(cursor_y, sheet);
						cursor_y = cons_newline(cursor_y, sheet);
					}else if(cmdline[0] != 0){/*不是命令,也不是空行 */
						putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "Bad command.", 12);
						cursor_y = cons_newline(cursor_y, sheet);
						cursor_y = cons_newline(cursor_y, sheet);
					}
/*到这里结束*/		putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
					cursor_x = 16;
				}else{/*一般字符*/
					if(cursor_x < 240){/*显示一个字符之后将光标后移一位 */
						s[0] = i - 256;
						s[1] = 0;
						cmdline[cursor_x / 8 - 2] = i - 256;/*这里*/
						putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
						cursor_x += 8;
					}
				}
			}
			if(cursor_c >= 0){
				boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, cursor_y,  cursor_x+7, cursor_y+15);
			}
			sheet_refresh(sheet, cursor_x, cursor_y, cursor_x+8, cursor_y+16);
		}
	}
}

18.6 cls命令 - 清屏

cls命令为清屏,就是将整个命令窗口全部涂黑,并刷新窗口。代码如下:
void console_task(struct SHEET *sheet, unsigned int memtotal){
	(中略)
	int x, y;/*这里*/
	(中略)
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			i = fifo32_get(&task->fifo);
			io_sti();
			(中略)
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					(中略)
				}else if(i == 10+256){/*回车键*/
					putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);/*空白擦除光标*/
					cmdline[cursor_x / 8 - 2] = 0;//-2的原因是cursor_x初始值为16
					cursor_y = cons_newline(cursor_y, sheet);
					if(strcmp(cmdline, "mem") == 0){
						(中略)
/*从这里开始*/		}else if(strcmp(cmdline, "cls") == 0){/*将整个命令窗口全部涂黑*/
						for(y = 28; y < 28+112; y++){//字符覆盖
							for(x = 8;x < 8+240; x++){
								sheet->buf[x+y*sheet->bxsize] = COL8_000000;
							}
						}
						sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);//刷新命令窗口
/*到这里结束*/			cursor_y = 28;
					}else if(cmdline[0] != 0){/*不是命令,也不是空行 */
						(中略)
					}
					putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
					cursor_x = 16;
				}else{/*一般字符*/
					(中略)
				}
			}
			(中略)
		}
	}
}

18.7 dir命令 - 打印文件信息

根据磁盘映像的内容,看起来是以 32 个字节为单位循环的,这 32 个字节的结构如下。
struct FILEINFO {
        unsigned char name[8], ext[3], type;
        char reserve[10];
        unsigned short time, date, clustno;
        unsigned int size;
};//该结构体需要f放到bootpack.c函数中
name:如果文件名的第一个字节为 0xe5,代表这个文件已经被删除了;文件名第一个字节为 0x00 ,代表这一段不包含任何文件名信息。从磁盘映像的 0x004200就开始存放文件 haribote.sys 了,因此文件信息最多可以存放 224 个。
ext扩展名,和文件名一样,不足3个字节时用空格补足,如果文件没有扩展名,则这3个字节都用空格补足。
type:文件的属性信息,一般的文件不是0x20就是0x00。
        0x01……只读文件(不可写入)         0x02……隐藏文件         0x04…… 系统文件
        0x08……非文件信息(比如磁盘名称等)         0x10…… 目录
time, date:存放时间和日期
clustno 代表这个文件的内容从磁盘上的哪个扇区开始存放
size:存放的文件大小
下面仅展示console_task函数中增加‘dir’命令的代码。
if(strcmp(cmdline, "dir") == 0){/*打印已创建的文件信息*/
	for(x = 0; x < 224; x++){//文件信息最多可以存放224个
		/*如果文件名的第一个字节为0xe5,代表这个文件已经被删除了
		  文件名第一个字节为0x00,代表这一段不包含任何文件名信息*/
		if(finfo[x].name[0] == 0x00)break;
		if(finfo[x].name[0] != 0xe5){
		    if((finfo[x].type&0x18) == 0){//排除非文件信息和目录
				sprintf(s, "filename.ext   %7d", finfo[x].size);
				for(y = 0; y < 8; y++)
					s[y] = finfo[x].name[y];
				s[ 9] = finfo[x].ext[0];
				s[10] = finfo[x].ext[1];
				s[11] = finfo[x].ext[2];
				putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
				cursor_y = cons_newline(cursor_y, sheet);
			}
		}
	}
	cursor_y = cons_newline(cursor_y, sheet);
}
  • 38
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值