2.1总述
--1.所选指令集来自经典指令集MIPS
2.2计算机硬件的操作
--毫无疑必须有执行基本算数运算的指令。------冯-诺依曼
指令:add a. b. c ---知道计算机将两个变量b和c相加,并把它们的和放在a中。
-每条MIPS算数运算指令仅仅执行一个操作,并且有且只有三个操作数。
例如:将b、c、d、e的值的和放在变量a中
--add a, b, c
--add a, a, d
--add a, a, e
硬件设计四基本原则的第一个原则:1.简单源自规整
两个基本算数运算的规则:
--加:add a, b, c
--减:sub a, b, c
2.3计算机硬件的操作数
与高级语言程序不同,算术运算指令的操作数是受限制的,它们必须直接取自寄存器;寄存器数量有限,内建于硬件的特殊位置。寄存器是计算机构成的基本元素:寄存器是硬件设计中最基本的元素,在计算机设计完成以后,对程序员来说仍然是可见的。MIPS系统结构中寄存器为32位;成组的32位数由于经常出现在MIPS系统结构中,所以专门有个名字,叫做字。
高级语言的变量和寄存器的主要区别是寄存器的数量有限。现在,典型的计算机中有32个寄存器。MIPS也有32个寄存器。
本节限制条件:MIPS算术运算指令的三个操作数必须从32个32位寄存器中选取。
设计原则2:越少越快
因为电信号传输距离越远,其传输时间就越长。因此,寄存器太多将会延长时钟周期。“越少越快”
的原则并不是绝对的;寄存器个数位31的机器,其速度不一定比寄存器个数为32的机器快。在这种情况下,设计者们必须在提供更多寄存器和缩短时钟周期之间权衡。MIPS约定:用一个“$”符后面跟两个字符来代表寄存器。现在,我们将使用$s0,$s1,$s2...来代表寄存器,以对应java和c程序中的变量;用$t0,$t1,$t2...来代表将程序编译为MIPS指令时所需的临时寄存器。
2.3.1 存储器操作数
计算机怎样来表示和访问这样复杂的数据结构?
回顾一下计算机的五个组成部分。处理器只能将少量数据保存在寄存器中,但存储器之间有上百万的数据单元。这样,像数组这样的数据结构,将只能放在存储器中。
正如上面所说,MIPS指令中的算数运算操作只作用于寄存器;因此,MIPS必须有在存储器和寄存器直接按你传送数据的指令,这些指令叫做数据传送指令。为了访问存储器的一个字,指令必须给出相应的存储器地址。存储器好比一个很大的一维数组,其地址相当于数组的下标,该坐标从0开始。
把数据从存储器复制到寄存器的数据传送指令,通常叫做取指令。取指令的格式:操作符名称后面紧跟存放数据代取的寄存器,然后是用来访问存储器的常数和一个寄存器。指令的常数部分和第二个寄存器中的内容相加即得存储器地址。实际的MIPS取指令的名字为lw,表示取字(load word).
例: 将A[8] 传送到寄存器。这个数组元素的地址由存放在$s3中的数组A的基址加上元素序号8得到。这个数据应该放在一个临时寄存器中以便在下一条指令中使用。第一条编译后的生成的指令为:
lw $t0,8($s3) #数组的基址存放在$s3中。
例:将h(放在寄存器$s2中)加上A[8](放在寄存器$t0中),并将结果放到对应于g的寄存器$s1中:
add $s1,$s2,$t0
数据传送指令中的常量叫做偏移量(offset),存放基址的寄存器叫做基址寄存器。
MIPS的字的起始地址必须是4的倍数。这叫对齐限制,许多系统结构都有这样的限制(第五章阐明了为什么地址对齐能使数据传送加快)。
有两种字节寻址的机器:一种使用最左端或“大端”字节的地址作为字地址;另一种使用最右端或“小端”字节的地址作为字地址。MIPS采用大端编址。字节寻址也影响到数组下标。在上面的代码中,为了得到正确的字节地址,加到基址寄存器$s3的偏移量必须是4*8。
与取指令相反的指令叫做存储指令;它将数据从寄存器传送到存储器。存储指令的格式和取指令相似:操作符名称,接着是包含着待存储数据的寄存器,然后是选择数组元素的偏移量,最后是基址寄存器。同样,MIPS地址由常数和基址寄存器内容共同决定。实际的MIPS存储指令的名字为sw, 表示存储字(store word)。
例:假设变量h放在寄存器$s2中,数组A的基址放在$s3中则:A[12] = h + A[8]的MIPS汇编代码是什么样的呢?
解:
lw $t0, 32($s3)
add $t0,$s2,$t0
sw $t0,48($s3)
2.3.2常数或者立即数操作数
许多时候一个程序 会在一个操作中使用常量--举例来说,给一个数组的下标加1以指向下一个数组元素。许多程序的变量个数要比机器的寄存器个数多得多。因此,通过使用存取指令在寄存器和存储器之间传送变量,编译器会尽量将最常用的变量保存在寄存器中而将其他的变量保存在存储器中。将不常使用的变量存放在存储器中的这个过程叫做寄存器换出。
根据硬件设计原则--大小和速度的关系是越小越快,存储器一定比寄存器慢--因为寄存器更小。另外,寄存器的数据用处更大。一条MIPS算数运算指令一次能够读两个寄存器,对它们进行运算,并写入运算结果。MIPS中一条数据传送指令一次只能读一个操作数或写一个操作数,并且不能对它们进行运算。因此,MIPS中寄存器与存储器相比,不仅访问时间短,而且吞吐率高。这是一种极难得的组合,使得寄存器中的数据访问速度快,且易于使用。为了获得最高的性能,MIPS编译器必须有效地使用寄存器。
设计原则3:加速执行常用操作。
有常数操作数的快速加指令叫做立即数加,挥着写作addi。
例: addi $s3, $s3, 4
2.4计算机中指令的表示
人们使用计算机指令的方式与计算机执行这些指令的方式是不同的。数字以一系列高、低电平信号的形式保存在计算机硬件中,因此使用二进制编码来表示。指令也以一系列高低电平信号形式保存在计算机中,并以数字形式表示。实际上,每条指令的各个部分可以看作单个的数字,将这些数字拼在一起就形成了指令。因为几乎所有的指令都用到了寄存器,所以必须规定如何将寄存器名字映射成对应的寄存器序号。MIPS汇编语言中,寄存器$s0~$s7分别对应寄存器16~23,同时,寄存器$t0~$t7分别对应寄存器8~15.
例:给出MIPS指令:add $t0, $sq, $s2的十进制数表示形式,接着给出其二进制数表示形式。
解:十进制表示为:0 17 18 8 0 32
#一条指令的每一段叫做一个字段。第一个字段和最后一个字段(0和32)组合起来告诉MIPS计算机这条指令执行加法运算。第二个字段表示加法操作的第一个源操作数寄存器号(17 = $s1),第三个字段表示加法操作的另一个源操作数寄存器号(18 = $s2)。第四个字段中存放目的操作数寄存器号(8 = $t0)。第五个字段在这条指令中没用用到。这样,这条指令将寄存器$s1和$s2的内容相加,并将结果(和)存放在寄存器$t0中。
该指令的二进制字段表示为:
000000 10001 10010 01000 00000 100000
6位 5位 5位 5位 5位 6位
为了将它与汇编语言指令区分开,把指令的数字形式称为机器语言,这样的指令序列叫做机器代码。指令的这种设计叫做指令格式。可以看出,上述MIPS指令刚好32位--与数据字长度一样。为了遵循“简单源自规整”的设计原则,所有的MIPS指令都是32位长。由于几乎所有的计算机的数据大小都是4的整数倍,因此十六进制的数很是流行。为了避免混淆,我们在这约定,给十进制数加下标ten,给十六进制加下标hex,给二进制数加下标two。BTW,C和java中用符号Oxnnnn来表示十六进制数。
MIPS字段:
给MIPS字段命名以使讨论更为简单:
op rs rt rd shamt funct
MIPS指令各个字段的含义如下:
--op: 指令的基本操作,通常称为操作码
--rs: 第一个源操作数寄存器
--rt:第二个源操作数寄存器
--rd: 存放操作结果的目的操作数寄存器
--shamt: 位移量。
--funct: 函数。这个字段选择字段op中操作码的各种特定的变体,有时叫做函数码。
设计原则4:优秀的设计需要适当的折中。
MIPS的折中方案:所有的指令长度相同,但不同的指令采用不同的指令格式。例如,上述格式叫做R型(寄存器)。另一种指令格式叫做I型(立即数)。
I型: op rs rt constant or address(16位).
不同的指令格式由第一个字段的值来区分:每种格式的第一个字段(op)都被分配了一套不同的值,因此计算机硬件可以根据op来确定指令的后半部分是那种类型。
2.5逻辑运算
一些指令被加进来以使那些需要打包或者拆包位的事情简单化。这些指令就是逻辑操作。
一些逻辑操作(C操作符):
左移:<< 右移:>> 按位与:& 按位或:| 按位非:~
第一类指令被称为移位(shift)指令。它们将一个字里面的所有位都往右或者往左移,并在空出来的位上填充0。
左移和右移的MIPS移位指令的确切名字是:逻辑左移(sll)和逻辑右移(srl)。
例:sll $t2, $s0, 4 #将$s0中的元素左移四位,然后将新元素存入$t2中。
R型指令中的shamt字段,它在移位指令中用作移位量。
sll 解码之后 op字段和funct字段都是0,rd包含$t2, rt包含$s0,shamt包含4.
按位与操作:and $t0, $t1, $t2 .#将$t1 和$t2按位与,所得结果存入$t0中。
如你所见,当源操作数位模式的某位为0时,AND可以设置目标数对应位为0.这种和AND联合使用的位模式习惯上被称为掩码,因为掩码"隐藏"了某些位。
按位或操作(MIPS指令):or $t0, $t1, $t2 #reg $t0 -reg $t1 | $t2
取反操作有些特别。为了保持双操作数的格式,MIPS的设计者决定引入指令或非(NOT OR)来取代(OR)。而将或非操作的一个操作数设为0则其就等同于NOT.
例:A NOR 0 = NOT(A OR 0) = NOT(A)。
将寄存器$t3中的值设为0,则NOT的MIPS指令为:
nor $t0, $t1, $t3
像在算术运算中一样,常数在AND和OR这些逻辑运算里4也是很有用的,因此MIPS也提供了
and immediate(andi)(立即数与)和or immediate(ori)(立即数或)指令