快捷搜索:  汽车  科技

常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令

常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令虽然不如上面的明亮行窗口美观,但是有那个意思。我们最终完成是这个样子的:在这个窗口里,我们可以运行命令,并且看到结果。比如,这里运行ls命令,看到了结果:当前文件夹下的文件列表这就是我们今天的目标了。

day16以及之前,我们完成了多任务管理,内存管理,显示层的管理等多并发操作系统的基础知识点。

那么今天day17,我们就来完成操作系统一个操作系统的基本程序:命令行.

或者叫shell 或者叫bash 或者叫cmd 或者叫terminal.或者叫console,tty等

我们想实现类似这样的命令行窗口:

常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令(1)

在这个窗口里,我们可以运行命令,并且看到结果。

常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令(2)

比如,这里运行ls命令,看到了结果:当前文件夹下的文件列表

这就是我们今天的目标了。

我们最终完成是这个样子的:

常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令(3)

虽然不如上面的明亮行窗口美观,但是有那个意思。

我们先分析一下。

  1. 我们多任务管理器设计完了,那么这个命令行窗口肯定是一个新的任务,那么新任务的名字就叫console_task吧 这个写法就像写task_b_main一样。
  2. 我们多层管理器设计完了,那么这个命令行窗口肯定是一个新的层了,新层作为参数输入给新的任务task_cons就行了。
  3. 任务有了,窗口有了,剩下的就是字符的输入。
  4. 还有就是文件系统。

3.1 关于字符输入:实现对鼠标,键盘的管理还是有点麻烦的,既然在操作系统主函数这个任务里已经实现了对键盘的解读,就没必要在task_cons里重复解读了,直接在操作系统主函数,也就是任务0中把解码后的键盘字符发送到任务task_cons里就行了,那么此时,task_cons里应该有一个fifo的数组才存储键盘字符。

3.2 关于文件系统:因为通常命令就是a调用程序,b运行程序,c显示结果。其中调用程序就是从磁盘上读取文件内容到内存中;运行程序就是指令指针从文件内容对应的开头内存地址开始运行;显示结果就是还是我们原来的绘制字符用的函数putfonts8_asc_sht. 其实b c步我们已经实现了,就是a还没有实现,这就要求我们要求我们熟悉磁盘上文件的存储结构,这样就可以方便的从磁盘里读取内容到内存了,为此需要建立文件系统。

分析完后,发现内容还是有点多的。

不过还好,1,2,3其实都是已经掌握的技术了,都是对已有技术的应用。4是新技术。

那我们就开始吧,会按照这样的顺序展开:

  1. 为命令行建立新任务console_task
  2. 为命令行建立新窗口
  3. 完成字符输入
  4. 检索磁盘,实现文件读取
为命令行建立新任务console_task

任务中,要实现对光标的绘制。

如果要实现对光标的绘制,就要实现一个fifo。

任务中,还要实现休眠和唤醒功能,此时就需要把当前任务的结构体拿出来,然后进行task_sleep(task结构体),所以,还要提前准备一个task_now()函数来取出当前任务的结构体。

任务的输入参数是sheet 这个我们建立窗口后,就有了。

void console_task(struct SHEET *sheet) { struct FIFO32 fifo;// 声明用于本任务的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);// 设定0.5秒的超时器 // 绘制光标 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内。

// 准备窗口需要的层,缓冲区 sht_cons = sheet_alloc(shtctl); buf_cons = (unsigned char *) memman_alloc_4k(memman 256 * 165); sheet_setbuf(sht_cons buf_cons 256 165 -1); //在缓冲区绘制名为console的256x165的窗口 make_window8(buf_cons 256 165 "console" 0); // 在窗口内绘制一个240x128的文本框,底色为COL8_000000黑色 make_textbox8(sht_cons 8 28 240 128 COL8_000000); // 从任务控制器里拿出一个空闲任务来运行我们刚才写的任务函数console_task 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; // 将窗口层作为任务函数console_task的第一个输入参数 *((int *) (task_cons->tss.esp 4)) = (int) sht_cons; // 以优先级level-2 priority=2来运行任务 task_run(task_cons 2 2);

到这里,就完成了一个新任务的建立,并且给新任务建立了一个新窗口。

任务运行起来后,还可以在窗口内绘制一个每秒钟闪烁一次的光标。

显示效果如下:

要实现用tab键来切换窗口,要做3个事情:

  1. 当键盘字符为 tab键时,把console设置为激活状态,把task_a设置为非激活
  2. 如何把console设置为激活状态,其实就是把窗口标题颜色从灰色改为蓝色。把task_a的标题颜色设置为灰色
  3. 如果单独改变窗口的标题颜色,不改变窗口的内容颜色,那么最好把绘制标题单独写一个函数,方便修改。

当键盘收到tab键时:

if (i == 256 0x0f) { /* Tab */ if (key_to == 0) { key_to = 1; make_wtitle8(buf_win sht_win->bxsize "task_a" 0); // 设置console为激活 make_wtitle8(buf_cons sht_cons->bxsize "console" 1); } else { key_to = 0; //设置task_a为激活 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和console的激活状态就会切换。

那么具体的 make_wtitle8里面是啥?其实就是将原来的make_window8成两部分:

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来绘制标题部分 make_wtitle8(buf xsize title act); return; } // 绘制标题部分的函数,参数 act表示是否激活 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; }

这样就完成了窗口的切换,效果如下:

完成字符输入

想给console窗口中输入字符,就必须在console_task函数中进行操作

想在console_task函数中操作呢?console_task就必须有能够接收到键值

所以,console_task中就需要准备一个fifo数组,然后在操作系统主函数中,将拿到的键值给这个fifo数组里放。

具体实现代码如下:

主程序中,将字符发送给task_cons的fifo

if (i < 0x54 256 && keytable[i - 256] != 0) { // 一般键的处理 if (key_to == 0) { 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 { // key_to=1时,将键盘字符发送给task_cons->fifo 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 { // key_to=1时,退格键发送给task_cons->fifo fifo32_put(&task_cons->fifo 8 256); } }

然后在task_cons所对应的console_task中添加字符显示代码:

// 命令行的前面输入一个>符号 putfonts8_asc_sht(sheet 8 28 COL8_FFFFFF COL8_000000 ">" 1); for(;;){ if (256 <= i && i <= 511) { 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); }

这样,就完成了在console中输入字符的功能了

不过,当前能够输入的字符其实只有键盘的一部分,我们的命令行窗口需要对键盘上的每个键都进行输入。

那么现在我们来完善一下。

其实主要是shift键按下后要输入一些特殊字符,还有就是大小写的支持

利用shift键支持特殊字符

... // 制作两个表,分别在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 }; int key_to = 0 key_shift = 0;// key_shift代表shift键有没有被按下 for(;;){ ... if (i == 256 0x2a) { //左shift按下 key_shift |= 1; } if (i == 256 0x36) { //右shift按下 key_shift |= 2; } if (i == 256 0xaa) { //左shift抬起 key_shift &= ~1; } if (i == 256 0xb6) { //右shift抬起 key_shift &= ~2; } ... if (256 <= i && i <= 511) { sprintf(s "X" i - 256); putfonts8_asc_sht(sht_back 0 16 COL8_FFFFFF COL8_008484 s 2); if (i < 0x80 256) { if (key_shift == 0) {//当没有shift按下时 s[0] = keytable0[i - 256]; } else {//当有shift按下时 s[0] = keytable1[i - 256]; } } else { s[0] = 0; } } }

这样,我们就完成了特殊字符支持。

那么如果实现大小写支持呢?其实将代码将大写字符 0x20就是小写字符了,不过我们需要在CapsLock键设置后,或者在shift键按下的同时,去决定到底输入大写还是小写。

也就是说,是否输入小写,是CapsLock键和shift键的共同作用的结果。

HariMain(){ ... int key_to = 0 key_shift = 0 key_leds = (binfo->leds >> 4) & 7; ... for(;;){ ... if ('A' <= s[0] && s[0] <= 'Z') { // 如果键值的范围在A-Z内 if (((key_leds & 4) == 0 && key_shift == 0) || // 如果Capslock灯没亮,shift没有被按下,输入小写字符 ((key_leds & 4) != 0 && key_shift != 0)) {//如果Casplock灯亮,shift被按下,输入小写字符 s[0] = 0x20; // 将字符转换为小写 } ... } ... } }

这里用到了key_leds=(info->leds>>4)&7来或者到Capslock灯信息,然后又用了key_leds&4来判断Capslock灯是否亮。

为什么可以这样判断呢?

键盘上的几个灯:CapsLock NumberLock ScrollLock,分别存储在了info->leds的第6,5,4,位。

利用(info->leds>>4)将7 6 5 4位的值移动到3 2 1 0位,然后 &7,即&0111b 将第3位设置为0,这样key_leds里就剩下CapsLock在第2位,NumberLock在第1位,ScrollLock在第0位。

所以key_leds 与4(0100b)做&后,就可以判断key_leds的第2位是否为1了。

好了,可以输入各种特殊字符和大小写字符了,我们试一下:

检索磁盘,实现文件读取

要完成一个dir 命令,就首先要对读到内存里的磁盘文件进行检索。

要检索文件,就要对资盘上文件存储的方式方法,存储结构有了解。

文件保存在磁盘上,是有固定格式的。

我们现在就是通过文件的固定格式把文件给检索出来。

这个固定格式可以用如下结构体来表示:

struct FILEINFO { unsigned char name[8] ext[3] type; char reserve[10]; unsigned short time date clustno; unsigned int size; };

这个结构体一共32个字节,其中
前8个字节name[8] 表示文件名。
第9-11字节ext[3]表示文件的扩展名,
第12个字节type,指文件类型,type取不同的值,代表不同的文件,比如隐藏文件,系统文件等。
第13-22个字节reserve[10]是保留位,一般不写内容,文件比较特殊时,将这些特殊信息写在这里。

第23 24 25 26,一共四个字节分别时time,date 即保存文件的时间,日期。

第27,28存放的是clustno,文件内容在磁盘上的蔟号,即文件内容在磁盘上的地址。

第29,30,31,32位为size表示文件的大小。

通过这样32个字节,我们就可以从磁盘上读取文件内容,并且知道文件的名字,保存日期,文件类型等。

我们在windows上往操作系统里放几个文件,然后尝试去看看,以上格式的文件信息存放在哪里。

首先要在编译操作系统的时候,把3个文件复制到操作系统的镜像img里:

这个在MakeFile里操作:

haribote.img : ipl10.bin haribote.sys Makefile $(EDIMG) imgin:../../../tolset/z_tools/fdimg0at.tek \ wbinimg src:ipl10.bin len:512 from:0 to:0 \ copy from:haribote.sys to:@: \ copy from:ipl10.nas to:@: \ copy from:make.bat to:@: \ imgout:haribote.img

注意:我们复制了额外的3个文件到操作系统镜像里: haribote.sys ipl10.nas make.bat。

每个文件都有32个字节的数据来保存自己的FILEINFO信息。

当我们加载操作系统的时候,这3个文件就会被复制到内存中。

常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令(4)

放在内存的0x0010 0000- 0x00267fff处。具体细节可以到 day03的教程中查看:day03:用C语言写操作系统常用系统命令,30天自制操作系统day17-day18:命令行窗口以及dir命令(5)

有了以上信息,我们就可以去内存里,把文件名读出来,然后显示了。

其实就是从内存的0x0010 2600处,把文件名读取出来,进行显示 这些主要在命令行中实现,所以,代码要写在命令行里:

#define ADR_DISKIMG 0x00100000 // 定义磁盘文件在内存中的开始地址 void console_task(struct SHEET *sheet unsigned int memtotal) { struct TIMER *timer; struct TASK *task = task_now(); int i fifobuf[128] cursor_x = 16 cursor_y = 28 cursor_c = -1; char s[30] cmdline[30]; struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR; int x y; //存放文件名的开始地址给FILEINFO结构体 struct FILEINFO *finfo = (struct FILEINFO *) (ADR_DISKIMG 0x002600); ... for (;;) { ... if (i == 2) { cursor_c = COL8_FFFFFF; } if (i == 3) { boxfill8(sheet->buf sheet->bxsize COL8_000000 cursor_x cursor_y cursor_x 7 cursor_y 15); cursor_c = -1; } if (256 <= i && i <= 511) { if (i == 8 256) {// if (cursor_x > 16) { putfonts8_asc_sht(sheet cursor_x cursor_y COL8_FFFFFF COL8_000000 " " 1); cursor_x -= 8; } } else if (i == 10 256) { //如果是回车键, putfonts8_asc_sht(sheet cursor_x cursor_y COL8_FFFFFF COL8_000000 " " 1); cmdline[cursor_x / 8 - 2] = 0; cursor_y = cons_newline(cursor_y sheet); if (strcmp(cmdline "mem") == 0) {//如果回车前输入了mem // 进行内存检查 sprintf(s "total %dMB" memtotal / (1024 * 1024)); putfonts8_asc_sht(sheet 8 cursor_y COL8_FFFFFF COL8_000000 s 30); cursor_y = cons_newline(cursor_y sheet); sprintf(s "free %dKB" memman_total(memman) / 1024);//运行内存检查函数 putfonts8_asc_sht(sheet 8 cursor_y COL8_FFFFFF COL8_000000 s 30); cursor_y = cons_newline(cursor_y sheet); cursor_y = cons_newline(cursor_y sheet); } else if (strcmp(cmdline "cls") == 0) { // 如果回车前输入了cls for (y = 28; y < 28 128; y ) { for (x = 8; x < 8 240; x ) { sheet->buf[x y * sheet->bxsize] = COL8_000000; } } sheet_refresh(sheet 8 28 8 240 28 128); cursor_y = 28; } else if (strcmp(cmdline "dir") == 0) {//如果回车前输入了dir for (x = 0; x < 224; x ) {//遍历所有的文件信息结构体 因为这里最多可以存放225个文件 if (finfo[x].name[0] == 0x00) {//如果文件名的第一个字符为0,不存在文件,退出 break; } if (finfo[x].name[0] != 0xe5) {//如果文件名的第一次字符不是0xe5 说明这里有文件 if ((finfo[x].type & 0x18) == 0) { //如果文件类型是正常的 sprintf(s "filename.ext }" finfo[x].size);//把文件大小打印到s中 for (y = 0; y < 8; y ) { s[y] = finfo[x].name[y]; //把文件名字放到s中 } s[ 9] = finfo[x].ext[0]; // 把文件后缀放到s中 s[10] = finfo[x].ext[1]; s[11] = finfo[x].ext[2]; // 把存放了文件信息的s绘制到窗口里 putfonts8_asc_sht(sheet 8 cursor_y COL8_FFFFFF COL8_000000 s 30); // 换行 cursor_y = cons_newline(cursor_y sheet); } } } cursor_y = cons_newline(cursor_y sheet); } else if (cmdline[0] != 0) { // 如果回车前输入了内容,但是这些内容我们没有处理,就输出Bad command putfonts8_asc_sht(sheet 8 cursor_y COL8_FFFFFF COL8_000000 "Bad command." 12); cursor_y = cons_newline(cursor_y sheet); cursor_y = cons_newline(cursor_y sheet); } // 输出命令符号”>” putfonts8_asc_sht(sheet 8 cursor_y COL8_FFFFFF COL8_000000 ">" 1); cursor_x = 16; } else { if (cursor_x < 240) { s[0] = i - 256; s[1] = 0; cmdline[cursor_x / 8 - 2] = i - 256; putfonts8_asc_sht(sheet cursor_x cursor_y COL8_FFFFFF COL8_000000 s 1); cursor_x = 8; } } } if (cursor_c >= 0) { boxfill8(sheet->buf sheet->bxsize cursor_c cursor_x cursor_y cursor_x 7 cursor_y 15); } sheet_refresh(sheet cursor_x cursor_y cursor_x 8 cursor_y 16); } } }

第51行至72行,我们对0x0100 2600处的文件信息进行了提取,并显示。

其实,提取文件名字,文件大小挺简单的。就是把文件放到软盘里,然后复制到内存里的那段汇编代码不那么容易理解。

那么在这段代码中,其实,不光完成了对 dir命令的响应,还有对mem cls等命令的响应。

还有实现换行的函数cons_newline(cursor_y sheet)

int cons_newline(int cursor_y struct SHEET *sheet) { int x y; if (cursor_y < 28 112) {// 如果光标的y坐标小于112 cursor_y = 16; // 换行,光标的y坐标加16 } else {// 如果光标大于112 // 实现滚动 for (y = 28; y < 28 112; y ) { for (x = 8; x < 8 240; x ) { //将字符移动到上一行 sheet->buf[x y * sheet->bxsize] = sheet->buf[x (y 16) * sheet->bxsize]; } } // 把超时112以下的窗口设置为0,即背景 for (y = 28 112; y < 28 128; y ) { for (x = 8; x < 8 240; x ) { sheet->buf[x y * sheet->bxsize] = COL8_000000; } } sheet_refresh(sheet 8 28 8 240 28 128); } return cursor_y; }

所以,cons_newline函数不仅实现了换行,还实现了内容的滚动。

我们再看看具体是如何实现了cls mem dir等命令字符串的传递的。

我们知道,键盘的键值只有在操作系统所在的任务中能够解读,在console_task函数中是不能解读的。但是我们命令行窗口又需要键值,怎么办的?

想办法把键值从操作系统主程序中传递出来。其实这就是进程间的通信。

操作系统本身的进程的信息传递给命令行进程。

如何传递呢?利用FIFO数组。

我们利用对FIFO数组检索,如果传过来的键值不是退格,不是回车,那么就把这个键值存到cmdline中

在用户输入回车时,就对比cmdline和"mem" 对比cmdline和"cls",对比cmdline和"dir",这样就能够去运行相应的命令了。

设置cmdline的代码在上面console_task函数的第87行。

清除命令cls很简单,就是把窗口对应的显示缓存冲区的设为0,然后刷新。

检查内存的命令mem也是之前写的内存管理代码的。

综上,我们一共实现了3个命令:mem cls dir,并且实现了滚动显示。

我们去看一下演示效果:

视频中的光标切换在console_task任务中实现:

void console_task(struct SHEET *sheet unsigned int memtotal) { // cursor_c=-1,表示光标不显示 int i fifobuf[128] cursor_x = 16 cursor_y = 28 cursor_c = -1; //存放文件名的开始地址给FILEINFO结构体 struct FILEINFO *finfo = (struct FILEINFO *) (ADR_DISKIMG 0x002600); ... for (;;) { ... if (i == 2) { // 当在操作系统主函数中按下tab键,发送2过来时, cursor_c = COL8_FFFFFF;// 把光标的颜色设置为白色,表示光标可以继续显示 } if (i == 3) {//当发送3时 boxfill8(sheet->buf sheet->bxsize COL8_000000 cursor_x cursor_y cursor_x 7 cursor_y 15); cursor_c = -1;//将光标设置为-1,表示不显示 } ... } }

我们去看看操作系统主函数中对tab键的操作:

if (i == 256 0x0f) { // 如果tab键被按下 if (key_to == 0) {//如果当前key_to的值为0 key_to = 1;//就设置key_to=1 make_wtitle8(buf_win sht_win->bxsize "task_a" 0); //把console激活 make_wtitle8(buf_cons sht_cons->bxsize "console" 1); cursor_c = -1; //把task_a的光标隐藏 boxfill8(sht_win->buf sht_win->bxsize COL8_FFFFFF cursor_x 28 cursor_x 7 43); fifo32_put(&task_cons->fifo 2); //给console_task发送2,激活console_task中的光标 } else { key_to = 0; make_wtitle8(buf_win sht_win->bxsize "task_a" 1); make_wtitle8(buf_cons sht_cons->bxsize "console" 0); cursor_c = COL8_000000; // fifo32_put(&task_cons->fifo 3); // } sheet_refresh(sht_win 0 0 sht_win->bxsize 21); sheet_refresh(sht_cons 0 0 sht_cons->bxsize 21); }

可以看到,光标的调度,在操作系统的主函数中,主函数给console_task发送2,表示要激活console_task中的光标。主函数给console_task 发送3,表示要事任务console_task的光标隐藏。

总结:

至此,我们完成了操作系统中的几个基本的应用程序:命令行。

这个简单的命令行可以实现3个命令:mem cls dir.

不过这3个命令都不是调用的新的并行的任务。而是在命令行任务内部串行执行的。

其实我们也可以写一个命令,可以建立新的命令行窗口出来。

我们建立命令行任务,并没有学习新的知识点,只是用了我们已经准备好的多任务管理器taskctl 多图层管理器sheetctl 以及内存管理器memman。

为了实现dir命令,我们学习了文件保存在磁盘上的结构:32个字节组成的FIFLEINFO结构体。

只要找到磁盘上FIFLINFO结果提存在的位置,就可以遍历处磁盘上的文件名字,文件大小。

今天涵盖了2天的内容day17 day18.

在明天day19,我们会继续完善命令行,在dir命令的基础上,开发出type命令,显示出文件的内容,比如显示出make.bat里的内容。

猜您喜欢: