自制了一台计算机,可编程哦!
The following article is from 涛歌依旧 Author 点击关注👉👉
出品 | 涛歌依旧(ID:ai_taogeyijiu_2021)
已获得原公众号的授权转载
一. 自制计算机的外形
在很长一段时间内,总是搞不清楚一些基本的概念和原理:比如什么是硬件? 硬件内部是怎样运行的?什么是软件?软件在哪里?软件究竟是怎样在硬件上跑起来的?硬件究竟是怎样执行软件的?
冯诺依曼结构 | 自制计算机 |
CPU运算器 | U3加法器 |
CPU控制器 | U1计数器 U4触发器 U5非门 时钟信号 |
存储器 | U2存储器 |
输入设备 | 不涉及 (因为输入内容已提前存放于U2中) |
输出设备 | 数码管显示器 |
二. 自制计算机的构成
很容易分析出逻辑关系:
S | R | Q | Q’ |
1 | 0 | 1 | 0 |
0 | 1 | 0 | 1 |
0 | 0 | 保持 | 保持 |
1 | 1 | 0 | 0 |
当S=0,R=0时,对或门没有任何贡献,所以不会对电路产生影响。此时,Q和Q'是相反的,必有一个为0,另一个为1.
当S=1,R=1时(这种输入并没有错),Q和Q'都为0,我们对这种情况并不感兴趣,所以可以忽略,故约定S和R不同时为1.
不考虑S=1,R=1这种输入组合时,Q和Q'总是相反的。
2. D锁存器
来看D锁存器电路。可用多个器件来确保RS锁存器两个输入端不能都为1,下图R和S输出端永远不可能同时为1:
当E=0时,无论D怎么变, 经历R和S两个与门后,R和S的输出端总是0,所以Q和Q'总是保持原状。 当E=1时,经历R和S两个与门时,R和S输出端的值完全取决于D, 显然有Q=D.
上下两个D锁存器的使能端是相反的,所以,无论控制开关E是0还是1,上下两个D锁存器必然有一个无效,导致不能把D数据保存到Q端。
当E从1变为0时,下面的D锁存器无效,因此也不能把D数据锁存到Q端。
当E从0变为1时,下面的D锁存器生效,上面的D锁存器无效,但U4的输出端之前已经获取了D端的数据,所以趁着下面的D锁存器的生效,把U4输出端的数据保存到Q端。因此,整体看来,当E从0变化到1时,能把D数据锁存到Q端。这种锁存,只在信号发生0到1的跳变瞬间,所以叫上升沿触发锁存。
可是,这个D触发器有点复杂,我们来抽象一下,直接使用封装好的D触发器,如下图,可以看到:当且仅当E从0变到1时,才把D保存到Q.
用开关提供上升沿,需要手动掰弄开关,非常麻烦。能不能搞一个自动上升沿呢?当然可以。
我们引入时钟信号,它不停地在0和1之间做变换,从而产生上升沿。时钟信号很简单,看动图:
想一下,为什么CPU需要时钟才能工作?这个问题很有趣,也很好回答,可以从两个方面来看:
计算机的理论模型是图灵机,而图灵机是有限状态机,需要有时钟信号驱动状态变化。 计算机中的数据需要按先后步骤进行保存和更新,以上述触发器为例,需要有上升沿信号来触发,所以需要时钟信号。
第n秒 | U1 | U2 | U3 | U4 | U4U3U2U1 |
0 | 0 | 0 | 0 | 0 | 0 |
1 | 1 | 0 | 0 | 0 | 1 |
2 | 0 | 1 | 0 | 0 | 2 |
3 | 1 | 1 | 0 | 0 | 3 |
4 | 0 | 0 | 1 | 0 | 4 |
5 | 1 | 0 | 1 | 0 | 5 |
6 | 0 | 1 | 1 | 0 | 6 |
7 | 1 | 1 | 1 | 0 | 7 |
8 | 0 | 0 | 0 | 1 | 8 |
9 | 1 | 0 | 0 | 1 | 9 |
10 | 0 | 1 | 0 | 1 | A |
11 | 1 | 1 | 0 | 1 | B |
12 | 0 | 0 | 1 | 1 | C |
13 | 1 | 0 | 1 | 1 | D |
14 | 0 | 1 | 1 | 1 | E |
15 | 1 | 1 | 1 | 1 | F |
000000000000000100000010000000110000010000000101
000000000000000100000010000000110000010000000101
这个程序不直观,为便于阅读,我们来分割一下:
00000000
00000001
00000010
00000011
00000100
00000101
U2输入端 A3A2A1A0 | U2输出端 D3D2D1D0 其具体内容与我们写的程序有关 |
0000 | 0000 |
0001 | 0001 |
0010 | 0010 |
0011 | 0011 |
0100 | 0100 |
0101 | 0101 |
U2存储器左边的U1刚好是计数器,从0一直数到F,因此,当计数器数到3时,U2输入端刚好就是3,而U2此处存储的数据刚好是3.
我们对照上面的动图,整体理解一下计算机电路图的执行流程:
时钟信号 | U1输出 | U2输出 | U3输出 | U4输出
|
0 | 0 | 0 | 0 | 0 |
上升沿 | 1 | 1 | 1 | 0 |
下降沿 | 1 | 1 | 1 | 1 |
上升沿 | 2 | 2 | 3 | 1 |
下降沿 | 2 | 2 | 3 | 3 |
上升沿 | 3 | 3 | 6 | 3 |
下降沿 | 3 | 3 | 6 | 6 |
上升沿 | 4 | 4 | A | 6 |
下降沿 | 4 | 4 | A | A |
上升沿 | 5 | 5 | F | A |
下降沿 | 5 | 5 | F | F |
00000000000000100000010000001000
三. 自制计算机的编程
用0和1写出来的程序,叫机器语言程序,直接在硬件上运行,比起掰弄电路开关,已经有很大进步。但是,如果一直用0和1来写程序,还不能出现一点点差错,谁受得了呢?
一大串的0和1,太难懂了,它是机器世界的语言,而人又有人的语言,这两种语言是不相通的,因此,我们需要探索出更好的编写程序的方法,让人更轻松点。怎么办?
我们回头看1+2+3+4+5的机器语言:
000000000000000100000010000000110000010000000101
怎么降低人编写程序的难度呢?我们可以考虑这么写:
ORG 0000H
DB 00H
DB 01H
DB 02H
DB 03H
DB 04H
DB 05H
END
这种语言叫汇编语言,看起来舒服多了。现在的问题是,需要一个工具把汇编语言转化为机器语言,这个转换的工具就叫汇编器,如下图:
现在编程就简单多了:先用汇编语言写程序,然后用汇编器这个工具,把汇编语言程序转化为机器语言程序。
如果要计算2+4+8,我们再也不用与0和1这种折磨人的机器语言打交道了,直接用汇编语言写汇编程序,如下:
ORG 0000H
DB 00H
DB 02H
DB 04H
DB 08H
END
经汇编器工具转换后,上述的汇编语言程序变为了机器语言程序,而这正是硬件电路所需要的语言,具体如下:
00000000000000100000010000001000
然后,我们自制的计算机执行这段机器语言程序,得到的结果是14. 可是,用汇编语言还是憋屈,不好理解,不近人情。
于是,需要再次抽象,越抽象越接近事物的本质,为了降低编程难度,我们再次优化编程方法,采用高级语言,如下:
int a=1+2+3+4+5;
printf("%d", a);
然而,电路毕竟不认识高级语言,只认识高低电平,即1和0,所以,也需要工具来对高级语言进行转换,关系如下所示:
我们从如下表格中,可以看到机器语言、汇编语言以及高级语言的对比。显然,用高级语言编程更容易,更加直观:
目的 | 机器语言 | 汇编语言 | 高级语言 |
计算 1+2+3+4+5 | 000000000000000100000010000000110000010000000101 | ORG 0000H DB 00H DB 01H DB 02H DB 03H DB 04H DB 05H END | int a=1+2+3+4+5; printf("%d", a); |
计算 2+4+8 | 00000000000000100000010000001000 | ORG 0000H DB 00H DB 02H DB 04H DB 08H END | int a=2+4+8; printf("%d", a); |
上述的汇编语言很简单,不涉及到指令,只涉及到数据存放的位置。有一些朋友可能觉得上述汇编语言的指令是伪指令,但没有关系,我们依然把它当汇编语言理解,并无副作用。而且经汇编器转换后,生成的机器代码,确实可以在我们制作的计算机上运行。 上述的高级语言,经通用的编译器和汇编器转换得到的机器语言程序,没法在我们自制的计算机上运行,必须经过特定的编译器才可以,我们无需对这种特殊的编译器做进一步了解,毕竟编译器只是个转换工具。
好的,到此为止,本文也将告一段落。对于想搞懂计算机原理的朋友而言,如果能亲自动手完成本文的实验,那么肯定会加深对计算机原理的理解,加油!
程序员专属卫衣
推荐阅读:
每日打卡赢积分兑换书籍入口