关于《编码》一书的总结【三】

15、字节与十六进制


  • 8位(bit)代表一个字节(byte)

  • 字节的一半叫做半字节(nibble或者nybble)

  • 十六进制

  • 相关进制转换不再赘述

  • 使用16进制, 一个16进制位表示4bit,4位16进制就能代表很多的可能性

16、储存器组织


制作一个8bit的RAM(Random Access Memory)

14章我们了解了D型电平触发器,我们对其进行稍加修改,将自动的时钟输入换为人为的写操作端

Q端作为输出端(这样和输入的数据保持一致),去除非Q端。

得到:

简化框图:

我们将写操作端链接在一起,可以构成多位锁存器

简略图示:

如果我们想要将8位数字每个都是单独储存该怎么办?,上面的电路想要只修改某一个的值,只能手动将其他七个的值进行复制进入数据段,使其保持一致否则一个不准就会数据改动。如果我们只有一个输出端口,又该怎样判别每个锁存器中的值呢?

我们先从输出端说起,要想实现自由对8个数据读出其中一个,我们需要一个数据选择器。

它可以根据S0、S1、S2的编码对D0~D7的数据选择一个进行输出

于是我们的输出端改造如下:

再说输入端,我们希望将一个数据,有目的性的进行对八个寄存器随机选择一个存储,此时我们需要一个译码器

3-8译码器的输出端口共有8个。在任何时刻,译码器只会有一个锁存器的输出为1,其余均为0。每一个输出端的结果都是由S0、S1、S2这三个信号的排列组合决定的。而数据的输出和输入一致。

最终我们完成了整个系统:

3-8译码器,是已有一个数据,根据编码从8个输出口中选择一个连通,8-1选择器,是已有1个输出口,根据编码从8个数据中选择一个输出。

分析:

地址端口为公共端口,选择后8-1选择器将选择的数据输出,3-8译码器选择相应输出口,输出写操作端的值到相应的某个寄存器写入端,根据其值判断是否进行写入操作,数据输入则为需要输入的数据的值

最终我们可以得到一个可以储存8bit的RAM(Random Access Memory):

RAM阵列的形成

我们通过地址共享的方式把两个8×1的RAM阵列链接起来:

得到一个8×2的RAM阵列:

分析:

通过地址可以同时选择两片RAM的某个寄存器,通过写操作端可以决定是否同时进行选中寄存器的写入操作,数据输入则可以分别输入,输入则同时输出两个寄存器相应的数据

我们吧8×1RAM阵列看做两个锁存器,使用一个2-1选择器和一个1-2译码器进行集成:

实际上选择端扮演了第4根地址线的角色,所以实际上它是16×1的RAM阵列:

分析:

选择端可以选择两片RAM的其中一片来进行输入和输出操作,可以看做四位地址码的最高位。我们假设0代表左面那片,1代表右面那片,选中后A0、A1、A2的地址代表了后三位地址码,所以我们可以对16个地址进行选择任意一个。假设我们的S、A0、A1、A2选择1101,则代表选择了右边那片的5号寄存器进行输出,再根据写操作端进行判断是否进行写操作,在根据数据输入的值,将其传输到该片寄存器上,当然先后顺序只是为了方便理解,实际操作可不是一样的.!!!!!!!!!!!!并且,此处运用的是电平触发,所以只要选中地址就会输出,在此时进行写操作,输出也会更改,之后我们使用边沿触发就不会有这样的问题了!!!!!!!!!!!!

同理通过不断的封装,增加选择器和译码器,我们依次可以得到16×1、32×1、64×1、128×1。。。七次后得到1024×1的RAM阵列,然后通过8块1024×1的地址线共享链接,我们有能进行宽位扩大,最终我们可以得到一个1024×8的RAM阵列

储存容量单位的进制关系

1B = 8bit

1KB = 1024B = 8192bit

1MB = 1024KB = 1048576B = 8388608bit

1GB = 1024MB = 1048576KB = 1073741824B = 8589934592bit

1TB = 1024GB = 1048576MB = 1073741824KB = 1099511627776B = 8796093022208bit

我们已经学会了如何构造任意大小的RAM阵列,假设我们构造了一个64K×8(65536字节)的储存器组织

为什么选择64K?因它的地址为16位,可以用两个字节表示地址范围转化为16进制就是0000h~FFFFh

管理RAM

此时零件太多难于管理,我们设计一个控制面板来加以管理。

初始状态下所有的开关均置为0。其中右下角有一个标识为控制端(takeover)的开关,这个开关的作用是确定由控制面板还是由外部所连接的其他电路来控制存储器。如果其他电路连接到与控制面板相连的存储器,这时控制端置0(如图所示),此时存储器由其他电路系统接管,控制面板上的其他开关将不起任何作用;当控制端置1时,控制面板重新获得对存储器的控制能力。

这种功能可以用一些2-1选择器来实现。仔细数一下会发现,我们需要25个2一1选择一一其中包括16个地址输入端、8个数据输入端,以及1个写操作端。电路如下图当控制端开关断开时,RAM阵列的地址端、数据输入和写操作端的数据全部来源于外部信号,也就是在2-1选择器的左上角的输入信号;当控制端开关闭合,RAM阵列的地址端、数据输入端和写操作端的数据来源于控制面板开关发出的信号。但最终RAM阵
列的输出信号都会传输到8个灯泡上或其他可能的地方。

64k×8的AM阵列和控制面板这一组合的确很实用它可以帮助我们存储63536个8位数据并且读取其中的任意一个。与此同时。我们也给其他部件提供了接入系统的机会一一需要接入系统的通常是一些电路部件一一一这些部件可以轻易地读取并利用存储中存放的数据,还可以把数据写入存储器。

值得注意的是但是该RAM都是逻辑门、继电器组成的,一旦断点,全部复原,数据全部丢失,所以RAM又被称为易失性存储器

17、自动操作


本章所有触发器均为边沿触发

14章加法器的缺点

回忆我们第十四章制造的一个加法器:

缺点:

1.无法显示大于255的数

2.假如要把1佣个二进制数加起来,你必须端坐于加法器前,并且耐心地输入所有的数并累加起来。但是当你终于完成时,却发现其中有两个数输错了,而你只能重复一一遍所有的工作。

利用RAM方便储存和修改数据、以及实现自动输入

很显然,RAM阵列的输出信号可以替代加法器的开关。用一个16位的计数器(比如我们在14章构造的那种)就可以控制RAM阵列的地址信号。在这个电路中,阵列的数据输入信号和写操作端信号可以省去。修改后的电路结构如下图所示。

分析:清零开关用来将计数器和锁存器清零,计数器清零使得地址从0000h开始选择,锁存器清零使得加和初始化0.当我们断开清零开关,计数器开始从0000h依次往后输出,用来选择RAM的地址,RAM的输入进入到加法器A,加法器求和将结果输出,同时保存到B端用来下一次加和

缺点:1.一旦开始就无法停止,哪怕将0000h-FFFh数全部加和完毕,下一次回滚到0000h又会继续加和在原来的数据上。2.只能做加法运算,而且只能做255以内的加法运算,不仅是数据不超过255,和也不能超过255

#### 使用RAM进行结果保存,能够对之前的结果进行修改和取用。

上述缺点必然会被解决,但是现在我们要考虑一个新的问题:如果我不是想求100个数的和而是想求100数中每两个一组50对数的和呢?

我们想到可以使用RAM阵列储存结果,用控制面板进行查看。这意味着我们可以用RAM来代替灯泡。

实现电路图如下:

现在假设我们要进行先对三个数求和,再对两个数求和最后再对三个数求和,则其RAM中的数据应该为:

改进加法器、使其功能不再单一

原加法器只能进行单一的吧RAM地址中的内容加到累加器中但是有时我亻门需要把存储器中的某个值直接加载到累加器,或者把累加器中的值直接保存到存储器。做完了这些工作算得上万事俱备只欠东风了,我们还希望自动加法器能方便地停下来,以便于查看RAM阵列中存放的值。现在我们需要四样工作:加载、相加、保存、终止。

改进:

1.我们用一些数字代码来标识加法器要做的工作

2.也许存放这些代码的最简单的方法(但肯定不是代价最小的)是把它们存放在一个独立的RAM阵列中。这个RAM应该和第一个RAM同时被访问。但是这个RAM中存放的是不需要求和的数,而是一些数字代码,用来标记自动加法器对第一个RAM中指定地址要做的一种操作。这两个RAM可以分别被标记为“数据”(第一个RAM阵列)和“代码”(第二个RAM阵列)。

特点:“数据”可以通过加法器自动求和写入而“代码”只能通过控制面板写入

下面我们指定这样一种方案:

比较一下该RAM阵列与存放累加数据的RAM阵列中的内容,你会发现,代码RAM
阵列中存放的每一一个代码都对应着数据RAM中要被加载或者加到累加器中的数,或者对
应需要存回到数据RAM中的某个数。以这种方式使用的数字代码常常被称为指令码
或操作码(operationcodej0ode)。它们指示电路要执行的某种操作。

改进后的电路:

分析:十六位计数器给两片RAM同时提供地址选择信号,根据操作码判断操作,Store指令可以通过W端控制数据写入,Load指令通过2-1数据选择器实现一个数直接加载到锁存器和加法器的B端,这样下一次加和会和Load的数相加而不用受之前数据的影响。Add操作就是将数正常送入加法器Halt操作可以通过停止计数实现。图中缺少的控制这些组件的信号统称为控制信号。

#### 增加减法(Subtract)运算

分析:当操作码为21h的时候除了RAM阵列传入加法器的数据取反和进位输入置1外其他和Add操作相同。当然这里假定结果不会是负数的情况下,因为根据第十三章的分析在此情况下减法=减数求补---->+1----->+被减数

#### 计算大于255的数

回想我们的的问题,还有如何计算大于255的数字。解决方法就是讲数字拆分为高位字节和低位字节分别进行求和,最后再合并即可。

通常左边称为高字节,右边称为低字节

例如:76ABh+232Ch

分别计算ABh+2Ch=D7h76h+23h=99h合并后结果为99D7h

储存器中:

我们需要考虑进位的情况,这种情况下我们需要怎样改进呢?

我们仅需要保存低位运算的进位输出并且将其送入到高位计算的进位输入中去。

最终我们决定使用一个1位寄存器来保存改数据,用一个进位加法(Add With Carry)来控制使用该寄存器。

分析:常规8位加法的时候,使用Add指令,进位输入为0,进位输出保存到进位锁存器,但是不会被用到。16位加法的时候,使用Add指令对两个低字节运算,进位输入为0,进位输出保存到进位锁存器,高位字节运算使用Add With Carry 指令,进位锁存器的值用于该计算中,是1则加入进位,是0则无进位。 值得一提的是,因为只有当前一次的加法或者进位加法操作使加法产生进位输出的时候,Add With Carry指令才会使加法器进位输入置1,所以只要是多字节数加法,不管实际是否需要,都应该使用Add With Carry指令

这样通过改进,我们可以进行16、24、32、40甚至更多位数的加法运算。例如一个32位数,我们需要一个Add指令和三个Add With Carry指令即可

16位的减法运算

如果要进行16位数的减法运算,则还需要一个新的指令,称为“借位减法”(SubtractandBorrow)。通常,subtract指令需要将减数取反并且把加法器的进位输入置1。进位输出通常不是1,因此应该被忽略。但对16位数进行减法运算时,进位输出应该保存在进位锁存器中。在进行第二步的高字节减法运算时,锁存器保存的结果应该作为加法器的进位输入。

原文是进位输出通常不是1,但是根据我在第十三章的分析我感觉怎么通常都是1呢。而且1作为高位计算的进位输入正好为减法提供+1计算。还有就是不理解,若是当16位数据相减结果不是负数,低位相减却产生了负数怎么办?。

现在我们有了七个操作码:

为了可以使用之前结果的进一步改进

现在电路有着缺点:

1.我们需要手动使用控制面板根据指令顺序存储相应数据到相应位置,为保存结果留出位置等

2.如果几个数相加后减去一个数,得到了结果,但是想使用这几个数字的和重新再去减去一个数,却做不到直接使用该和了,因为几个数相加后的和没有保存下来,在减去一个数后已经改变了。我们必须重新安排数据和指令重新计算。产生此问题的原因在于代码存储器和数据存储器是同步的、顺序的,代码存储器每一条指令对应数据存储器相同地址的存储单元。

改进:

每一个操作码在存储器中占1个字节。现在除了HaIt操作码外,我希望每一个指令在存储器中仅占椐3个字节的空间,其中第一个字节为代码本身,另外的两个字节用来存放1个16位存储器单元地址。对于Load指令来说,后两个字节保存的地址用来指明数据RAM阵列的一个存储单元,该单元存放的是需要被加载到累加器中的字节。对于Add,Subtract,Add With Carry,subtractwithBorrow指令来说,该地址指明的存储单元所保存的是要从累加器中加上或减去的字节。对于Store指令来说,该地址指明的是累加器中的内容将要保存到的存储单元地址。

改进后的求和我们就不用太过考虑指令和数据的放置必须同步了,只要地址和操作准确,甚至我们可以在RAM中分散操作

这样我们通过0000h到0012h地址的操作可以把4000h到4004h地址的高位低位数据进行加和操作,不必和之前两片RAM中操作和数据摆放必须相应了

改进后的电路:

前一个锁存器存放操作码后两个储存器构成地址码

其操作可能会更加复杂,因为操作码最终是通过控制信号控制各个部件来完成各种操作的,我们不在深究如何达到目的,接下来我们需要再次进行改进。

再次改进后:

分析:当我们可以分散操作的时候,我们发现两块RAM不再是必要的,我们在一块RAM中保存操作码和地址码,确保其对应数据和操作准确的情况下一块RAM也可以支持我们的操作。这样的话我们根据地址直接从数据RAM选择数据需要更换为从原RAM中选择数据,我们需要一块2-1选择器,来区分计数器和锁存器的地址码到底执行哪一个。 这样开始运作时计数器选择地址,RAM输出改地址的数据,若为指令,通过该指令指定的一些列控制信号,输入接下来的地址到锁存器,锁存器通过控制信号通过2-1选择器给RAM选择地址的数据,最终输出到数据端口在根据一系列控制信号进行数据操作。完成后计数器继续工作选择下一个指令。

跳转指令

在上面的电路中我们还是有一些缺点:

地址空间很容易被覆盖。假设我们0000h到0010h储存了操作码和其对应数据的地址码,在0013h到0020h储存了数据,接下来我们突然想再添加指令,我们只能从0021h之后添加操作码和地址码,不然会损毁数据,可我们想使得操作从计数器开始到结束一路执行下去的话,这样显然不行的,因为执行完0010h之后很可能在顺序寻址执行中0011h到0020h的数据就可能会被当做指令,而不是0021h之后我们期望的下一个操作。这下就乱了套了。所以我们必须进行改进。

于是我们引入了新指令:Jump指令,从一个指定地址开始继续寻址。这样我们就可以从0010h指令后跳转到0021h进行寻址,来进行下一个操作

实现:

通过构成16位计数器的D边沿触发器的预置和清零功能来实现:

Pre和Clr端一般都为0.但是Pre端置1则Q=1 clr端置1则Q=0

改进后:

该电路实现了,复位信号不收置位影响。置位时Q端将和A端信号相同,接下来我们将16位计数器中每一个都进行这样的改造,然后将A端依次链接到RAM阵列的锁存地址就能实现跳转。

如图:

现在我们需要做的就是在需要的时候打开置位开关就可以实现跳转

条件跳转

怎样让自动加法器进行两个8位数的乘法运算?

例如a×b我们可以理解为连续进行将a连续+b次。而我们又不想用那么多空间去储存一个相同的操作和相应地址。这时候我们想到了跳转指令。但是我们要知道跳转需要在b次后结束。我们就需要对跳转设置一个条件。

于是我们设置了新指令:条件跳转

关键就在于我们如何实现判断条件

首先我们为8位加法器增加一个一位锁存器,其功能为当加法器全部为0时锁存器显示1否则为0:

这样我们新增四条操作码于是我们的操作码成了下表:

例如非零转移指令,只有在锁存器的输出为0时才会跳转到指定位置,即当加、减、进位加法、借位减法、的结果非零时就会跳转

例:实现A7h×1Ch

  1. 首先乘数被乘数和结果保存的的数据位置:

  2. 之后将被乘数(00A7h)进行一次加法运算

    3.然后使用条件跳转指令重复剩余的次数

分析:这个方法很巧妙,我们将001Ch这个乘数,用来限制00A7h的加和次数完成乘法运算。先将其存入累加器,之后和我们001Eh中的停止操作数相加,巧的是我们的停止操作数设置为FFh,当其不作为操作数而是作为数据和乘数相加时等效为001Ch减1,即FFh+1Ch=1Ch-01h,然后通过零锁存器判断这个过程的结果,不为0则跳转回0000h进行新一轮被乘数加和,直到乘数不断减1,到了最后一次运算结果为0时,零锁存器为1,不再跳转,16位计数器置数段不再工作,之后正常顺序计数,那么接下来执行的是001Eh的指令,停止操作。至于如何实现,很简单,在常规跳转指令的控制信号之上再加上一个控制信号:例如当执行Jump If Not Zero时,那么只有当零锁存器标志位0时,16位计数器的置数功能才会被触发

其他运算

首先我们要知道:

能否控制重复操作或者循环(looping)是计算机(computer)和计算器(calculator)的区别。

**我们现在利用循环,实现了乘法操作,相似的我们也可以实现除法操作,而且这不仅局限于8位,我们可以进行更高位的运算。当我们能够成功掌握加减乘除,那么开平方根,取对数,三角函数运算也完全可以胜任了**

最后的本章杂谈

  • 一台数字计算机主要由4部分构成:处理器(processor)、存储器(memory),至少
    一个输入(input)设备和一个输出(output)设备。我们装配的计算机中,存储器是64
    的RAM阵列输入和输出设备分别是RAM阵列控制面板上的开关和灯泡

  • 我们装配的其他部件都归于处理器

  • 处理器也被称作中央处理单元(central processing unit)或者CPU更通咨的说法是将其称作计算机的大脑,但本文将避免使用这样的词,因为我们所设计的处理器称不上是大脑(今天,微处理器这个词使用
    得非常普遍,它是一种非常小的处理器,可以通过本书第18章讲到的技术来构造它。但本章中通过继电器构造的机器无论如何也栋不上“微小”的)

  • 我们的处理器为8位处理器,累加器宽度为8位,而且大部分数据通路都是8位,唯一的16位数据通路是RAM阵列的地址通路。

  • 处理器包括若干组件,累加器就是其中一个。在我们的设计中,8位反相器和8位加法器一起构成了算术逻辑单元(arithmetic and logic unit),我们的ALU只能进行部分基本的算术运算,而更高级的计算机ALU还可以进行逻辑运算。如与(AND)、或(OR),异或(XOR)等等。

  • 16位的计数器被称作程序计数器(PC,Program Counter)

  • 能够被处理器相应的操作码(比如Load、Store的代码10h和11h)称做机器码或机器语言

  • 我们为指令配置一些助记符:

    在结合一些短语,我们就可以清晰的在纸上列出一个想要实现过程:

    再添加一些格式和规范,写一些必要的注释:

    这样的语言我们成为汇编语言

  • 我们可以在纸上使用汇编语言,快速整洁的写出想要执行的过程,同样理解汇编语言的人也可以看懂,

    当我们反复检查无误后,手工编译,减每一条汇编语言转换成与之对应的机器语言(仍在纸上完成),最后通过开关将这些机器码输入到RAM阵列中去并运行该程序

    • 汇编语言能够减少代码的错误率,同时易于理解。
-----------------------本文结束 感谢阅读-----------------------
坚持原创技术分享,您的支持将鼓励我继续创作!恰饭^.^~