Day18
一些感想以及优化
可改进的光标闪烁
因为目前只有两个窗口,所以可以通过按tab来判断当前是哪个窗口在执行,从而每个任务都要判断是否该显示光标。但是这样其实不太好,因为每个任务里还要和别的任务产生一些关联(这个应该是不必要的)。个人感觉比较好的方法是当这个任务被唤醒的时候,显示光标,休眠的时候则停止光标的计时。不过目前看起来2个窗口的时候,这么写也没问题(而且貌似这两个都是在前台活动的窗口)。那就先按照这样写吧。估计以后窗口多了,作者又要自己改了。
回车与窗口滚动
本质上都是对像素进行的操作。所以待会直接看代码应该就足够了
文件存储方式
当写到dir命令的时候,就不得不知道文件在磁盘里是怎么存储的:
struct FILEINFO
{
unsigned char name[8], ext[3], type;
char reserve[10];
unsigned short time, date, clustno;
unsigned int size;
};
name: 8字节文件名,全大写。不足时用空格补足(超过没说该怎么办)
若第一个字节为0xe5,则代表文件被删除
若第一个字节为0x00,则代表不包含任何文件名信息
ext:3字节扩展名,全大写。不足用空格补足
type:文件的属性信息
- 0x00 一般文件
- 0x01 只读
- 0x02 隐藏
- 0x04 系统
- 0x08 非文件信息
- 0x10 目录
- 0x20 一般文件
文件多种方式可以或起来(上面都是可以位或的)
reserve: 10字节保留。Windows定义的
time: WORD整数,存放文件时间
date: WORD整数,存放文件日期
clustno:(簇号) WORD整数,文件的内容从磁盘上的哪个扇区开始存放。
size: DWORD整数,存放文件大小
对于命令的改进比较方法
本节目前只加了三个命令:mem、cls、dir。但是可想而知命令多了以后,光if比较就会判断上很长时间。这个点以后也可以优化,改成哈希+函数指针什么的应该会比较好。
因为今天还是初步的命令行,各种功能还不太完善。所以想到了两个目前看起来需要优化的点。看看以后会是怎么样。
控制光标闪烁(1)
harib15a
作者通过tab键的切换,来判断当前窗口是否应该闪烁光标。先从HariMain窗口的光标开始。
对于tab键,额外添加一个状态来记录是否应该显示当前光标
if(i == 256 + 0x0f) // Tab
{
if(key_to == 0)
{
key_to = 1;
make_wtitle8(buf_win, sht_win->bxsize, "task_a", 0);
make_wtitle8(buf_cons, sht_cons->bxsize, "console", 1);
cursor_c = -1; // 不显示光标
boxfill8(sht_win->buf, sht_win->bxsize, COL8_FFFFFF, cursor_x, 28, cursor_x + 7, 43);
}
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; // 显示光标
}
sheet_refresh(sht_win, 0, 0, sht_win->bxsize, 21);
sheet_refresh(sht_cons, 0, 0, sht_cons->bxsize, 21);
}
如果状态切换了,需要重新显示光标
if(cursor_c >= 0)
{
boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
}
定时器部分,如果cursor当前是-1的话,就不要绘图
if (i <= 1) { /* カーソル用タイマ */
if (i != 0) {
timer_init(timer, &fifo, 0); /* 次は0を */
if (cursor_c >= 0) {
cursor_c = COL8_000000;
}
} else {
timer_init(timer, &fifo, 1); /* 次は1を */
if (cursor_c >= 0) {
cursor_c = COL8_FFFFFF;
}
}
timer_settime(timer, 50);
if (cursor_c >= 0) {
boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
}
}
这个是作者写的光标定时器部分,但是这么多cursorc >= 0不太爽,所以改一下顺序,其实也差不多
if(i <= 1) // 光标用定时器
{
timer_init(timer, &fifo, 0); // 随便置0
timer_settime(timer, 50);
if(cursor_c >= 0)
{
if(i != 0)
{
// 0 已经被置过了
cursor_c = COL8_000000;
}
else
{
timer_init(timer, &fifo, 1); // 下次置1(覆盖了)
cursor_c = COL8_FFFFFF;
}
boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
}
}
语句之间的顺序和之前会不太一样。。但是估计也没什么太大改变吧。。
修改完HariMain的光标,然后是console的
控制光标闪烁(2)
harib15b
为了让console知道自己该不该闪烁,通过Harimain向其FIFO里传递信息。。。
if(i == 256 + 0x0f) // Tab
{
if(key_to == 0)
{
key_to = 1;
make_wtitle8(buf_win, sht_win->bxsize, "task_a", 0);
make_wtitle8(buf_cons, sht_cons->bxsize, "console", 1);
cursor_c = -1; // 不显示光标
boxfill8(sht_win->buf, sht_win->bxsize, COL8_FFFFFF, cursor_x, 28, cursor_x + 7, 43);
fifo32_put(&task_cons->fifo, 2); // 命令行窗口光标ON
}
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); // 命令行窗口光标OFF
}
sheet_refresh(sht_win, 0, 0, sht_win->bxsize, 21);
sheet_refresh(sht_cons, 0, 0, sht_cons->bxsize, 21);
}
主函数加了两行,然后是console
if(i <= 1) // 光标用定时器
{
if(i != 0)
{
timer_init(timer, &task->fifo, 0); // 下次置为0
if(cursor_c >= 0)
{
cursor_c = COL8_FFFFFF;
}
}
else
{
timer_init(timer, &task->fifo, 1); // 下次置为1
if(cursor_c >= 0)
{
cursor_c = COL8_000000;
}
}
timer_settime(timer, 50);
}
if(i == 2) // 光标ON
{
cursor_c = COL8_FFFFFF;
}
if(i == 3) // 光标OFF
{
boxfill8(sheet->buf, sheet->bxsize, COL8_000000, cursor_x, 28, cursor_x + 7, 43);
cursor_c = -1;
}
为了稳定性(省事),这里就不魔改代码逻辑了。
光标的问题到这里就解决好了。
对回车键的支持
harib15c
回车键的响应方式,就是画一个换行,然后改变当前光标的坐标。
首先,当接收到’\n’时(ASCII值为10,键盘输入为0x1c),主程序向控制台FIFO发送一个数据
if(i == 256 + 0x1c) // 回车键
{
if(key_to != 0) // 发送至命令行窗口
{
fifo32_put(&task_cons->fifo, 10 + 256);
}
}
控制台接收到数据之后,进行重绘操作
if(i == 10 + 256) // 回车键
{
if(cursor_y < 28 + 112)
{
// 用空格将光标擦除
putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
cursor_y += 16;
// 显示提示符
putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
cursor_x = 16;
}
}
对窗口滚动的支持
harib15d
元素上移+最下面一行涂黑
if(i == 10 + 256)
{
// Enter
// 用空格将光标擦除
putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
if(cursor_y < 28 + 112)
{
cursor_y += 16; // 换行
}
else
{
// 滚动
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];
}
}
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);
}
// 显示提示符
putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
cursor_x = 16;
}
没什么好说的)
mem命令
harib15e
之前只是涉及了图像的改变,所以现在要把字符也记录下来,方便我们进行指令判断。
对于一般字符,把它记录到缓冲区里:
// 一般字符
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;
}
cmdline是缓冲区,用于记录当行的字符。
记录完成之后,当遇到换行的时候,就要进行指令的判断
if(i == 10 + 256)
{
// Enter
// 用空格将光标擦除
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(cmdline[0] == 'm' && cmdline[1] == 'e' && cmdline[2] == 'm' && cmdline[3] == 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(cmdline[0] != 0)
{
// 不是命令,也不是空行
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;
}
但是,memtotal也需要从主函数传进来。这样的话就只能再在主函数里修改栈了。
task_cons->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12;
*((int *) (task_cons->tss.esp + 4)) = (int) sht_cons;
*((int *) (task_cons->tss.esp + 8)) = memtotal;
之前是-8,现在是-12了。
为了方便理解,新换行(包括滚动)也写成了一个函数,就是把之前的复制到这里来。
int cons_newline(int cursor_y, struct SHEET *sheet)
{
int x, y;
if(cursor_y < 28 + 112)
{
cursor_y += 16; // 换行
}
else
{
// 滚动
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];
}
}
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;
}
接着搞下一条指令
cls命令
harib15f
把之前的比较改成strcmp来比较。
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;
}
也没什么问题。。
dir命令
harib15g
在了解dir命令之前,先要知道文件是怎么存放的。。(参考最上面)
然后因为我们一开始也把10个柱面加载到了内存中(0x00102600开始),
然后,定义这个结构体
struct FILEINFO
{
unsigned char name[8], ext[3], type;
char reserve[10];
unsigned short time, date, clustno;
unsigned int size;
};
确定指针位置
#define ADR_DISKIMG 0x00100000
if(strcmp(cmdline, "dir") == 0)
{
// dir命令
for (x = 0; x < 224; x++)
{
if(finfo[x].name[0] == 0x00)
{
break;
}
if(finfo[x].name[0] != 0xe5)
{
if((finfo[x].type & 0x18) == 0)
{
sprintf(s, "filename.ext %7d", finfo[x].size); // 先写个默认的
for(y = 0; y < 8; ++y)
{
s[y] = finfo[x].name[y];
}
s[9] = finfo[x].ext[0];
s[10] = finfo[x].ext[1];
s[11] = finfo[x].ext[2];
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);
}
这样就做好了3个命令。今天比起前三天的多任务来说还是简单好多了)