zoukankan      html  css  js  c++  java
  • 6、自动操作

    先来回忆下我们提到过的加法器:

    从图中可以看出,8位锁存器利用触发器来保存8位数据。使用这个设备时,首先需要按下清零开关使锁存器中的内容全部为0,然后通过开关输入第一个数。加法器只是简单的将这个数字和锁存器输出的0进行求和,因此相加的结果与原先输入的数字是一样的。按下相加开关可以把这个数保存在锁存器中,最后会点亮某些灯泡以显示它。现在通过开关输入第二个数,加法器把它与已经存放在锁存器的第一个数相加。再次按下相加开关,就可以把相加的结果存入锁存器中,通过灯泡显示这个结果。通过这种方式,可以把一串数相加并显示运行结果。显然,这种设计方案存在一个缺陷:8个灯泡无法显示大于255的数。

    对于这种电路,我们目前只讲到了一种锁存器,电平触发。在电平触发锁存器中,为了保存数据必须将时钟端首先置1,然后回置为0.当时钟输入端为1时,锁存器的数据输入端可以改变,而这些变化将会影响到数据输出。我们还介绍了边沿触发的锁存器,这种锁存器在0跳变为1的瞬间保存数据。边沿触发器在很多方面更加易于使用,因此假定本章用到的所有触发器都是边沿触发的。

    用来累加多个数的锁存器称作累加器(accumulator)

    上面的加法器存在一个很大的缺陷,假如要把100个二进制数加起来,你必须坐在加法器前,耐心的输入所有的数并累加起来,如果中间两个数输错了,又要重复开始。

    但是,也许并非如此。上一章我们学习了RAM阵列,我们如果可以把100个二进制数输入到RAM阵列中而不是直接输入到加法器中,一旦需要修改一些数据,我们工作将会变得容易很多。

    因此我们现在面临的是如何把RAM阵列和累加器连接起来。RAM阵列的输出信号可以替代加法器的开关。而用一个16位的计数器就可以控制RAM阵列的地址信号。在这个电路中,RAM阵列的数据输入信号和写操作端信号可以省去。修改后的电路结构如下图所示:

    当然,这并不是最易于使用的计算设备。要使用它,首先要闭合清零开关,这样做的目的是,清楚锁存器中的内容并把16位计数器的输出置为0000h,然后闭合RAM控制面板的控制端开关。现在你可以从地址0000h开始输入一组你想要相加的8位数。如果有100个数,那么他们将被存放在0000h-0063h的地址空间中(也应该把RAM阵列中未使用的单元设置为00h)。然后闭合RAM控制面板的控制端开关(这样控制面板就不再控制RAM阵列了),同时断开清零开关。做完了这些,我们可以静静坐下来观察灯泡显示运算结果。

    让我们看下是如何工作的:当清零开关第一次断开时,RAM阵列的地址输入是0000h。RAM阵列的该地址中存放了8位数值是加法器的输入数据。加法器的另一个输入数据为00h,因为此时锁存器也已经清零了振荡器提供的时钟信号—— 一个可以在0,1之间快速切换的信号。清零开关断开后,当时钟信号由0跳变为1时,将有两件事同时发生:锁存器保存加法器的计算结果,同时16位计数器增1,指向RAM阵列的下一个地址单元。清零开关断开后,时钟信号第一次从0跳变为1时,锁存器就将第一个数值保存下来,同时计数器增加为0001h;当时钟发生第二次跳变时,锁存器保存之前的两个数的求和结果,同时计数器增加为0002h;按这种方式反复操作。

     要注意的时,这里首先做了一些假设。最重要的一点就是,振荡器要足够慢以使电路的其他部分可以工作。每次时钟震荡过程中,在加法器输出有效的结果之前,一些继电器必须去触发其他继电器。

    这个电路存在两个缺陷:我们没办法使它停下来、它目前只能做8位加法。

    现在关注另外一个问题:如果不需要把100个数加一起,只是想用自动加法器把50对数分别相加,得到50个不同的结果。

    我们可能想到把运算结果存回到RAM阵列中去,这样的话,就可以在适当的时候用RAM阵列的控制面板来检查运算结果。为了这个目的,控制面板上专门设计了灯泡。

    这意味着我们可以去掉与锁存器连接的灯泡,取而代之的是把锁存器的输出端连接到RAM阵列的数据输入端,这样就可以把结果写回到RAM阵列中去。如下图:

    上图略去了自动加法器的其他部分,其中包括振荡器和清零开关,因为我们现在不需要特别标注计数器和锁存器的清零及时钟输入。此外,既然我们现在已经开始利用RAM的数据输入,因此需要一种用来控制RAM写入信号的方法。

    我们希望它不仅仅可以对一组数字做累加运算,还希望它能够自主地确定要累加多少个数字,而且还能记住在RAM中存放了多少个计算结果,这样就可以简化查询工作。

    例如,假设我们要先对三个数进行求和,然后对两个数进行求和,最后再对三个数进行求和。想象一下,我们可以把这些数保存再RAM阵列中以0000h开始的一组空间中,这些数存储在RAM阵列中的具体形式如下图所示:

     实际上,我们希望它能做四件事情:

    进行加法操作,首先它要把一个字节加(Add)到累加器中,这个操作称为加载(Load)。

    把存储器中的一个字节加(Add)到累加器的内容中去。

    把累加器中的计算结果取出并村放到存储器中。

    需要一个方法另自动加法器停(Halt)下。

    这一过程如下,上面提到的自动加法器所做的运算为例:

    1、把0000h地址处的内容加载到累加器

    2、把0001h地址处的内容加到累加器中

    3、把0002h地址处的内容加到累加器中

    4、把累加器中的内容存储到0003h地址处

    5、把0004h地址处的内容加载到累加器

    6、把0005h地址处的内容加到累加器

    7、把累加器中的内容存储到0006h地址处

    8、把0007h地址处的内容加载到累加器

    9、把0008h地址处的内容加到累加器

    10、把0009h地址处的内容加到累加器

    11、把累加器中的内容存储到000Ah地址处

    12、令自动加法器停止工作

     为了更灵活的实现我们的需求,我们需要把存储器的某个值直接加载到累加器中,或者直接把累加器的值保存在存储器中,还希望它能自动的停下来。便于观察RAM值。这个时候我们就需要加一些数字代码来标识加法器要做的每一项工作:加载、相加、保存、终止。

    操作码   代码
    Load(加载)   10h  
    Store(保存)   11h
    Add(加法) 20h
    Halt(停止) FFh

    我们可能想到方便的方法是另外新增一块RAM,用户存放每个地址对应的指令(被称为指令码或操作码,指示电路要执行的操作)

    之前我们说自动加法器的8位输出要作为数据RMA阵列的输入,这就是Save功能。还需要另一个改变,以前8位加法器的输出是8位锁存器的输入,但是现在为了执行Load指令,数据RAM阵列的输出有时也需要作为8位锁存器的输入,这种新变化需要一个2-1选择器来实现。改进后的自动加法器如下图所示:

    图中略去了一些组件,但是仍然清晰的描述了各个组件之间的8位数据通路。16位的计数器为两个RAM阵列提供地址输入。通常,数据RAM阵列的输出传入到8位加法器执行加操作。8位锁存器的输入可以是数据RAM阵列的输出(当执行Load指令),也可以是加法器的输出(执行Add指令),这种情况下就需要2-1选择器。通常锁存器电路的输出又流回到加法器中,但是当执行Save指令时,它就成为了数据RAM阵列的输入数据。

    上图缺少的是控制所有这些组件的信号,它们统称为控制信号。包括16位计数器的“时钟”输入和“清零”输入,8位锁存器的“时钟“输入和”清零“输入,数据RAM阵列的”写“(W)输入,2-1选择器的"选择”(S)输入。其中一些信号明显是基于代码RAM阵列的输出,例如,如果代码RAM阵列的输出是Load指令,那么2-1选择器的“选择”输入必须是0(即选择数据RAM的输出)。只有当操作码是指令Store时,数据RAM阵列的“写”(W)输出必须是1.这些控制信号可以通过逻辑门的各种组合来实现。

    利用最少的附加硬件和一些新增的操作码,可以让这个电路从累加器中减去一个数。第一步是向操作码表增加一些代码。

    操作码 代码
    Load 10h
    Store 11h
    Add 20h
    Subtract(减法) 21h
    Halt FFh

    如果是剑法,数据RAM阵列的数据传入加法器之前要取反,并且加法器进位输入置1,然后的操作和Add一样。增加一个反相器,C0信号可以完成这两项任务。改进后电路结构如下:

    假如现在要把56h和2Ah想加,然后再从中减去38h,可以按照下图中两个RAM阵列的代码(操作码)和数据(操作数)完成该运算。

    累加器中的值,先加载56h,和24A相加,即80h.Subtract操作使数据RAM阵列的下一个值(38h)按位取反,得到C7h。当加法器的进位输入置1时,取反得到C7h,然后跟80h相加。C7h+80h+1h=48h。

    如果我们要进行16位的计算呢?一个解决方法是两个8位连一起,但还有个更小的代价,就是把低8位计算,再计算高8位就可以了,这里就会出现低8位计算的时候,有进位的话怎么办?我们可以再低字节加法的时候,把进位给保存起来。如何保存呢?1位锁存器就是最好的选择了,该锁存器应该被称为进位锁存器(Carry latch)

    为了使用进位锁存器,还需要增加“进位加法”(Add with Carry)操作码。8位加法操作时,使用Add指令。加法的进位输入是0,它的进位输出将会保存到进位锁存器(尽管根本不会用到)。

    如果要对16位的数进行加法运算,我们仍然使用常规的Add指令对两个低字节数进行加法运算。加法器的进位输入为0,进位输出被锁存到进位锁存器中。当把两个高字节数相加时,要用Add with Carry指令。这时,两个数相加时要用进位锁存器的输出作为加法器的进位输入。

    如果要进行16位数的减法运算,则还需要一个新的指令,称为“借位减法”(Subtract and Borrow)。通常,Subtract指令需要将减数取反并且把加法器的进位输入置1.进位输出正常情况下为1,因此应该被忽略。但对16位数进行减法运算时,锁存器保存的结果应该作为加法器的进位输入。在加入了Add with Carry和Subtract and Borrow之后,目前我们已经有7个操作码,如下:

    操作码 代码
    Load 10h
    Store 11h
    Add 20h
    Subtract 21h
    Add with Carry(进位加法) 22h
    Subtract with Borrow(借位减法) 23h
    Halt FFh

    在执行减法或借位减法运算时,送入加法器中的操作数需要进行取反预处理。加法器的仅为输出是进位锁存器的数据输入。

    现在假设我们要对三个8位数求和,再减去一个8位数并保存结果。可能需要一条Load,两条Add,一条Subtract指令以及一条Store指令。如果想要从原来的求和结果(3个8位数的和)减去另一个数该怎么做?这个求和结果已经不能被访问了,每次我们使用它的时候都必须重新计算。

    产生上面的原因在于我们构造的自动加法器具有如下的特性:它的代码存储器和数据存储器是同步的、顺序的,并且都从0000h开始寻址。代码存储器中的每一条指令对应数据存储器中相同地址的存储单元。一旦执行了一条Store指令,相应的,就会有一个数被保存到数据存储器中,而这个数将不能重新加载到累加器中。

    要解决这个难题,需要对自动加法器的设计做一个根本性的且程度极大的修改。这个想法实现起来似乎非常困难,但是改进的加法器具有更高的灵活性。

    每个指令占一个字节,现在我们除Halt之外,其他的都占用3个字节,一个为代码本身,另外两个用来存放一个16位的存储单元地址。如对于Load,后两个字节保存的地址用来指明数据RAM阵列的一个存储单元,该单元存放的是需要被加载到累加器中的字节。对于Add,SubStract,Add with Carry,Subtract with borrow指令来说,该地址知名的存储单元是要从累加器中加上或减去的自己。对于Sotre,该地址指明累加器中的内容将要保存到的存储单元地址。

    如下求和:

    改进的自动加法器中,每条指令(除Halt指令)需要3个字节

    后面的两个字节的地址可以是其他的地址。

    前面我们讲到过的两个16位76ABh和232Ch相加,运算结果存储的问题。现在我们有更合理的方式了。

    这6个存储单元不必像途中这样全连在一起,它们可以分散在整个64KB数据RAM阵列的任意位置。为了把这些地址中的数相加,代码RAM阵列中的指令必须用以下方式设置。

    上图描述的是两个16位数相加,然后先计算低字节,然后计算高字节。然后分别存放在4004h 4005h。

    实现该设计的关键是把代码RAM阵列的数据输出到3个8位锁存器中。每个锁存器保存该3字节指令的一个字节。第一个锁存器保存代码,第二第三个分别保存地址高字节和地址低字节。

     

     从存储器中取指令的过程 称为取指令。在我们设计的加法器中,每一条指令的长度是3个字节。因为每次从存储器取回一个字节,所以取每条指令需要的时间为3个时钟周期。此外,一个完整的指令需要4个时钟周期。这些变化使得控制信号更加复杂。

    机器响应指令码做一系列操作的过程称为执行指令,每一种机器码用其唯一的方式出发多种控制信号,从而引发机器执行各种操作。

    由于我们现在使用的是3字节长度的指令格式,第二和第三个字节用来指明操作数的存储地址,因此就没必要再使用两个独立的RAM阵列,操作码和操作数可以存放在同一个RAM阵列了。

    我们需要一个2-1选择器来确定如何对RAM进行寻址。通常,和前面方式相同,我们用一个16位的计数器来计算地址。数据RAM阵列的输出仍然链接到3个锁存器,分别用来保存指令代码及其相应操作数的16位地址,其16位的地址输出是2-1选择器的第二种输入。地址被锁存后,可以通过选择器将其作为RAM阵列的地址输入。

    现在可以把操作指令和操作数据保存在同一个RAM阵列中,下面演示了如何把两个8位数相加,然后从结果中再减去一个8位数。

     由于计数器复位后,从0000h开始访问,所以我们指令从0000h开始存放。我们可以把他们的操作数以及运算结果放在除了指令之外的任何地方,所以我们选择在从0010h地址开始保存操作数。

    如果现在我们需要添加指令,则0010h显得不够用。或许我们可以试着将新添加的指令放在0020h,新添加的操作数据地址放在0030h。我们希望自动加法器从0000h开始执行所有指令完成计算任务。

    我们当然希望移除000Ch处的Halt指令,但是移除后,后面0010h操作数据都会被当作指令,所以这个时候我们用到Jump指令,跳转到0020h。

    操作码 代码
    Load 10h
    Store 11h
    Add 20h
    Subtract 21h
    Add With Carry 22h
    Subtract With Borrow 23h
    Jump(跳转) 30h
    Halt FFh

    Jump指令通过作用于16位计数器实现其功能。无论如何,只要自动加法器遇到指令,计数器就会被强制输出该Jump指令后的16位地址。这可以通过16位计数器的D型边沿触发器的预置(Pre)和清零(Clr)输入来实现:

    Pre和Clr端正常输入都应该是0,当Pre=1,Q=1,当Clr=1,则Q=0.

    如果你希望向一个触发器加载一个新的值(用A代表地址),可以如下图这样链接:

    通常,置位信号为0.此时,触发器的预置端输入为0,在复位信号不为1的情况下,清零信号也为0.当置位信号为1时,如果A为1,则清零输入为0,预置输入为1;如果A为0,则预置输入0,清零输入1。这就意味着Q端将被设置为与A端相同的值。

    我们需要为16位计数器的每一位设置这样一个触发器。一旦加载了某个特定的值,计数器就会从该值开始计数。

    这对电路的改动并不是很大,从RAM阵列得到的16位地址既可以作为2-1选择器(它允许该地址作为RAM阵列的地址输入)的输入,也可以作为16位计数器置位信号的输入。

    我们必须确保只有指令代码为30h并且其后的16位地址被所存时,置位信号才为1.

    跳转很重要,思考一下:怎样让自动加法器进行两个8位数的乘法运算呢?例如A7h和1Ch相乘。

    两个8位数相乘得到一个16位数,为了方便起见,把该乘法运算中涉及的3个数均表示为16位数。第一步确定要把乘数和乘积保存到什么位置。

    其实乘法就是累加的结果。下图演示如何把A7h加到该地址。

    上图就是A7h乘以1的结果。我们需要把这6条指令再反复执行27次。为了达到这个目的,可以在0012h地址开始把这6条指令连续输入27次。也可以在0012h处保存一个Halt指令,然后将复位键连续按28次得到最终结果。

    当然这两种方法都不是很理想,它们要求你做很多重复繁琐的事情。但是如果我们在地址0012处放置一条Jump指令会怎样?这个指令使得计数器再次从0000h处开始计数。

    所以我们需要一种jump指令,能够指定这个过程需要重复执行的次数,这种指令就是条件跳转指令。要实现它,要做的第一步就是增加一个与进位锁存器类似的1位锁存器。该锁存器被称为零锁存器(Zero latch),这是因为只有当8位加法器的输出全部为0时,它所存的值才是1.

     和仅为锁存器的是中暑如一样,只有当Add、Subtract、Add with Carry、Subtract with Borrow这些指令执行时,零锁存器才所存1个数,该数称为零标志位。

    有了仅为锁存器和零锁存器以后,我们可以为指令表新增4条指令。

    例如,非零转移指令(Jump If Not Zero)只有在零锁存器的输出为0时才会跳转到制定的地址,换言之,如果上一步的加法、减法、进位加法、或者借位减法等运算的结果为0时,将不会发生跳转。为了实现这个设计,只需要在常规跳转命令的控制信号之上再加一个控制信号:如果指令是Jump If Not Zero,那么只有当零标志位是0时,16位计数器才被触发。

    其实就是在最后加上一个乘数与FFh相加,保存到乘数中去的结果。FFh相加等于减1.当减成0的时候,零标志位就是1了。程序就停止了。

    现在可以断言,我们一直不断完善的这组硬件构成的机器确实可以被称为计算机。当然,它很原始,但毕竟是一台真正的计算机。条件跳转指令将它与我们以往设计的加法器区别开来,能否控制重复操作或者循环是计算机和计算器的区别。这里演示了乘法,类似的,也可以运行除法,而且不局限于8位.16、24、32更高位都可以。既然它能完成这些运算,那么开平方根,取对数,三角函数等运算也完全可以胜任。

    既然我们已经装配了一台计算机,因此可以使用计算机相关词汇了

    我们装配的属于数字计算机,只处理离散数据。一台数字计算机主要由4部分构成:处理器,存储器,至少一个输入设备和一个输出设备。我们装配的计算机中,存储器是64KB的RAM,输入和输出设备分别是RAM阵列控制面板上的开关和灯泡。除了前面的3种设备之外的其他设备都归类于处理器(CPU)。

    我们设计的是8位处理器。累加器的宽度是8位,大部分数据通路都是8位宽度。唯一的16位数据通路是RAM阵列的地址通路。如果该通路也是8位的话,存储容量就变为256字节而非65536字节。

    在我们的设计中,8位反相器和8位加法器一起构成了算术逻辑单元(ALU)。该ALU只能进行算数运算,复杂的可以进行逻辑运算。16位计数器被成为程序计数器。

    一直以来,我们都在使用很长的短语来引用机器所执行的指令,如Add with Carry指令。通常而言,机器码都分配了对应的短助记符,这些助记符都是用大写字母来表示,包括2个或3个字符。下面是一系列上述计算机大致能够识别的机器码的助记符。

    这些助记符与另外一对短语结合使用时,其作用更加突出。例如,对于这样一条长语句“把1003h地址处的字节加载到累加器”,我们可以使用如下简洁的句子替代:

    LOD   A,  [1003h]

    位于助记符右侧的A和[1003h]称为参数,它们是这个LOD指令的操作对象。参数由两部分构成,左边的操作数成为目标操作数(A代表累加器),右边的操作数成为源操作数。方括号“[]"表明要加载到累加器的不是1003h这个数值而是位于1003h存储地址的值。

    类似的,指令”把001Eh地址的字节加到累加器“,可以简写为: ADD A, [0010Eh]

    指令”把累加器中的内容保存到1003h地址“,可以简写为:STO [1003h], A

    指令”如果零标志位不是1,则跳转到0000h地址处,可以简写为:JNZ 0000h  (0000h地址就是跳转指令的操作数)

    用缩写很方便,因为在这种形式下指令以可读的方式顺序列出而不必画出存储器的空间分配情况。通过在一个十六进制地址后面加上一个冒号,可以表示某个指令保存在某个特定的地址空间。例如

    0000h:     LOD  A, [1005h]

    下列语句表示了数据在特定地址空间的存储情况。

    1000h:       00h, A7h

    1002h:       00h, 1Ch

    1004h:       00h, 00h

    等价于:

    1000h:       00h, A7h, 00h, 1Ch, 00h, 00h

    因此上面讨论的乘法程序可以用如下一系列语句来表示:

    0000h:     LOD  A, [1005h]

          ADD  A,  [1001h]

          STO  [1005h],  A

          LOD  A,  [1004h]

          ADC  A,  [1000h]

          STO  [1004h],  A

          LOD  A,  [1003h]

          ADD  A,  [001Eh]

          STO  [1003h],  A

          JNC  0000h

    001Eh:    HLT

    1000h:    00h,A7h

    1002h:    00h,1Ch

    1004h:    00h,00h

    空格空行仅仅为了人们更方便阅读程序。

    在编码的时候最好不要使用实际的数字地址,因为它们是可变的。用标号(label)来指代存储器的地址空间是个较好的办法。或是类似单词的字符串。上面的代码可以改写为:

    BEGIN:      LOD  A,  [RESULT  +  1]

            ADD  A, [NUM1  +  1]    :低字节相加

            STO  [RESULT+1],  A

            

            LOD  A,  [RESULT]

            ADC  A,  [NUM1]      :高字节相加

            STO  [RESULT],  A

            LOD  A,  [NUM2 + 1]

            ADD  A, [NEG1]        :第二个数减1

            STO  [NUM2+1],  A

            JNZ  BEGIN

    NEG1:    HLT

    NUM1:    00h, A7h

    NUM2:    00h,1Ch

    RESULT:      00h,00h

    +1代表了第二个字节, 冒号意思为注释

    这里给出的是一种计算机程序设计语言,称为汇编语言。它是全数字的机器语言和指令的文字描述的一种结合体。同时它用标号表示存储地址。每一条汇编语句都对应着机器语言中的某些特定字节。

  • 相关阅读:
    树状数组基本操作
    P1802 5倍经验日 题解
    函数学习总结
    康托展开
    中国电信CDMA无线上网卡短信收发
    报错:无法获取实体类XXX对应的表名!
    javajvisualvm远程监控云服务器上的Tomcat8.5
    服务器防火墙开放端口
    报错:列"AGENT_ENTITY_NAME"的值太大(实际值: 60,最大值50) oracle VARCHAR2和NVARCHAR2的区别
    linux中查看端口是否被占用lsof i:port
  • 原文地址:https://www.cnblogs.com/Garin/p/6871568.html
Copyright © 2011-2022 走看看