YSYX-PA1学习记录

1 RTFSC

这一部分最关键的是反复阅读源码!
不是像读课文一样反复阅读,而是了解源码结构和功能之后,在后面的练习中遇到不懂的反复回看源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
ics2023
├── abstract-machine # 抽象计算机
├── am-kernels # 基于抽象计算机开发的应用程序
├── fceux-am # 红白机模拟器
├── init.sh # 初始化脚本
├── Makefile # 用于工程打包提交
├── nemu # NEMU
└── README.md
───────────────────────────────────────────────
nemu
├── configs # 预先提供的一些配置文件
├── include # 存放全局使用的头文件
│ ├── common.h # 公用的头文件
│ ├── config # 配置系统生成的头文件, 用于维护配置选项更新的时间戳
│ ├── cpu
│ │ ├── cpu.h
│ │ ├── decode.h # 译码相关
│ │ ├── difftest.h
│ │ └── ifetch.h # 取指相关
│ ├── debug.h # 一些方便调试用的宏
│ ├── device # 设备相关
│ ├── difftest-def.h
│ ├── generated
│ │ └── autoconf.h # 配置系统生成的头文件, 用于根据配置信息定义相关的宏
│ ├── isa.h # ISA相关
│ ├── macro.h # 一些方便的宏定义
│ ├── memory # 访问内存相关
│ └── utils.h
├── Kconfig # 配置信息管理的规则
├── Makefile # Makefile构建脚本
├── README.md
├── resource # 一些辅助资源
├── scripts # Makefile构建脚本
│ ├── build.mk
│ ├── config.mk
│ ├── git.mk # git版本控制相关
│ └── native.mk
├── src # 源文件
│ ├── cpu
│ │ └── cpu-exec.c # 指令执行的主循环
│ ├── device # 设备相关
│ ├── engine
│ │ └── interpreter # 解释器的实现
│ ├── filelist.mk
│ ├── isa # ISA相关的实现
│ │ ├── mips32
│ │ ├── riscv32
│ │ ├── riscv64
│ │ └── x86
│ ├── memory # 内存访问的实现
│ ├── monitor
│ │ ├── monitor.c
│ │ └── sdb # 简易调试器
│ │ ├── expr.c # 表达式求值的实现
│ │ ├── sdb.c # 简易调试器的命令处理
│ │ └── watchpoint.c # 监视点的实现
│ ├── nemu-main.c # 你知道的...
│ └── utils # 一些公共的功能
│ ├── log.c # 日志文件相关
│ ├── rand.c
│ ├── state.c
│ └── timer.c
└── tools # 一些工具
├── fixdep # 依赖修复, 配合配置系统进行使用
├── gen-expr
├── kconfig # 配置系统
├── kvm-diff
├── qemu-diff
└── spike-diff

2 基础设施:简易调试器

2.1 解析命令

掌握一些字符串处理函数

  1. man strtok:字符串切分和解析函数
  2. man strncpy:字符串复制函数
  3. man sscanf:字符串解析函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
strtok(char *str, const char *delim);
/*
- 第一次调用时,str需要指定,后续对同一个str解析时,str指定为NULL
- 每次调用,delim可以制定不同的分界符
- 每次调用,strtok都会返回一个指针指向下一个token,直到下一个token为空指向NULL
*/
strncpy(char *dest, const char *str, size_t n);
/*
- 复制str字符串的前n个字节至dest中
- str不足n长,用空字节补全
- 如果str的前n字节无空字节,dest将不会以空字节`\0`结尾
*/
int sscanf(const char *str, const char *format, ...);
/*
- sscanf返回成功提取的参数的个数,否则返回0
- format可以自定义解析的格式
- 特殊用法:sscanf(str, "%d %50[^\n]", &num, expr); 其中%50[^\n]指的是提取宽度为50的字符串并将其解析成字符串(除非遇到换行符\n)
*/

2.2 单步执行

掌握cpu_exec(n)的作用

2.3 打印寄存器

寄存器符号regs[i]和寄存器值cpu.gpr[i]
别忘了pc寄存器和寄存器值cpu.pc

2.4 扫描内存

掌握sscanf()函数解析字符串

3 表达式求值

3.1 利用正则表达式识别token

3.2 递归求值——分治法

3.2.1 匹配括号

3.2.2 寻找主操作符

3.3 随机测试

  • [ ] 如何保证表达式进行无符号运算?
  • [x] 如何随机插入空格?
  • [ ] 如何生成长表达式, 同时不会使buf溢出?
  • [x] 如何过滤求值过程中有除0行为的表达式?

4 监视点

单向链表的插入操作:头插法