Day17
一些感想和问题
闲置任务
闲置任务是为了保证所有前台的任务都休眠的时候,系统不至于无法切换到一个任务而出错。把它注册到最下层即可。(Windows下也有system idle process)
void task_idle(void)
{
for(;;)
{
io_hlt();
}
}
一个bug
在控制台输入的时候,输入第一个字符,虽然已经释放了,但是就像是一直按着一样,蹦出来一串相同的字符。而且后序按什么键都不起效果。
经过检查,发现是中文输入法开了)虽然不知道中文输入法会和这个虚拟机、或者操作系统起什么冲突,但是还是挺奇怪的。。
目前创建的是命令行的第一步:支持各种符号的输入
闲置任务
harib14a
只需要在init的时候,把这个idle放进去就好了。
创建命令行窗口
harib14b
先画出来一个窗口,并且让它目前还不可以接收输入的字符吧
窗口创建:
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);
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); // 下次置0
cursor_c = COL8_FFFFFF;
}
else
{
timer_init(timer, &fifo, 1); // 下次置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);
}
}
}
}
因为console需要休眠自己,所以在最开始通过task_now获得了自己的地址。
目前的话还不能输入什么文字,因为还没切换到这个任务来。
切换输入窗口
harib14c
绘制窗口就省略了,看一下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);
}
对应tab的话会绘制不同的窗口(对应的窗口会高亮,另一个则会变暗)
实现字符输入
harib14d
因为task基本上都会用到IO,所以把FIFO也放到task里了
struct TASK
{
int sel, flags; // sel代表GDT的编号
int level, priority;
struct FIFO32 fifo;
struct TSS32 tss;
};
然后再Harimain和console里,都要进行对应的修改
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 < 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);
}
}
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不同,往console或者是taskA的FIFO发字符
console的键盘处理部分的话,因为和之前的HariMain挺相似的,所以就不再写了。
符号的输入
harib14e
输入符号的话,就要处理shift键。所以需要两张表,一张对应没按shift,一张对应按了shift。不过因为作者是日式键盘,所以有些地方好像对应不太上。之后再修改好了。
shift的状态对应不同的输出
if(key_shift == 0)
{
s[0] = keytable0[i - 256];
}
else
{
s[0] = keytable1[i - 256];
}
进入和退出状态:
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;
}
这就处理好了shift,接下来就可以进行大小写了
大写字母与小写字母输入
harib14f
切换大小写,既可以通过按shift,也可以通过capslock。锁定键状态储存在了binfo->leds中
binfo->leds的第4位:ScrollLock状态
binfo->leds的第5位:NumLock状态
binfo->leds的第6位:CapsLock状态
然后判断是否转变为小写
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; // 将大写字母转换为小写字母
}
}
接下来是锁定键
对各种锁定键的支持
harib14g
按键编码其实很熟悉,但是因为一般的锁定键上面都有灯,要让等亮灭的话,需要进行额外的操作。
简而言之,等待状态寄存器变为0之后,要先向0x0060发送0xed(说明要设置LED)。再等待状态寄存器变为0之后,发送1字节的数据。如果返回为0xfa,说明成功发送;如果返回0xfe,需要重新发送。
于是程序这么写:
等待部分:
if(fifo32_status(&keycmd) > 0 && keycmd_wait < 0)
{
// 如果存在向键盘控制器发送的数据,则发送它
keycmd_wait = fifo32_get(&keycmd);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, keycmd_wait);
}
keycmd是一个缓冲区,用于顺序发送数据。
更改部分:
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);
}
这样各种输入就支持了。。