本文来自微信公众号:低并发编程 (ID:dibingfa),作者:闪客
我和小宇早恋了,我们家住隔壁。
一、编码与电路 —— 信号的转换
晚上父母会把手机没收,但我们还想继续聊天,又不敢发出声音,于是我们想到了这个办法…
三、加法器 —— 信号的计算
十进制数可以转换成二进制数,而二进制数又可以对应到门电路的输入端与输出端。
于是我和小宇有了一个大胆的想法,能不能设计一个计算加法的电路呢?
我们首先从最简单的一位二进制数相加开始:
0+0=0;0+1=1;1+0=1;1+1=10
变成一张表格如下
加数 A |
加数 B |
加和输出 |
进位输出 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
0 |
1 |
即我们需要设计出一种电路,可以达到表中的输入与输出效果。
经过不懈努力,终于发现这个电路可以由异或门和与门两个门电路组成。
这个装置实现了二进制的一位加法,但它并不完美,因为只考虑了这两个数的进位输出,但没有考虑上一位的进位,所以只能叫半加器。
如果将前一个进位考虑进来,只需再多一个半加器,并且拼接一个或门即可。
此时我们已经建立好了一个完美的一位加法器,并自豪地称之为全加器。
全加器做出来之后,无论多少位的加法器就都可以做出来了,只需将全加器逐个拼起来即可。我们尝试做一个八位加法器。
OK,大功告成,有了加法器,理论上就可以实现任何的数学运算了。
因为我们知道乘法可以转换成加法,除法可以转换成减法,而减法又可以转换成补码的加法。现在我们可以自豪地称这个部件为,算术逻辑单元 ALU。
四、时钟 —— 信号的震荡
我和小宇都非常高兴,终于用电路的方式实现了计算功能。
但慢慢的觉得没什么意思了,于是我们又突发奇想,设计了如下诡异的电路。
然后我们把多个锁存器组合起来,再加上一些 3-8 译码器,8-1 选择器等电路,就可以实现一个能保存 8 位二进制的存储器,并且可以随机地读写它,我们把它叫做 RAM,简称为内存。
这个组件通过再次组合,可以形成 N × M 的 RAM 阵列。比如我们可以表示一个 1024 * 8 的 RAM 阵列。
这表示存储容量为 1024 个单位,每个单位占 8 位。
为了更方便地表示,我们规定 1024 = 1K,8 位 = 1 字节(8 bit = 1 byte),那么我们就可以说,这个 RAM 的存储容量为 1K 个单位,每个单位占 1B。或者说,地址空间为 1K,存储容量是 1KB。
此时这个 RAM 模块已经近乎完美了,我们甚至可以单独对其进行使用,将数据存入某个地址,将某个地址中的数据读出。
怎么方便人操作呢?只需要将地址输入、数据输入、写操作端分别接入一个控制面板,由开关来控制这些信号的输入是 1 还是 0 即可,然后再将数据输出接入一些灯泡方便观察,这样一个单独的可以手动操作的存储装置,就搞定啦。(下图中有彩蛋~)
有了可读写的内存,我们就可以事先把几个数字存储内存中了,接下来,我们能否让算术逻辑单元 ALU 自动地读取这个数字,进行加法运算呢?
六、程序 —— 自动化
我们先引入一个新的组件,10 位计数器,这里的 Clk 就接入我们在第四部分讲的时钟信号,Clr 是清零端,具体效果下面动图一目了然。
计数器的输出就是 0,1,2,3,4,5,可以当作内存中的地址。
我们把这个计数器,以及上面讲的 ALU 与 RAM 全部连在一起,尝试实现一个可以累积求和的装置。
我们想计算的是 1+2+3+4+5+6+7, 这个自动化的计算器是这么运行的
1、用控制面板在 RAM 的地址 0~6 处存上 1~7 这几个数字的,在上一节已经实现了。
2、当计数器的值是 0 时,数据 1 被输出到加法器进行计算,此时加法器 A=1,B=0,计算结果为 1,但记住锁存器存储的是上一次的加法器输出 0,这次的计算结果要等下一次锁存器遇到上升沿信号。
3、当计数器的值是 1 时,数据 2 被输入到加法器,此时锁存器存储了上一次的计算结果 1,并将这个 1 输出给小灯泡,并同时回传到加法器的 B,所以此时加法器 A=2,B=1,计算结果为 3
4、当计数器的值是 3 时,以此类推,请看下图
我们将累加求和这个过程自动化了!之后如果想计算累加和,只需要用控制面板事先在内存里存好数据就可以了!是不是很方便?
七、程序指令
我们还想要更多的自动化!
现在这个装置,只能无脑地将 RAM 中的数据从头到尾一直累加下去,无法选择加哪个不加哪个,也无法选择什么时候停止。
比如我们 RAM 中的数据是这样的。
地址(16 进制) |
数据(10 进制) |
0x00 |
… |
0x01 |
10 |
0x02 |
… |
0x03 |
20 |
0x04 |
30 |
0x05 |
… |
… |
… |
我们只想让 RAM 蓝色地址处的数据进行累加,其他地方的数据忽略,并且到 RAM 0x05 处就停止,该怎么做呢?
我们可以再增加一个 RAM,这个 RAM 里存放的数据,表示 ” 指令 ” 的含义!
我们先发明三种指令。
add:把 RAM 这个位置处的值进行累加
nop:忽略此处的值(也就是什么都不做)
halt:停止(禁止计数器的值加一)
那么要想达到上述功能,相应的这个指令 RAM 中的数据应该是这样的。
注意:下面指令 RAM 的地址和上面数据 RAM 的地址之间有一一对应关系!
地址 (16 进制) |
指令 RAM 的值 |
指令含义 |
0x00 |
nop |
什么都不做 |
0x01 |
add |
累加 |
0x02 |
nop |
什么都不做 |
0x03 |
add |
累加 |
0x04 |
add |
累加 |
0x05 |
halt |
停止 |
… |
… |
… |
我们需要引入一个控制单元,放在如下位置。
遇到 nop 指令 0x00),那输出就将锁存器的 W 位禁止,不允许锁存器写操作,这样累加结果就不会录入。
再比如遇到输入为 halt 指令 0x05),就将计数器的 EN 位禁止,不允许计数器 +1,这样就达到了停止的效果。
此时再让时钟信号震荡起来,就可以达到有选择地求和过程,并且在指定位置悬停。那现在我们就让时钟信号震动起来,看看这个过程吧。(此处只留关键组件)
这个控制单元该怎么实现呢?我们知道,只要给出输入,给出输出,任何组件都可以造出来。本文就不再展开了。
有了三个指令,我们知道了通过指令这种方式,配合各种复杂的控制器,即可实现将所有操作统统自动化。
接下来我们需要做的,就是设计控制器,以及约定好一大堆指令,使得通过这一大堆指令的排列组合,可以实现任何自动化的计算操作。
我们将设计好的一大堆指令
称作指令集
我们将指令排列组合后可以实现的功能
称作程序
我们将指令的排列组合这个过程
称作编程
我们将排列组合这些指令的人
称作程序员
而我们将承载这一切的装置,叫做什么呢?
没错,这个破玩意,就是
计
算
机
本文来自微信公众号:低并发编程 (ID:dibingfa),作者:闪客