【OSLab】Nasm实现加减乘法;FAT镜像查看工具

OSLab:Nasm实现加减乘法;FAT镜像查看工具

  • OSLab1:Nasm实现加减乘法
    • 基本环境
    • NASM基础语法
    • gdb调试nasm
    • 加减乘法实现逻辑
    • 编码总结
  • OSLab2:FAT镜像查看工具
    • FAT12结构理解
    • gdb调试C++
    • 编码总结

OSLab1:Nasm实现加减乘法

基本环境

  1. 共享文件夹
    每次都要用该条命令重新运行一下。如果报错mounting with error就把增强功能.exe双击重新安装一下。
    【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第1张图片
  2. makefile中不宜写,会有蜜汁报错也会重复编译,没必要。
  3. 编译命令如下,发现不加g也可调试。【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第2张图片
    nasm -felf -g cal.asm
    gcc -g -o cal cal.o

NASM基础语法

len:  equ  2 #并没有在最终代码中分配任何空间,它只是将len符号设置为等于2。相当于#define len 2
len:  db   2 #在内存中分配了一个字节存储2,len是该字节的地址。相当于int len=2

【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第3张图片【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第4张图片

【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第5张图片【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第6张图片

gdb调试nasm

【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第7张图片
根据地址查看内容:在这里插入图片描述【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第8张图片
查看gdb运行到哪一条:
【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第9张图片

加减乘法实现逻辑

offset的作用:防止取到负号’-‘进行运算

  1. 减法逻辑:(异号加法)
    1. esi指向pos_string的末尾,edi指向neg_string的末尾,ebx记录较短的operand_len(不算负号),edx指向add_res的末尾
    2. cal_sub:永远是esi-edi,多余的位数直接存储进edx(为什么不用’\0’计算?引发数字和字符转换的麻烦)
    3. 通过add_res第一个不为0的数判断正负,ebx记录add_res长度
      1. 数字为负,从最后一位开始遍历,若为正,该位-10,上一位+1
      2. 数字为正,从最后一位开始遍历,若为负,该位+10,上一位-1
    4. 从首位开始循环看是否为0,是就后移,直到最后一位
  2. 加法逻辑:(同号加法)
    1. ecx记录进位,edx指向add_res的末尾
    2. cal_add:看该轮次是否有进位
    3. 加上剩余的first/second string
    4. 最后一个进位未处理,和’1’比较
  3. 乘法逻辑:
    1. ecx记录地址的最小值,edx指向mul_res的末尾
    2. cal_mul
    3. 判断ecx/ecx+1是mul_res的开头
    4. 数字转字符
  4. 开头:判断有效性和符号&操作;结尾:清空所有变量和寄存器

编码总结

  1. 有符号跳转和无符号跳转的区别,跳转符号往往容易出错。
  2. 得到了1、4这样的值后,记得加48变成ascii码。(区分数字和字符的值)
  3. gdb有时输r显示权限不够,再输一次r就可以了(不理解为什么)
  4. 函数和跳转的区别:call会把地址保存,ret时就能返回(往往伴随着保存现场和恢复现场)。而跳转只能顺序执行。
  5. 判断两个操作数的符号时用加法(3种情况分类讨论),而不是简单用减法。
  6. 循环开始时记得清理.bss,否则无法进行多次运算。
  7. 减法的符号是否为正负看第一个非0位。(不是直接看首位,反例:100±101)
  8. 减法和乘法最后循环处理多余0(加法检查进位)

OSLab2:FAT镜像查看工具

FAT12结构理解

【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第10张图片

  1. 引导扇区FAT12Header:存基本信息:见12个字段,共25字节(不全,只是fread了所需字段)

  2. FAT1/FAT2每个表项与数据区的一个簇对应,FAT项的值代表文件的下一个簇号

    1. 通过当前文件的簇号传入FAT,得到FAT项的值==下一个簇号。一个文件被存进若干个簇。利用簇号即可进入数据区读取相应内容。

    2. fatBase = RsvdSecCnt * BytsPerSec;//FAT1的偏移字节 = Boot记录占用的扇区数*每扇区字节数
      
  3. 根目录项RootEntry:见7个字段,共32字节

    1. 文件属性DIR_Attr可判断是目录还是文件

    2. 文件开始的簇号可以知道文件的内容

    3. fileRootBase = (RsvdSecCnt + NumFATs * FATSz) * BytsPerSec; //根目录首字节的偏移数=boot+fat1&2的总字节数
      
    4. 如何获取tree图?

      1. 利用RootEntCnt根目录最大文件数,遍历每个根目录项,得到HOUSE/NJU/ROLL.TXT(见initRootEntry)

        1. 如果是文件

          1. addFileChild:直接加入file_child
          2. 利用开始簇号和FAT遍历结束即可得到文件内容(RetrieveContent)。其中FAT项不仅用于判断该簇是否损坏,还代表文件的下一个簇号,如此遍历到文件的最后一个簇可以获得文件的完整内容
        2. 如果是目录

          1. addDirChild:在加入dir_child时要加入.和…

          2. 开始簇号指示数据区的某个扇区,这个扇区存放着该目录的所有子内容。所以只需要在最开始计算这个簇的FAT值,如果不是坏簇就遍历整个簇的内容(遍历次数为簇的大小512字节/目录项大小32字节=16,比根目录的7要多)

          3. 值大于或等于0xFF8,表示当前簇已经是本文件的最后一个簇

            经实际操作,目录项所在的簇可以且均>=0xFF8,不受该条件约束,推断数据区先存文件再存目录

    5. 是否需要将RootEntry和FileNode联系起来?不需要,RootEntry只存有根目录下的内容,而FileNode通过链表存有所有内容

  4. 数据区

    1. dataBase = BytsPerSec * (RsvdSecCnt + FATSz * NumFATs + (RootEntCnt * 32 + BytsPerSec - 1) / BytsPerSec);//数据区的开始扇区号
      int startByte = dataBase + (currentClus - 2) * BytsPerClus;//通过数据区的起始扇区和簇号可以知道要读取内容的起始字节位置
      
    2. 文件和目录可以自定义结构体存储连接成树,我定义了FileNode。
    3. 从本质上讲,根目录区和数据区都保存着与文件相关的数据,只不过根目录区只能保存目录项信息,而数据区不但可以保存目录项信息,还可以保存文件内的数据。

gdb调试C++

  1. g++ -std=c++11 -g main.cpp -o main 编译cpp文件。如果没有-g可以编译运行,但是不可调试。
  2. 调试命令 常用的有b+代码行数,r,next(不进入函数体),step(进入函数体),print(很好用,如print this->getName()均支持)

编码总结

  1. string->const char *:用c_str()转换
  2. int->string:用std::to_string() 转换。C++和Java/Python不同,string+int不能自动转换。
  3. 获取FAT值:因为2个FAT项占用3个字节,如下图【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第11张图片
    所以簇号的奇偶决定了值:
//获得第num个FAT簇的值
int getFAT(FILE *fat12, int n){
    int pos = FATBase + n * 3 / 2;

    uint16_t bytes;
    //设置流fat12的文件位置为给定的偏移fatPos,SEEK_SET文件的开头
    fseek(fat12, pos, SEEK_SET);
    //从给定流fat12读取数据到bytesPtr所指向的数组中 size=1 nmemb=2
    fread(&bytes, 1, 2, fat12);// 读入2字节

    if(n % 2 == 0){//取的是3字节中的1/2
        return bytes & 0x0fff;//低12位
    }else{//取的是3字节中的2/3
        return bytes >> 4;//高12位
    }
}
  1. 在一个成员函数中调用另一个成员函数时写不写this->均可,和成员变量一个道理
  2. 大bug:【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第12张图片
    注意uint8_t是char,所以不能直接用于乘法运算(uint16_t同理),而要先变成int(所以即使成员变量中有这些值,依然要赋给全局变量,不经过类型转换的值乘法时会出错)
    【OSLab】Nasm实现加减乘法;FAT镜像查看工具_第13张图片
    经正确赋值给int后发现扇区位置符合该表
  3. 注意movTargetNode(相当于BFS的一层)和findFileByName(DFS)时遇到空字符串时返回root而不是error,这样路径开头有没有/都不会影响程序,免得split后产生空字符串传入得到errNode。
  4. findFileByName(DFS)和findFileByPath不同,前者是全局搜索,后者要沿着路径顺序搜索。二者结合使用对比结果可以判断到底是路径问题还是文件/目录本身就不存在。
  5. fseek和fread结合使用可以读取流文件指定位置的内容。
//设置流 stream 的文件位置为给定的偏移 offset, 这里stream=fat12, offset=11
    fseek(fat12, 11, SEEK_SET); // BPB块从11字节开始 SEEK_SET:文件的开头

    //从给定流 stream 读取数据到 ptr 所指向的数组中
    // headptr:指向带有最小尺寸为size*nmemb字节的内存块的指针 size=1要读取的每个元素的大小 nmemb=元素的个数 stream=fat12
    fread(this, 1, 25, fat12);

你可能感兴趣的:(操作系统,linux)