5 mem命令(harib15e)

之前我们已经成功实现了屏幕滚动,现在该是到了让它执行命令的时候了。不过这值得纪念的第一个命令到底该花落谁家呢?纠结了一番之后,还是决定来做mem命令吧。

mem命令就是memory的缩写,也就是用来显示内存使用情况的命令。画面上虽然已经显示了剩余内存,不过现在被命令行窗口挡住看不见了。因此我们就不让它继续显示了,改成用命令来查询好了。

而且,这种功能对于一个命令来说也是很容易实现的,比起之前在背景上面显示剩余内存来说,还是用命令来查询比较像个操作系统的样子。既然如此,那我们干脆顺便把按键编码、鼠标坐标的显示也一起去掉吧。

■■■■■

既然方针已定,那就开工吧。首先从去掉多余的显示开始,这个非常容易,我们给这次去掉的行加上了“//”注释标记。

本次的bootpack.c节选

  1. void HariMain(void)
  2. {
  3. (中略)
  4. // sprintf(s, "(%3d, %3d)", mx, my);
  5. // putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
  6. // sprintf(s, "memory %dMB free : %dKB",
  7. // memtotal / (1024 * 1024), memman_total(memman) / 1024);
  8. // putfonts8_asc_sht(sht_back, 0, 32, COL8_FFFFFF, COL8_008484, s, 40);
  9. (中略)
  10. for (;;) {
  11. (中略)
  12. if (fifo32_status(&fifo) == 0) {
  13. (中略)
  14. } else {
  15. (中略)
  16. if (256 <= i && i <= 511) { /*键盘数据 */
  17. // sprintf(s, "%02X", i - 256);
  18. // putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
  19. (中略)
  20. } else if (512 <= i && i <= 767) { /*鼠标数据*/
  21. if (mouse_decode(&mdec, i - 512) != 0) {
  22. // /*数据已达到3个字节,显示*/
  23. // sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
  24. // if ((mdec.btn & 0x01) != 0) {
  25. // s[1] = 'L';
  26. // }
  27. // if ((mdec.btn & 0x02) != 0) {
  28. // s[3] = 'R';
  29. // }
  30. // if ((mdec.btn & 0x04) != 0) {
  31. // s[2] = 'C';
  32. // }
  33. // putfonts8_asc_sht(sht_back, 32, 16, COL8_FFFFFF, COL8_008484, s, 15);
  34. (中略)
  35. // sprintf(s, "(%3d, %3d)", mx, my);
  36. // putfonts8_asc_sht(sht_back, 0, 0, COL8_FFFFFF, COL8_008484, s, 10);
  37. (中略)
  38. }
  39. } else if (i <= 1) { /*光标用定时器*/
  40. (中略)
  41. }
  42. }
  43. }
  44. }

噢噢,代码居然减少了21行,真不错呢。不过接下来我们要实现mem命令,行数又会变多啦。

■■■■■

增加了mem命令的部分后,就变成了这样。

本次的bootpack.c节选

  1. void console_task(struct SHEET *sheet, unsigned int memtotal)
  2. {
  3. (中略)
  4. char s[30], cmdline[30]; /*这里!*/
  5. struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR; /*这里! */
  6. (中略)
  7. for (;;) {
  8. io_cli();
  9. if (fifo32_status(&task->fifo) == 0) {
  10. (中略)
  11. } else {
  12. (中略)
  13. if (256 <= i && i <= 511) { /*键盘数据(通过任务A) */
  14. if (i == 8 + 256) {
  15. /*退格键*/
  16. (中略)
  17. } else if (i == 10 + 256) {
  18. /*回车键*/
  19. /*将光标用空格擦除后换行*/
  20. putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
  21. /*从此开始 */ cmdline[cursor_x / 8 - 2] = 0;
  22. cursor_y = cons_newline(cursor_y, sheet);
  23. /*执行命令*/
  24. if (cmdline[0] == 'm' && cmdline[1] == 'e' && cmdline[2] == 'm' && cmdline[3] == 0) {
  25. /* mem命令*/
  26. sprintf(s, "total %dMB", memtotal / (1024 * 1024));
  27. putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
  28. cursor_y = cons_newline(cursor_y, sheet);
  29. sprintf(s, "free %dKB", memman_total(memman) / 1024);
  30. putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, s, 30);
  31. cursor_y = cons_newline(cursor_y, sheet);
  32. cursor_y = cons_newline(cursor_y, sheet);
  33. } else if (cmdline[0] != 0) {
  34. /*不是命令,也不是空行 */
  35. putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "Bad
  36. command.", 12);
  37. cursor_y = cons_newline(cursor_y, sheet);
  38. cursor_y = cons_newline(cursor_y, sheet);
  39. /*到此结束*/ }
  40. /*显示提示符*/
  41. putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
  42. cursor_x = 16;
  43. } else {
  44. /*一般字符*/
  45. if (cursor_x < 240) {
  46. /*显示一个字符之后将光标后移一位 */
  47. s[0] = i - 256;
  48. s[1] = 0;
  49. /*这里! */ cmdline[cursor_x / 8 - 2] = i - 256;
  50. putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s,1);
  51. cursor_x += 8;
  52. }
  53. }
  54. }
  55. (中略)
  56. }
  57. }
  58. }

介绍一下重点。首先我们添加了memtotal和memman两个变量,它们是执行mem命令所必需的。关于memtotal,我们采用和sheet相同的方法从HariMain传递过来,因此我们还需要改写一下HariMain。

本次的bootpack.c节选

  1. void HariMain(void)
  2. {
  3. (中略)
  4. /* sht_cons */
  5. sht_cons = sheet_alloc(shtctl);
  6. (中略)
  7. task_cons->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12; /*这里! */
  8. (中略)
  9. *((int *) (task_cons->tss.esp + 4)) = (int) sht_cons;
  10. *((int *) (task_cons->tss.esp + 8)) = memtotal; /*这里! */
  11. (中略)
  12. }

回来继续讲解console_task,我们还添加了一个cmdline变量,也就是“命令行”(command line)的缩写。这个变量用来记录通过键盘输入的内容,在“键盘数据”处理的“一般字符”部分,将输入的内容顺次累积起来。

然后,处理回车键输入的部分我们已经完全改写了。在这里出现的cons_newline是一个用来换行的函数,当到达最后一行的时候还会自动滚动。如果把换行处理直接写在程序中会导致代码非常难读,因此我们把它做成了一个单独的函数,函数的内容如下。

本次的bootpack.c节选

  1. int cons_newline(int cursor_y, struct SHEET *sheet)
  2. {
  3. int x, y;
  4. if (cursor_y < 28 + 112) {
  5. cursor_y += 16; /*换行*/
  6. } else {
  7. /*滚动*/
  8. for (y = 28; y < 28 + 112; y++) {
  9. for (x = 8; x < 8 + 240; x++) {
  10. sheet->buf[x + y * sheet->bxsize] = sheet->buf[x + (y + 16) * sheet->bxsize];
  11. }
  12. }
  13. for (y = 28 + 112; y < 28 + 128; y++) {
  14. for (x = 8; x < 8 + 240; x++) {
  15. sheet->buf[x + y * sheet->bxsize] = COL8_000000;
  16. }
  17. }
  18. sheet_refresh(sheet, 8, 28, 8 + 240, 28 + 128);
  19. }
  20. return cursor_y;
  21. }

这里应该没有什么难点,我们继续回到console_task。

当按下回车键时,换行后程序会读取cmdline的值,分析所输入的内容。如果输入的内容为“mem”,则执行mem命令,显示内存的相关信息;如不为“mem”,程序无法理解这个命令,则显示“Bad command”错误信息。不过,在什么都不输入的情况下按回车键不会显示错误信息,也不会执行什么操作。

我们的系统能够出现错误信息了,看上去是不是超有操作系统范儿呢?笔者认为错误信息特别有操作系统的感觉,于是就给加上去啦(笑)。

讲解到此结束。

■■■■■

我们来运行一下,“make run”,然后输入一些东西试试看。结果如下。

5 mem命令(harib15e) - 图1

哇,好酷!

天啊,这实在是酷毙了!操作系统的感觉大幅上升,真是越来越有干劲了!