写点什么

重学计算机组成原理 (4)- 还记得纸带编程吗?

作者:JavaEdge
  • 2021 年 12 月 15 日
  • 本文字数:2811 字

    阅读完需:约 9 分钟

重学计算机组成原理(4)-还记得纸带编程吗?

以前写程序用“打孔卡(Punched Card),没法像今天,掏出键盘就能打字,而是要先在脑海/纸写出程序,然后在纸带/卡片上打洞。这样,要写的程序、要处理的数据,就变成一条条纸带或者一张张卡片,之后再交给当时的计算机去处理。

  • 上世纪 60 年代晚期或 70 年代初期,Arnold Reinold 拍摄的 FORTRAN 计算程序的穿孔卡照片



  • 人们在特定的位置上打洞或者不打洞,来代表“0”或者“1”。

计算机或说 CPU 本身,并没有能力理解这些高级语言,即使在 2019 年的今天,我们使用的现代个人计算机,仍然只能处理所谓的“机器码”,也就是一连串的“0”和“1”这样的数字。

那高级语言的程序,最终是怎么变成一串串“0”和“1”的?这一串串“0”和“1”又是怎么在 CPU 中处理的?

1 软硬件接口中,CPU 做了啥?

CPU(Central Processing Unit,中央处理器)就是计算机的大脑

  • 硬件的角度一个超大规模集成电路,通过电路实现了加法、乘法乃至各种各样的处理逻辑。

  • 软件工程师的角度一个执行各种计算机指令(Instruction Code)的逻辑机器这里的计算机指令,就好比一门 CPU 能够听得懂的语言,即机器语言(Machine Language)

不同的 CPU 能够听懂的语言不太一样 Intel 的 CPU、ARM 的 CPU 两者能听懂的语言就不一样,这样两种 CPU 各自支持的语言,就是两组不同的计算机指令集(Instruction Set)。“Set”就是数学上的集合,代表不同的单词、语法。如果在自己电脑上写一个程序,然后把这个程序复制一下,装到自己的手机上,肯定是没办法正常运行的,因为这两者语言不通。而一台电脑上的程序,简单复制一下到另外一台电脑上,通常就能正常运行,因为这两台 CPU 有着相同的指令集,它们语言相通。

存储程序型计算机(Stored-program Computer)计算机程序,不可能只有一条指令,而是成千上万条指令组成但 CPU 不能一直放着所有指令,所以程序平时是存储在存储器这种程序指令存储在存储器里面的计算机,我们就叫作

Plugboard Computer 在没有现代计算机之前,有着聪明才智的工程师们,早就发明了一种叫 Plugboard Computer 的计算设备在一个布满了各种插口和插座的板子上,工程师们用不同的电线来连接不同的插口和插座,从而来完成各种计算任务

  • IBM 的 Plugboard



2 编译=>汇编 代码=>机器码

代码是怎么变成一条条计算机指令,最后被 CPU 执行的?

  • test.c



  • 编译(Compile)成汇编代码


    要让这段程序在 Linux 跑起来,需要把整个程序翻译成汇编语言(ASM,Assembly Language)的程序

针对汇编代码,可以再用汇编器(Assembler)翻译成机器码(Machine Code)这些机器码由“0”和“1”组成的机器语言表示,这一条条机器码,就是一条条的计算机指令这样一串串的 16 进制数字,就是我们 CPU 能够真正认识的计算机指令。

在 Linux 上,可使用 gcc 和 objdump,把对应的汇编代码和机器码都打印出来。

左侧一堆数字,就是一条条机器码右边一系列的 push、mov、add、pop 等,这些就是对应的汇编代码

一行 C 语言代码,有时候只对应一条机器码和汇编代码,有时候则是对应两条机器码和汇编代码汇编代码和机器码之间是一一对应的。



实际在用 GCC(GUC 编译器套装,GUI Compiler Collectipon)编译器的时候,可直接把代码编译成机器码,为什么还需要汇编代码呢?那一串数字表示的机器码,摸不着头脑但即使你没有学过汇编代码,看的时候多少也能“猜”出一些这些代码的含义。

汇编代码就是“给程序员看的机器码”也正因为这样,机器码和汇编代码是一一对应的很容易记住 add、mov 这些用英文表示的指令而 8b 45 f8 这样的指令,由于很难一下子看明白是在干什么,所以会非常难以记忆

从高级语言到汇编代码,再到机器码,就是一个日常开发程序,最终变成了 CPU 可以执行的计算机指令的过程。

3 解析指令和机器码

了解了这个过程,下面我们放大局部,来看看这一行行的汇编代码和机器指令,到底是什么意思。Intel CPU,有 2000 条左右的 CPU 指令,实在是太多了,没法一一讲解。不过一般来说,常见的指令可以分成五大类。

算术类指令

加减乘除,在 CPU 层面,都会变成一条条算术类指令

数据传输类指令

给变量赋值、在内存里读写数据,用的都是数据传输类指令。

逻辑类指令

逻辑上的与或非

条件分支类指令

日常的“if/else”

无条件跳转指令

写一些大一点的程序,我们常常需要写一些函数或者方法在调用函数的时候,其实就是发起了一个无条件跳转指令。


汇编器是怎么把对应的汇编代码,翻译成为机器码的。

不同 CPU 有不同指令集,对应不同的汇编语言和不同的机器码。为方便快速理解机器码的计算方式,选用最简单的 MIPS 指令集,看机器码是如何生成的。

MIPS 是一组由 MIPS 技术公司在 80 年代中期设计出来的 CPU 指令集。最近,MIPS 公司把整个指令集和芯片架构都完全开源了。想要深入研究 CPU 和指令集的同学,推荐一些资料

MIPS 的指令是一个 32 位的整数,高 6 位操作码(Opcode):代表这条指令具体是一条什么样的指令。剩下的 26 位有三种格式,分别是:R 指令一般做算术和逻辑操作,里面有读取和写入数据的寄存器的地址。如果是逻辑位移操作,后面还有位移操作的位移量。而最后的功能码,则是在前面的操作码不够的时候,扩展操作码表示对应的具体指令的。

I 指令通常是用在数据传输、条件分支,以及在运算的时候使用的并非变量还是常数的时候。这时,没有位移量和操作码,也没有第三个寄存器,而是把这三部分直接合并成了一个地址值或者一个常数。

J 指令一个跳转指令,高 6 位之外的 26 位都是一个跳转后的地址

add $t0,$s2,$s1
复制代码

下面都用十进制来表示对应的代码,对应的 MIPS 指令的:

  • opcode 是 0

  • rs 代表第一个寄存器 s1 的地址是 17

  • rt 代表第二个寄存器 s2 的地址是 18

  • rd 代表目标的临时寄存器 t0 的地址是 8

  • 因为不是位移操作,所以位移量是 0

把这些数字拼在一起,就变成了一个 MIPS 的加法指令。一般把对应的二进制数,用 16 进制表示。这里 0X02324020。这个数字也就是这条指令对应的机器码。

回到开头我们说的打孔带

  • 打孔代表 1

  • 没有打孔代表 0

  • 用 4 行 8 列代表一条指令来打一个穿孔纸带,那么这条命令大概就长这样:

  • 你应该学会了怎么作为人肉编译和汇编器,给纸带打孔编程了,不用再对那些用过打孔卡的前辈们顶礼膜拜了。

4 总结

打孔卡,其实就是一种存储程序型计算机。

只是这整个程序的机器码,不是通过计算机编译出来的,而是由程序员的人脑“编译”成一张张卡片的对应的程序,也不是存储在设备里,而是存储成一张打好孔的卡片但是整个程序运行的逻辑和其他 CPU 的机器语言没有什么分别,也是处理一串“0”和“1”组成的机器码而已。

我们看到了一个 C 语言程序,是怎么被编译成为汇编语言,乃至通过汇编器再翻译成机器码的。

除了 C 这样的编译型的语言之外,不管是 Python 这样的解释型语言,还是 Java 这样使用虚拟机的语言,其实最终都是由不同形式的程序,把我们写好的代码,转换成 CPU 能够理解的机器码来执行的。

只是解释型语言,是通过解释器在程序运行的时候逐句翻译,而 Java 这样使用虚拟机的语言,则是由虚拟机对编译出来的中间代码进行解释,或者即时编译成为机器码来最终执行。

参考

  • 《计算机组成与设计:软/硬件接口》第 5 版的 2.17 小节

  • 深入浅出计算机组成原理

发布于: 3 小时前阅读数: 5
用户头像

JavaEdge

关注

还未添加个人签名 2019.09.25 加入

还未添加个人简介

评论

发布
暂无评论
重学计算机组成原理(4)-还记得纸带编程吗?