如果有什么地方有误 请多多指教;
写这个不是让同学们直接抄的,请弄懂原理哦,我觉得我解释的蛮清楚了。
遇到了问题也可以评论留言或者私信我;
pa1代码➕思路17元,pa2代码+思路30元,pa3仙剑奇侠传仅代码自己看13元。扫码备注邮箱,一般立刻发,如果有事24小时内发。
1 PA1 – 开天辟地的篇章:最简单的计算机
1.1 在开始愉快的PA之旅之前
讲义中提到使用union,前面知道CPU的寄存器是公用内存的,所以把gpr[8]结构体改成了union,但是实验报错,然后继续改,看到下面的eax,ecx之类的寄存器,于是在外面又套了一个union,但是还是报错,后来在网上搜,看到简书上面有一个解答,就是在eax,ecx之类的寄存器外面套一个struct,尝试之后成功了,但是原理不知道。
修改后的结构体:
进入nemu:
在cmd_c()函数中调用cpu_exec()传入参数-1,根据讲义,我在cpu_exec()看到如下:
建一个commit,恢复master暂存区文件到工作区,将pa0合并到当前分支,新建一个分支pa1。
配置X Servier。安装结束后为SSH打开X11转发功能(每次);
虽然显示有error,但是马里奥可以玩。
NEMU是什么?
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
思考题:一个程序是从main函数开始执行的。
实现正确的寄存器结构体。
究竟要执行多久?
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中看到:
谁来指示程序的结束?
readline(),读取一行文本。
strtok(),根据给定的字符结点,将字符串分解成一段段的字符串。
sscanf(),根据字符串读入相符的数据
看了代码之后,就明白了是用readline读取文本行之后,用strtok()分解第一个字符串,与cmd_table[]中的name比较,执行对应的操作。
于是做如下变动:
cmd_si函数读取字符串参数,用sscanf转化为相符的数据,执行对应指令。
代码如下:
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)regsl[],32位寄存器,regsw[],16位寄存器,regsb[],8位寄存器。在前面实现寄存器的正确结构时知道cpu.gpr[i]._32是uint32_t地址。判断参数为r后输出32位寄存器的地址。
代码如下:
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)结果
扫描内存
1)讲义中提到过vaddr_read()在memory.c中,查看如下:
其实就是vaddr_read函数调用paddr_read,传入两个参数:起始地址,扫描长度。
所以我们通过strtok分别获得字符串型的地址和扫描长度,用sscanf转换为要求的形式,而后循环调用vaddr_read函数扫描内存。
代码如下:
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
结果
pa1全部代码7元,pa2全部代码15元。扫码备注邮箱。