第25天 增加命令行窗口
2020.4.30
1. 蜂鸣器发声(harib22a)
-
蜂鸣器和定时器一样,都是由PIT来控制的,而PIT位于芯片组中,因此所有型号的电脑都能使用它。
-
关于蜂鸣器的相关资料:
- 其中,赫兹大小 = 1.19318MHz / 设定值。
- PIT时钟频率与CPU时钟频率无关,PIT时钟频率大小恒定为1.19318MHz。
-
设计蜂鸣器发声的API:
- EDX = 20
- EAX = 声音频率(单位是mHz,毫赫兹),比如EAX=200000,发出200Hz的声音。当EAX=0时,停止发声。
-
修改hrb_api:
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax) { …… } else if (edx == 20) { if (eax == 0) { /*停止发声*/ i = io_in8(0x61); io_out8(0x61, i & 0x0d); } else { i = 1193180000 / eax; /*获得设定值*/ io_out8(0x43, 0xb6); io_out8(0x42, i & 0xff); /低8位*/ io_out8(0x42, i >> 8); /*高8位*/ i = io_in8(0x61); io_out8(0x61, (i | 0x03) & 0x0f); } } return 0; }
-
编写可以供C语言调用20号API的函数api_beep:
_api_beep: ; void api_beep(int tone); MOV EDX,20 MOV EAX,[ESP+4] ; tone INT 0x40 RET
-
编写应用程序beepdown.c:
void api_end(void); int api_getkey(int mode); int api_alloctimer(void); void api_inittimer(int timer, int data); void api_settimer(int timer, int time); void api_beep(int tone); void HariMain(void) { int i, timer; timer = api_alloctimer(); api_inittimer(timer, 128); for (i = 20000000; i >= 20000; i -= i / 100) { /* 20KHz到20Hz : 人类可以听见的声音的范围 */ /* i以1%的速度递减 */ api_beep(i); api_settimer(timer, 1); /* 0.01s */ if (api_getkey(1) != 128) { break; } } api_beep(0); /*停止发声*/ api_end(); }
- 应用程序每0.01s便降低一次发出声音的频率,当声音频率降至20Hz或者用户按下任意键时结束。
-
make
后用VMware运行:
2. 增加更多的颜色(1)(harib22b)
-
到目前为止,此OS只使用了16种颜色,虽然现在已经能够使用256色的显示模式。剩下240种颜色还可以用。
-
规定,为RGB三原色中每种颜色赋予6个色阶,这样就可以定义6*6*6=216种颜色。此时还剩下24种颜色了。
- 色阶:亮度。
-
修改init_palette函数(graphic.c):
void init_palette(void) { static unsigned char table_rgb[16 * 3] = { 0x00, 0x00, 0x00, /* 0:黑 */ 0xff, 0x00, 0x00, /* 1:亮红 */ 0x00, 0xff, 0x00, /* 2:亮绿 */ 0xff, 0xff, 0x00, /* 3:亮黄 */ 0x00, 0x00, 0xff, /* 4:亮蓝 */ 0xff, 0x00, 0xff, /* 5:亮紫 */ 0x00, 0xff, 0xff, /* 6:浅亮蓝 */ 0xff, 0xff, 0xff, /* 7:白 */ 0xc6, 0xc6, 0xc6, /* 8:亮灰 */ 0x84, 0x00, 0x00, /* 9:暗红 */ 0x00, 0x84, 0x00, /* 10:暗绿 */ 0x84, 0x84, 0x00, /* 11:暗黄 */ 0x00, 0x00, 0x84, /* 12:暗青 */ 0x84, 0x00, 0x84, /* 13:暗紫 */ 0x00, 0x84, 0x84, /* 14:浅暗蓝 */ 0x84, 0x84, 0x84 /* 15:暗灰 */ }; unsigned char table2[216 * 3]; int r, g, b; set_palette(0, 15, table_rgb); for (b = 0; b < 6; b++) { for (g = 0; g < 6; g++) { for (r = 0; r < 6; r++) { table2[(r + g * 6 + b * 36) * 3 + 0] = r * 51; table2[(r + g * 6 + b * 36) * 3 + 1] = g * 51; table2[(r + g * 6 + b * 36) * 3 + 2] = b * 51; } } } set_palette(16, 231, table2); return; }
- 比如,R的6个色阶分别是0, 51, 102, 153, 204, 255。
- 现在可以指定的颜色是RGB=[51*a, 51*b, 51*c],其中abc是整数,且都介于0和6之间。也就是说RGB=[1, 2, 3]是不合法的。
- 当指定RGB=[51, 102, 153]时,此时a=1,b=2,c=3,
16 + 1 \* 6^0 + 2 \* 6^1 + 3 \* 6^2 = 137
,那么只要使用色号137即可。 - 当这样设定时,色号0和色号16的RGB都是#000000,浪费了一个色号。同样,#ff0000等颜色也会发生重复。
-
编写应用程序color.c
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title); void api_initmalloc(void); char *api_malloc(int size); void api_refreshwin(int win, int x0, int y0, int x1, int y1); void api_linewin(int win, int x0, int y0, int x1, int y1, int col); int api_getkey(int mode); void api_end(void); void HariMain(void) { char *buf; int win, x, y, r, g, b; api_initmalloc(); buf = api_malloc(144 * 164); win = api_openwin(buf, 144, 164, -1, "color"); for (y = 0; y < 128; y++) { for (x = 0; x < 128; x++) { r = x * 2; g = y * 2; b = 0; buf[(x + 8) + (y + 28) * 144] = 16 + (r / 43) + (g / 43) * 6 + (b / 43) * 36; } } api_refreshwin(win, 8, 28, 136, 156); api_getkey(1); api_end(); }
神奇的数字之43
。设定的显示颜色的区域大小是128*128。x*2是R,那么R的范围是[0, 256),同理G,而B恒为0。- 色阶数是6,128/6=21,如果一行显示6个色阶,每个色阶需要占用21(或22)个像素,x每隔21就需要改变一种色阶,r=x*2,也就是r每隔42就要变换一种色阶。
16 + a + b \* 6 + c \* 36
代表一种色号,那么也就是ab每隔42就要让这个数值发生变化,这里向上取整,因此得到了神奇的数字43。
-
make
后用VMware运行:
3. 增加更多的颜色(2)(harib22c)
-
显然,如果不使用256色模式,而是使用VESA的全色彩模式的话,就可以很容易地显示更多的色彩。
- 这样做的问题有二:
- 其一,时间不允许,如果使用VESA的全色彩模式,可能需要花上一整天的时间,这有点偏离了我们的初心(笑)。
- 其二,有的电脑不支持VESA的全色彩模式。
- 这样做的问题有二:
-
使用一个小技巧轻松解决增加更多颜色的问题。
- 6种不同的亮度,是6级色阶。那么每两种相邻的亮度可以产生3种介于这两种亮度之间的亮度,一共有5个空位,那么就会多产生15级色阶,因此共21级色阶。
-
编写应用程序color2.c:
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title); void api_initmalloc(void); char *api_malloc(int size); void api_refreshwin(int win, int x0, int y0, int x1, int y1); void api_linewin(int win, int x0, int y0, int x1, int y1, int col); int api_getkey(int mode); void api_end(void); unsigned char rgb2pal(int r, int g, int b, int x, int y); void HariMain(void) { char *buf; int win, x, y; api_initmalloc(); buf = api_malloc(144 * 164); win = api_openwin(buf, 144, 164, -1, "color2"); for (y = 0; y < 128; y++) { for (x = 0; x < 128; x++) { buf[(x + 8) + (y + 28) * 144] = rgb2pal(x * 2, y * 2, 0, x, y); } } api_refreshwin(win, 8, 28, 136, 156); api_getkey(1); api_end(); } unsigned char rgb2pal(int r, int g, int b, int x, int y) { static int table[4] = { 3, 1, 0, 2 }; int i; x &= 1; /* 判断奇偶 */ y &= 1; i = table[x + y * 2]; /* 用来产生中间色的变量 */ r = (r * 21) / 256; /* r为0~20 */ g = (g * 21) / 256; b = (b * 21) / 256; r = (r + i) / 4; /* r为0~5 */ g = (g + i) / 4; b = (b + i) / 4; return 16 + r + g * 6 + b * 36; }
- 这里的函数rgb2pal用多个if语句也可以实现。
- rgb2pal函数没看懂。
-
make
后用VMware运行:
4. 窗口初始位置(harib22d)
-
做个小修改:让窗口总是位于画面中央,并且能够判断当前屏幕中窗口的数量并自动显示在最上面(但还是在鼠标图层的下面)。
-
修改hrb_api:
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax) { …… } else if (edx == 5) { sht = sheet_alloc(shtctl); sht->task = task; sht->flags |= 0x10; sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax); make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0); sheet_slide(sht, (shtctl->xsize - esi) / 2, (shtctl->ysize - edi) / 2); sheet_updown(sht, shtctl->top); /* 将窗口图层高度指定为当前鼠标所在图层的高度,鼠标移到上层 */ reg[7] = (int) sht; } else if (edx == 6) { …… }
- 注意:
sheet_updown(sht, shtctl->top);
,不是sheet_updown(sht, shtctl->top - 1);
。- 当没有新增图层时,top-1,因为top是鼠标图层。
- 当有新增图层时,top,因为此时鼠标图层高度是top+1。
- 可以看sheet_updown函数
- 注意:
-
make
后用VMware运行:
5. 增加命令行窗口(1)(harib22e)
-
现在的问题是:当运行应用程序时,命令行就不能再进行其他命令的输入了。
-
解决方法有二:
- 其一:修改命令行窗口,使其能够在应用程序运行过程中也可以输入下一条指令。
- 显然,这样修改的话,修改的代码量巨大。
- 其二:增加命令行窗口的数量。
- 命令行窗口是任务,这样修改的话代码量不大。
- 其一:修改命令行窗口,使其能够在应用程序运行过程中也可以输入下一条指令。
-
在harib22e中只修改bootpack.c。
- 准备两个命令行:
- task_cons --> task_cons[0] 和 task_cons[1]。
- 用数组是为了以后的拓展性。
- 将task_cons的相关变量都准备两份。
- 准备两个命令行:
-
修改bootpack.c中HariMain:
void HariMain(void) { …… unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_cons[2]; struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_cons[2]; struct TASK *task_a, *task_cons[2]; …… /* sht_cons */ for (i = 0; i < 2; i++) { sht_cons[i] = sheet_alloc(shtctl); buf_cons[i] = (unsigned char *) memman_alloc_4k(memman, 256 * 165); sheet_setbuf(sht_cons[i], buf_cons[i], 256, 165, -1); make_window8(buf_cons[i], 256, 165, "console", 0); make_textbox8(sht_cons[i], 8, 28, 240, 128, COL8_000000); task_cons[i] = task_alloc(); task_cons[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12; task_cons[i]->tss.eip = (int) &console_task; task_cons[i]->tss.es = 1 * 8; task_cons[i]->tss.cs = 2 * 8; task_cons[i]->tss.ss = 1 * 8; task_cons[i]->tss.ds = 1 * 8; task_cons[i]->tss.fs = 1 * 8; task_cons[i]->tss.gs = 1 * 8; *((int *) (task_cons[i]->tss.esp + 4)) = (int) sht_cons[i]; *((int *) (task_cons[i]->tss.esp + 8)) = memtotal; task_run(task_cons[i], 2, 2); /* level=2, priority=2 */ sht_cons[i]->task = task_cons[i]; sht_cons[i]->flags |= 0x20; } …… sheet_slide(sht_back, 0, 0); sheet_slide(sht_cons[1], 56, 6); /*命令行2窗口位置*/ sheet_slide(sht_cons[0], 8, 2); /*命令行1窗口位置*/ sheet_slide(sht_win, 64, 56); sheet_slide(sht_mouse, mx, my); sheet_updown(sht_back, 0); sheet_updown(sht_cons[1], 1); /*命令行2的图层高度是1*/ sheet_updown(sht_cons[0], 2); /*命令行1的图层高度是2*/ sheet_updown(sht_win, 3); sheet_updown(sht_mouse, 4); key_win = sht_win; …… for (;;) { …… io_cli(); if (fifo32_status(&fifo) == 0) { …… } else { …… if (256 <= i && i <= 511) { /* 键盘数据 */ …… if (i == 256 + 0x3b && key_shift != 0 && task_cons[0]->tss.ss0 != 0) { /* Shift+F1 */ cons = (struct CONSOLE *) *((int *) 0x0fec); cons_putstr0(cons, "\nBreak(key) :\n"); io_cli(); task_cons[0]->tss.eax = (int) &(task_cons[0]->tss.esp0); task_cons[0]->tss.eip = (int) asm_end_app; io_sti(); } …… } else if (512 <= i && i <= 767) { /* 鼠标数据 */ if (mouse_decode(&mdec, i - 512) != 0) { …… if ((mdec.btn & 0x01) != 0) { /* 按下左键 */ if (mmx < 0) { for (j = shtctl->top - 1; j > 0; j--) { …… if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) { if (sht->buf[y * sht->bxsize + x] != sht->col_inv) { …… if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) { if ((sht->flags & 0x10) != 0) { cons = (struct CONSOLE *) *((int *) 0x0fec); cons_putstr0(cons, "\nBreak(mouse) :\n"); io_cli(); task_cons[0]->tss.eax = (int) &(task_cons[0]->tss.esp0); task_cons[0]->tss.eip = (int) asm_end_app; io_sti(); } } break; } } } } else { …… } } else { …… } } } else if (i <= 1) { /* 光标定时器 */ …… } } } }
- 关于Shift+F1和“X”按钮的地方,不加判断直接写cons[0]的原因是让编译器通过。代码很快就会修复。所以在harib22e中千万不要使用Shift+F1和“X”按钮关闭应用程序。
-
make
:- 用VMware运行试试
- 出现了两个命令行窗口。可以实现窗口的切换。
- 执行命令试试:
- 好像可以执行命令。
- 启动应用程序试试(启动a.hrb):
- 在左边的命令行窗口执行a.hrb,输出好像到了右边的命令行窗口里去了。
- 在右边的命令行窗口执行a.hrb,输出正常。
- harib22e的输出好像只能是一个命令行。
- 用VMware运行试试
6. 增加命令行窗口(2)(harib22f)
-
harib22e出现问题的原因是hrb_api中的代码:
struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
- 从地址0xfec获取cons变量,而cons是用来判断“要向哪个命令行窗口输出字符”的。
- 顺便提一下:
- 0xfe8:获取ds_base
- 0xfe4:获取shtctl
- cons变量的地址是从内存地址0xfec获得的,而无论从哪个任务读取这个内存地址的值,得到的肯定是一个相同的值,因此不管在那个窗口运行a.hrb,都只能在一个窗口中输出。
- 因此,需要为不同的命令行窗口获取不同的cons。
- 显然,写入内存的方法已经不行了。
- 所以,只能修改数据结构。
-
修改TASK结构体:
struct TASK { int sel, flags; int level, priority; struct FIFO32 fifo; struct TSS32 tss; struct CONSOLE *cons; int ds_base; };
- 每个任务都有各自的TASK结构,因此,只需要将cons存放在TASK结构中,就能根据不同的任务读取不同的cons了。
- 将ds_base也放入到了TASK结构中,理由和cons类似。
- ds_base之前是从0xfe8读取的。显然,cons[0]的应用程序的数据段地址和cons[1]的应用程序的数据段地址是不同的。如果不加以区分,字符串的显示就会出问题。
-
修改console.c:
void console_task(struct SHEET *sheet, int memtotal) { …… task->cons = &cons; /*保存cons*/ …… } int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline) { …… if (finfo != 0) { …… if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) { …… task->ds_base = (int) q; /*记录应用程序数据段地址*/ …… } else { cons_putstr0(cons, ".hrb file format error.\n"); } …… } return 0; } int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax) { struct TASK *task = task_now(); int ds_base = task->ds_base; struct CONSOLE *cons = task->cons; …… } int *inthandler0c(int *esp) { struct TASK *task = task_now(); struct CONSOLE *cons = task->cons; …… } int *inthandler0d(int *esp) { struct TASK *task = task_now(); struct CONSOLE *cons = task->cons; …… }
-
make
后用VMware运行:
- 在左边和右边的命令行中执行应用程序a.hrb都正确地显示在了各自的窗口中。
- 且实现了同时运行两个应用程序的功能(color.hrb和color2.hrb)。
- 注意:此时退出color.hrb和color2.hrb不能使用Shift+F1和“X”按钮。可以使用回车退出。
- 不好,使用回车键退出应用程序color.hrb时怎么产生了一般保护性异常?
- 接下来就要使应用程序能够用Enter正常退出。
7. 增加命令行窗口(3)(harib22g)
-
harib22f在退出应用程序时产生一般保护性异常的原因是:应用程序的内存段突然消失了。
- 问题出在cmd_app:
set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60); set_segmdesc(gdt + 1004, segsiz - 1, (int) q, AR_DATA32_RW + 0x60); …… start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
- 首先,color.hrb在某个窗口运行,启动程序一切顺利,然后显示窗口并绘图,接下来等待键盘输入并进入休眠状态。
- 然后在另一个命令行窗口启动了color2.hrb,程序顺利启动、显示窗口并绘图,随后进入休眠状态。
- 其实现在就已经产生了问题,那就是明明是两个应用程序,却共用了数据段和代码段。后运行的color2.hrb已经把先运行的color.hrb的内存段覆盖了。因而再使用Enter唤醒color.hrb时,结果错误地运行了color2.hrb,显然,当应用程序试图访问其他应用程序的内存段时就会产生0x0d中断。
- 因此,只需要为不同的应用程序分配不同的内存段即可。
- 问题出在cmd_app:
-
修改cmd_app:
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline) { …… if (finfo != 0) { …… if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) { …… set_segmdesc(gdt + task->sel / 8 + 1000, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60); set_segmdesc(gdt + task->sel / 8 + 2000, segsiz - 1, (int) q, AR_DATA32_RW + 0x60); …… start_app(0x1b, task->sel + 1000 * 8, esp, task->sel + 2000 * 8, &(task->tss.esp0)); …… } else { cons_putstr0(cons, ".hrb file format error.\n"); } …… } return 0; }
- task->sel代表的是写入TSS的段号*8。因此task->sel/8就代表段号,段号的范围是31002。将其加上1000,得到的范围是10032002,将这个
task->sel/8 + 1000
作为应用程序的代码段编号。- 注意:GDT的1号是OS的数据段,GDT的2号是OS的代码段。3~1002是多任务使用的(共1000个)。
- 同理,
task->sel/8 + 2000
就代表应用程序用的数据段编号。 - 一个任务只能运行一个应用程序。
- task->sel代表的是写入TSS的段号*8。因此task->sel/8就代表段号,段号的范围是31002。将其加上1000,得到的范围是10032002,将这个
-
make
后用VMware运行:
- 能够使用Enter让color.hrb和color2.hrb正常退出。
- 接下来接下来就要使应用程序能够用Shift+F1和“X”按钮退出。
8. 增加命令行窗口(4)(harib22h)
-
修改bootpack.c中的HariMain:
void HariMain(void) { …… struct TASK *task_a, *task_cons[2], *task; /*task用于指向正在运行的任务*/ …… for (;;) { …… if (fifo32_status(&fifo) == 0) { …… } else { …… if (256 <= i && i <= 511) { /* 键盘数据 */ …… if (i == 256 + 0x3b && key_shift != 0) { task = key_win->task; /*获取当前处于输入状态的窗口指向的task*/ if (task != 0 && task->tss.ss0 != 0) { /* Shift+F1 */ cons_putstr0(task->cons, "\nBreak(key) :\n"); io_cli(); task->tss.eax = (int) &(task->tss.esp0); task->tss.eip = (int) asm_end_app; io_sti(); } } …… } else if (512 <= i && i <= 767) { /* 键盘数据 */ if (mouse_decode(&mdec, i - 512) != 0) { …… if ((mdec.btn & 0x01) != 0) { /* 鼠标左键按下 */ if (mmx < 0) { for (j = shtctl->top - 1; j > 0; j--) { …… if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) { if (sht->buf[y * sht->bxsize + x] != sht->col_inv) { …… if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) { /* “X”按钮 */ if ((sht->flags & 0x10) != 0) { /* 是否是应用程序的窗口 */ task = sht->task; cons_putstr0(task->cons, "\nBreak(mouse) :\n"); io_cli(); task->tss.eax = (int) &(task->tss.esp0); task->tss.eip = (int) asm_end_app; io_sti(); } } break; } } } } else { …… } } else { …… } } } else if (i <= 1) { /* 光标定时器 */ …… } } } }
- 就是将原来是
task_cons[0]
的地方改成key_win->task
或sht->task
,这样,用键盘强制结束(Shift+F1)时会根据当前处于输入状态的窗口进行结束,当点击“X”按钮时,会以被点击的窗口进行结束。
- 就是将原来是
-
make
后用VMware运行:
- 可以使用Shift+F1和“X”按钮结束应用程序了。
- 至此,完成了增加命令行窗口的任务。
9. 变得更像真正的操作系统(1)(harib22i)
-
Windows和其他OS中可没有向task_a这样的窗口。task_a窗口不是应用程序,而是操作系统的一部分,连关都关不掉。
- 因此,现在就让task_a消失吧!如果哪天突然想念task_a窗口了,只要写一个应用程序出来就好了。而且应用程序的话,可以随时关掉呢(笑)。
- 在bootpack.c中,只有task_a这个窗口时搞特殊化的,删掉“搞特殊化”的task_a的窗口。
- 只是删除task_a的窗口,而不是删除task_a,task_a运行的是HariMain,是不可以关掉的。
-
修改bootpack.c中的HariMain:
void HariMain(void) { …… int mx, my, i; /*删掉了cursor_x和cursor_c*/ unsigned int memtotal; struct MOUSE_DEC mdec; struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR; unsigned char *buf_back, buf_mouse[256], *buf_cons[2]; /*删掉了buf_win*/ struct SHEET *sht_back, *sht_mouse, *sht_cons[2]; struct TASK *task_a, *task_cons[2], *task; /*删掉了用于光标闪烁的timer*/ static char keytable0[0x80] = { /*向keytable0和keytable1中追加了退格键和回车键的编码*/ 0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0x08, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0x0a, 0, 'A', 'S', …… }; static char keytable1[0x80] = { 0, 0, '!', 0x22, '#', '$', '%', '&', 0x27, '(', ')', '~', '=', '~', 0x08, 0, 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '`', '{', 0x0a, 0, 'A', 'S', …… }; …… /* sht_back */ …… /* sht_cons */ …… /*删掉了sht_win的相关部分*/ /* sht_mouse */ …… sheet_slide(sht_back, 0, 0); sheet_slide(sht_cons[1], 56, 6); sheet_slide(sht_cons[0], 8, 2); /*删掉了sht_win*/ sheet_slide(sht_mouse, mx, my); sheet_updown(sht_back, 0); sheet_updown(sht_cons[1], 1); sheet_updown(sht_cons[0], 2); sheet_updown(sht_mouse, 3); key_win = sht_cons[0]; /*将当前输入状态窗口变成命令行1*/ keywin_on(key_win); …… for (;;) { …… if (fifo32_status(&fifo) == 0) { …… } else { …… if (256 <= i && i <= 511) { /* 键盘数据 */ …… if (s[0] != 0) { /* 一般字符,退格键,回车键 */ fifo32_put(&key_win->task->fifo, s[0] + 256); } if (i == 256 + 0x0f) { /* Tab */ keywin_off(key_win); j = key_win->height - 1; if (j == 0) { j = shtctl->top - 1; } key_win = shtctl->sheets[j]; keywin_on(key_win); } …… /*删掉了重新显示光标的部分*/ } else if (512 <= i && i <= 767) { /* 鼠标数据 */ if (mouse_decode(&mdec, i - 512) != 0) { …… if ((mdec.btn & 0x01) != 0) { /* 左键按下 */ if (mmx < 0) { for (j = shtctl->top - 1; j > 0; j--) { …… if (0 <= x && x < sht->bxsize && 0 <= y && y < sht->bysize) { if (sht->buf[y * sht->bxsize + x] != sht->col_inv) { sheet_updown(sht, shtctl->top - 1); if (sht != key_win) { keywin_off(key_win); key_win = sht; keywin_on(key_win); } …… } } } } else { …… } } else { …… } } } /*删掉了光标定时器部分*/ } } }
- 修改keywin_off和keywin_on:
void keywin_off(struct SHEET *key_win) { change_wtitle8(key_win, 0); if ((key_win->flags & 0x20) != 0) { fifo32_put(&key_win->task->fifo, 3); } return; } void keywin_on(struct SHEET *key_win) { change_wtitle8(key_win, 1); if ((key_win->flags & 0x20) != 0) { fifo32_put(&key_win->task->fifo, 2); } return; }
- 这样修改,少了70行代码,清爽了不少。
- 修改keywin_off和keywin_on:
-
make
后用VMware运行试试:
- 貌似两个命令行窗口的光标都没显示啊?
- 这里并没有出现书上说说不断重启,无法操作的现象。相反,点几下两个命令行窗口就恢复了。
10. 变得更像真正的操作系统(2)(harib22j)
-
harib22i出现上述问题的原因是没有对缓冲区进行初始化。
-
因为命令行窗口的优先级比较低,只有当bootpack.c中的HariMain(task_a)休眠之后才会运行命令行窗口,如果不运行命令行任务的话,console_task就不会执行,命令行任务的FIFO缓冲区就不会被初始化,这就是为什么刚启动时没有命令行窗口光标的原因。
-
因此,只需要将console_task中对命令行缓冲区初始化的代码移动到bootpack.c中去就行了,修改bootpack.c中的HariMain:
void HariMain(void) { …… int fifobuf[128], keycmd_buf[32], *cons_fifo[2]; /*cons_fifo代表缓冲区*/ …… /* sht_cons */ for (i = 0; i < 2; i++) { …… sht_cons[i]->task = task_cons[i]; sht_cons[i]->flags |= 0x20; cons_fifo[i] = (int *) memman_alloc_4k(memman, 128 * 4); /*为缓冲区分配内存空间*/ fifo32_init(&task_cons[i]->fifo, 128, cons_fifo[i], task_cons[i]); /*初始化缓冲区*/ } }
-
修改console.c中的console_task:
void console_task(struct SHEET *sheet, int memtotal) { struct TASK *task = task_now(); struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR; int i, *fat = (int *) memman_alloc_4k(memman, 4 * 2880); /*删掉了fifobuf[128]*/ struct CONSOLE cons; char cmdline[30]; cons.sht = sheet; cons.cur_x = 8; cons.cur_y = 28; cons.cur_c = -1; task->cons = &cons; /*删掉了fifo32_init(&task->fifo, 128, fifobuf, task)*/ cons.timer = timer_alloc(); timer_init(cons.timer, &task->fifo, 1); …… }
-
make
后用VMware运行:
- 刚启动就有光标在命令行上闪烁。
11. 写在5.1和5.2
- 现在是2020.5.2 14:04。
- 昨晚终于下雨了,几近一月没有下雨的家乡,已经干涸地像一块干瘪的牛肉。
- 真是春雨贵如油,趁着刚下完的雨,爸妈赶紧去种花生了。
- 昨日,去参加了喜宴,没太写文档,到今日才完成昨日应该完成的任务,真是羞赧万分啊。
- 就要走到黑暗尽头了,冲刺穿过最后的荆棘吧!