任务自查表
序号 | 是否已完成 |
---|---|
必做任务1 | 是 |
必做任务2 | 是 |
必做任务3 | 是 |
必做任务4 | 是 |
必做任务5 | 是 |
必做任务6 | 是 |
必做任务7 | 是 |
选做任务1 | 是 |
选做任务2 | 是 |
思考题
- 思考题 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:
(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 有了更深的了解。
- 思考题 3
1)查阅 i386 手册 :
① EFLAGS 寄存器中的 CF 位是什么意思?
答:P34页中提到参阅附录c,CF是进位标志
P419页中写到CF位:在最高位发生进位或者借位的时候将其置1,否则清零。
② ModR/M 字节是什么?
阅读P241-243页。ModR/M 由 Mod,Reg/Opcode,R/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的区别:
Struct与Union主要有以下区别:
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