发布于 

30天自制操作系统(17)

DAY17_命令行窗口

1.闲置任务

  • 即使不改写程序,也能自动在适当的LEVEL运行适当的任务,这样的操作系统才是优秀的操作系统
  • 因此,一般情况下可以让任务休眠,但当所有LEVEL中都没有任务存在的时候,就需要HTL了。接下来我们就按照这个要求来改写mtask.c。
  • 如果“所有LEVEL中都没有任务”就会出问题,那我们只要避免这种情况发生不就可以了吗?这类似于我们写定时器的时候所采用的“卫兵”的思路。
void task_idle(void) 
{
for (;;) {
io_hlt();
}
}
  • 综上所述,我们完全不需要对task_sleep等代码进行任何改动,只需在task_init中将这个闲置任务放在最下层LEVEL中就可以了。

2.创建命令行窗口

3.切换输入窗口

  • 目标:我们要让系统在按下“Tab”键的时候,将输入窗口切换到命令行窗口上去。
  • 我们先改变窗口标题栏的颜色。

4.实现字符输入

  • 要实现字符的输入,只要在键盘被按下的时候向console_task的FIFO发送数据即可。
  • 我们还是把struct FIFO放到struct TASK里面去吧。基本上没有什么任务是完全用不到FIFO的,因此我们把它们绑定起来
  • 在向命令行窗口发送键盘数据的时候,并不是直接发送从键盘接收到的原始数据,而是发送经过keytable[]转换后的值。究其原因,是由于这样做可以省去在命令行窗口任务中将按键编码转换成字符编码的步骤
    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 (;;) {
    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) { /*键盘数据(通过任务A) */
    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);
    }
    }
    }

5.符号的输入

  • 目标:实现!和%的输入
  • 我们必须要处理shift键

6.大写字母和小写字母

  • 我们必须同时判断Shift键的状态和CapsLock的状态
    • CapsLock 为 OFF & Shift 键为 OFF → 小写英文字母
    • CapsLock 为 OFF & Shift 键为 ON → 大写英文字母
    • CapsLock 为 ON & Shift 键为 OFF → 大写英文字母
    • CapsLock 为 ON & Shift 键为 ON → 小写英文字母

7.对各种锁定键的支持

  • 点亮/熄灭键盘上指示灯的方法
  • 原理:
    • 对于NumLock和CapsLock等LED的控制,可采用下面的方法向键盘发送指令和数据。
      • 读取状态寄存器,等待 bit 1 的值变为 0。
      • 向数据输出(0060)写入要发送的 1 个字节数据。
      • 等待键盘返回 1 个字节的信息,这和等待键盘输入所采用的方法相同(用 IRQ等待或者用轮询状态寄存器 bit 1 的值直到其变为 0 都可以)。
      • 返回的信息如果为 0xfa,表明 1 个字节的数据已成功发送给键盘。如为 0xfe则表明发送失败,需要返回第 1 步重新发送。
    • 要控制LED的状态,需要按上述方法执行两次,向键盘发送EDxx数据。其中,xx的bit 0代表ScrollLock,bit 1代表NumLock,bit2代表CapsLock(0表示熄灭,1表示点亮)。bit 3~7为保留位,置0即可。
#define KEYCMD_LED 0xed 
void HariMain(void)
{
(中略)
struct FIFO32 fifo, keycmd;
int fifobuf[128], keycmd_buf[32];
(中略)
int key_to = 0, key_shift = 0, key_leds = (binfo->leds >> 4) & 7, keycmd_wait = -1;
(中略)
fifo32_init(&keycmd, 32, keycmd_buf, 0);
(中略)

/*为了避免和键盘当前状态冲突,在一开始先进行设置*/
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 {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) { /* 键盘数据 */
(中略)
/*从此开始*/ 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);
/*到此结束*/ }
(中略)
} else if (512 <= i && i <= 767) { /*鼠标数据*/
(中略)
} else if (i <= 1) { /*光标用定时器*/
(中略)
}
}
}
}
  • 实现方法:
    • 创建了一个叫keycmd的FIFO缓冲区,它不是用来接收中断请求的,而是用来管理由任务A向键盘控制器发送数据的顺序的。如果有数据要发送到键盘控制器,首先会在这个keycmd中累积起来。
    • keycmd_wait变量,用来表示向键盘控制器发送数据的状态。当keycmd_wait的值为-1时,表示键盘控制器处于通常状态,可以发送指令;当值不为-1时,表示键盘控制器正在等待发送的数据,这时要发送的数据被保存在keycmd_wait变量中。