当前位置: 首页 > 图灵资讯 > 技术篇> 05 | 计算机指令:让我们试试用纸带编程

05 | 计算机指令:让我们试试用纸带编程

来源:图灵教育
时间:2023-05-23 09:28:17

 

05 | 计算机指令:让我们试试用纸带编程_指令集

学写程序的时候,你有没有想过古代的计算机程序是怎么写的?

上大学的时候,我们系教书 C 语言程序设计的老师说,当他们学习写程序时,他们使用了一种叫做“打孔卡”的古老物理设备(Punched Card)”。用这种设备写程序,不能像今天那样拿出键盘打字,而是先在脑海或纸上写程序,然后在纸带或卡片上打洞。这样,要写的程序和要处理的数据就会变成纸带或卡片,然后交给当时的计算机处理。

05 | 计算机指令:让我们试试用纸带编程_计算机原理_06

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

你觉得这个穿孔纸带有点像我们现在考试用的答题卡吗?当时人们在特定位置打洞或者不打洞,代表“0”或者“1”。

为什么早期的计算机程序要使用打孔卡,而不是像我们现在这样使用 C 或者 Python 写这样的高级语言怎么办?原因很简单,因为计算机或 CPU 没有能力理解这些高级语言。即使在 2019 今天,我们使用的现代个人计算机仍然只能处理所谓的“机器代码”,即一系列的“0”和“1”。

那么,我们每天用高级语言的程序最终是如何变成一串“0”和“1”的呢?这串“0”和“1”是怎么发生的? CPU 中处理?今天我们就来仔细介绍一下“机器码”和“计算机指令”到底是怎么回事。

在软硬件接口中,CPU 我们做了什么?

我们常说,CPU 是计算机的大脑。CPU 的全称是 Central Processing Unit,中文是中央处理器。

硬件

软件 从工程师的角度看,CPU 各种执行都是一种执行 计算机指令 (Instruction Code)逻辑机器。这里的计算机指令就像一扇门 CPU 能理解的语言,我们也可以称之为 机器语言

计算机指令集

因为这两台 CPU 有相同的指令集,也就是说,它们的语言是相互关联的。

存储程序计算机

我把它直接翻译成“插头板计算机”。在一个布满各种插座和插座的板上,工程师用不同的电线连接不同的插座和插座,以完成各种计算任务。下图是一个 IBM 的 Plugboard,它看起来像蒸汽朋克吗?

05 | 计算机指令:让我们试试用纸带编程_计算机原理_07

IBMPlugboard

代码是如何从编译到汇编成机器代码的?

在了解了计算机指令和计算机指令集之后,让我们来看看通常编写的代码是如何成为计算机指令的,最终被收录的 CPU 执行呢?我们拿一小段真实的。 C 看看语言程序。

// test.c              int                               main               ()                             {                            int                               a                               =                               1              int                               b                               =                               2                              a = a + b;                                           }

这是一段简单的段落。 C 即使你不知道语言程序,即使你不知道 C 语言也应该被理解。我们给了两个变量。 a、b 分别赋值 1、2,然后再将 a、b 两个变量中的值加在一起,重新赋值 a 这个变量。

汇编语言

计算机指令

在一个 Linux 我们可以简单地使用操作系统 gcc 和 objdump 这两个命令打印出相应的汇编代码和机器代码。

$ gcc -g -c test.c

$ objdump -d -M intel -S test.o

你可以看到左边有一堆数字,这些是机器代码;右边有一系列 push、mov、add、pop 等等,这些是相应的汇编代码。一行 C 语言代码,有时只对应一个机器代码和汇编代码,有时对应两个机器代码和汇编代码。汇编代码和机器代码一一对应。

test .o : file format elf64-x86- 64

Disassembly of section .text:

0000000000000000

main

{

0 : 55

1 : 48 89

1

4 : c7 45 fc 01 00 00 00 mov DWORD PTR [rbp- 0 x4], 0 x1

2

b : c7 45 f8 02 00 00 00 mov DWORD PTR [rbp- 0 x8], 0

a = a + b;

12 : 8 b 45 f8 mov eax,DWORD PTR [rbp- 0

15 : 01 45 fc add DWORD PTR [rbp- 0

}

18 : 5

19

在这个时候,你可能不得不再次问,我们实际上正在使用它 GCC(GUC 编译套装,GNU Compiler Collectipon)编译器时,您可以直接将代码编译成机器代码。为什么需要编码?原因很简单。看着那串数字表示的机器代码,你感到困惑吗?但即使你没有学会汇编代码,你也可以“猜”这些代码的含义。

因为汇编代码实际上是“向程序员展示的机器代码”,所以机器代码和汇编代码是一一对应的。人类很容易记住 add、mov 这些指令用英语表示, 8b 45 f8 这样的指令很难记住,因为很难一下子理解它在做什么。虽然早年在互联网上随处可见,大神程序员用刀在光盘上刻下操作系统的梗,但估计浪费的卡比使用的卡多得多。

05 | 计算机指令:让我们试试用纸带编程_计算机原理_08

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

分析指令和机器代码

了解了这个过程,我们放大局部,看看这个行的汇编代码和机器指令是什么意思。

常见指令可分为五类。

算术类指令

数据传输指令

逻辑类指令

条件分类指令

无条件跳转指令

你可能一下子记不住,或者你不能一下子掌握这些指令的含义。在这里,我画了一张表格,给你举个例子来解释,帮助你理解和记忆。

05 | 计算机指令:让我们试试用纸带编程_c语言_09

让我们来看看汇编器如何将相应的汇编代码翻译成机器代码。

不同的 CPU 有不同的指令集,对应于不同的汇编语言和不同的机器代码。为了方便您快速理解机器代码的计算,我们选择了最简单的方法 MIPS 让我们来看看机器代码是如何生成的。

一些资料

05 | 计算机指令:让我们试试用纸带编程_计算机原理_10

操作码

R 指令

I 指令 ,它通常用于数据传输、条件分支和不是变量或常数的操作。此时,没有位移和操作代码,也没有第三个寄存器,但将这三部分直接合并为地址值或常数。

J 指令 是跳转指令,高 6 位之外的 26 位置是跳转后的地址。

$t0 , $s2 , $s1

我用一个简单的加法算术指令 add t0, s1, $s2, 例如,给你解释一下。为了方便起见,我们都用十进制来表示相应的代码。

对应的 MIPS 指令里 opcode 是 0,rs 代表第一个寄存器 s1 的地址是 17,rt 代表第二个寄存器 s2 的地址是 18,rd 临时寄存器代表目标 t0 的地址,是 8。因为不是位移操作,所以位移是 0。把这些数字拼在一起,就变成了一个 MIPS 加法指令。

为了方便阅读,我们通常使用相应的二进制数 16 表示进制。在这里,也就是说, 0X02324020。这个数字就是这个指令对应的机器代码。

05 | 计算机指令:让我们试试用纸带编程_计算机原理_11

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

05 | 计算机指令:让我们试试用纸带编程_指令集_12

好了,恭喜你,看完这里,你应该学会了如何作为人肉编译和汇编器打孔纸带,不用崇拜用过打孔卡的前辈。

总结延伸

在这里,你也必须明白,我们在本文开头介绍的打孔卡实际上是一种存储程序计算机。

只是整个程序的机器代码不是通过计算机编译的,而是由程序员用人脑“编译”成卡片。相应的程序不是存储在设备中,而是存储在打孔卡中。但是整个程序的逻辑和其他逻辑 CPU 机器语言没有区别,只是处理一串由“0”和“1”组成的机器代码。

在这个讲座中,我们看到了一个 C 语言程序是如何编译成汇编语言,甚至通过汇编器翻译成机器代码的。

不管是 Python 这种解释性语言,或者 Java 事实上,虚拟机语言的使用最终是通过不同形式的程序将我们写的代码转换为不同形式的程序 CPU 执行可以理解的机器代码。

只有解释性语言是通过解释器在程序运行时逐句翻译的 Java 这样使用虚拟机语言,就是虚拟机解释编译的中间代码,或者立即编译成机器代码进行最终执行。

然而,仅仅理解一个指令是如何成为机器代码肯定是不够的。在接下来的几节中,我将深入解释完整的程序,包括条件、循环、函数和递归。 CPU 执行在里面。

推荐阅读

在这个讲座中,我们使用的是相对最简单的 MIPS 指令集作示例。我们想使用我们的日常使用 Intel CPU 如果您对指令集有所了解,请参阅计算机组成与设计:软 / 第一个硬件接口 5 版的 2.17 小节。

课后思考

我们在命令行打印一个数字。背后对应的机器代码是什么?你可以试试通过 GCC 打出此汇编代码和机器代码。