3.5.4 大小端——小问题,大折腾
在不同的处理器中,相同的数据在存储器中的存储格式可能不一样。如下图所示,数值0x12345678在大端字节序和小端字节序处理器的存储器中存储形式分别为:
大端、小端这两个术语来源于小说《格利弗游记》(Gulliver’s Travels)。小人国里的小人们分成了两大阵营:一派敲破鸡蛋“大”的一头吃鸡蛋,一派敲破鸡蛋“小”的一头吃鸡蛋。其实怎么吃都没有关系,只是习惯不同。
Intel的x86处理器使用小端字节序,很多处理器既可以配置为大端,又可以配置为小端,在不同字节序计算机之间通信时,就需要注意数据格式的转换。
3.5.5 指令类型——我们需要哪些指令
处理器要完成计算的任务,需要具备哪些指令呢?我们用如下这个计算为例:
xi为输入信号,coeffi为滤波器系数。要让处理器完成这些工作,首先需要两个运算:乘法和加法。与此类似的还有移位、减法等指令,这些指令被称为算术逻辑指令。
除了做计算外,处理器还要能实现循环,上面这个计算循环了count次。循环是由跳转指令来实现的,跳回去执行就能实现循环。如右图 所示。
循环需要在一定条件下跳出,否则就成死循环了,条件跳转指令能完成这个功能。条件跳转指令在一定条件下实现跳转,它能实现分支功能,例如:“如果明天下雨,我就在家,否则,我就出去玩。”这个功能就可以用条件跳转指令完成。跳转指令也称为控制指令。
解决了这些基本的运算问题后,还剩下一个问题,那就是:操作的数据在哪?操作的数据都放在存储器中。在x86指令集中,算术逻辑指令的操作数可以是寄存器,也可以是存储器,而在其他的RISC指令集中,算术逻辑指令的操作数只能是寄存器,因此需要先使用导入(load)指令将存储器中的数据导入到寄存器中,运算完成后,再用导出(store)指令将寄存器中的数据导出到存储器中。这类指令称为数据传送指令。不同的指令集,指令命名、寄存器命名不一样,不过基本规则类似。
导入、导出指令的基本格式如下:
load *A1, A2;将存储器某地址处的值导入到寄存器A2中,该存储器地址记录在寄存器A1中
store A3, *A4;将寄存器A3的值导出到存储器某地址处,该存储器地址记录在寄存器A4中
有了这3类指令(算术逻辑指令,控制指令,数据传送指令),处理器就能完成各种复杂的运算。
3.5.6 寻址方式——千万里,我追寻着你
指令的操作数可以是一个具体的值,如100(称为立即数),也可以是寄存器,也可以是存储器。真实参与运算的值,到底是立即数,还是在寄存器中,还是在存储器中,在存储器中的哪个位置,这些就要靠寻址方式来指定。以RISC指令集的代表MIPS为例:
add $s1,$s2,100;源操作数是寄存器$s2和立即数100,称为立即数寻址
add $s1,$s2,$s2;源操作数是两个寄存器,称为寄存器寻址
lw $s1,100($s2);存储器地址为100+$s2,将这个地址处的值导入到寄存器$s1中,称为存 储器寻址
存储器寻址又会分成更多的类型,RISC指令集的寻址方式较为简单,x86的寻址方式则非常复杂,不过x86复杂的寻址方式也会在内部转化为类似RISC的多条简单微操作。
3.5.7 总结
指令集是处理器的脸面,就如同容貌对于人的重要性一样,程序员在为处理器编写程序时,指令集就是他(她)们所看到的处理器的容貌。好的指令集,应该具备如下特征:
兼容性:新一代处理器的指令集要兼容上一代的指令集。
易实现:指令所完成的功能,在处理器硬件上要容易实现。
易编程:编程人员容易使用该指令集进行编程。
高性能:指令集设计合理,以使得程序的执行效率最高。