南京大学计算机系统基础实验 ICSPA(2023)PA 1 阶段 3

在上一章我们已经实现了 +,-,*,/,以及括号这几个基本的表达式,现在我们还需要实现:

  • 读取十六进制数,以 0x 开头。
  • 读取寄存器,以 $ 开头。
  • 逻辑运算符,例如 !=&& 等。
  • 解指针引用 *<expr>

实现方式和之前还是一样的,首先添加匹配规则,然后在 expr() 中进行处理。需要注意的是:

  • 符号的优先级,我们应该要从优先级低到高进行递归计算;
  • 读取寄存器所使用的框架代码 isa_reg_str2vel() 需要我们自己实现;
  • 解引用使用之前用过的 paddr_read() 函数。

我理解的监视点就是一个动态的断点。你可以设置一个表达式,当这个表达式的值发生变化的时候,程序就会停止,以供我们检查。

关于结构体的成员,我本来的想法是添加 tokens[] 成员,用来存储表达式的 token。但是后来发现,监视点以及处理 token 的很多相关变量都是 static 的,也就是说无法在声明文件外面访问,而处理指令和计算表达式的代码不在同一个文件里,如果想要能够访问的话,需要修改很多的代码,也可能不符合讲义的期望,因此我放弃了。

最终在结构体中我只额外添加了 char *eword_t old_value,前者用来存储表达式,后者用来存储旧的值。需要计算表达式的时候直接调用 expr() 计算就好了,这个函数一开始就已经在 sdb.h 中声明了,可以直接调用。

字符串拷贝(待解决)
到这里还有一个问题,就是如何拷贝字符串到 char *e 中。NEMU 禁止了 newdelete 指令,我也没有找到很好的办法,只能直接定义 char e[200] 了。
温故而知新

在一个全局变量前面添加关键词 static 的作用是,将其作用域限制在该文件内。

添加了 static 的全局变量被称为静态全局变量。静态全局变量和普通全局变量的生命周期都是从程序开始到程序结束,没有区别。但是普通的全局变量可以在别的文件里使用 extern 后访问。但是静态全局变量不行,只能在声明的文件里访问。

此外,我们还需要实现指令 winfo w,具体的实现方式和之前一样。

讲义还提到在 trace_and_difftest() 函数中设置新的宏 CONFIG_WATCHPOINT,并且在 Kconfig 文件中编写对应的配置项,来控制监视点的启用。在代码中使用 #ifdef#endif 将代码包裹起来,就可以根据宏是否定义来动态编译了。而具体宏的定义是不需要我们手动定义的,只需要在 Kconfig 中设置好对应的配置项 WATCHPOINT,mconf 会自动将前缀 CONFIG_ 补全,根据我们设定好的值,在头文件中生成 CONFIG_WATCHPOINT 这个宏。

这一章节的内容是在给我们普及一些软件工程的概念。

你会如何测试你的监视点实现?
其实很简单,我们之前已经发现了 NEMU 的默认程序是直接声明在代码中的,因此直接在代码中修改 NEMU 的默认镜像,添加一些能够修改寄存器或者内存的代码就好了。

这一章的问答题有时间的时候再补充吧。

PA 1 到此结束。