第17天:命令行窗口

17.1、闲置任务

上一章共有四个任务,可供切换,但是,如果现在只有任务A(bootpack),等任务A闲置,它就会休眠,休眠之后每个level都没有任务,但是task_switchsub还是会返回最后一个level,最后一个level的now等于0,但0并没有任务,这一些列操作将导致错误。
所以,这里将引入一个闲置任务,放在最后一个level里,如果只有任务A,并且任务A休眠,就会调用这个闲置任务,执行hlt指令。

// mtask.c
void task_idle(void)
{
	for (;;) {
		io_hlt();
	}
}
// mtask.c
struct TASK *task_init(struct MEMMAN *memman)
{
	// -
	idle = task_alloc();
	idle->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024;
	idle->tss.eip = (int) &task_idle;
	idle->tss.es = 1 * 8;
	idle->tss.cs = 2 * 8;
	idle->tss.ss = 1 * 8;
	idle->tss.ds = 1 * 8;
	idle->tss.fs = 1 * 8;
	idle->tss.gs = 1 * 8;
	task_run(idle, MAX_TASKLEVELS - 1, 1);

	return task;
}

17.2、创建命令行窗口

void HariMain(void){

	// -
	/* sht_cons */
	sht_cons = sheet_alloc(shtctl);
	buf_cons = (unsigned char *) memman_alloc_4k(memman, 256 * 165);
	sheet_setbuf(sht_cons, buf_cons, 256, 165, -1); /* 无透明色 */
	make_window8(buf_cons, 256, 165, "console", 0);// 0:title是灰色
	make_textbox8(sht_cons, 8, 28, 240, 128, COL8_000000);
	task_cons = task_alloc();
	task_cons->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 8;
	task_cons->tss.eip = (int) &console_task;
	task_cons->tss.es = 1 * 8;
	task_cons->tss.cs = 2 * 8;
	task_cons->tss.ss = 1 * 8;
	task_cons->tss.ds = 1 * 8;
	task_cons->tss.fs = 1 * 8;
	task_cons->tss.gs = 1 * 8;
	*((int *) (task_cons->tss.esp + 4)) = (int) sht_cons;
	task_run(task_cons, 2, 2); /* level=2, priority=2 */

	// -
	
	sheet_slide(sht_back,  0,  0);
	sheet_slide(sht_cons, 32,  4);
	sheet_slide(sht_win,  64, 56);
	sheet_slide(sht_mouse, mx, my);
	sheet_updown(sht_back,  0);
	sheet_updown(sht_cons,  1);
	sheet_updown(sht_win,   2);
	sheet_updown(sht_mouse, 3);

	// -

}

void console_task(struct SHEET *sheet)
{
	struct FIFO32 fifo;
	struct TIMER *timer;
	struct TASK *task = task_now();

	int i, fifobuf[128], cursor_x = 8, cursor_c = COL8_000000;
	fifo32_init(&fifo, 128, fifobuf, task);

	timer = timer_alloc();
	timer_init(timer, &fifo, 1);
	timer_settime(timer, 50);

	for (;;) {
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			task_sleep(task);
			io_sti();
		} else {
			i = fifo32_get(&fifo);
			io_sti();
			if (i <= 1) { /* 光标定时器 */
				if (i != 0) {
					timer_init(timer, &fifo, 0);
					cursor_c = COL8_FFFFFF;
				} else {
					timer_init(timer, &fifo, 1);
					cursor_c = COL8_000000;
				}
				timer_settime(timer, 50);
				boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
				sheet_refresh(sheet, cursor_x, 28, cursor_x + 8, 44);
			}
		}
	}
}

17.3、切换窗口输入字符

17.3.1、切换窗口

HariMain函数:
按下tab键切换焦点

if (i == 256 + 0x0f) { /* Tab */
		if (key_to == 0) {
				key_to = 1;
				make_wtitle8(buf_win,  sht_win->bxsize,  "task_a",  0);
				make_wtitle8(buf_cons, sht_cons->bxsize, "console", 1);
		} else {
				key_to = 0;
				make_wtitle8(buf_win,  sht_win->bxsize,  "task_a",  1);
				make_wtitle8(buf_cons, sht_cons->bxsize, "console", 0);
		}
				sheet_refresh(sht_win,  0, 0, sht_win->bxsize,  21);
				sheet_refresh(sht_cons, 0, 0, sht_cons->bxsize, 21);
	}
		/* 重新显示光标 */
		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);

这里引入了key_to变量,如果等于0切换到任务A,如果等于1切换到任务B。

17.3.2、追加内容,删除内容:

if (i < 0x54 + 256 && keytable[i - 256] != 0) { /* 一般字符(按一下键盘) */
    if (key_to == 0) {	/* 发送给任务A */
        if (cursor_x < 128) {
            /* 显示一个字符后将光标后移一位 */
            s[0] = keytable[i - 256];
            s[1] = 0;
            putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
            cursor_x += 8;
        }
    } else {	/* 发送给命令行窗口缓冲区 */
        fifo32_put(&task_cons->fifo, keytable[i - 256] + 256);
    }
}
if (i == 256 + 0x0e) {	/* 退格键 */
    if (key_to == 0) {	/* 发送给任务A */
        if (cursor_x > 8) {
            /* 用空格擦除光标闪烁的黑条 */
            putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
            cursor_x -= 8;
        }
    } else {	/* 发送给命令行窗口的缓冲区 */
        fifo32_put(&task_cons->fifo, 8 + 256);
    }
}

以上代码说明任务A,和命令行窗口处理数据的方式:
任务A是直接处理显示在屏幕上。
命令行窗口是先把操作的按键发送的缓冲区,然后命令行任务的处理函数再取出指令,进行操作:

void console_task(struct SHEET *sheet)
{
	struct TIMER *timer;
	struct TASK *task = task_now();
	int i, fifobuf[128], cursor_x = 16, cursor_c = COL8_000000;
	char s[2];

	fifo32_init(&task->fifo, 128, fifobuf, task);
	timer = timer_alloc();
	timer_init(timer, &task->fifo, 1);
	timer_settime(timer, 50);

	/* 绘制一个“>”符号 */
	putfonts8_asc_sht(sheet, 8, 28, COL8_FFFFFF, COL8_000000, ">", 1);

	// for循环取指令
	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); /* 置0 */
					cursor_c = COL8_FFFFFF;
				} else {
					timer_init(timer, &task->fifo, 1); /* 置1 */
					cursor_c = COL8_000000;
				}
				timer_settime(timer, 50);
			}
			if (256 <= i && i <= 511) { /* 如果是键盘数据 */
				/* 退格键 ,第一次发现,退格键时钟响应怎么不是0xe+256了?,是因为这个任务里作者指定的是8*/
				if (i == 8 + 256) {
					if (cursor_x > 16) {
						/* 用空格擦除后面闪烁的光标 */
						putfonts8_asc_sht(sheet, cursor_x, 28, COL8_FFFFFF, COL8_000000, " ", 1);
						cursor_x -= 8;
					}
				} else {
					/* 一般文字 */
					if (cursor_x < 240) {
						/* 显示一个字符,光标后移一位 */
						s[0] = i - 256;
						s[1] = 0;
						putfonts8_asc_sht(sheet, cursor_x, 28, COL8_FFFFFF, COL8_000000, s, 1);
						cursor_x += 8;
					}
				}
			}
			/* 重新显示光标 */
			boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
			sheet_refresh(sheet, cursor_x, 28, cursor_x + 8, 44);
		}
	}
}

17.4、组合键

17.4.1、符号输入

上面都是输入单个按键,现在来实现组合键:

void HariMain(void){
	static char keytable0[0x80] = {
		0,   0,   '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0,   0,
		'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0,   0,   'A', 'S',
		'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', ':', 0,   0,   ']', 'Z', 'X', 'C', 'V',
		'B', 'N', 'M', ',', '.', '/', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
		'2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   0x5c, 0,  0,   0,   0,   0,   0,   0,   0,   0,   0x5c, 0,  0
	};
	static char keytable1[0x80] = {
		0,   0,   '!', 0x22, '#', '$', '%', '&', 0x27, '(', ')', '~', '=', '~', 0,   0,
		'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '`', '{', 0,   0,   'A', 'S',
		'D', 'F', 'G', 'H', 'J', 'K', 'L', '+', '*', 0,   0,   '}', 'Z', 'X', 'C', 'V',
		'B', 'N', 'M', '<', '>', '?', 0,   '*', 0,   ' ', 0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   '7', '8', '9', '-', '4', '5', '6', '+', '1',
		'2', '3', '0', '.', 0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
		0,   0,   0,   '_', 0,   0,   0,   0,   0,   0,   0,   0,   0,   '|', 0,   0
	};
	int key_to = 0, key_shift = 0;

	for (;;) {
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			task_sleep(task_a);
			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);
				if (i < 0x80 + 256) { /* 是否在0x80个字符内 */
					if (key_shift == 0) { // 没有按下shift
						s[0] = keytable0[i - 256];
					} else {
						s[0] = keytable1[i - 256];
					}
				} else {
					s[0] = 0;
				}
				if (s[0] != 0) { /* 通常文字 */
					if (key_to == 0) {	/* 发个任务A */
						if (cursor_x < 128) {
							/* 显示一个字符后光标后移 */
							s[1] = 0;
							putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
							cursor_x += 8;
						}
					} else {	/* 发给命令行窗口 */
						fifo32_put(&task_cons->fifo, s[0] + 256);
					}
				}
				if (i == 256 + 0x0e) {	/* 退格键 */
					if (key_to == 0) {	/* 发个任务A */
						if (cursor_x > 8) {
							/* 用空格覆盖光标颜色 */
							putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
							cursor_x -= 8;
						}
					} else {	/* 发给命令行窗口 */
						fifo32_put(&task_cons->fifo, 8 + 256);
					}
				}
				if (i == 256 + 0x0f) {	/* Tab */
					if (key_to == 0) {
						key_to = 1;
						make_wtitle8(buf_win,  sht_win->bxsize,  "task_a",  0);
						make_wtitle8(buf_cons, sht_cons->bxsize, "console", 1);
					} else {
						key_to = 0;
						make_wtitle8(buf_win,  sht_win->bxsize,  "task_a",  1);
						make_wtitle8(buf_cons, sht_cons->bxsize, "console", 0);
					}
					sheet_refresh(sht_win,  0, 0, sht_win->bxsize,  21);
					sheet_refresh(sht_cons, 0, 0, sht_cons->bxsize, 21);
				}
				if (i == 256 + 0x2a) {	/* 左shift ON */
					key_shift |= 1;
				}
				if (i == 256 + 0x36) {	/* 右shift ON */
					key_shift |= 2;
				}
				if (i == 256 + 0xaa) {	/* 左shift OFF */
					key_shift &= ~1;
				}
				if (i == 256 + 0xb6) {	/* 右shift OFF */
					key_shift &= ~2;
				}
				/* 光标显示 */
				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) { /* 鼠标数据 */
				if (mouse_decode(&mdec, i - 512) != 0) {
					
				}
			} else if (i <= 1) { /* 光标定时器 */
				
			}
		}
	}
}

这里设置了 key_shift 变量,用来标识shift是否按下。
按下左shift 或 右shift 为1:在 keytable1数组匹配对应字符
都没按下为0 : 在keytable0数组匹配对应字符

这里的键盘顺序不太对的样子,可能是日本的键盘布局吧,英文键盘布局:

static char keytable0[0x54] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0, 0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '[', ']', 0, 0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '\'', '`', 0, '\\', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.' };
static char keytable1[0x54] = {
0, 0, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 0, 0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', 0, 0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', '"', '~', 0, '|', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.' };

17.4.2、大小写

按下Shift或者CapsLock就是大写。

CapsLockShift结果
OFFOFF小写
OFFON大写
ONOFF大写
ONON小写

关于CapsLock状态,之前在asmhead.nas文件中获取过键盘灯状态,当按下CapsLock为ON时,键盘灯是亮的。

AH=02,中断AL返回值的每一位为1时:
位7—INS开状态
位6—CAPS LOCK开状态
位5—NUM LOCK开状态
位4—SCROLL LOCK开状态
位3—ALT键按下
位2—CTRL键按下
位1—左SHIFT键按下
位0—右SHIFT键按下

返回值位4就是CapsLock状态,可以通过binfo取到。

// HariMain节选
int key_to = 0, key_shift = 0, key_leds = (binfo->leds >> 4) & 7;
if ('A' <= s[0] && s[0] <= 'Z') {	/* 输入字符为英文字符 */
		if (((key_leds & 4) == 0 && key_shift == 0) || ((key_leds & 4) != 0 && key_shift != 0)) {
			s[0] += 0x20;	/* 大写转换成小写 */
		}
}

17.5、各种锁定键的支持

0x3a:CapsLock
0x45:NumLock
0x46:ScrollLock

如果键盘数据为这几个值,把binfo -> leds对应位改成 1 就好了。
这样操作后,实现功能了,可以LED却不亮。
控制键盘LED:
LED
代码:

// HariMain节选

/*为了避免和键盘当前状态冲突,在一开始先进行设置*/
fifo32_put(&keycmd, KEYCMD_LED);
fifo32_put(&keycmd, key_leds);
	
for (;;) {
		if (fifo32_status(&keycmd) > 0 && keycmd_wait < 0) {
			/*如果存在向键盘控制器发送的数据,则发送它 */
			keycmd_wait = fifo32_get(&keycmd);
			wait_KBC_sendready();
			io_out8(PORT_KEYDAT, keycmd_wait);
		}
		io_cli();
		if (fifo32_status(&fifo) == 0) {
			task_sleep(task_a);
			io_sti();
		} else {
// HariMain节选
if (i == 256 + 0x3a) { /* CapsLock */
	key_leds ^= 4;
	fifo32_put(&keycmd, KEYCMD_LED);
	fifo32_put(&keycmd, key_leds);
}
if (i == 256 + 0x45) { /* NumLock */
	key_leds ^= 2;
	fifo32_put(&keycmd, KEYCMD_LED);
	fifo32_put(&keycmd, key_leds);
}
if (i == 256 + 0x46) { /* ScrollLock */
	key_leds ^= 1;
	fifo32_put(&keycmd, KEYCMD_LED);
	fifo32_put(&keycmd, key_leds);
}
if (i == 256 + 0xfa) { /*键盘成功接收到数据*/
	keycmd_wait = -1;
}
if (i == 256 + 0xfe) { /*键盘没有成功接收到数据*/
	wait_KBC_sendready();
	io_out8(PORT_KEYDAT, keycmd_wait);
}

keycmd_wait:控制是否向键盘发送数据(-1就发)
过程:
1、先把 0xed目前键盘灯状态(binfo->leds) 写入keycmd缓冲区
2、如果缓冲区有数据,keycmd_wait=-1。
2.1、等到键盘控制电路可以接收数据,就从缓冲区取出0xed发送给键盘(0x60是给键盘发送数据)。
2.2、如果键盘返回0xfa,则发送成功,keycmd_wait设为-1,接着发送 目前键盘灯状态(binfo->leds) 数据,来控制键盘灯是亮是灭。
2.3、如果键盘返回0xfe,发送不成功,则从 2.1 重试。

之后如果按下CapsLock等按键,都会改变 key_leds(保存着目前键盘灯状态的值),再次发送给重复以上过程,从而改变键盘灯亮灭。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值