NEMU PA1

本文详细介绍了NEMU PA1的各个任务,包括任务自查、寄存器结构体、表达式求值和监视点管理。讨论了opcode_table的类型、单步执行的实现、内存扫描、算术表达式的词法分析和递归求值。此外,还阐述了如何管理和实现监视点,包括添加、删除和打印监视点。
摘要由CSDN通过智能技术生成

任务自查表

序号 是否已完成
必做任务1
必做任务2
必做任务3
必做任务4
必做任务5
必做任务6
必做任务7
选做任务1
选做任务2

思考题

  1. 思考题 1:opcode_table 到底是一个什么类型的数组?
    答:在 nemu/src/cpu/exec/exec.c 目录下中看到了opcode_table数组:
    在这里插入图片描述
    该数组为 helper_fun 类型,在同一个文件中看到 helper_fun 的定义为:
    在这里插入图片描述
    查阅资料后知道了typedef 返回类型(*新类型)(参数表) 的这种使用方式,此处 typedef 的功能是定义新的 helper_fun 类型,并定义这种类型为指向某种函数的指针,这种函数以一个 swaddr_t 为参数并返回 int 类型。因此,opcode_table 数组是一个函数指针数组。
  2. 思考题 2:
    (1)在 cmd_c()函数中, 调用 cpu_exec()的时候传入了参数-1 , 你知道为什么吗?

    答:在cpu_exec.c文件中找到了函数 cpu_exec()。
    在这里插入图片描述n是无符号整型,所以-1就是无符号最大的数字,那么函数里的for循环可以执行最大次数的循环,从而让cpu处理之后的指令。

(2)框架代码中定义 wp_pool 等变量的时候使用了关键字 static,static 在此处的含义是什么? 为什么要在此处使用它?
答:框架代码中定义wp_pool等变量时使用了关键字static,在此处的含义是静态全局变量,该变量只能被本文件中的函数调用,并且是全局变量,而不能被同一程序其他文件中的函数调用。在此处使用static是为了避免它被误修改。

查阅资料 后对 static 有了更深的了解。

  1. 思考题 3
    1)查阅 i386 手册 :
    ① EFLAGS 寄存器中的 CF 位是什么意思?
    答:P34页中提到参阅附录c,CF是进位标志
    在这里插入图片描述
    P419页中写到CF位:在最高位发生进位或者借位的时候将其置1,否则清零。
    在这里插入图片描述

② ModR/M 字节是什么?
阅读P241-243页。ModR/MModReg/OpcodeR/M 三部分组成。
Mod 是前两位,提供寄存器寻址和内存寻址,
Reg/Opcode为3-5位,如果是Reg表示使用哪个寄存器,Opcode表示对group属性的Opcode进行补充;
R/M为6-8位,与mod结合起来查图得8个寄存器和24个内存寻址
在这里插入图片描述

③ mov 指令的具体格式是怎么样的?
答: 在P347页,格式是DEST ← SRC.

2) shell 命令
答:nemu 目录下的所有.c 和.h 和文件总共有4207行代码。通过find . -name "*[.h/.c]" | xargs wc -l命令得到了这个结果。和框架代码相比, 我在 PA1 中编写了 445行代码。在Makefile中添加了相应的代码,实现了make count命令。
在这里插入图片描述

除去空行之外, nemu 目录下的所有.c 和.h 文件总共有3418代码。 通过find . -name "*[.h/.c]" | xargs grep "^." | wc -l命令得到了这个结果。

3)Make文件 :-Wall 和-Werror 有什么作用? 为 什么要使用-Wall 和-Werror?
答:-Wall 使GCC产生尽可能多的警告信息,取消编译操作,打印出编译时所有错误或警告信息。
-Werror 要求GCC将所有的警告当成错误进行处理,取消编译操作。
使用 -Wall-Werror就是为了找出存在的错误,尽可能地避免程序运行出错,优化程序。

寄存器结构体

必做任务 1:实现正确的寄存器结构体

在现阶段的NEMU 中通用寄存器为:
32位寄存器:EAX , EDX , ECX , EBX , EBP , ESI , EDI , ESP
16位寄存器:AX , DX , CX , BX , BP , SI , DI , SP
8 位寄存器:AL , DL , CL , BL , AH , DH , CH , BH
但它们在物理上并不是相互独立的, 例如 EAX 的低 16 位是 AX , 而 AX 又分成 AH 和 AL。因此EAX寄存器结构图如下(图中没有标出AH):
在这里插入图片描述
reg.h文件中的源代码里,用struct结构定义寄存器。查阅资料可以知道struct和union的区别:

StructUnion主要有以下区别:
1)struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。
2)对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。
参考网页.

由此可以看出寄存器的特征符合联合体,修改后的代码为:

typedef struct {
   
           union {
   
	          union {
   
		           uint32_t _32;
		           uint16_t _16;
		           uint8_t _8[2];
	          } gpr[8];

	/* Do NOT change the order of the GPRs' definitions. */
    	      struct {
   
		           uint32_t eax, ecx, edx, ebx, esp, ebp, esi, edi;
		      };
		   };
		  swaddr_t eip;
}CPU_state;	
必做任务 2:实现单步执行、打印寄存器、扫描内存
实现单步执行

单步执行的格式为si [N] ,程序单步执行N条指令后暂停, 当N没有给出时, 缺省为默认为1。根据单步执行的说明得出解题步骤:

1)传入cmd_si()函数的参数为字符串,现在需要利用一些方法将其分解为两部分,分别为“si (空格)”和“N”(N是字符串类型的数字),N的部分存到字符串arg中,此过程中需要用到strtok()库函数,查阅资料知道:

strtok( ) 的使用
C 库函数 char *strtok(char *str, const char *delim) 分解字符串 str 为一组字符串,delim 为分隔符。

2)根据字符串arg来判断需要执行的指令数 i,需要使用sscanf()库函数,将字符串arg改为int型的数字 i,查阅资料知道:

sscanf( ) 的使用
C 库函数 int sscanf(const char *str, const char *format, …) 从字符串读取格式化输入。
str – 这是 C 字符串,是函数检索数据的源。
format – 这是 C 字符串,包含了以下各项中的一个或多个:空格字符、非空格字符和format 说明符。

① 若arg为NULL,默认 i = 1
② 若 i < -1, 提示输入错误
③ 若 i >= -1, 调用次 cpu_exec(steps) (当steps > 10时没能输出结果,询问同学后才知道,cpu_exce.c文件中的#define MAX_INSTR_TO_PRINT 10 中的10改为最大值就可以了)

最后附上代码:

static int cmd_si(char *args){
     
    char *arg = strtok(NULL," ");  
    int steps = 0;  
    if(arg == NULL){
     
        cpu_ex
  • 67
    点赞
  • 321
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值