PA-1实验报告¶
211275022田昊东¶
PA1-1¶
目标¶
- 实现内存的模拟
- 实现寄存器的模拟
ppt后题目¶
- struct
- 是一种自定义数据类型,由若干成员组成
- 可以用于存储一组数据类不同或相同的变量
- 不同数据成员之间相互独立,存储在一段连续的内存空间的不同位置
- union
- 一个union内可以包含多个不同的成员变量,不同成员变量共享同一块内存。
-
union占用空间的大小等于成员变量中占用空间最大的成员的大小。
-
union与struct的区别:
-
struct中的成员是按顺序依次排列,互相独立,其内存是其成员占用内存的加和(由于内存对齐机制可能会更大)。而union中的成员共用同一块内存,union类型占用的内存就是其成员中占用内存的最大值,union中的成员是对同一地址的的不同表现方式。
-
改变union中一个成员的值可能会改变其他成员的值,而struct中不同成员之间是相互独立的
-
寄存器结构体的参考实现为什么把部分
struct
改成了union
? - 如图所示,除了直接访问%eax(全部的32位),还要能够访问%ax(前16位)、%al以及%ah。通过第一层union实现这一点:uint32_t、uint64_t、和uint8_t[2]共用一块内存,实现分区访问
- 第二层union使得第一层union和一个uint32_t的变量val公用bb一块内存,提供通过val直接访问一整行的数据
- 第三层union将细分的结构(第二层union)的数组与八个带有寄存器名称的uint32_t变量组合再一起,使得可以通过名称来访问对应的区域。
小结¶
- 这一部分需要实际操作的内容很少,但需要理解union的含义,在pa很多代码中都使用了union这种数据结构(比如 CPU_STATE以及FLOAT),这种数据结构实现了对统一数据的不同访问方式。在不占用额外空间的前提下,极大的方便了数据的访问。
- 在 CPU_STATE,可以根据具体寄存器不同,实现对寄存器一行内不同片段的访问(比如访问EAX前8位AL,前16位AX)
- 在FLOAT中既可以通过fval直接获取浮点数的真值,也可以分别获取浮点数的符号位、阶码、尾数。
PA1-2¶
目标¶
- 了解无符号整数和带符号整数的机器级表示
- 实现NEMU对ALU的模拟
问题和解决¶
-
首先为了简化代码自定义了一些函数
-
mysign用于提取符号位
-
c uint32_t mysign(uint32_t x, size_t data_size) { return (x>>(data_size-1))&1; }
-
set0用于将多余的高位置零
-
c uint32_t set0(uint32_t x, size_t data_size) { return x & (0xFFFFFFFF>>(32-data_size)); }
-
getcom获取相反数的补码(包括符号位在内各位取反末尾加一)
-
c uint32_t getcom(uint32_t x, size_t data_size)//求-x补码 { x=~x; return set0(x+1,data_size); }
-
alu_add
-
alu_add的实现不难,主要是要注意对标志位的处理(比较容易出错的是CF和OF)
- CF进位:针对无符号数,通过
result<src
判断 - OF溢出:针对有符号数,如果两个同号数相加结果为异号则显然说明发生了溢出
- CF进位:针对无符号数,通过
-
alu_adc
-
相当于进行了两次加法,主要需要注意对两次加法得到的标志位的处理
- 两次中至少发生了一次进位就将CF置1
- 两次中发生了恰好一次溢出就将OF置1(溢出两次相当于运算数和结果同好,即没有发生溢出)
-
alu_sub
-
通过getcom把src的补码转化为-src的补码,再转化为加法进行计算
-
alu_mul
-
如果高位不全是0,则将CF和OF都置一
-
alu_sar
-
注意要先提取出符号位,右移过程中高位要补符号位
-
alu_idiv
-
这个修改了比较久实现的比较复杂:提取sec和dest的符号位,如果是负数则用getcom转化为正数的补码,然后直接相除,最后得到的也是正数。根据两个操作数的正负情况决定是否进行一次getcom翻转为负数。
小结¶
- 本部分的实现有一定的难度,需要对编码类型、数据计算原理,操作指令含义,标志位含义有充分的理解,稍有偏差就会产生错误。
PA1-3¶
目标¶
- 实现浮点数规格化
- 实现浮点数加减乘除运算
问题和解决¶
- 规格化浮点数
- 实验指南中多次提到:”为了配合非规格化数的阶码为0表示2-126需要额外将尾数右移一次“。这段化我开始没有完全理解,时间上也有错误,经过调试才明白真正的含义:尾数左移一位x相当于乘以2,exp相应减一相当于浮点数大小保持恒定。而exp从1到0时浮点数的阶数都是2-126没有发生变化,也就相当于浮点数的值变为原来的二倍,因此要再额外右移一次进行修正
- 其他的部分就按照框架的思路写就能完成。就是要足以一些边界值的处理,尤其是exp=0,exp=255等情况,需要讨论分别处理。
ppt后题目¶
- 为浮点数加法和乘法各找两个例子:1)对应输入是规格化或非规格化数,而输出产生了阶码上溢结果为正(负)无穷的情况;2)对应输入是规格化或非规格化数,而输出产生了阶码下溢结果为正(负)零的情况。是否都能找到?若找不到,说出理由。
- 加法:
- 1:0:11111110:1(*23)+0:11111110:1(*23)
- 由于会产生进位,因此规格化后会变为inf
- 2:0:11111110:1(*23)+1:11111110:1(*23)乘法:
- 规格化后阶码为0会产生下溢,结果为0
- 乘法:
- 1:0:11111110:1(*23)*0:11111110:1(*23)
- 由于乘法阶码相加,自然会造成阶码上溢,结果为inf
- 2:0:00000001:0(*23)*0:00000001:0(*23)
- 阶码相加之后小于最低值,发生阶码下溢,结果为0
小结¶
- 这一部分需要完成的代码不算多,但我认为是整个pa1中最难的部分,浮点数的表示规则相对复杂,有些概念不是很好理解。而且这部分的细节很多,遗漏任何一步都无法正确完成,并且由于需要自己写规格化函数,在确认出错位置、调试上有一定的难度,只能通过打印大量数据,再进行分析。
总结¶
- 经过这一阶段的实践,对理论课上所学的对应内容有了更深的理解。只有真正上手操作,才能理解许多规则及方法的制定原因。nemu框架代码有降低了体验这个过程的门槛,能很有效的帮助学习和理解。