今天,我们首先继续稍微完善一下多任务的部分。然后,我们开始制作每一个操作系统都必须有的命令行窗口吧!!!从今天开始,我们将一步一步的完成我们的命令行窗口,并能够输入一些命令进行调用吧!
嘿嘿嘿想想就有点激动了!!让我们现在就开始撸起袖子加油干吧。
在昨天的多任务机制中,我们还有一些小地方还需要进一步的完善一下。比如说,假设我们的任务b系列已经完成了工作,也就是说此时任务b系列的都没有启动,那么由于我们设置了任务a在没有对应的中断信息产生时就会休眠,那此时所有级别中的任务都没有了,那这样子可能就会因为找不到其他任务而导致运行出现异常的!!
那么该怎么来解决这个问题呢??? 仔细一想,我们是否还记得之前有一个技巧叫“哨兵” ,对的我们可以利用哨兵这个技巧来设置一个闲置的任务,以此防止运行出现异常:
mtask.c
//多任务的哨兵,idle代表闲置。以防止当鼠标的那个任务进入休眠,下面又没有其他任务时多任务系统破溃
void task_idle(void)
{
for(;;){
io_hlt();
}
}
然后进一步的修改一下task_init函数:
//返回一个内存地址,将当前运行的调用该函数的程序作为一个任务
struct TASK *task_init(struct MEMMAN *memman)
{
int i;
struct TASK *task, *idle;
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
taskctl = (struct TASKCTL *) memman_alloc_4k(memman, sizeof (struct TASKCTL));
for(i = 0; i < MAX_TASKS; i++)
{
taskctl->tasks0[i].flags = 0;
taskctl->tasks0[i].sel = (TASK_GDT0 + i) * 8;
set_segmdesc(gdt + TASK_GDT0 +i, 103, (int) &taskctl->tasks0[i].tss, AR_TSS32);
}
for (i = 0; i < MAX_TASKLEVELS; i++) {
taskctl->level[i].running = 0;
taskctl->level[i].now = 0;
}
task = task_alloc();//接收到akkoc返回的第一个任务
task->flags = 2;//活动中的标志
task->priority = 2;//0.02s
task->level = 0; //先将当前调用程序的任务暂时设置为0,即最高
task_add(task);
task_switchsub();//level 设置
load_tr(task->sel);
task_timer = timer_alloc();
timer_settime(task_timer, task->priority);
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;
}
最后,我们将之前的task_b这个部分的任务启动关闭:
for (i = 0; i < 3; i++) {
//task_run(task_b[i], 2, i + 1);
发现确实可行,任务b的三个子程序都没有进行计数了!
这里,我们一步一步的来,首先呢先把大致的窗口做出来:
主函数:
/* 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-yuan", 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);
修改窗口的函数:
bootpack.c:
void console_task(struct SHEET *sheet)
{
struct FIFO32 fifo;
struct TIMER *timer;
struct TASK * task = task_now();
int i, fifobuf[32], 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);
}
}
}
}
这里,其实没啥难度的,这些窗口的制作啊啥的,直接利用前面的就可以了:
这里我们的目的主要就,当我们按下tab键后这两个窗口能进行切换,即一开始是task_a的标题栏是显示的,而console是灰色的。 当我们按下tab后,console是显示的,而task这个变灰了:
具体修改:先将之前的窗口拆分成标题栏和窗口其余部分:
//窗口的剩余部分
void make_window8(unsigned char *buf, int xsize, int ysize, char *title, char act)
{
boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, xsize - 1, 0 );
boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, xsize - 2, 1 );
boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, 0, ysize - 1);
boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, 1, ysize - 2);
boxfill8(buf, xsize, COL8_848484, xsize - 2, 1, xsize - 2, ysize - 2);
boxfill8(buf, xsize, COL8_000000, xsize - 1, 0, xsize - 1, ysize - 1);
boxfill8(buf, xsize, COL8_C6C6C6, 2, 2, xsize - 3, ysize - 3);
boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);
boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);
make_wtitle8(buf, xsize, title, act);
return;
}
//窗口的标题栏
void make_wtitle8(unsigned char *buf, int xsize, char *title, char act)
{
static char closebtn[14][16] = {
"OOOOOOOOOOOOOOO@",
"OQQQQQQQQQQQQQ$@",
"OQQQQQQQQQQQQQ$@",
"OQQQ@@QQQQ@@QQ$@",
"OQQQQ@@QQ@@QQQ$@",
"OQQQQQ@@@@QQQQ$@",
"OQQQQQQ@@QQQQQ$@",
"OQQQQQ@@@@QQQQ$@",
"OQQQQ@@QQ@@QQQ$@",
"OQQQ@@QQQQ@@QQ$@",
"OQQQQQQQQQQQQQ$@",
"OQQQQQQQQQQQQQ$@",
"O$$$$$$$$$$$$$$@",
"@@@@@@@@@@@@@@@@"
};
int x, y;
char c, tc, tbc;
if (act != 0) {
tc = COL8_FFFFFF;
tbc = COL8_000084;
} else {
tc = COL8_C6C6C6;
tbc = COL8_848484;
}
boxfill8(buf, xsize, tbc, 3, 3, xsize - 4, 20);
putfonts8_asc(buf, xsize, 24, 4, tc, title);
for (y = 0; y < 14; y++) {
for (x = 0; x < 16; x++) {
c = closebtn[y][x];
if (c == '@') {
c = COL8_000000;
} else if (c == '$') {
c = COL8_848484;
} else if (c == 'Q') {
c = COL8_C6C6C6;
} else {
c = COL8_FFFFFF;
}
buf[(5 + y) * xsize + (xsize - 21 + x)] = c;
}
}
return;
}
然后,修改主函数里的关于键盘输入部分,具体增加如下:
if(i == 256 + 0x0f){//tab键
if(key_to == 0){//通过0, 1 来决定将哪一个标题栏显示,哪一个变成灰色
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键后就可以看见确实切换了:
要想输入字符串呢,依据我们task_a的处理,就是把键盘产生的中断数据,放入到FIFO缓冲区。那么这里,我们也利用同样的思路进行输送字符吧!
但是有一点,就是我在task_cons这个任务里所建立的fifo缓冲区,是不与主程序的fifo缓冲区所共享的,那么我们任务A里想要把键盘数据发送过去,就必须找到任务B的fifo的地址。 如果直接利用任务A创建时告诉任务B的fifo地址的这种方法的话,我感觉略显麻烦! 那么,我们可以直接在任务结构体TASK中增加一个声明指向fifo缓冲区即可。 具体的修改如下:
bootpack.h:
struct TASK
{
int sel, flags; //sel用于存放GDT编号
int level, priority; //设置优先级
struct FIFO32 fifo;
struct TSS32 tss;
};
而后,修改一下console这个任务:
void console_task(struct SHEET *sheet)
{
struct TIMER *timer;
struct TASK * task = task_now();
int i, fifobuf[32], 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);
}
}
}
然后,我们还需对主函数的传递进行控制一下,以确定是传给哪一个任务的:
for(;;)
{
io_cli(); //IF=0
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 < 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_C6C6C6, s, 1);
cursor_x +=8;
}
}else{
//发送给命令行
fifo32_put(&task_cons->fifo, keytable[i - 256] + 256);
}
}
if(i == 256 + 0x0e ) //退格键
{//用空格把光标消去后移动一次
if(key_to == 0){
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);//z这里定义在console中退格键的编码为8
}
}
if(i == 256 + 0x0f){//tab键
if(key_to == 0){//通过0, 1 来决定将哪一个标题栏显示,哪一个变成灰色
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);
}
然后,make run 一下即可运行:
在此之前,我们即使想要输入键盘的!是无法做到的,这是因为我们想要产生这一个按键,是需要我们利用起shift的。因此,我们在这里需要建立起支持shift产生特殊字符的功能!! (对了,插一句题外话,目前我们使用的编码字符是小键盘喔!)
我们设置一个变量key_shift , 当左shift按下时置为1, 当右shift按下时 置为2,左右同时按下,则置为3.。。
然后设置两个字符表,一个是没按下shift的,一个按下后的:
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
};
然后,我们更改主函数中的for循环:
其代码逻辑是: 先将按键编码转化为字符编码,将转化结果存于s[0]中,若遇到无法转化的就置为0 。(在此过程中就会判断是否右按下左shift)
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){
//将按键编码转化为字符编码
if(key_shift == 0){
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_C6C6C6, s, 1);
cursor_x += 8;
}
}else{
//发送给命令行
fifo32_put(&task_cons->fifo, s[0] + 256);
}
}
退格键,tab的代码不变!
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以及CapsLock同时进行判断即可:
而我们的CapsLock 需要我们从之前在asmhead.nas中,从bios保存下来的键盘状态的数据里取得,也就是binfo->leds 。 其中:
还有一件事,即我们设置CapsLock启用后,键盘上的指示灯一般是会亮的!因此,这里还需要我们对键盘的控制电路发送对应的信号,才能实现:
具体的修改代码如下:
主函数:
略
struct FIFO32 fifo, keycmd;
struct SHTCTL *shtctl;//用于管理的
int fifobuf[128], keycmd_buf[32];
略
int key_to = 0, key_shift = 0, key_leds = (binfo->leds >> 4) & 7, keycmd_wait = -1;
略
//为了避免和键盘当前状态冲突,需要一开始先进行设置
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=0
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){
//将按键编码转化为字符编码
if(key_shift == 0){
s[0] = keytable0[i - 256];
}else{
s[0] = keytable1[i -256];
}
}else{
s[0] = 0;
}
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; //大小写的转化
}
}
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_C6C6C6, s, 1);
cursor_x += 8;
}
}else{
//发送给命令行
fifo32_put(&task_cons->fifo, s[0] + 256);
}
}
if(i == 256 + 0x0e ) //退格键
{//用空格把光标消去后移动一次
if(key_to == 0){
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);//z这里定义在console中退格键的编码为8
}
}
if(i == 256 + 0x0f){//tab键
if(key_to == 0){//通过0, 1 来决定将哪一个标题栏显示,哪一个变成灰色
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;
}
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);
}
//光标在显示
具体效果如图所示:
至此,今天我们完成了闲置任务的创建,以及命令行窗口的初步建造,接下来我们将进行逐步的完善,敬请期待!