《30天自制操作系统》19_day_学习笔记--<前面的整理ing,这两天会更...>

harib16a:
  这一部分,我们在系统中实现读取文件内容的命令type。在windows中,输入“type 文件名”,在Linux中,输入“cat 文件名”都可以显示文件的内容。我们先来看看如何读取文件本身的内容。这一节的前面部分,笔者花了大量篇幅去寻找文件内容在内存中的位置得到了以下规律:
 1、clustno 表示文件从哪个扇区开始放:

  • HARIBOTE.SYS  02 00 -> clustno = 0x0002 :0x004200
  •        IPL10.NAS  39 00 -> clustno = 0x0039 :0x00b000
  •        MAKE.BAT  3F 00 ->  clustno = 0x003F :0x00bc00

 2、磁盘映像中的地址 = clustno * 512 +0x003e00
     好了接下来我们修改CMD任务中的内容

void console_task(struct SHEET *sheet, unsigned int memtotal)
{    //.....
    char s[30], cmdline[30], *p;
    //.....
    for (;;) {
            //.....
                    } else 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++) {//从cmdline[30]的第5个字符开始,将文件名写到数组s中
                            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; ) {         //在224个文件段信息里面找
                            if (finfo[x].name[0] == 0x00) {  //第一字节为0x00,表示这一段不包含文件信息
                                break;
                            }
                            if ((finfo[x].type & 0x18) == 0) {//包含信息
                                for (y = 0; y < 11; y++) {  //该段的文件名信息和S数组比较
                                    if (finfo[x].name[y] != s[y]) {
                                        goto type_next_file; //不想等,不是这个文件,
                                    }
                                }
                                break; /* 找不到文件 */
                            }
                            type_next_file:    //继续往下找
                            x++;
                        }
                        if (x < 224 && finfo[x].name[0] != 0x00) {
                            /* 找到文件的情况,x为文件的段号 */
                            y = finfo[x].size; //文件的大小
                                            //文件内容在内存中的位置
                            p = (char *) (finfo[x].clustno * 512 + 0x003e00 + ADR_DISKIMG);
                            cursor_x = 8;    //用来记录是否到达行尾,行首初始化为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 {
                            /* 没有找到文件的情况,输出提示信息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);
                    }
            //.....
        }
}

harib16b:
  我们发现系统中的type命令无法对制表符、换行、回车进行相应的操作和处理;笔者首先给出了这三个特殊字符的字符编码,接着Windows和Linux中的换行符进行了比较,最后作者决定用以下编码来表示这三个特殊字符:

  • 0x09......制表符:这里设定4个空格的长度为一个制表符
  • 0x0a......换行符:换行
  • 0x0d......回车符:忽略

  按照上述字符编码,修改CMD任务:

void console_task(struct SHEET *sheet, unsigned int memtotal)
{    //.....
    for (;;) {
            //.....
                    } else if (strncmp(cmdline, "type ", 5) == 0) {    //只比较前面的5个字符
                            //......
                                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) {//CMD窗口宽度为8个像素笔者规定的一个制表符是4个字符,因此是32个像素的值
                                            break;         /* 被32整除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);
                                    }
                                }
                            }
                        }
            //.....
    }
}

harib16c:
  type命令有时无法正确显示文件的内容。而且如果文件大小超过了512字节,也会出现问题。怎么存储大于512字节的内容呢?我们来看看FAT和clustno.
  FAT(file allocation table):文件分配表,用来记录文件在磁盘中存放位置的表。它位于从0柱面、0磁头、2扇区开始的9个扇区中,在磁盘映像中相当于0x000200-0x0013ff。微软已经对这个FAT进行了压缩,我们将数据以3个字节为一组进行解压缩:规则如下:ab ce ef ->dab efc;微软将FAT看作是很重要的磁盘信息,为此在磁盘中放了两份FAT:0x000200-0x0013ff和0x001400-0x0025ff。

  

  clustno:表示文件从哪个扇区开始放

  • HARIBOTE.SYS   02 00 -> clustno = 0x0002 :0x004200
  • IPL10.NAS      39 00 -> clustno = 0x0039 :0x00b000
  • MAKE.BAT      3F 00 -> clustno = 0x003F :0x00bc00

  以HARIBOTE.SYS为例,读取文件内容顺序如下
    1、clustno = 0x0002;读取从clustno * 512 +0x003e00开始的512字节的内容
    2、clustno = FAT(clustno) = 3;读取从clustno * 512 +0x003e00开始的512字节的内容
    3、clustno = FAT(clustno) = 4;读取从clustno * 512 +0x003e00开始的512字节的内容
    4、依次类推,直到clustno = FAT(clustno) = (FF8-FFF)表示文件尾部
  我们根据上面的原理和信息来写程序

void console_task(struct SHEET *sheet, unsigned int memtotal)
{    //......
    int *fat = (int *) memman_alloc_4k(memman, 4 * 2880);
    //将FAT信息解压缩到fat数组中。。。
    file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
    for (;;) {
            //.......
                     else if (strncmp(cmdline, "type ", 5) == 0) { //type命令
                        //......
                        if (x < 224 && finfo[x].name[0] != 0x00) { //第x段有文件信息
                            p = (char *) memman_alloc_4k(memman, finfo[x].size);//分配一块和文件大小相同的内存p
                            //file_loadfile将文件中的内容都入到上面分配的内存中
                            file_loadfile(finfo[x].clustno, finfo[x].size, p, fat, (char *) (ADR_DISKIMG + 0x003e00));
                            cursor_x = 8;               //接下来和以前的一样,只不过这里要显示的文件内容都在数组P中
                            for (y = 0; y < finfo[x].size; y++) { //遍历文件(大小为finfo[x].size)
                                s[0] = p[y];
                                s[1] = 0;
                                //.....
                                //这里处理特殊字符,制表符,换行,回车(看一部分)!
                            }
                            memman_free_4k(memman, (int) p, finfo[x].size);//用完了之后把分配的内存p释放掉
                        }
            //......
    }
}

void file_readfat(int *fat, unsigned char *img){ //将FAT信息解压缩到fat中|规则如下:ab ce ef ->dab efc
    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;
}

void file_loadfile(int clustno, int size, char *buf, int *fat, char *img)
{    //将文件的内容都写到分配的内存中buf
    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;
}

harib16d:
  我们到这里发现代码量已经比较大了,读起来比较吃力,下面我们来对代码进行一下整理:

    • 窗口相关函数->winsows.c
    • 命令行窗口相关函数->console.c
    • 文件相关的函数->

  整理后,工程代码变得清晰了很多,虽然内容没有什么变化。

harib16e:
  前面我们已经能够读取文件的内容了,下面我们要尝试着运行应用程序。我们从下面最简单的开始,将下面四行汇编代码保存为hlt.nas,接着用nask进行汇编,生成hlt.hrb(就是haribote的缩写,这里我们用了自己定义的扩展名.hrb)。之后将hlt.hrb写到镜像中,再用前面编写的file_loadfile将hlt.hrb的内容放到一个大小相等的临时变量中p中(相当于一个缓冲区),接着把这个临时变量p加载到GDT的某个段中,最后调用farjmp()执行GDT中这个段中的程序hlt.hrb。怎么样,原理是不是很容易,下面我们看看代码具体怎么做的。

;hlt.nas 4行的汇编代码,我们将要汇编的源程序
[BITS 32]
fin:
    HLT
    JMP fin
void console_task(struct SHEET *sheet, unsigned int memtotal)
{    //.....
    for(;;)
    {    //....
                    else if (strcmp(cmdline, "hlt") == 0) {
                        /* 接收到了hlt字符串 */
                        for (y = 0; y < 11; y++) {
                            s[y] = ‘ ‘;//s初始化
                        }
                        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;   //找到第一个没有文件信息的片段(片段号退出时为X)
                            }
                            if ((finfo[x].type & 0x18) == 0) {    //有文件信息的片段
                                for (y = 0; y < 11; y++) {
                                    if (finfo[x].name[y] != s[y]) {//文件不是hlt.hrb文件
                                        goto hlt_next_file;     //跳转
                                    }
                                }
                                break;   /* 文件是hlt.hrb文件,BREAK */
                            }
                            hlt_next_file://这里继续循环,找下一个文件是不是hlt.hrb
                            x++;
                        }
                        if (x < 224 && finfo[x].name[0] != 0x00) {
                            /* 找到了hlt.hrb */
                            p = (char *) memman_alloc_4k(memman, finfo[x].size);//分配一块和文件hlt.hrb大小相同的内存p
                            //把这块内存写到GDT的1003号中(这里一般是空闲的,可以写东西
                            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);                     //任务切换,这里会加载TR寄存器到GDT的1003号,执行
                            memman_free_4k(memman, (int) p, finfo[x].size);    //执行完成后,释放掉内存p
                        } else {
                            /* 没有找打文件hlt.hrb,输出提升信息 "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);
                    }
        //.....
    }
}
时间: 2025-01-01 02:30:17

《30天自制操作系统》19_day_学习笔记--<前面的整理ing,这两天会更...>的相关文章

《30天自制操作系统》学习笔记-第1天

为了加深对操作系统的理解,我决定照着<30天自制操作系统>这本书实践一下.项目的github链接是https://github.com/YatesXu/YatesOSASK/ 关于十六进制编辑器 第一个问题就是书中给的十六进制编辑器是日文的,在我的电脑上打开之后是一片乱码,于是我比较之后选用了这个十六进制编辑器wxMEdit,链接是https://wxmedit.github.io/. 另外,visual studio也可以,但是express版不能用,所以还是选用免费开源的软件吧(笑) (伪

《30天自制操作系统》读书笔记(3) 引入C语言

这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是乱码, 而且代码的分隔用了两个Tab, 在这里要处理一下: :%s/;.*//g 删除所有的注释; :%s/\t\t/\t 把两个Tab替换为一个Tab; 要让作者的nas文件和asm文件拥有相同的语法规则, 在_vimrc文件的最后一行添加 au BufNewFile,BufRead *.nas

《30天自制操作系统》读书笔记(2)hello, world

让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实上你用的是U盘还是软盘对我们的操作系统没有影响,缺点是你的U盘刷入系统后容量只能是1440 MB,即当年流行的3.5英寸软盘的大小,当然不用担心,再格式化一次(用DiskGeniu),就可以恢复. 我做事情的话,总是怕自己的努力的结果白费了,害怕辛辛苦苦看完这本书但是发现做出来的东西现在根本没法用,

《30天自制操作系统》读书笔记(4) 绘图

暑假果然是滋生懒散的温床. (╯‵□′)╯︵┻━┻ 好久不动都忘记之前做到哪里了, 上次好像做到了C语言的引入, 这一节所做的东西都相当轻松, 将会绘制出操作系统的基本界面. 绘图的原理 按照书中所说, 将值写入到显存中就能在屏幕上显示相应的像素, 在asmhead.nas 中有这一段: 1 CYLS EQU 0x0ff0 ; 设定启动区 2 LEDS EQU 0x0ff1 3 VMODE EQU 0x0ff2 ; 关于颜色数目的信息,颜色的位数 4 SCRNX EQU 0x0ff4 ; 分辨率

《30天自制操作系统》读书笔记(1)读前感

做一个自己的操作系统, 在我看来一直是不可以思议的,而且奇妙的,像是吉他手亲手打造一把自己的吉他? 似乎这个比喻不太恰当, 但是,感觉是一样的. <30天自制操作系统> 为日本的川和秀实先生所著, 有人说他是 "<XX天学会XXX>之类的书中为数不多的几本好书之一." 这本书的优点非常明显,通俗生趣,甚至于有点啰嗦:而且作者无私地提供了源代码而且允许你以任何方式使用,也提供了编译的所有工具,所有东西都是"开箱即用",不容易出问题. 作为后生我

《30天自制操作系统》读书笔记(5) GDT&amp;IDT

梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLoader, 占用了软盘的第一个扇区并符合启动盘的规范, 默认被载入地址是0x7c00 到 0x7e00, 负责将10个柱面读入到0x8200到0x34fff (10个柱面共10*2*18 = 360 个扇区但是第一个没有被读入); asmhead.nas     包含一些暂时未知的设定; naskf

多定时器处理1(30天自制操作系统--读书笔记)

自认为写过很多MCU程序,但总是回头想想,我所了解的MCU编程思想大体有两种,其中具体的想法我得再找时间写下来. 总想总结出一个可扩展的,易移植的写法,但能力还没到这个层次.但<30天自制操作系统>这本书确实给我了一个思路,就像我已经写过的两篇读书笔记. 将两个独立的内容--FIFO和内存动态管理做到高度模块化,尤其是其中数据结构模型的设计更是我学习的好例子. 今天要学习的设计内容是多定时器处理.原书对这部分的处理讲的很详细,由浅入深,看得我由衷佩服作者,也可能是因为我水平低,稍稍看出点门道来

单字节的FIFO缓存(30天自制操作系统--读书笔记)

从今天起,写一些读书笔记.最近几个月都在看<30天自制操作系统这本书>,书虽说看的是电子书,但可以花钱买的正版书,既然花费了金钱,就总得有些收获. 任何人都不能总是固步自封,想要进步就得学习别人的知识,对于程序员而言,最简单的方法即是学习别人的代码. 今天的标题是“单字节的FIFO缓存”,其实就是做一个FIFO,看名字就知道了.也就4个函数和1个相关结构体,这样的小代码在嵌入式系统中很常用,也会很好用. 1.相关数据结构体 struct FIFO8 { unsigned char *buf;

《30天自制操作系统》笔记(12)——多任务入门

<30天自制操作系统>笔记(12)——多任务入门 进度回顾 上一篇介绍了设置显示器高分辨率的方法.本篇讲一下操作系统实现多任务的方法. 什么是多任务 对程序员来说,也许这是废话,不过还是说清楚比较好. 多任务就是让电脑同时运行多个程序(如一边写代码一边听音乐一边下载电影). 电脑的CPU只有固定有限的那么一个或几个,不可能真的同时运行多个程序.所以就用近似的方式,让多个程序轮换着运行.当轮换速度够快(0.01秒),给人的感觉就是"同时"运行了. 多任务之不实用版 我们首先从