PA 代码+笔记

如果有什么地方有误 请多多指教;

写这个不是让同学们直接抄的,请弄懂原理哦,我觉得我解释的蛮清楚了。

遇到了问题也可以评论留言或者私信我;

 

 pa1代码➕思路17元,pa2代码+思路30元,pa3仙剑奇侠传仅代码自己看13元。扫码备注邮箱,一般立刻发,如果有事24小时内发。

                          PA 代码+笔记_第1张图片

1 PA1 – 开天辟地的篇章:最简单的计算机

1.1 在开始愉快的PA之旅之前

 

讲义中提到使用union,前面知道CPU的寄存器是公用内存的,所以把gpr[8]结构体改成了union,但是实验报错,然后继续改,看到下面的eax,ecx之类的寄存器,于是在外面又套了一个union,但是还是报错,后来在网上搜,看到简书上面有一个解答,就是在eax,ecx之类的寄存器外面套一个struct,尝试之后成功了,但是原理不知道。

 

修改后的结构体:

PA 代码+笔记_第2张图片

进入nemuPA 代码+笔记_第3张图片

 

cmd_c()函数中调用cpu_exec()传入参数-1,根据讲义,我在cpu_exec()看到如下:

 

  1. 建一个commit,恢复master暂存区文件到工作区,将pa0合并到当前分支,新建一个分支pa1。PA 代码+笔记_第4张图片
  2. 配置X Servier。安装结束后为SSH打开X11转发功能(每次);
  3. 虽然显示有error,但是马里奥可以玩。
  4. NEMU是什么?
  5. NEMU是一个虚拟出来的计算机系统,通过程序实现物理计算机的基本功能。

    2)初识虚拟化。在Windows中使用Docker安装了一个GNU/Linux container,然后在container中完成PA,通过NEMU运行Hello World程序的层次图:

    -------------------------------

    “Hello World”program

    -------------------------------

    Simulated x86 hardware

    --------------------------------

    NEMU

    --------------------------------

    GNU/Linux

    ---------------------------------

    Docker

    ----------------------------------

    Computer hardware 

     

    1.2 开天辟地的故事

                                                                          32位

                                                                                                      

     

     

    8位

    8位

     

                                                                                                                                  16

    1.3 RTFSC

  6. 思考题:一个程序是从main函数开始执行的。
  7. 实现正确的寄存器结构体。
  8. 究竟要执行多久?

n是无符号整型,所以-1就是无符号最大的数字,那么for循环可以执行最大次数的循环,而ecex_wrapper()函数就是执行%eip指向的当前指令并更新%eip。最终就可以执行完所有指令。

温故而知新

opcode_table数组是一个函数指针数组(helper函数),对应某条指令的某种形式。

 

return语句是返回结果,并终止当前函数。全局对象的析构函数会在main函数之后执行,用atexit注册的函数也会在main之后执行。main函数执行完之后还要去执行一些诸如释放空间之类的操作。Main函数结束时会隐式的调用exit()函数,运行时会执行由atexit()函数登记的函数,做一些清理,刷新所有输出流,关闭所有打开的流。所以应该是exit()函数指示吧。

证明在main函数返回后可以再执行的代码:

#include   
#include   
  
void fn1(void)  
{  
    printf("next.\n");  
}  
  
int main(void)  
{  
    atexit(fn1);  
      
    puts("This is executed first.");  
    return 0;  
}  

 

 

1.4 基础设施

解析命令

 

单步执行

1)在ui.c中看到:

PA 代码+笔记_第5张图片

 

  1. 谁来指示程序的结束?
  2. readline(),读取一行文本。
  3. strtok(),根据给定的字符结点,将字符串分解成一段段的字符串。
  4. sscanf(),根据字符串读入相符的数据

看了代码之后,就明白了是用readline读取文本行之后,用strtok()分解第一个字符串,与cmd_table[]中的name比较,执行对应的操作。

于是做如下变动:

PA 代码+笔记_第6张图片

cmd_si函数读取字符串参数,用sscanf转化为相符的数据,执行对应指令。

PA 代码+笔记_第7张图片

 

代码如下:

static int cmd_si(char *args){  
    char *arg = strtok(NULL," ");  
    int i=0;  
    if(arg == NULL){  
        cpu_exec(1);  
        return 0;  
    }  
    sscanf(arg,"%d",&i);  
    if(i<-1){  
        printf("Parameter error\n");  
        return 0;  
    }  
    if(i==-1){  
        cpu_exec(-1);  
    }  
    for(int j=0;j
  1. 结果截图

 

PA 代码+笔记_第8张图片

PA 代码+笔记_第9张图片

PA 代码+笔记_第10张图片

打印寄存器

1regsl[]32位寄存器,regsw[]16位寄存器,regsb[]8位寄存器。在前面实现寄存器的正确结构时知道cpu.gpr[i]._32uint32_t地址。判断参数为r后输出32位寄存器的地址。

PA 代码+笔记_第11张图片

 

代码如下:

static int cmd_info(char *args)  
{  
    char *arg=strtok(NULL," ");  
    if(strcmp(arg,"r")==0)  {  
        for(int i=0;i<8;i++) {  
            printf("%s %x %d\n",regsl[i],cpu.gpr[i]._32,cpu.gpr[i]._32);  
        }  
    }  
    return 0;  
}  

 

2)结果

PA 代码+笔记_第12张图片

 

 

扫描内存

1)讲义中提到过vaddr_read()memory.c中,查看如下:

PA 代码+笔记_第13张图片

 

其实就是vaddr_read函数调用paddr_read,传入两个参数:起始地址,扫描长度。

所以我们通过strtok分别获得字符串型的地址和扫描长度,用sscanf转换为要求的形式,而后循环调用vaddr_read函数扫描内存。

PA 代码+笔记_第14张图片

 

代码如下:

static int cmd_x(char *args)  
{  
    char *arg1=strtok(NULL," ");  
    char *arg2=strtok(NULL," ");  
    int len;  
    vaddr_t address;  
      
    sscanf(arg1,"%d",&len);  
    sscanf(arg2,"%x",&address);  
      
    printf("0x%x:",address);  
    for(int i=0;i

 

  1. 结果
  2. PA 代码+笔记_第15张图片

 

       pa1全部代码7元,pa2全部代码15元。扫码备注邮箱。

                          PA 代码+笔记_第16张图片

 

 

 

 

 

你可能感兴趣的:(PA,and,Lab)