【操作系统】30天自制操作系统--(18)应用程序

        本章主要介绍了文件处理的相关操作以及尝试制作第一个应用程序hlt。

一 type命令

        与Linux里面的type命令不同,Windows命令行中的type命令是用来查看文件内容的。在自制的操作系统中,我们模仿的是Windows的type功能。

        怎么才能把文件内容读到命令行窗口中呢?首先要找到它存放的扇区。这个在上一章【操作系统】30天自制操作系统--(17)命令行窗口2中有所描述,关注clustno这个变量,存放的就是文件的存储扇区:

struct FILEINFO {
	unsigned char name[8], ext[3], type;  //1.文件名  2.扩展名  3.文件类型
	char reserve[10];                     //4.保留
	unsigned short time, data, clustno;   //5.存放时间  6.存放日期  7.存放扇区
	unsigned int size;                    //8.文件大小
};

        将 clustno 的值与在磁盘映像中找到他们的位置对应起来:

         可以发现,IPL10.NAS与MAKE.BAT相差 6 个扇区,实际位置相差0xc00字节,正好对应0xc00 / 6 = 0x200 = 512字节,也就是一个扇区的容量。那么可以倒推得到扇区0的位置是0x3e00,扇区 clustno 对应的位置 = clustno * 512 + 0x3e00。由此,我们只要将文件的内容逐字节读出来并且显示在屏幕上就可以了:

char *p;
// ...
if (cmdline[0] == 't' && cmdline[1] == 'y' && cmdline[2] == 'p' && cmdline[3] == 'e' && cmdline[4] == ' ') {
    // type命令
    for (y = 0; y < 11; y++) {
        s[y] = ' ';
    }
    y = 0;
    for (x = 5; y < 11 && cmdline[x] != 0; x++) {
        if (cmdline[x] == '.' && y <= 8) {
            y = 8;
        } else {
            s[y] = cmdline[x];
            if ('a' <= s[y] && s[y] <= 'z') {
                // 转大写
                s[y] -= 0x20;
            }
            y++;
        }
    }
    for (x = 0; x < 224; ) {
        if (finfo[x].name[0] == 0x00) {
            break;
        }
        if ((finfo[x].type & 0x18) == 0) {
            for (y = 0; y < 11; y++) {
                if (finfo[x].name[y] != s[y]) {
                    goto type_next_file;
                }
            }
            break; 
        }
        type_next_file:
        x++;							
    }
    if (x < 224 && finfo[x].name[0] != 0x00) {
        // 找到文件,则显示文件内容(从对应内存区间打印字符)
        y = finfo[x].size;
        p = (char *) (finfo[x].clustno * 512 + 0x003e00 + ADR_DISKIMG);
        cursor_x = 8;
        for (x = 0; x < y; x++) {
            // print word by word
            s[0] = p[x];
            s[1] = 0;
            putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
            cursor_x += 8;
            if (cursor_x == 8 + 240) {
                // 到达最右端后换行
                cursor_x = 8;
                cursor_y = cons_newline(cursor_y, sheet);
            }
        }
    } else {
        // 没找到文件,则显示“File not found.”
        putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
        cursor_y = cons_newline(cursor_y, sheet);
    }
    cursor_y = cons_newline(cursor_y, sheet);
}

         完成上面的改动,能够查找文件,并从对应内存区间将文件内容拎出来。但是如果文件内容含有一些奇奇怪怪的内容(制表符、换行符、回车符、汉字等),就需要额外的特殊处理:

 

if (s[0] == 0x09) {  //制表符处理
    for (;;) {
        putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
        cursor_x += 8;
        if (cursor_x == 8 + 240) {
            cursor_x = 8;
            cursor_y = cons_newline(cursor_y, sheet);
        }
        if (((cursor_x - 8) & 0x1f) == 0) {
            // 以32分割
            break;	
        }
    }
} else if (s[0] == 0x0a) {  //换行符处理
    cursor_x = 8;
    cursor_y = cons_newline(cursor_y, sheet);
} else if (s[0] == 0x0d) {  //回车符处理
    // do nothing
} else {
    putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
    cursor_x += 8;
    if (cursor_x == 8 + 240) {
        cursor_x = 8;
        cursor_y = cons_newline(cursor_y, sheet);
    }
}

        这样便能通过type命令,正确显示大部分的文件内容(尚不支持汉字字符)。

二 支持FAT(file allocation table,文件分配表)

        上面的处理针对长度小于512字节的文件适用,因为文件是存在一个扇区内的。但是如果遇到大于512字节的文件的话,在windows中可能不是存在连续的扇区中的,这种情况就是磁盘碎片

        那么操作系统是怎么管理磁盘碎片的呢?答案是通过位于位于 0x000200~0x0013ff 的FAT(file allocation table,文件分配表),来记录某个文件的下一段存放的扇区信息

        对于FAT需要关注两点:

【1】FAT是经过微软的算法压缩过的,解码之后是下面这样:

        我们对于文件信息部分的 clustno 进行解读:
        已知clustno = 2,因此我们读取 0x004200~0x0043ff这512个字节。那么接下来应该读取哪里的数据呢? 我们来看FAT的第2号记录,其值为003,也就是说下面的部分存放在 clustno = 3这个位置。按照顺序,我们一直读取到clustno = 57(0x39),参照对应的 FAT 记录,57 号后面是 FFF,代表文件的末尾(一般来说,如果遇到FF8~ FFF的值,就代表文件数据到此结束)。

         命令行任务 console_task 中实现代码如下:

// ...
int *fat = (int *) memman_alloc_4k(memman, 4 * 2880);
// 加载 FAT
file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
// ...
else if (strncmp(cmdline, "type ", 5) == 0) {
	// 接收输入、转换格式到 s[8]
    // 找到文件格式所在的位置 x
    if (x < 224 && finfo[x].name[0] != 0x00) {
        // 加载文件到 p
        p = (char *) (finfo[x].clustno * 512 + 0x003e00 + ADR_DISKIMG);
        file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
        cursor_x = 8;
        for (y = 0; y < finfo[x].size; y++) {
            //
            s[0] = p[y];
            s[1] = 0;
            if (s[0] == 0x09) {	//
                for (;;) {
                    putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 1);
                    cursor_x += 8;
                    if (cursor_x == 8 + 240) {
                        cursor_x = 8;
                        cursor_y = cons_newline(cursor_y, sheet);
                    }
                    if (((cursor_x - 8) & 0x1f) == 0) {
                        break;	//
                    }
                }
            } else if (s[0] == 0x0a) {	//
                cursor_x = 8;
                cursor_y = cons_newline(cursor_y, sheet);
            } else if (s[0] == 0x0d) {	//
                //
            } else {	//
                putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, s, 1);
                cursor_x += 8;
                if (cursor_x == 8 + 240) {
                    cursor_x = 8;
                    cursor_y = cons_newline(cursor_y, sheet);
                }
            }
        }
        memman_free_4k(memman, (int) p, finfo[x].size);
    } else {
		// 找不到文件
        putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
        cursor_y = cons_newline(cursor_y, sheet);
    }
    cursor_y = cons_newline(cursor_y, sheet);
}
/* 将磁盘映像中的FAT解压缩 */
void file_readfat(int *fat, unsigned char *img)
{
	int i, j = 0;
	for (i = 0; i < 2880; i += 2) {
		fat[i + 0] = (img[j + 0]      | img[j + 1] << 8) & 0xfff;
		fat[i + 1] = (img[j + 1] >> 4 | img[j + 2] << 4) & 0xfff;
		j += 3;
	}
	return;
}

/* 按照FAT中的顺序,从各个扇区加载文件 */
void file_loadfile(int clustno, int size, char *buf, int *fat, char *img)
{
	int i;
	for (;;) {
		if (size <= 512) {
			for (i = 0; i < size; i++) {
				buf[i] = img[clustno * 512 + i];
			}
			break;
		}
		for (i = 0; i < 512; i++) {
			buf[i] = img[clustno * 512 + i];
		}
		size -= 512;
		buf += 512;
		clustno = fat[clustno];
	}
	return;
}

【2】对于分段存放的文件,读取文件是一个接力的过程,不能允许其中有损坏,因为一旦中间出现问题,那么后面就都乱了。所以微软将FAT视为最重要的磁盘信息,为此在磁盘中存放了2份。第1份FAT位于0x000200~0x0013ff,第2份位于 0x001400~0x0025ff。其中第2份是备份FAT,内容和第1份完全相同。

三 制作应用程序(HLT)

        设计一个可以执行HLT功能的应用程序,实现步骤如下:

【1】应用程序的执行首先需要创建应用程序  hlt.nas:

[BITS 32]
fin:
	HLT
	JMP	fin

【2】将上面的文件保存为 hlt.nas,然后用 nask 进行汇编,生成专属可执行文件 hlt.hrb。这个扩展名是自定义的,类似于Windows中的exe。


【3】像type命令一样,我们用file_loadfile将文件的内容读到内存中去。但是应用程序不知道自己被读到了那个内存地址,所以我们需要为其创建一个内存段(这边注册为1003号,因为1~2号由dsctbl.c使用, 而3~1002号由mtask.c使用,所以我们用了1003号)。

【4】段创建好之后,只要goto到该段中的程序(goto相当于汇编里面的farjmp),程序就会开始运行了。

        代码实现如下:

// ...
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT;
// ...
if (strcmp(cmdline, "hlt") == 0) {  //hlt命令
    // 启动应用程序 hlt.hrb
    for (y = 0; y < 11; y++) {
        s[y] = ' ';
    }
    s[0] = 'H';
    s[1] = 'L';
    s[2] = 'T';
    s[8] = 'H';
    s[9] = 'R';
    s[10] = 'B';
    for (x = 0; x < 224; ) {
        if (finfo[x].name == 0x00) {
            break;
        }
        if ((finfo[x].type & 0x18) == 0) {
            for (y = 0; y < 11; y++) {
                if (finfo[x].name[y] != s[y]) {
                    goto hlt_next_file;
                }
            }
            break;   //找到文件
        }

hlt_next_file: 
       
        x++;
    }
    if (x < 224 && finfo[x].name[0] != 0x00) {
        // 找到文件
        p = (char *) memman_alloc_4k(memman, finfo[x].size);
        file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
        set_segmdesc(gdt + 1003, finfo[x].size - 1, (int) p, AR_CODE32_ER);
        farjmp(0, 1003 * 8);
        memman_free_4k(memman, (int) p, finfo[x].size);
    } else {
        // 没有找到文件
        putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, "File not found.", 15);
        cursor_y = cons_newline(cursor_y, sheet);
    }
    cursor_y = cons_newline(cursor_y, sheet);
}

        完成之后,在命令行窗口中执行hlt命令,即可以调用 hlt.hrb 应用程序,成功将程序HLT住。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值