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){/*不是命令,也不是空行 */