从半导体开始讲起。
半导体
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
半导体,就是导电性能介于导体与绝缘体之间的材料,比如二极管。
电流可以从正极流向负极,但反过来则不行。所以,可以它可以防止电流逆流。
当负极12V,正极0V,二极管断开;
当负极0V,正极12V,二极管导通。
正极的电流源源不断的流向负极,导最终达到正负极都是12V,稳定后,可将其视为导线。
有了半导体这个性能,我们可以尝试做一个【与门】。
即AB同时输入1,输出端Y才是1;AB有一个为0,输出端Y则为0。
按照此逻辑,我们同样可以设计出 【或门】【非门】【异或门】【或非门】【异或非门】![fb00f7257e462be217276b8c429af7fa.png](https://i-blog.csdnimg.cn/blog_migrate/54b18ee1a8b66ad11dd6010358be8840.jpeg)
有了这些基础的门电路,我们就可以做基础逻辑了。从最基础的加法器开始说。
加法器
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
S表示计算的结果,C表示是否要进位。
1 + 1 = 10,那么S就是0,C就是1。
由逻辑状态表可写出逻辑式:
(a)逻辑图
(b)逻辑符号
到这里,我们就能计算“1+1”了,有成就感吗?那如果1+2,咋办呢?用上面的加法器好像搞不定了。也就是要解决:第二位需要处理第一位有可能进位的问题。所以,要设计一个“全加器”。相对应地,上面的加法器称为“半加器”。上面的图使用太复杂,简化一下:
(a)逻辑图
(b)逻辑符号
实现减法
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
在4位数中,一共可以表达16个数,需要引入‘补数'的概念,例如3的补数是13,4的补数是12,5的补数是11,当你计算7减去4的时候,可以变成7加上3的补数,即7+13。
可是7+13是20,但是7-3等于4啊?
20已经超出4位能表达的16个数了,已经溢出,所以20还得减去16,就是4了。用二进制算一下: 7-3 = 0111 - 0011 = 0111 + 1101(二进制13) = 10100 10101已经溢出了,去掉最高位是0100,就是十进制4了。 其实,补数的概念源于钟表。 比如,现在是7点,我想让它回到4点。有两种办法, 一种方法是让时针后退3格,另外一种方法是让时针前进9格,前进到12点的时候,其实就相当于溢出了,需要舍弃。 等等,这不就是我们的求模(MOD)运算吗?钟表是12进制的,那么向后退3格:7 - 3 = 4
向前进9格 :(7 + 9) mod 12 = 4
向前进21格:(7+9+12) mod 12 = 4
向前进33格:(7+9+12+12) mod 12 = 4
.....
但是,我们怎么得到所谓的补数呢?从3怎么得到13呢? 其实,这些问题前辈们都已经考虑过,并有非常简单的操作实现了,即代码世界里的“补码”的概念。7-3可以换算成7+13了,如果是3-7呢?
负数的引入,使得系统变得更复杂了。首先我们得用一个标志位来表示正数还是负数。
![79683f6e40f3bb76aeb641cac6ce106e.png](https://i-blog.csdnimg.cn/blog_migrate/77279204e3a9b0bf407355832afe871c.png)
最高位的0表示正数,1表示负数,真正有效的数字只剩下3位了,正数的范围是从1到7,负数的范围从-1到-7,不过这里出现了两个零!一个正0,一个负0,这不妥吧。
我们又想到了前面提到的“补码”。
例如8-3相当于8+(-3)的补码,那我们完全可以把表格中的负数用“补码”表示,然后把那个负0 特别当做-8来处理:![ebe13443023a7295907a0b5b9a0891ed.png](https://i-blog.csdnimg.cn/blog_migrate/22d5b854b46578b7563e2ca585882a7e.png)
我们来算一下7-4。7是0111,-4是1100。注意我们把符号位也算进去了,两者相加:
![c9f834a566afad9351dd5a40418e971e.png](https://i-blog.csdnimg.cn/blog_migrate/3326a1a025d6375b5803038c13edcc03.png)
再我试试4-7,4是0100,-7是1001,两者相加:
所以,补码让二进制的表示更加易于理解。
加法,减法都有了,那乘法和除法呢?
乘法和除法
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
比如
5=101(2)
10=1010(2)
我们只要把输入往左移动一位,再在最低位上补个零就是乘2。所以在C代码的逻辑中,乘法除了用*乘外,还可以用左移一位实现。当然,*乘最终也是通过移位实现。看完简单的乘2了,那乘3呢?很简单,先移一位(乘2)再加一。乘5呢?先左移两位(乘4)再加一。是不是相比于加减而言,乘除操作更加易于理解。
四则运算
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
有了前面的基础,我们就可以计算基础的四则运算了吧。
试试看。比如计算A+B*2,用前面的基础来计算,不难吧。
那如果,我再计算(A+B)*2呢?也不难,把前面加法器模块和位移模块的接线调整一下,就能实现。
没错啊,编程的过程就是把线拔过来,插过去啊。
这肯定不行吧。需要引入两个有用的模块。
两个模块
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
触发器的作用是存储1个bit数据。
比如RS型的FF,R是Reset,输入1则清零。S是Set,输入1则保存1。RS都输入0的时候,会一直输出刚才保存的内容。
用FF来保存计算的中间数据(也可以是中间状态),1 bit肯定是不够的。但是通过并联后,用4个或8个来保存4位或者8位数据。这种保存数据的东西,我们称之为寄存器(Register)。
选择器:MUX
有了这选择器模块以后,我们就可以给加法器和移位模块设计一个激活针脚。
这个激活针脚输入1则激活这个模块,输入0则不激活。如此,就可以控制数据是走加法器模块还是移位模块了。
指令和数据
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
其实,数据与寄存器相加,结果保存到寄存器,这一条执行起来,也至少需要三步,读取指令,执行指令,写寄存器。
经典的RISC设计则是分5步:读取指令(IF),解码指令(ID),执行指令(EX),内存操作(MEM),写寄存器(WB)。
下面我们用前面定义的3个指令测试下吧。以(1+4)*2+3为例。
0100 0001 ;寄存器存入1
0001 0100 ;寄存器的数字加4
0010 0000 ;乘2
0001 0011 ;再加三
把程序整理一下:
01000001000101000010000000010011
顺便一提,其实,用程序控制CPU是个很高级的想法,很早的计算机(器)CPU都是单独设计的。
1969年,有一家日本公司BUSICOM,它想搞个程控的计算器,而负责设计CPU的美国公司也觉得每次都重新设计CPU是个很low的事。于是,双方一拍即合,于1970年推出一种划时代的产品:世界上第一款微处理器4004。
这个架构改变了世界,那家负责设计CPU的美国公司也一步一步成为了业界巨头。它就是Intel,可别说你没听说过。
回到我们的程序,一个简单的运算,如果按照上面的程序执行,是不是效率有点低?确实。小朋友估计掰着手指头都已经计算出来了,这里还没有结果呢。
后来,计算机行业的前辈们进行了优化。
把我们机器语言写成的程序
0100 0001 ;寄存器存入1
0001 0100 ;寄存器的数字加4
0010 0000 ;乘2
0001 0011 ;再加三
改成如下:
MOV 1 ;寄存器存入1
ADD 4 ;寄存器的数字加4
SHL 0 ;乘2(介于我们设计的乘法器暂时只能乘2,这个0是占位的)
ADD 3 ;再加三
熟悉吗?没错,这就是汇编语言。汇编语言和机器语言相互紧靠的交互层。
我们写的汇编可以完美的改写成机器语言,直接指挥CPU,进行底层开发。同时,我们也可以把内存中的数据dump出来,以汇编语言的形式展示出来,方便调试和debug。 汇编语言极大的增强了机器语言的可读性和开发效率,但对于人类来说也依然是太晦涩了,于是程序猿前辈们又发明了高级语言,以近似于人类的语法来表现数据结构和算法。 从高级编程语言到机器语言处理,需要编译器的介入,编译过程比较复杂,三言两语也难以说明,大家知道有这么回事儿就行了。如果想深入研究,可以整本《编译原理》看看。目前比较流行的高级语言有VB、Java、C、C++、Python、PHP等。
下面是2019年最新的世界编程语言排行榜:
写在后面
![08fe52ad15a471138ffa8cecc0ccf5d2.png](https://i-blog.csdnimg.cn/blog_migrate/e4a4b4fd9803d9634525ab6147a6dfc3.png)
作者简介:
Demu,传统汽车电控向智能驾驶转变的汽车人。从事发动机控制器系统工程师和软件工程师多年,有丰富的ECU系统和软件设计经验。欢迎大家一起留言交流,共同进步。