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