计算机结构            到目前为止,我们已经学习了通用数字系统。本书剩余的部分将介绍数字计算机的设计。本章中,我们将介绍计算机的结构。在第10章中,我们将介绍在这里称为MODEL的示例计算机的设计。   图8.1中给出了计算机的框图。中央处理器(CPU)中包括了计算机中所有的内部寄存器、运算和逻辑电路(例如加法器),以及控制器(用来产生机器协调动作的定时信号)。总线是一组电路连线,用作在CPU与计算机其余部分间的数据、地址及控制信号的传输(在大部分计算机中,CPU内部也有一条或多条总线)。我们将在第9章中具体介绍总线的结构。   主存储器是一个快速的随机存取1存储器。通常计算机中还有二级存储器件,例如磁盘,另外还包括控制器(适配器)并与总线连接。输入/输出(I/O)控制器用来连接总线和外设。我们将在第11章中详细讨论存储器和I/O接口。   存储器中最小的可寻址单元是字。字长一般为16到64位,当然有时也会用到更小或更大的字长2。现代计算机中的主存储器容量一般为几百万字,因此其地址通常为24位(16万)或更多。事实上,存储器容量为几十亿字节(十亿级字节)是非常常见的。 图8.1 计算机组成框图   尽管对主存储器的读取已经非常快,但该速度常常无法满足CPU使用信息的要求。因此在计算机的存储器及逻辑部分中会有一定数量的内部寄存器用来保存正在处理的指令的信息,其中可能也会包括一定数量的用户存储器。大部分计算机中都有一套用户可寻址的寄存器(通常为16到64个),用来进行数据的临时存储。对这些寄存器的访问速度要比对主存储器的访问速度快,并且对这些寄存器的访问并不需要使用总线,这样就使总线可以响应其他器件的请求。此外,寄存器地址也比存储器地址要短,例如,对于32()个寄存器,其仅需5位地址信号。许多计算机中还有高速缓冲存储器(图8.1中并未给出)——一个小型的存储器,其硬件极为复杂,用来存储最近使用过的存储器字(包括指令及数据)。   寄存器和逻辑部分的工作单元是算术逻辑部件(ALU)。ALU中包含对定点数进行加减运算、进行如与、或运算的位逻辑运算符,及对位进行移位或循环操作的电路。有些ALU中还有进行乘、除运算的硬件,或将乘、除运算由一系列加、减及移位运算操作实现的硬件电路。有些ALU中还含有进行浮点运算的逻辑,或由独立的单元进行浮点运算。ALU中的操作数均来自于寄存器。   计算机执行一系列指令,并将结果保存在存储器中(尽管某些专用机中会有独立的存储器用来保存指令及数据,但对于大多数计算机而言,其只有一个主存储器)。计算机按照顺序将指令保存在主存储器中。计算机中有一个通常称为程序计数器的寄存器,将下一条指令的起始地址保存在其中。该寄存器中的地址数据将在每次指令执行完毕后自动更新。   计算机指令示例:   两数相加并将结果保存(ADD)   对操作数增加(加1)(INC)   若标志位3为0则跳出序列(修改程序计数器)      每条指令中必须包含所需执行操作的代码(操作码),并要说明从哪里得到数据,将结果保存在哪里,有时还需指出下一条指令的地址4。在某些计算机架构中,每条指令都有一个存储器字。此外,我们在下节中将会看到,某些指令需要两个字。在某些结构中将多条指令打包在一个字中。   执行一套顺序指令时,计算机是按照下面步骤执行的(在完成每条指令后,返回步骤1):   1. 将指令地址发送(从程序寄存器中)到存储器中以取得该指令。将该字保存在CPU的一个内部寄存器中,该寄存器有时也称为指令寄存器。(当得到完整指令所需超过一个字时,程序计数器会增加以得到每一个额外的字,见第8.1.1节)。   2. 更新程序计数器,以使其指向下一条指令(的第一个字)。(即将程序计数器中的值加1;若当前指令所需字数超过一个时还要重复该步骤。)   3. 对指令进行译码,即确定所需执行的操作以及(若需要的话)所需的操作数。(在可变长指令的机器中,该步骤将作为步骤1中的一部分来完成。)   4. 得到操作数。操作数可能在寄存器或在存储器中。在后一种情况下,就需要计算出所需地址并将数据从存储器中读取到CPU的一个寄存器中。   5. 执行该指令,即完成所需(若有)运算。   6. 保存结果,可能保存在存储器或一个寄存器中。(对于跳转指令,其结果可能是下一条指令的地址,则该结果要保存在程序计数器中。) 8.1 字结构   在计算机设计中,有两个因素会影响到一个字的位数。一个因素是说明一条指令所需的位数,另外一个因素是所需处理数据的长度。而指令字与所需处理的数据类型对字长的影响常常是有冲突的。   CPU中许多内部寄存器都是保存一个字的。而无论内部还是外部总线通常都能同时传输一个字。因此,长度大的字将会增加CPU硬件及总线的成本。而另外一方面,字长大的话会提高处理速度。(在某些机器中,可能同时访问存储器中的几个字,这样会增加总线数,而不需增加CPU中的寄存器数。)最终的设计通常是在成本、速度及效率间找到平衡。 8.1.1 指令格式及字长   典型的两操作数指令中(例如加运算指令),必须指定每个操作数及得到的结果所保存的位置。一种方法是在指令中给出三个地址空间,如图8.2a所示。指令中的每个地址可以是一个存储器中的位置、一个寄存器编号、实际数据或可以计算出有效地址(数据或得到的结果在存储器中保存位置的地址)的部分信息。因此,地址域中常包括两部分内容:地址类型及地址本身。在第8.3节中我们将介绍地址类型的区别。若三个地址都是存储器地址,则指令将非常长。若操作码是八位的话,则共需104位,每个地址类型说明需要4位,存储器地址为28位。该方法并未广泛使用的原因有两个。首先,这种方法所需指令太长;其次,两个数据字均取自主存储器,且计算结果也必须保存在主存储器中。由于涉及到的存储器速度低于CPU运行速度,这样会降低计算机的运行速度。   对于该问题有几种解决方法。一种缩短指令的方法是将得到的结果保存在其中一个操作数的位置上。这样,第一个地址将变为目的/源1,如图8.2b所示。(因此,在加指令执行后得到的和将取代第一个操作数。)这种方法不会改变地址检索速度,但可将指令缩短近30%。 图8.2abc 指令格式   我们在第10章的常见示例MODEL中将会看到,若目的/源1为一个寄存器的话,其指令长度会缩短更多。其将第一个地址域从32-36位缩短为只有6-8位,如图8.2c所示(其中RN域表示的是一个寄存器编号)。该方法只适用于只有一个数据来自于存储器的情况。   有些机器要求所有运算数据均保存在寄存器中,且得到的结果返回到一个寄存器中。只有读和存储操作会涉及到存储器。尽管其加指令的格式与图8.2a所示类似,但其地址域将全部变短。   图8.2中b和c部分的格式同样适用于两地址指令,例如将一个位置中的内容移动到另外一个位置。(在如图8.2c所示的结构中,这些指令称为从存储器将数据载入寄存器,及将寄存器中的数据存入存储器)。   还有许多机器中的指令并不需要存储器地址,其只涉及一个或多个寄存器。这样,这些指令地址只需要16位。   最简单的结构是每条指令都是一个单字。但这样对于有两个或三个地址的指令来说,所需字长就太长。即使对于如图8.2c所示的单地址结构,字长也要48位(8+8+32)或更长。但是,并非所有的指令都需要这么多位,这样会对存储器空间造成极大的浪费。   第二种方法是提供足够的空间使短指令可在一个存储器字中完成,而对于长指令则提供两个或更多的存储器字。图8.2c中给出了一个MODEL指令的第一个字的格式。其类型域用来确定指令是否需要第二个字。 图8.2d MODEL指令格式   第三种方法是采用长字格式,例如64位。每条指令以16位为一节来使用所需的空间。指令可能只需一个单字或几个级联字。图8.3a中给出了示例,其中指令A使用了两节,指令B使用了一节,指令C使用了三节,而指令D使用了两节。 图8.3a 可变长指令   这种排列方式的一个问题是指令B、C、D均是在一个字的中间开始。若机器只能对整字进行寻址的话,则这些指令分支目的都必须从一个字起始位开始(如A)。若我们想跳转到指令D(而不是B或C),则将浪费两节并要如图8.3b所示来排列指令,其中我们在第二个字的最后两节中插入no-op(无操作)指令。 图8.3b 增加跳转到指令D的功能   示例8.1   我们有一个计算机,其最大存储器空间为1G字(字)、32个寄存器、200条指令(需要一个8位操作码)。此外,还需4位用来指定地址类型。对于该机器中可涉及到一个寄存器及一个存储器地址的指令而言,该指令将需要   8+5+4+30=47位 若字长为24位,则需要一个地址的指令将要使用两个字。使用1(其中在30位的地址处所指定的地址类型是一个5位的寄存器号)的指令可在一个字中完成。 8.1.2 数据及字长   最简单的结构就是增加字长,使其足够容纳所需的最大的数据块。   注释:   对数据字长的考虑是与指令字长完全独立的。事实上,两者常常需要一个折中。   数据的位数决定了该数据的精度。对于定点数而言,32位的精度大概是9.5位十进制数,且其表示的整数范围大约为20亿。若需要小数的话(例如美元与美分5),则其表示的数值范围会变小。   大部分计算机中除提供整数外,还提供浮点数,其中约三分之一位用来表示指数,其余位用来表示尾数(见第1.2.5节)。浮点数通常要求字长至少为32位。   对于数据而言,使用两个(或更多)字即可实现更好的精度和更大的表示范围。某些机器会同时提供单、双精度指令。还有一些机器通过存储一个加法器最高的有意义位作为进位,并提供指令使用该进位作为下一个加法(或减法)运算的最低有意义位,从而实现多精度加法和减法运算。(显然,该方法运算速度很慢,这是因为计算机必须对两条指令进行读取及解码。)   对于这种情况下的指令,我们常常需要不同长度的数据(即,有时数据位数较多,而有时数据位数较少)。   示例8.2   字长为24位的计算机其表示数值范围为   无符号整数 0到 (1600万)   带符号整数 -到 (800万)   对于浮点数而言,其可能使用8位来表示指数,因此其表示范围为或大约。还有16位用来表示尾数(包括符号),允许的精度约为4.5位十进制数。 8.2 寄存器集   大多数计算机中都有一套通用寄存器用来保存数据及存储器地址。这些寄存器都进行了编号,一般为32(25)或64(26)个。某些计算机中有独立的寄存器用来存储浮点数据,有些计算机中寄存器集的数目会更大。每个寄存器的尺寸是一个存储器字长。将数据保存在寄存器中有两个好处。首先,数据访问速度会更快,一般只需一个CPU时钟。换句话说,访问在存储器中的数据时常需要几个时钟周期(有时甚至更长)。其次,寄存器的地址位数(5或6位)与用来在存储器中寻址的20到32位的地址位数相比要小很多。这些寄存器由程序员(用户)控制。   此外计算机中还有用户可访问寄存器,即堆栈指针和程序计数器6。此外,还有一些内部寄存器用来在执行一条指令期间存储信息,例如指令寄存器。   其中有代表性的是状态寄存器或一组标志位。其中的某些位用来表示最后一条指令(修改该位的指令)得到的结果为零或为负,或产生溢出或进位(对于加法而言,对于减法是产生借位)。其余位可能用于表示输入/输出信息。   用户可访问寄存器(包括状态寄存器)被称为计算机的状态。这些寄存器是唯一的在指令间包含有用信息的寄存器。   示例8.3   (MODEL中的用户寄存器) (MODEL中的内部寄存器)   寄存器集() 指令寄存器(IR)   堆栈指针(SP) 有效地址寄存器(EA)   程序计数器(PC) 工作数据(WORK)   标志(z,n,c,v) 8.3 寻址模式   寻址域即可用来获得一个有效地址(EA),即实际使用的存储器位置,也可用来得到指令所需数据。根据寻址模式的不同,有几种获得信息的方式。在介绍不同的寻址模式时,我们将会用到载入(LOD)指令,该指令用来将数据从由模式和地址指定的位置移动到由RN域指定的寄存器位置。此外还会用到MODEL(在第10章中要设计的机器)的结构,其指令格式如图8.2d所示。   示例8.4   为介绍定义,我们将假设(其中所有的地址、寄存器编号及数据均为十六进制数表示):   RN=6      ADDRESS=0123   第二个字(若需要的话)=456789AB      7      最简单的寻址模式是直接寻址。地址域中包含有完整的地址。但地址必须足够大以指定出一个存储器位置(在MODEL中是32位)。对于图8.2d给出的格式,地址在指令的第二个字中。指令第一个字中的ADDRESS域未使用。若地址足够短,则可将其放在第一个字的剩余部分(但这对于字长为32位的机器而言就意味着存储器非常小),见习题3。若地址比32位长,则可能有部分地址放在第一个字中,或者需要指令有第三个字(见示例8.6)。   示例8.4a   直接寻址   8      页零(有时也称为页零直接)寻址是将存储器看作是由页组成的,其中地址的前几位用来指出页号,剩余位用来指出字在页中的位置。页零就是指地址中用来表示页号的几个起始位均为0的页。在MODEL中,第一个字的16到31位用来指出一个页零地址的最后16位。这种方法常称为零扩展地址。页零寻址的优点是允许用户只使用一个指令字就指定一个直接地址。常使用的信息都存储在页零中。   示例8.4b   页零         当前页是页零的一种变化,其地址的前几个起始位与当前指令地址的起始位(即程序计数器的起始位)是相同的。这样就使得涉及到的字与当前指令更接近。         示例8.4c   当前页         对于间接寻址而言,指令中给出的地址是有效地址的指针。间接寻址指针既可使用全地址也可使用页零地址。指向的存储器位置中的内容就用作地址(在某些系统中(不是MODEL中),取得的地址中有1位用来表示该地址是真实地址还仍然是一个间接地址。这称为多级间接寻址,且其级数可以是无限的。)在那些地址不能在一个单字中表示的机器中(示例8.6),间接地址可能需要对存储器进行两次读取。   示例8.4d   全地址间接寻址      示例8.4e   页零间接寻址         立即寻址提供的是一个常数。数据就放在指令中。这种模式不会产生一个有效地址,只会得到一个数据。立即寻址不能用在需要保存结果的指令或分支指令中。立即寻址有两种形式。全立即(通常直接称为立即)寻址是将数据放在指令的第二个字中;短立即寻址是将地址域进行扩展,使其可以产生一个完整的数据字。它常常是符号扩展的,即将符号位复制到前面。   示例8.4f   立即寻址      示例8.4g   短立即寻址      示例8.4h   短立即寻址   若将地址改为:ADDRESS=9876,其起始位为1,则其扩展时起始位(二进制数)为全1:         寄存器寻址(或寄存器直接寻址)是从一个寄存器(MODEL中有64个寄存器)中得到数据(或将数据存入寄存器中)。地址域中的六位用来指定寄存器。在MODEL中,这六位是地址中的最后六位。这种形式的寻址不会计算出一个有效地址且可被用来得到或保存数据(但不适用于指令地址)。   示例8.4i   寄存器         寄存器间接寻址或地址寄存器寻址是从由地址域中六位指定的寄存器中得到有效地址。   示例8.4j   寄存器间接寻址         带自动后增寄存器间接寻址使用寄存器中的内容作为该指令的有效地址,但随后将该寄存器值增加(加1)。这种方法适用于处理阵列,即存储在连续存储器地址中的一组数据。寄存器初始化为阵列的第一个数据所在的地址。每次用这种方式寻址时,指针会指向下一个矢量值。某些机器中提供的是自降(减1)的寻址方式,或两者均提供。有些机器提供的是预增或预减寻址方式,即在使用该寄存器前就将其值进行更新。(在提供双精度算子的机器中,自增方式每次会将寄存器中的值加2。)   示例8.4k   带自动后增寄存器间接寻址         相对寻址是通过将地址域中的常数与下一条指令(在程序计数器中,并已经在取该指令时它已经增加后)中的地址相加来计算有效地址。这通常是单字指令且地址域是符号扩展的(这样允许地址在下一条指令之前或之后)。这种寻址模式最常用于分支指令中,但也可用于其他指令的地址计算。   示例8.4l   相对寻址            变址寄存器寻址是使用一个寄存器作为地址的偏移量。指令中的地址部分与变址寄存器中的内容相加得到有效地址。变址寄存器可能是一个专用寄存器,从寄存器集中指定一个,或者是一个任意的寄存器。若是后一种情况,则需用一个6位的域来指定该寄存器。变址寄存器寻址是另外一种用于访问一个阵列中某项的方法。阵列中首项的地址保存在地址域中,而偏移量或项号或索引则保存在变址寄存器中(这种寻址方式也因此得名。这种寻址方式对于数据在页零的情况下,只需一个字即可,或用在地址放在第二个字中的两字情况)。这种寻址方式对于涉及到两个不同阵列中类似项的寻址非常有效。   基址寄存器寻址方式与变址寄存器寻址方式使用的硬件相同。该术语是指当地址保存在寄存器中,而指令中的地址部分保存的是偏移量的情况。由于偏移量可能比较小,所以偏移量可以保存在地址部分中的剩余位,通常是符号扩展的。   示例8.4m   变址或基址寄存器寻址   若变址或基址寄存器中保存的是1234,且使用页零,则         堆栈寻址使用的是后入/先出的数据结构。堆栈就像是在咖啡馆中放成一叠的盘子。项是推入堆栈,堆栈中的原有项就向下推入。项也可从堆栈中弹出,即最顶端的项被删除且其余项均顺次向上弹出。图8.4给出了堆栈的示意图,是从一个空堆栈开始的,随后是进行了两次推入,一次弹出,再有另外一次推入。 图8.4 堆栈操作   堆栈通常是通过分配一块主存储器来实现,再用一个称为堆栈指针的寄存器来给出堆栈顶端的位置。我们在第10章中将详细讨论。   示例8.5   对于示例8.4中的各部分,这里列出了取指令加计算有效地址加取数据的存储器访问数。       对于页零 对于全地址   对于间接寻址(d和e),在取间接地址时需要进行一次存储器访问。对于立即寻址及寄存器寻址(f、g、h及i),则不需要从存储器中取数据。在下一节中,我们将看到除载入外的其他指令如何修改这一计算结果。   示例8.6   假设一个计算机字长为20位,有16个寄存器,主存储器容量为个字。其指令第一个字的格式为 该字中的最后8位是28位地址中最低有效位。以下各项都是用十六进制数表示的。那些寻址模式用来产生给出地址的一个地址,那些产生数据的用来表示数据。对于间接寻址和寄存器间接寻址,该字指向包含了地址中的最后8位(在字中最右端的8位)。地址中的其余20位可在相连的存储器位置或寄存器中找到。寄存器编号在Address中的最后4位中。   地址      第二个字          有效地址   直接寻址:   页零:   当前页:   页零间接寻址:   寄存器间接寻址:   寄存器间接寻址,自后增:      相对寻址:   数据   立即:   短立即:   寄存器: 8.4 指令集   指令可按照它们执行的操作类型,或所需操作数,或所需地址数来进行分类。我们在这里使用第一种方式进行分类,并首先来观察数据传送指令,然后是算术指令、逻辑及移位指令、分支指令,最后是输入/输出指令。   许多指令根据操作的结果会影响到一个或多个标志位。例如,若结果为0则位零标志置位(为1),若结果非0则将该位清零。在第10章中关于MODEL指令集的描述中,将详细说明对标志位的影响。 8.4.1 数据传送指令   计算机中最基本的操作就是将数据从一个位置移动到另外一个位置。源数据及目的数据均可来自于寄存器、存储器或堆栈。源数据还可以是一个常数(立即寻址)。注意:传送数据在计算机术语中表示的是将数据复制到一个新位置中,而原位置中的数不变。(某些计算机中有一种交换指令,可实现在两个寄存器间进行数据交换。)   对于单存储器地址的机器而言(就如MODEL)通常有两条独立的指令:载入,将由地址域9指定的位置中的数据传送到由寄存器域指定的寄存器中;及保存,将寄存器中的数据传送到由地址域指定的位置。两地址机器则只需一条单独的传送指令。对于保存指令(或其他的一元指令,例如自加指令,该指令读取数据然后将计算结果保存在相同的位置)不允许使用立即寻址方式,这是因为立即寻址表示的是一个常数。   示例8.7a   使用示例8.6中的值,并指定寄存器8及页零寻址。   载入   保存   如果堆栈顶部包含FFFFF,   弹出   推入 堆栈顶部 8.4.2 算术指令   最简单的算术指令就是对定点数(通常是整数)的加和减。因为几乎所有的机器都是按照二的补码格式来保存有符号数,所以对于无符号数和有符号数进行计算时使用的指令相同。在大多数机器中,这些指令集标志位用来表示结果是否为零或负,及从加法器/减法器的最高有效数字得到进位值。可保存进位的机器常会提供独立的带进位加及带借位减的指令,这样就实现了多精度计算。对进位的加或对借位的减都是对最低有效位进行操作的。   这些指令有两个操作数。在单存储器地址机器中,第一个操作数来自一个寄存器并将计算结果保存在该寄存器中,第二个操作数来自由地址域指定的位置。在两地址机器中,两个操作数位置均由地址域指定,并将计算结果保存在第一个操作数的位置。   在加法中有一种特殊情况就是增量(减量)指令,该指令用来对指定位置的数据加一个常数1(-1)。该指令用地址域指定操作数和计算结果的位置10。同样取反(求二的补码或用0减)指令通常也可用。   有些机器支持对堆栈中的操作数进行算术操作。对于双值操作(两操作数),两操作数从堆栈中弹出(分两步进行),并将计算结果推入堆栈中。   除最小型机器外,所有的计算机都提供乘、除指令。(注意,对于无符号数和有符号数,两者使用的指令是不同的。)许多机器中还提供浮点数计算的算术指令。   示例8.7b   使用示例8.6中给出的指令格式以及值      加,使用页零寻址      减,使用页零寻址       (对按位取反)      增量,使用寄存器寻址,(使用)      减量,使用直接寻址    8.4.3 逻辑、移位及循环指令   位操作逻辑指令(与、或、异或及非)适用于大多数机器。同算术指令一样, 地址域指定一个操作数,寄存器域指定另外一个操作数并将计算结果保存在该位置。与指令常作为屏蔽使用,用来选出一个字中的一位或几位。(该字和另外一个除所希望选出的部分外其余位均为0的字相与。)下面给出了用二进制数表示的示例。      示例8.7c   与,页零   屏蔽: 0000 1111 0000 0000 0000   数据: 1000 1010 1011 1100 1101   结果: 0000 1010 0000 0000 0000      类似地,或可用来在数据字中希望的位置填1。   示例8.7d   或,页零   屏蔽: 1111 1111 0000 1111 1111   数据: 1001 1010 1011 1100 1101   结果: 1111 1111 1011 1111 1111   移位指令是将字的每位向一个指定方向移动(右或左),舍掉末端的位。循环指令是将一端的位取出并将这些位按顺序放到另外一端。许多机器通过用一个计数域来指定移位或循环的位数。   常用的移位指令有两种:逻辑移位与算术移位。逻辑移位是在空出的位中填入0。算术右移位是保存起始位(对该位进行复制)以保证移位后得到的数与移位前的数符号相同。(算术左移位与逻辑左移位是相同的。)   示例8.7e   页零寻址   数据: 1001 1010 1011 1100 1101   左移5 位 0101 0111 1001 1010 0000   左循环5位 0101 0111 1001 1011 0011   右逻辑移3位 0001 0011 0101 0111 1001   右算术移3位 1111 0011 0101 0111 1001   右循环3位 1011 0011 0101 0111 1001      对于整数而言,右移一位相当于将其除2,左移一位相当于将其乘2。(算术移位用于有符号数,而逻辑移位用于无符号数。)结果进行四舍五入。   示例8.7f(移一位,使用8位数据)   带符号或无符号数   数据: 0001 1010 (26)   左移 0011 0100 (52)   右移 0000 1101 (13)   带符号数   数据: 1100 0101 (-59)   左移 1000 1010 (-118)   算术右移 1110 0010 (-30)   无符号数   数据: 1100 0101 (197)   左移 10001010 (超出8位)   逻辑右移 01100010 (98) 8.4.4 分支   当执行大部分指令时,程序计数器都会增加以指向下一条指令(的第一个字)。当需要执行不在序列中的指令时需要使用分支或跳转指令。   无条件分支指令是将有效地址载入到程序计数器中。无条件分支指令后的那条指令只能是另外一条指令的分支。   若某些二进制条件满足时,有条件分支指令会将程序引入一个分支,否则程序还会继续执行随后的指令。典型的条件包括是否有某一位标志位为零。此外,许多机器还提供在两数比较(事实上,是对两数做减法)后的分支,根据第一个数大于、大于或等于、小于、小于或等于、等于、或不等于第二个数。(对于有符号数和无符号数的测试条件是不同的。)   某些机器中还有跳转(skip)指令,该指令用于所有指令均为单字的简化机器中。   示例8.7g   使用示例8.6给出的结构,      地址   跳转,页零 PC0000098   按进位分支,直接寻址   若进位=1 PC3456798   若进位=0 PC1234569      子程序或程序调用指令是在分支前保存按顺序的下一条指令地址的分支指令(无条件或有条件)。该地址通常是推入堆栈并通过一条返回指令来得到该地址。(这里只要求子程序将堆栈状态与调用前保持一致。)图8.5给出了其动作的示例,其中子程序调用指令是在地址051111。堆栈的示例已在调用指令示例前给出,堆栈操作是在程序执行时和调用完成后进行。(在调用前堆栈可能不为空,但在上述的过程中对于堆栈中已有的内容将不会改变。)程序可使用堆栈的时间是从其首次将项推入堆栈到将其推入堆栈的所有项弹出为止。示例8.8给出了用一个程序调用另外一个程序调用的过程(网状子程序)。 图8.5 子程序调用指令示例   对于分支和调用指令而言,只允许使用能产生有效地址的寻址模式(不能从一个常数或一个寄存器中得到下一条指令。)   示例8.8 8.4.5 输入/输出及中断   正如图8.1所示,输入及输出器件与主总线的连接方式与存储器与总线的连接方式类似。由于这些器件工作速度不同且其数据配置格式也不同,所以在器件和总线间需要一个接口或控制器。   在某些机器中,有专用的输入、输出指令。最常见的是,I/O控制器使用的某些地址存储器也可使用。这被称为存储器映射I/O。在这种情况下,可使用标准的数据传送指令(第8.4.1节中给出的指令)。   中断是当某些事件需立即响应时出现的一个信号(通常与当前运行的程序相关)。中断可能在任意时刻出现,但只有在处理完一条完整的指令后控制器才会进行中断的相关处理。尽管中断源可能是来自内部,但通常是来自外部器件。计算机对于中断的处理和一个子程序调用是相同的,两者的区别在于中断分支控制的地址是由硬件决定的。若中断处理程序需要使用CPU中的某些用户寄存器,则首先要保存这些寄存器中的内容,并在重新运行原有程序前将这些内容重新放回寄存器中。有时,这一动作由硬件自动完成,有时是由软件功能实现的。(该过程的描述见第11章的描述。) 8.4.6 指令执行时间   在许多机器中(其存储器访问时间明显比时钟慢),大部分指令访问和执行的时间是由对存储器的读、写所占据的。对于那些需要从存储器中取出操作数或将结果保存在存储器中的指令(例如载入指令)而言,取得数据需要一个存储器访问(见示例8.5)。使用立即寻址或寄存器寻址方式的指令不需进行数据的存储器访问。分支也不需进行存储器访问。某些一元指令(例如增量指令)需要两次存储器访问:一次用于数据的读取,另外一次用于数据的保存。与堆栈相关的指令(例如调用或推入),每次堆栈访问都需要一次存储器访问。   示例8.9   这里给出了示例8.7中每条指令需要的存储器访问次数。   a. 载入 1+1=2    保存 1+1=2    弹出 1+1=2    推入 1+1=2   b. 加 1+1=2    减 1+1=2    增量 1    减量 2+2=4   c,d和e 1+2=3   g. 页零 1    直接寻址 2 8.5 例题   1. 计算机支持单地址及两地址指令(同样支持无地址指令)。指令前24位段的格式为 地址为24位,使用单存储器地址的指令需要另外一个24位段,而使用两地址的指令需要另外两个24位段。(对于无地址指令,只需24位即可。)   考虑下面给出的指令串(三段指令A,然后再是一个三段指令B,依次类推):   指令 A B C D E F G H   段数 3 3 1 2 1 3 2 3   存储器字长为96位。给出当按照每四分之一字为一组时指令的划分情况,若:   a. 系统可寻址到每个四分之一字。   b. 每个字中只有第一个四分之一字可以被寻址,且指令D和G可能作为分支指向的指令。   2. 假设有一台机器,其字长为16位,有16个寄存器,主存储器有个字。指令的第一个字格式为 其中模式中的最后4位可以是一个寄存器号、地址或某些模式下的数据。   假设下列值(在取指令前):      若指令为载入寄存器指令,对于下面给出的地址类型,给出有效地址及有关寄存器值的变化。 a. 寄存器寻址 b. 寄存器间接寻址 c. 带自后增量的寄存器间接寻址 d. 短立即寻址(符号扩展位为12:15) e. 直接寻址 f. 间接寻址 g. 相对寻址 h. 立即寻址 i. 变址寻址(作为变址寄存器使用)       3. 对于例题2中的寻址模式,求出取指令,计算有效地址并执行指令所需要进行存储器访问的次数。   4. 这里有一种新的寻址模式,带自预减量的间接寻址。(注意,这里指的是存储器间接寻址,而不是寄存器间接寻址。)除例题3中给出的值以外,   M[5677]=2222   若指令为载入寄存器指令,给出使用这种寻址模式时的有效地址及相关寄存器及存储器位置的变化。此外,还要给出存储器访问次数。   读取存储器位置1234中的值。该值自减并用作有效地址,然后再将自减后的值保存到存储器1234中。      存储器访问次数=2+2+1=5(在计算地址时必须对M[1234]进行读取和写入)。      5. 下面给出的指令是连续执行的。给出在每条指令完成后堆栈中的内容(假设堆栈为空)。寄存器的初始值为   REG2=1357   REG7=ABC4   REG8=4567   a. 推入REG2   b. 推入REG7   c. 弹出REG1   d. 推入REG2   e. 推入REG8   f. 推入REG1   g. 弹出REG3   h. 弹出REG5   在c.REG1←ABC4   在g. REG3←ABC4   在h. REG5←4567      6. 使用例题2中给出的结构以及下面的初始值,      给出所有寄存器(包括PC)及存储器中内容的变化,并指出所需的存储器访问次数。每部分都是一个独立的问题。   a. 载入12,页零寻址   b. 载入,寄存器寻址   c. 保存,寄存器间接寻址   d. 保存,寄存器寻址   e. 加,直接寻址   f. 加,寄存器寻址   g. 带进位的加,页零寻址   h. 减,寄存器寻址   i. 增量13寄存器寻址   j. 带自后增量间接寄存器寻址   k. 加堆栈   l. 与,页零寻址   m. 与,寄存器寻址   n. 或,直接寻址   p. 非,直接寻址   对于移位和循环指令,这里假设RN域中有移动的位数,它会在操作后给出。地址域中指定了操作数和结果的位置。   q. 左移3位,寄存器寻址   r. 逻辑右移4位,页零寻址   s. 算术右移4位,页零寻址   t. 右循环5位,间接寻址   u. 直接跳转   v. 跳转寄存器间接寻址   w. 间接调用   x. 立即调用   y. 返回             8.6 习题   1. 对于下面给出的系统,重复例题1    i. 48位字    ii. 144位字   2. 这里有一台机器,其指令格式如图8.2d,有64个寄存器,存储器为字。地址域中为数据或部分地址。对于需要寄存器的寻址模式而言,地址域中的最后6位表示的是寄存器号。   假设下列值(在取指令前):   若指令为载入寄存器指令,对于下面给出的地址类型,给出有效地址及有关寄存器值的变化。 a. 寄存器寻址 b. 寄存器间接寻址 c. 带自后增量的寄存器间接寻址 d. 短立即寻址(符号扩展) e. 直接寻址 f. 间接寻址 g. 相对寻址 h. 立即寻址   *3. 这里有一台机器,存储器为32位字长的字,有16个寄存器。所有指令均为单字,其格式如下:      假设下列值(在取指令前):      若指令为载入寄存器指令,对于下面给出的地址类型,给出有效地址及有关寄存器值的变化。(对于a,b,c中的寄存器号在位28:31中。) a. 寄存器寻址 b. 寄存器间接寻址 c. 带自后增量的寄存器间接寻址 d. 短立即寻址(符号扩展) e. 直接寻址 f. 间接寻址 g. 相对寻址 h. 带自后增量的寄存器间接寻址   4. 这里有一台机器,存储器为20位字长的228字,有16个寄存器。指令格式如下:      当需要一个28位地址时,地址的低(右)8位保存在指令的第一个字中,其余20位地址保存在指令的第二个字中。当地址保存在一个寄存器或在存储器中时,该字中保存低8位(在其最右端的8位),地址的剩余位保存在下一个字中。   假设下列值(在取指令前):          若指令为载入寄存器指令,对于下面给出的地址类型,给出有效地址及有关寄存器值的变化。    a. 寄存器寻址    b. 寄存器间接寻址    c. 带自后增量的寄存器间接寻址    d. 短立即寻址(符号扩展)    e. 直接寻址    f. 间接寻址    g. 立即寻址    h. 带自前增量的寄存器间接寻址   5. 对于下面给出的每部分 a. 习题2 b. 习题3 c. 习题4    分别求出各自进行指令访问,计算有效地址及取数据所需的存储器访问次数。   6. 下面给出的指令是连续执行的。给出在每条指令完成后堆栈中的内容(假设堆栈为空)。寄存器的初始值为 a. 推入REG1 b. 推入REG3 c. 推入REG1 d. 推入REG6 e. 弹出REG9 f. 弹出REG1 g. 弹出REG3 h. 推入REG4 i. 弹出REG3 j. 弹出REG6   *7. 使用图8.2d(及习题2中)所示的指令结构以及下面的初始值(注意:寄存器号是用十六进制数表示的):       给出所有寄存器(包括PC)及存储器中内容的变化。每部分都是一个独立的问题。此外,还要求指出所需的存储器访问次数。 a. 载入,页零寻址 b. 载入,寄存器寻址 c. 保存,寄存器间接寻址 d. 保存,寄存器寻址 e. 加,直接寻址 f. 加,寄存器寻址 g. 带进位的加,页零寻址 h. 减,寄存器寻址 i. 增量间接寻址 j. 带自后增量间接寄存器寻址 k. 加堆栈 l. 与,页零寻址 m. 与,寄存器寻址 n. 或,直接寻址 p. 非,直接寻址   对于移位和循环指令,这里假设RN域中有移动的位数,它会在操作后给出。地址域中指定了操作数和结果的位置。    q. 左移3位,寄存器寻址    r. 逻辑右移4位,页零寻址    s. 算术右移4位,页零寻址    t. 右循环5位,间接寻址    u. 直接跳转    v. 跳转寄存器间接寻址    w. 间接调用    x. 返回   8. 使用习题3中给出的机器及下面的初始值, 进位=1 堆栈同习题7。 a. 载入,页零寻址 b. 载入,寄存器寻址 c. 保存,寄存器间接寻址 d. 保存,寄存器寻址 e. 加,直接寻址 f. 加,寄存器寻址 g. 带进位的加,间接寻址 h. 减,寄存器寻址 i. 增量间接寻址 j. 带自后增量间接寄存器寻址 k. 加堆栈 l. 与,页零寻址 m. 与,寄存器寻址 n. 或,直接寻址 p. 非,直接寻址 对于移位和循环指令,这里假设RN域中有移动的位数,它会在操作后给出。地址域中指定了操作数和结果的位置。 q. 左移3位,寄存器寻址 r. 逻辑右移4位,寄存器间接寻址 s. 算术右移4位,堆栈 t. 右循环5位,间接寻址 u. 直接跳转 v. 跳转寄存器间接寻址 w. 间接调用 x. 返回   9. 使用习题4中给出的机器及初始值,给出所有寄存器(包括PC)及存储器中内容的变化。此外,还要求指出所需的存储器访问次数。每部分都是一个独立的问题。 a. 载入,页零寻址 b. 载入,寄存器寻址 c. 保存,寄存器间接寻址 d. 保存,寄存器寻址 e. 加,直接寻址 f. 加,寄存器寻址 g. 带进位的加,页零寻址(进位=1) h. 减,寄存器寻址 i. 增量间接寻址 g. 带自后增量间接寄存器寻址 k. 与,页零寻址 l. 与,短立即寻址 m. 非,直接寻址 n. 或,直接寻址   对于移位和循环指令,这里假设RN域中有移动的位数,它会在操作后给出。地址域中指定了操作数和结果的位置。 o. 左移3位,寄存器寻址 q. 逻辑右移4位,页零寻址 r. 算术右移4位,页零寻址 s. 直接跳转 t. 跳转寄存器间接寻址 8.7 第8章测试(50分钟)   1. (20)这里有一台机器,其指令需要32位或64位。主存储器字长为96位。考虑下面指令串:   指令 A B C D E F G H   位数 32 64 64 32 64 32 64 64   给出当按照每三分之一字为一组时指令的划分情况,若: a. 系统可寻址到每个三分之一字。 b. 每个字中只有第一个三分之一字可以被寻址,且指令B和G可能作为分支指向的指令。   2. (20)下面给出的指令是连续执行的。给出在每条指令完成后堆栈中的内容(假设堆栈为空)。假设寄存器的初始值为 REG0 = 1234 REG1 = 4567 REG3 = 89AB a. 推入 REG0 b. 推入 REG0 c. 推入 REG1 d. 弹出 REG3 e. 弹出 REG7   3. 这里有一台机器,字长为24位,有64个寄存器,主存储器有字。指令第一个字格式如下:      该字中最后8位为32位地址的最低8位有效位。下面值均用十六进制数表示。对于间接寻址和寄存器间接寻址模式,该字中保存了地址中的最低8位(在其右端的8位)。地址的前24位保存在下一个(更高号)存储器位置或寄存器中。对于涉及到寄存器的寻址模式,寄存器号保存在位18:23。 地址 = 46 Second word = 123456 该指令的地址: 00112222 M[00000046] = 6789AB M[00000047] = 012345 M[00010203] = 765432 M[12345646] = FFFFFF M[12345647] = 123456 M[123456FF] = 776655 M[44444403] = ABCDEF    REG6 = 010203    REG7 = 444444   a. (每小题3分)对于下面给出的寻址模式,给出执行载入寄存器1指令后存入REG1中的值。    i. 寄存器寻址    ii. 页零寻址    iii. 寄存器间接寻址    iv. 直接寻址    v. 短立即寻址    vi. 立即寻址   b. (每小题7分)对于下面给出的指令,给出所有有变化的存储器及寄存器(包括PC)的值。此外,给出存储器访问次数。每部分都是一个独立的问题。    i. 保存14,直接寻址    ii. 加,寄存器寻址    iii. 增量间接寻址    iv. 或,寄存器寻址    v. 左移1位,页零寻址    vi. 直接跳转 1 这里随机存取的意思是访问(读或写)存储器中的每一个字时是同时进行的。而在其他器件(例如磁盘)中,访问时间取决于该字与读/写头的位置关系。   2 有些计算机中的寻址字节(8位)以及可能提供对不同字长的数据进行操作的指令。 3 标志位是寄存器及逻辑部分的一部分,用来存储机器操作信息。标志位中的内容有时称为条件码。例如,其中可能有一位用来表示最后一个运算操作后的结果是否为负数。 4 在大部分现代计算机中,除分支指令外的其他指令均假设下一条指令的第一个字保存在当前指令后的位置。在某些早期的计算机中,每条指令均包括了下一条指令的地址。   5 由于美分(这里是小数部分)不能精确地用二进制数表示(见第1.2.5节内容),所以财经数据常使用BCD码来表示。   6 用户通过流程改变指令(如跳转指令)来控制程序计数器。   7 符号M[00000123]表示的是存储器位置00000123。   8 箭头()表示的是将箭头右边的值载入到箭头左边的位置中,在这里表示的就是在REG6中载入91919191。   9 这里可以是另外一个寄存器。在这种情况下,载入和保存将执行相同的数据传送,但对标志位的改变可能不同。 10 某些有短指令字的机器中同样提供一条指令用来自加或自减一个寄存器而只需一个寄存器号(不是一个存储器地址域)。图8.2中给出的MODEL格式中允许只使用一个字来进行寄存器寻址。   11 对于该示例的功能,这里假设所有指令均为单字。字长为16位且存储器地址同样为16位。   12 其后指定的寄存器(这里是REG2)是由RN域指定的。   13 对于单操作数指令(例如增量指令)忽略其RN域。   14 其后指定的寄存器(这里是REG7)是由RN域指定的。 ---------------      ------------------------------------------------------------      ---------------      ------------------------------------------------------------    398 逻辑与计算机设计导论    399 第8章 计算机结构