英语原文共 12 页,剩余内容已隐藏,支付完成后下载完整资料
2.1 引言
要计算机服从指挥,就必须用计算机的语言。计算机语言中的基本单词称为指令,一台计算机的全部指令称为该计算机的指令集。本章将展现实际计算机指令集的两种形式:一种是人们编程书写的形式,另一种是计算机所能识别的形式。我们将以自顶向下的方式来介绍。从看似受约束的汇编语言助记符开始,逐步精炼到实际计算机的真实语言。第三章将继续采用这种向下探究的方式,揭示算数运算的硬件以及浮点数的表示方法。
尽管机器语言种类繁多,但它们之间十分类似,其差异性更像人类语言中的“方言”,而非各自独立的语言。因此,理解了一种机器语言,其他的也就容易理解了。这种相似性一方面是因为所有计算机都是基于相似基本原理的硬件技术所构建的,另一方面是因为有一些基本操作是所有计算机都必须提供的。此外,计算机设计者有一个共同的目标:找到一种语言,可方便硬件和编译器的设计,且使性能最佳、成本和功耗最低。但这个目标需要长期的探索。下述引文谢雨计算机出现不久的1947年,但今天它仍然适用:
用形式逻辑的方法可以很容易看到,在理论上存在着某种[指令集]足以控制任何的操作序列并使之执行hellip;hellip;从当前的观点出发,在选择一个[指令集]时,真正的决定性因素是要更多地考虑其实际性质:[指令集]要求的设备简单性,它的应用对于解决实际重要问题的明确性以及它解决该类问题的处理速度。
Burks、Goldstine和冯·诺依曼,1947
无论是对20世纪50年代的计算机而言,还是对现代的计算机来说,“设备简单性”都是值得考虑的重要问题。本证的目的就是讲解符合此原则的一种指令集,介绍它怎样用硬件表示,以及它和高级编程语言之间的关系。我们的示例使用C语言编写,光盘上的2.15节介绍了在使用像Jave这样的面向对象语言时会有什么不同。
通过理解如何表述指令,读者也将发现计算机的秘密:存储程序概念。此外,通过使用机器语言编程,并在本书提供的模拟器中运行,读者将更加体会到编程语言和编译优化对程序性能的影响。本章结束时我们将简要概括指令集的历史发展和其他的计算机“方言”。
本章所选指令集来自MIPS公司,它是20世纪80年代以来出现的各类指令集的优秀代表之一。然后,我们将简略介绍另外两个常见的指令集。一个是ARM指令集,它与MIPS非常相似,2008年ARM处理器在嵌入式设备中的使用量超过了30亿个。另一个是Intel x86指令集,2008年所销售的3.3亿台PC中大多安装了Intel处理器。
我们结合计算机的结构,逐步讲解MIPS指令集。采用自顶向下、循序渐进、结合部件及其说明的方法,尽量使机器语言变得不再枯燥。图2-1给出了本章将要介绍的指令集的总体情况。
2.2计算机硬件的操作
毫无疑问,必须有执行基本算数操作的指令。
Burks、Goldstine和冯·诺依曼,1947
任何计算机必须能够执行算数运算。MIPS汇编语言的下述记法。
add a,b,c #The sum of b and c is placed in a.
add a,a,d #The sum of b,c,and d is now in a.
add a,a,e #The sum of b,c,d,and e is now in a.
可知,使用3条指令完成了4个变量的相加。上述每一行中,符号“#”右边的是注释,用于帮助人们理解程序,而计算机将忽略它们。注意与其他编程语言不通的是,这种语言的每一行最多只有一条指令。另一个与C语言不用的是,注释总是在一行之尾结束。
与加法类似的指令一般都有三个操作数:两个进行运算的数和一个保存结果的数。要求每条指令有且仅有三个操作数,符合硬件简单性的设计原则:操作数个数可变将给硬件设计带来更大的复杂性。这种情况说明了应急设计四条基本原则的第一条:
设计原则1:简单源于规整。
2.3计算机硬件的操作数
不同于高级语言程序,算数运算指令的操作数是受限的,它们必须来自寄存器。寄存器由硬件直接构建,数量有限,是计算机硬件设计的基本元素。当计算机设定完成后,寄存器对程序员是可见的。所以也可以把寄存器想想成构造计算机“建筑”的“砖块”。在MIPS体系结构中寄存器大小为32位;由于32位为一组的情况经常出现,因此在MIPS体系结构中将其称为“字”。
高级语言的变量与寄存器的一个主要区别在于寄存器的数量有限。典型的现代计算机如MIPS有32个寄存器。(参见光盘上2.20节有关寄存器数目的演变历史)。下面继续以自顶向下的方式引入新的MIPS语言的符号表示。在本节MIPS算数指令的三个操作数限定为必须从32个32位寄存器中读取。
寄存器个数限制为32个的理由可以表示为硬件设计四条基本原则中的第二条:
设计原则2:越少越快。
大量的寄存器可能会使时钟周期变长,因此需要更远的电信号传输距离。
当然,该原则也不是绝对的,31个寄存器不见得比32个更快。但表象背后的物理事实值得计算机设计者认真对待。在这种情况下,设计者必须在程序期望更多寄存器和加快时钟周期之间进行权衡。另一个不使用多于32个寄存器的原因是受指令格式位数的限制,2.5节有相应介绍。
- 论证了寄存器在硬件结构中所扮演的核心角色。正如该章所述,有效利用寄存器对于提高程序性能极为重要。
尽管可以简单使用序号0到31表示相应的寄存器,但MIPS约定书写指令时,用一个“$”符号后面跟两个字符来表示一个寄存器。2.8节将解释这一做法的理由。现在,我们使用$s0,$s1hellip;hellip;来代表C和Jave程序中的变量所对应的寄存器;用$t0,$t1hellip;hellip;来代表将程序编译为MIPS指令时所需的临时寄存器。
2.3.1 存储器操作数
编程语言中,有像上面这些例题中仅含一个数据元素的简单变量,也有像数组或结构那样的复杂数据结构。这些负责数据结构中的数据元素可能远多于计算机中寄存器的个数。计算机怎样来表示和访问这样的结构呢?
回忆一下第一章和本章首页图片所描述的计算机的五个组成部分。处理器只能将少量数据保存在寄存器中,但寄存器有数十亿的数据元素。因此,复杂数据结构(如数组和结构)是存放在存储器中的。
如上所述,MIPS的算数运算只对寄存器进行操作,因此,MIPS必须包含在存储器和寄存器之间传送数据的指令。这些指令叫做数据传送指令。为了访问存储器中的一个字,指令必须给出存储器地址。存储器就是一个很大的下表从0开始的一维数组,地址就相当于数组的下标。例如,在图2-2中,第三个数据元素的地址为2,存放的值为10。
将数据从存储器拷贝到寄存器的数据传送指令,通常叫取数指令。取数指令的格式是操作码后接着目标寄存器,再后面是用来访问存储器的常数和寄存器。常数的第二个寄存器中的值相加既得存储器地址。实际的MIPS取数指令记符为lw,为load word的缩写。
硬件/软件接口
除了将变零与寄存器对应起来,编译器还在存储器中为诸如数组和结构这样的数据结构分配相应的位置。然后,编译器可以将它们在存储器中的起始地址放到数据传送指令中。
很多程序都用到字节类型,且大多数体系结构按字节编址。因此,一个字的地址必和它所包括的四个字节中某个的地址相匹配,且连续字的地址相差4。例如,如2-3给出了图2-2的实际MIPS地址,其中第三个字的字节地址是8。
在MIPS中,字的起始地址必须是4的倍数。这叫对齐限制,许多体系结构都有这样的限制。
有两种类型的字节寻址的计算机:一种使用最左边或“大端”字节的地址作为字地址;另一种使用最右边或“小端”字节的地址作为字地址。MIPS采用大端编址。
字节寻址也影响到数组下表。在上麦你的代码中,为了得到正确的字节地址,与基址寄存器$s3相加的偏移量必须是4*8,即32,这样才能正确督导A[8],而不会错读到A[8/4]。
与取数指令相对应的指令通常叫做存数指令;它将数据从寄存器拷贝到存储器。存数指令的格式和取数指令相似:操作码,接着是包含存数数据的寄存器,然后是选择数组元素的偏移量,最后是基址寄存器。同样,MIPS地址由常数和基址寄存器内容共同决定。实际的MIPS存数指令的名字为sw,为store word的缩写。
硬件/软件接口
许多程序的变量个数要远多于计算机的寄存器个数。因此,编译器会尽量将最常用的变量保持在寄存器中,而将其他的变量存放在存储器中,方法是使用取数/存数指令在寄存器和存储器之间传送变量。将不常使用的变量(或稍后才使用的变量)存回到存储器中的过程叫做寄存器溢出。
根据硬件设计原则2,存储器一定比寄存器慢,因为寄存器数量更少。事实的确如此,访问寄存器中的数据要远快于访问存储器中的数据。
另外,寄存器中的数据更容易利用。一条MIPS算数运算指令能完成读两个寄存器、对它们进行运算、并写回运算结果的操作。另一条MIPS数据传送指令只能完成读一个操作数或写一个操作数的操作,并且不能对它们进行运算。
寄存器与村暑期相比,访问时间短、吞吐效率高,寄存器中的数据访问速度快并易于利用,访问寄存器相对于访问存储器功耗更小。因此,为了获得高性能和节约功耗,编译器必须高效率地利用寄存器。
2.3.2 常数或立即数操作数
程序中经常会在某个操作中使用到常数——例如,将数组的下标加1,用以指向下一个数组元素。实际上,在运行SPEC2006测试到基准程序集时,有超过一半的MIPS算数运算指令会用到常数作为操作数。
仅从已介绍过的指令看,如果要使用常数必须先将其从存储器中取出。(常数可能是在程序被加载时放入存储器的。)
带有立即数的指令说明了硬件设计四条原则的第三条,这已原则在第1章的“谬误与陷阱”中曾提到过。
设计原则3:加速执行常用操作。
常数操作数出现频率高,而且相对于从存储器中取常数,包含常数的算数运算指令执行速度快很多,并且能耗较低。
常数0还有另外的作用,有效使用它可以简化指令集。例如,数据传送指令正好可以被视作一个操作数为0的加法。因此,MIPS将寄存器$zero恒置为0。(此寄存器编号也为0)
2.4有符号和无符号数
首先让我们快速回顾一下计算机是如何表示数的。人类所受的教育是以十进制为基础的,但数的进制可以是任意的。例如,十进制的123等于二进制的1111011。
在计算机硬件中数是以一串或高或低的电信号来体现的,这恰好可以被认为是二进制数。所有的信息都由二进制数位或位组成,因此二进制数运算基本单位是bit,取值可以是两种状态只有:高或低,开或关,真或假,1或0。
需要注意的是,上式是二进制数的一般表示。实际上数是由无穷多的位数组成,其中除了最右边的少数位以外其余大部分都是0。正常情况下不用表示左边的0。
硬件可以被设计成对这些二进制数进行加减乘除操作。如果操作结构不能被有段的硬件位所表示,那么久发生了溢出。如何处理溢出是由编程语言、操作系统和程序来决定的。
计算机程序对证书和负数都要进行计算,所以需要一种方法来区分正数和负数。显而易见的解决方案是增加一个独立的符号位,这种表示方法称为符号和幅值方法。
符号和幅值方法有若干缺点。首先,符号位放在哪里不够明确,放在右边还是左边?早期的计算机对两种方式都尝试过。其次,因为不可能在计算机提前得知结果的符号,对于符号和幅值表述的数进行计算需要额外的一步来设置符号。最后,一个单独的符号位以为着符号和幅值表示的数中不但有正零还有负零,这将给粗心的程序员带来问题。这些缺点导致这种表示方法很快就被放弃了。
在研究更具吸引力的替代方案时产生了这样一个问题,当我们试图用一个较小的数减去一个较大的数时,无符号数表示方法的结果将会是什么?答案是较小的数字将会从前面的0中借位,所有结果中前面的位都变成了一串1。
在没有其他明显更好选择的情况下,最终的解决方案是选择一种使硬件简单的表达方式:前导位为0表示正数,前导位为1表示负数。这种常用的表示有符号二进制数的方法称为二进制补码。
剩余内容已隐藏,支付完成后下载完整资料
资料编号:[32076],资料为PDF文档或Word文档,PDF文档可免费转换为Word
课题毕业论文、文献综述、任务书、外文翻译、程序设计、图纸设计等资料可联系客服协助查找。