ICS5 PA

PA1-1 24.5.30

又开始了ICS之旅,这次又给自己下了一个难度,找到了汪亮老师讲解的ICS 5!

target

第一课的目标是修正一个register错误声明

insteresting

  • 中途网易源Bad Gateway 502了,更换清华源,学会了:%s/163/tuna/g非常爽!
  • 又学了几个终端快捷键
  • 想到了用 ccache 加速我的PA

problems

  1. unionstruct 的区别?
    unioin 在同一个内存空间中存储不同的数据类型。
    struct 则是同时存储不同的数据类型。
  2. 为什么要用 union?阅读i386手册
    2.3.1 General Registers
    As Figure 2-5 shows, the low-order word of each of these eight registers has a separate name and can be treated as a unit. This feature is useful for handling 16-bit data items and for compatibility with the 8086 and 80286 processors. The word registers are named AX, BX, CX, DX, BP, SP, SI, and DI.
    对于CPU来说,可以把AH AX AL看成单独的单元,拆分成小块。所以它们是共用关系。

PA1-2 ALU 24.6.5

target

实现ALU中的各类运算,包括设置标志位

knowledge

Appendix C

Name Function
CF Carry Flag ── Set on high-order bit carry or borrow; cleared otherwise.
PF Parity Flag ── Set if low-order eight bits of result contain an even number of 1 bits; cleared otherwise.
ZF Zero Flag ── Set if result is zero; cleared otherwise.
SF Sign Flag ── Set equal to high-order bit of result (0 is positive, 1 if negative).
OF
Overflow Flag ── Set if result is too large a positive number or too small a negative number (excluding sign-bit) to fit in destination operand; cleared otherwise.

ADD

Operation
DEST ← DEST + SRC;
Flags Affected
OF, SF, ZF, AF, CF, and PF as described in Appendix C

ADC

ADD with Carry:“带进位的加法”操作,需要看操作数是否已经进位
Operation
DEST ← DEST + SRC + CF;
Flags Affected
OF, SF, ZF, AF, CF, and PF as described in Appendix C
注意CF标志位需要特殊设置,考虑之前的进位状态

SUB

借位:A - B,当A小于B时,结果为一个负数,产生了借位。
根据这个规则完成set_CF_sub()
在做减法时,
如果用一个正数减去一个负数得到一个负的结果,
或者用一个负数减去一个正数然后得到一个正的结果,
则发生了溢出 。
根据上面的规则比较符号,即可完成set_OF_sub()

SBB

Sub with Borrow
什么时候产生借位?
1. 和普通减法一样10-1, 0<1,产生借位
2. 已经借位的时候,130-31,3 不小于 3,但是3已经产生借位,所以(3-1借位)<3的时候,产生借位
根据上面规则,即可完成set_CF_sbb()

SHL

Shift Left
左移操作,CF位是最高位;只移1位时才需要设置OF(CF和操作数不相同时)
注意:符号位不等于最高位!

DIV

实现除法需要完成MOD
注意,dest是被除数,src是除数

PA1-3 FPU 24.7.30

按照手册和伪代码实现FPU的规格化操作,以及加法和乘法。注意NEMU的实现中,fraction包含Guard、Round、Sticky位,共23+3=26位。

加法

小阶向大阶对齐,shift right,exp–
根据guide处理各种情况即可

inline uint32_t internal_normalize(uint32_t sign, int32_t exp, uint64_t sig_grs) {
if( /* 需要进行右规 */ ) {
while( /* 需要右规 且 未发生阶码上溢*/ ) {
/* 右规一次 */
}
if( /* 发生了阶码上溢 */ ) {
/* 根据符号将结果置为 +∞ 或 -∞ */
}
} else if( /* 需要进行左规 */ ) {
while( /* 需要左规 且 阶码大于0 */ ) {
/* 左规一次 */
}
if( /* 发生了阶码==0 */ ) {
/* 右移一次化为非规格化浮点数 */
}
} else if( /* 两个非规格化数运算后得到了一个规格化数 */ ) {
exp++;
}

if( /* 规格化过程中未发生溢出 */ ) {
/* 根据sig_grs最后三位GRS bits的取值决定舍入,采取就近舍入到偶数的方式 */
/* 移除sig_grs最后三位保留的GRS bits*/
if( /* 舍入后破坏了规格化 */ ) {
/* 再进行规格化并判断溢出 */
}
}

// 假设最后的结果的符号、阶码、尾数保留在sign, exp, sig_grs中
FLOAT f;
f.sign = sign;
f.exponent = (uint32_t) (exp & 0xff);
f.fraction = sig_grs; // here only the lowest 23 bits are kept
return f.val;
}

乘法

非常简单,浮点相乘就是尾数相乘,阶码相加

加入GRS bits后,等同于约定中间结果的小数部分为26位。
对于除法而言,将被除数左移shift位后除以除数得到的小数部分为shift位,为了保证中间结果的小数部分为26位且真值不变,将多余的shift – 26位从阶码中减去,最后的阶码等于fa.exponent - fb.exponent + 127 – (shift – 26)
对于乘法而言,用两个小数点后为23位的尾数相乘得到的乘积尾数小数点后为46位,为了保证中间结果的小数点后依然是26位,我们将多出来的46 – 26 = 20位归于阶码,因此阶码就是fa.exponent + fb.exponent - 127 - 20

规格化

如果sig_grs >> (23 + 3) > 1,则需要右规直至sig_grs >> (23 + 3) == 1;
反之如果sig_grs >> (23 + 3) == 0且不是非规格化浮点数(exp > 0),则需要左规直至sig_grs >> (23 + 3) == 1。
注意这里的23 + 3的取值是因为我们在临时尾数的最低三位保留了GRS bits的缘故,也就是上文所述的等同于中间结果尾数sig_grs的小数部分约定为26位。
在规格化过程中,根据规格化的方向确定尾数右移还是左移,并对阶码进行增减操作。右规过程中需要保留粘位。
同时在每次右移前都要检查阶码上溢(exp >= 0xff)的情形。
在左规过程中,则有可能发生结果出现非规格化浮点数的情况(阶码变为exp == 0),此时需要将尾数额外右移1位以对应非规格化浮点数阶码是2 ^-126的约定(否则单纯从数值上看阶码全0对应2 ^ -127)。

整数部分不是1,(sig_grs >> 26) > 1,,需要进行右移;整数部分 < 1(也就是等于0)则需要进行左移

舍入

就近舍入偶数的规则可以参考视频和袁春风老师的书。

  1. Guard位为0,Round、Sticky不为0,舍去
  2. Guard位为1,Round、Sticky为0,确保尾数是偶数
  3. Guard位为1,Round、Sticky不为0,进位
    注意最后要处理掉GRS位

PA2-1

示例修改文件

objdump -d ./testcase/bin/mov | less

nemu/src/cpu/instr/jmp.c // 包含.h
nemu/include/cpu/instr/jmp.h // 声明函数

nemu/src/cpu/decode/opcode.c // 替换

nemu/include/cpu/operand.h // OPERAND read/write