30天自制操作系统(第19天)

19.1 type命令 - 打印文件内容

按照对windows系统的了解,当我们输入一个文件名,并按下回车键时将在屏幕上显示文件的内容。结合第18天的学习,应该在 console_task中的按下回车键时增加一个字符串(就是文件名)比对,如果匹配上,则打印文件内容。那么文件名存储在扇区中,而扇区对应的磁盘映像中的地址具体位置在哪?通过查看磁盘映像文件,发现HARIBOTE.SYS在0x004200处、 IPL10.NAS在0x00b000处、MAKE.BAT在 0x00bc00处。
首先,从 0x0039 0x003f开始看,把它们相减,0x3f – 0x39 = 6 ,也就是说, culstno 每增加 6 ,磁盘映像中的位置就增加 0xc00个字节,将 0xc00 除以 6 得到 0x200 ,即 512个字节。如果以clustno = 0x0002为起点出发倒推,clustno = 0x0000应该就相当于0x003e00这个位置。
磁盘映像中的地址 = clustno * 512 + 0x003e00
思路:①先从cmdline中读取输入的文件名,并将文件名字符串全部变成大写字母;②遍历所有文件,当文件名匹配时从该 磁盘映像中的地址开始读取文件内容并打印,当前窗口行全部输出字符后自动换到下一行继续打印。
void console_task(struct SHEET *sheet, unsigned int memtotal){
	(中略)
	char s[30], cmdline[30], *p;
	(中略)
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			(中略)
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					(中略)
				}else if(i == 10+256){/*回车键*/
					(中略)
					if(strcmp(cmdline, "mem") == 0){
						(中略)
					}else if(strcmp(cmdline, "cls") == 0){/*将整个命令窗口全部涂黑*/
						(中略)
					}else if(strcmp(cmdline, "dir") == 0){/*打印已创建的文件信息*/
						(中略)
					}else if(cmdline[0] == 't' && cmdline[1] == 'y' && cmdline[2] == 'p' && cmdline[3] == 'e'){
						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_type;
								}
								break;/*找到文件*/
							}
type_next_type:
							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++){
								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{
							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);
					}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;
				}else{/*一般字符*/
					(中略)
				}
			}
			(中略)
		}
	}
}

19.2 type命令改良

当在命令窗口输入‘type ipl10.nas’时,会出现乱码。是特殊字符编码存在问题, 忘记处理换行和制表符的字符编码了。
0x09 …… 制表符:显示空格直到 x 4 整除为止
0x0a …… 换行符:换行
0x0d …… 回车符:忽略
当读出的字符为0x09时,将执行如下操作:先打印一个空格字符,更新cursor_x,如果cursor_x超出窗口长度则将光标移到下一行的头。
void console_task(struct SHEET *sheet, unsigned int memtotal){
	(中略)
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			(中略)
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					(中略)
				}else if(i == 10+256){/*回车键*/
					(中略)
					}else if(strncmp(cmdline, "type ", 5) == 0){
						(中略)
						//接下来就是输出文件内容
						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++){
								s[0] = p[x];
								s[1] = 0;
								if(s[0] == 0x09){/*制表符*/
									for(;;){
										putfonts8_asc_sht(sheet, cursor_x, cursor_y, COL8_FFFFFF, COL8_000000, " ", 4);
										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;//被32整除则退出,打印多个空格(不一定是4个),用于表示一个制表符
									}
								}else if(s[0] == 0x0a){/*换行*/
									cursor_x = 8;
									cursor_y = cons_newline(cursor_y, sheet);
								}else if(s[0] == 0x0b){/*回车*/
									
								}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);
									}
								}
							}
						}else{
							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);
					}else if(cmdline[0] != 0){/*不是命令,也不是空行 */
						(中略)
					}
					putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
					cursor_x = 16;
				}else{/*一般字符*/
					(中略)
				}
			}
			(中略)
		}
	}
}

19.3 对FAT的支持

现在的 type 命令,肯定可以正确显示文件开头的 512个字节的内容,但是如果遇到大于 512个字节的文件,中间可能就会突然显示出其他文件的内容。
对于文件的下一段存放在哪里,在磁盘中是有记录的,只要分析这个记录,就可以正确读取文件内容了。这个记录在哪里呢?它位于从 0 柱面、 0 磁头、2扇区开始的 9 个扇区中,在磁盘映像中相当于 0x000200 0x0013ff。这个记录被称为FAT ,是 “file allocation table” 的缩写,翻译过来叫作 文件分配表 ”。
转换方式如下:
F0 FF FF → FF0 FFF
                                                            ab cd  ef      dab  efc
//将磁盘映像文件中的数据转换成FAT(文件分配表file allocation table)
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+2]<<4 | img[j+1]>>4)&0xfff;
		j += 3;
	}
	return;
}
//加载文件数据
void file_loadfile(int clustno, int size, char *buf, int *fat, char *img){
	int i;
	for(;;){
		if(size <= 512){//文件大小<512字节
			for(i = 0; i < size; i++)
				buf[i] = img[clustno*512+i];
			break;
		}
		for(i = 0; i < 512; i++)//仅读前512字节
			buf[i] = img[clustno*512+i];
		size -= 512;
		buf += 512;
		clustno = fat[clustno];
	}
	return;
}

由于文件大于512字节,所以开辟一块内存存放文件数据。在数据打印完后,释放内存。

void console_task(struct SHEET *sheet, unsigned int memtotal){
	(中略)
/*这里*/ int *fat = (int *) memman_alloc_4k(memman, 4 * 2880);
	//从0柱面、0磁头、2扇区在磁盘映像中相当于0x000200
/*这里*/ file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
	for(;;){
		io_cli();
		if(fifo32_status(&task->fifo) == 0){
			task_sleep(task);
			io_sti();
		}
		else{
			(中略)
			if(256 <= i && i <= 511){ /*键盘数据*/
				if(i == 8+256){/*退格键*/
					(中略)
				}else if(i == 10+256){/*回车键*/
					(中略)
					}else if(strncmp(cmdline, "type ", 5) == 0){
						(中略)
						//接下来就是输出文件内容
						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));
							cursor_x = 8;
/*这里*/                    for(y = 0; y < finfo[x].size; y++){
/*这里*/                    	s[0] = p[y];
								s[1] = 0;
								(中略)
							}
/*这里*/                    memman_free_4k(memman, (int) p, finfo[x].size);
						}else{
							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);
					}else if(cmdline[0] != 0){/*不是命令,也不是空行 */
						(中略)
					}
					putfonts8_asc_sht(sheet, 8, cursor_y, COL8_FFFFFF, COL8_000000, ">", 1);
					cursor_x = 16;
				}else{/*一般字符*/
					(中略)
				}
			}
			(中略)
		}
	}
}

19.4 第一个应用程序

按照19.3节,可将文件内容读到一块内存中。而应用程序并不知道内容被读到哪个内存地址,按照任务切换(mtask.c)操作,可将该内容放到GDT中,并调用farjmp实现任务切换功能。
回顾一下:farjmp(0, new_task->sel); 参数1一般为0,参数2为需要跳转到的GDT地址。(为何要*8,是因为每个GDT元素占64位,即8字节)
}else if(strcmp(cmdline, "hlt") == 0){
	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[0] == 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);
}else if(cmdline[0] != 0){/*不是命令,也不是空行 */
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值