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就是大写。
CapsLock | Shift | 结果 |
---|---|---|
OFF | OFF | 小写 |
OFF | ON | 大写 |
ON | OFF | 大写 |
ON | ON | 小写 |
关于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:
代码:
// 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(保存着目前键盘灯状态的值),再次发送给重复以上过程,从而改变键盘灯亮灭。