采用16位定点整数方案表示数值数据时(虽然我在其中都采用字母大写,但是建议养成字母小写的习惯)。
有一点要提前说明:
-128的补码是1,0000000?
[+0]原=0,000 0000 [-0]原=1,000 0000
[+0]反=0,000 0000 [+0]反=1,111 1111
[+0]补=0,000 0000 [-0]补=0,000 0000
正数的原码补码反码一样
而负数的反码是原码数值位全部取反,负数的补码则是原码数值位全部取反+1,符号位向上进位得到第九位,得到[-0]补=10,000 0000,但是采用机器字长只有8bit,所以第九位1丢弃,最终得到的[-0]补=0,000 0000=[+0]补
因此真值0的补码只有一种表现形式,而多出来一种表现形式,则将[x]补=1,0000000表示x=-128 ,补码整数的表示范围就会比原码多表示 -2^n )(可认为补码除符号位1剩下都为0,可认为前面的那个1是数值位,符号位隐藏,也是1)。
采用十六位定点小数方案表示数值数据时(这次我采用小写字母表示)。
首先我们要了解,十六位定点小数,整数位为0位,小数点前面的那个是符号位。小数位有15位。
采用8位定点小数方案表示-0.33时
下面的迷迷糊糊没事,熟悉就行。不必了解原理。
采用8位补码解答下列定点整数问题,并说明结果是否溢出:
1,(-71)+(-29) 2,111+30 3,(-80)-(-55)
对于第一个,-71的补码是Oxb9,-29的补码是Oxe3,所以补码之和是Ox9c,没有溢出。
对于第二个,111的补码是Ox6f,30的补码是Ox1e,运算结果是Ox8d,更改了符号位,显然是有溢出的。第三个,同第一题,无溢出。解决此题可以看结果范围,如果不在-127到127之间,可认为是溢出的。
采用16位浮点数,其中含有6位指数表示数值数据时。
显然,在机器格式中,1位符号位,6位指数,9位尾数位。所以偏移量是2的5次方-1=31。(以上是个人理解)
通俗来说指数位前面的和后面的都是尾数位,所以尾数位有10位。
6位补码范围是-32到31,(上节已经提到,100000表示最小数-32),指数偏移量是31。也可以 这样理解,6位无符号数的范围是0到63,0和63有特殊用途,剩下的范围是1到62,62的一半是31,所以指数偏移量是31。
规格化指数的范围是-30到31,这样理解,无符号数0和63都有特殊用途,所以规格化指数的范围是0-31到63-31即-30到31,非规格化指数是-30,(这一块儿好奇怪)。将非规格化数转化为小数*2的-30次方。
不说了,直接看题:
规格化数的尾数范围:小数点前是1,小数点后全0到全1呗。
规格化数的指数范围:-30到31。无符号数的范围是0到63,其中0和63不表示规格化数,所以无符号数范围是1到62表示规格化数,对应的有符号数是-30到31。对应的机器数是1到62。机器数就是指数范围的无符号形式。也就是对应机器格式的无符号形式。
规格化数的指数偏移量:明显是62/2=31,也可以用2的5次方-1。
非规格化尾数的范围:小数点前是0,小数点后全0到全1呗。
非规格化数的指数范围:-30,上面已经提到过,对应的机器数是0。记住,所有非规格化数的指数范围就是规格化数指数范围的最小值,机器数是0。也就是对应机器格式的指数位的无符号值。即对应机器格式的指数位都是0。
最小的规格化正数,尾数位是1后面全是0,指数位是2的-30次方。
最大的非规格化负数:符号是‘-’,尾数位是除了最后一位是1外全0,指数位固定2的-30次方。
最小的非规格化负数:符号是‘-’,尾数位小数点后全部是1,指数位是2的-30次方。
尾数的精度:小数点后有9位,所以精度是2的-9次方。
-1.101(2)*2的-33次方超过规格化数范围,采用非规格化形式表示:
机器数0x80a3的真值是:
采用带5位指数位的12位浮点数,如果在尾数求和时采用3位整数,9位小数,则在计算15.75+3.15625时:
含有五位指数时:偏移量是2的4次方-1=15,五位指数真值的范围是0到31,其中0和31有特殊用途,所以指数范围是1到30,表示成有符号数为-14到15,所以规格化数的指数位是-14到15,非规格化的指数位是-14。
运算中,对阶后,3.15625的真值是:对阶过程就是指数小的向指数大的看齐。
运算中,尾数四舍五入后,和的规格化真值是:
运算后和的机器数形式是:0100 1100 1100 =0x4cc。
到指令这一块儿了:
假设计算机中指令长度是32位,格式是:操作码+目的寄存器+源寄存器+立即数
如果有200条不同的指令,有32个寄存器,那么立即数最多有:
200条不同的指令说明操作码至少有8位(2的7次方=128,即如果只有7位,最多只能对应128条指令)。为了区分32个不同的寄存器,目的寄存器和源寄存器至少各为5位,剩下的可供立即数使用的最多有32-10-8=14位。
关于RISV-V指令格式:有以下说法:rs1和rs2总是作为源寄存器。有些指令没有funct字段,有些指令没有rd字段,rs1和rs2总是用来读的。
关于add x2,x1,x1,有以下说法:add指令是R类型指令,所有操作数都采用寄存器寻址。
addi指令是I类型指令,其imm字段长度是12位,imm字段的范围是:imm字段为12位长,采用补码,对应真值的范围是-(2的11次方)到2的11次方-1,即-2048~2047。
指令addi x0,x1,-50的imm字段是:-50的12位补码是0xfce。
指令lw采用I类型格式,其offset字段的范围是:同上,offset也是立即数(imm),字段是12位长,所以范围也是-2048~2047。
指令lw x1,-12(x2)的offset字段是:-12的12位补码。即0xff4。
设x2的内容是0x0000 0000 1001 0010,则指令lw x1 -12(x2)读内存数据使用的地址是:指令lw的源操作数为内存变量,采用基址寻址,目标地址=基址+offset(-12),所以目标地址是该内容(基址)-12。
利用指令lw读取内存中起始地址为0x0000 0000 1002 0004的一个字(连续4个字节)时,如果基址是0x0000 0000 1001 ff00,其offset字段是:显然,用目标地址(起始地址)-基址即可得到。结果是:0x104。
利用指令lw读取内存中起始地址为0x0000 0000 1002 0004的一个字(连续四个字节)时,如果基址是0x0000 0000 1002 07f4,则其offset是:offset=目标地址-基址=起始地址-基址,显然题中,基址>起始地址,offset也可以表示为:-(基址-起始地址)。
指令beq采用SB类型格式,其offset字段的范围是:很显然,SB类型的imm也是12位,采用补码,所以范围是-2048~2047。
指令beq能实现的转移范围(前后指令数):每条指令占4个字节(1个字),offset字段是以2个字节(半个字)为单位的,往前能转移到第1023(int(2047/2))条指令,往后(往回)能转移到第1024(int(2048/2))条指令。
设指令beq x1,x1,abc的地址是0x0000 0000 0040 0100,指令abc:addi x2,x2,-4的地址是0x0000 0000 0040 0200,则分支指令的offset字段为:offset=(目标地址-分支指令地址)/2=128。
设指令beq x1,x1,abc的地址是0x0000 0000 0040 0200,指令abc:addi x2,x2,-4的地址是0x0000 0000 0040 0100,则分支指令的offset字段是:-128,从向前到向后而已。
显然,目标地址是0x0000 0000 1000 0000+offset*2,offset是以半字为单位的,也就是2个字节,地址是以字节为单位的,所以,要将offset*2,表示字节偏移量。
同理,offset是以半字(2字节)为单位的偏移量,而地址是以字节为单位的偏移量,所以要将当前地址+offset*2计算出目标地址。
显然,偏移量为正,而指令的大小1个字,offset是以半字为偏移量,所以,offset/2是以字为偏移量的,往前进100条,也就是往前100个字,200个半字,所以,offset字段是200。或者说,每四个字节对应一条指令,offset是以半字(2个字节)为单位的偏移量,往前进100条指令,也就是400个字节,对应offset为200。
同上,只不过这个是往后走。
jal是UJ类型指令,imm有20位,采用补码,对应的真值是的offset字段的范围是-(2的19次方)到2的19次方-1。
同理,offset是以半字(2个字节)为单位的偏移量,所以目标地址=当前地址+offset*2。(地址是以字节为单位)。
很显然,offset=(目标地址-当前地址)/2。目标地址-当前地址=字节偏移量,而offset是以半字偏移量为单位的,所以要再除以2。
显然,x1的内容+2*offset即x1的内容-240即可。(秀逗麻袋,jalr的偏移量是以半字为单位还是以字节为单位啊?)
jump and link,x1的地址是当前地址+4。
同理,区分6种寻址方式,需要3个比特编码,2个比特只能区分4种,3个比特能区分8种。
显然,30种不同的类型,对应肯定是5个比特位,而寻址方式是5*6*6=180种,对应8位,一共有13位,这道题容易出错,主要是因为,我们要用到的是寻址方式对应的比特位,而不是源寄存器对应的比特位。
厚礼蟹:
只有分支指令和jal指令是半字偏移量,其余与立即数有关的指令,比如ld,sd,jalr都是字节偏移量。
下一篇:
注意ld x5,67(x6),ld指令加载双字,也就是8个字节,图中每个字节对应一个单元内容,采用小端模式,8个字节单元加载出来的内容分别是:(0x省略)34,56,78,9a,bc,de,f0,12。先往最小的位置存,在往高的位置存:存之后x5的内容是:0x12f0debc9a785634。注意采用小端模式,最先加载出来的存到未存数据的最低位。
lw x5,67(x6)加载单字,四个字节,即34,56,78,9a。小端模式是9a785634。要进行有符号扩展。加载后x5的内容是0xffff ffff 9a78 56 34。任然采用小端模式,加载出来的数据首先存储到x5的低位。逐渐往高位存。
读出的32位内容是无符号数,要扩展为64位。记住,lwu读出的数据要进行扩展。
注意:有符号拓展前面全补符号位,很明显9a...最高位是1,表示负数,所以扩展为全补1,无符号扩展前面全补0。x5里面的内容是8个字节,采用小端模式,前高位四个字节要进行扩展。
插入:
lh指令,加载一个半字,一个半字是2个字节,加载出34和56,采用小端模式,所以加载出来0x5634,是有符号数,符号位是0,所以扩展前面补0,结果是0x00...005634。
lhw同理,加载出来是无符号数,前面全补0,结果也是0x00..005634。
lb指令是加载字节,只加载一个字节,就是34,有符号数扩展,符号位是0,前面全补0,lbw是无符号数,无符号数扩展,前面全补0。
lui指令,将其中的立即数加载到寄存器的高20位,在将该寄存器扩展成64位,显然将0x12345加载到x5的高20位,是0x12345000,(低12位全补0),而后将其扩展为64位,符号位是0,所以前面全补0。
在jal指令中,x5的值被赋为下一条指令的地址,也就是PC+4。jalr同理。(需要注意的是,只有分支指令和jal指令是半字偏移量(offset*2才是相应的字节偏移量),剩余其他的比如ld,sd,jalr等都是字节偏移量)。
sd存双字,即8个字节,将图示区域(刚好8个字节单元)存满。小端存储,从低位开始存。
sw存单字,即4个字节,将图示区域从左往右数前四个(低四个)字节单元存满,高位不变。
sh存半字,即2个字节,将图示区域从左往右数前两个(低两个)字节单元存满,高位不变。
sb存字节,即1个字节,将图示区域从左往右数第一个(最低位)字节单元存满,高位不变。