第一台计算机程序是如何创建的?

18

可能重复:
第一个编译器是如何写出来的?

这个问题一直困扰着我。 想要编译程序,你需要一个编译器,也是一种程序,那么是什么编译了编译器呢? 有人告诉我,最初的编译器是用汇编或机器码编写的。 但是思考一下,这还不是完整的故事。毕竟,机器码如何从硬盘到RAM再到CPU,没有操作系统和驱动程序的帮助呢?驱动程序必须已经被某种方式编程过了。

我知道很早期的计算机有开关,并允许您翻转开关以指示位。我想知道的是,从开关到一种无需计算机程序就可以让CPU读取机器码的方式是如何实现的。


不是“完全重复”,但 我对早些时候的一个问题的回答 在某种程度上是有回应的。 - dmckee --- ex-moderator kitten
1
任何曾经使用前面板开关在没有硬盘或编译器的计算机上输入程序的人都知道,这个问题不是有关编译器的精确重复问题。 - Windows programmer
硬盘?操作系统?驱动程序?这些东西都不存在。“一种使CPU在不需要计算机程序指导的情况下读取机器代码的方法。”这没有任何意义;CPU一遍又一遍地读取并执行由程序计数器指向的内存中的指令。 - Jim Balter
可能重复:第一个编译器是如何编写的?-- 这显然不是重复。关闭这个问题的人似乎没有阅读过它或重复的内容。 - Jim Balter
3个回答

19
简短回答:最初的程序是以原始机器码精心编写的,然后逐步构建起来的。
这个想法被称为引导。假设你有一台裸机,其中包含处理器、一些闪存和硬盘。通常情况下,处理器在上电时被配置为从非易失性存储器(例如CMOS或闪存)中的固定位置加载一个简单的操作系统,称为引导加载程序。这个操作系统非常简单,只有足够的功能可以将计算机指向磁盘上真正的操作系统所在位置。这个操作系统可以打开越来越多的设备并加载越来越复杂的程序,直到整个操作系统都启动运行。
但是,这个引导加载程序是用什么编写的呢?最初它们是用原始机器码编写并硬编码到机器中的。它要运行的程序也是用机器码编写的,这样做会非常缓慢和乏味。最终,有人用机器码编写了第一个简单的汇编程序。一旦你有了这个汇编程序,就可以开始用汇编语言编写程序,包括汇编程序本身。实际上,一旦你有了一个简单的汇编程序,就再也不需要编写机器码了。你可以继续用汇编语言编写汇编程序!
从这个点开始,你可以通过使用现有工具(例如汇编程序)编写编译器来构建更复杂的编程语言,以获取足够的功能,使得编译器可以进行基本编程。然后,你可以使用该编译器为编程语言本身编写编译器,并使用相同的技巧在之前的工作基础上构建更大、更酷的东西。这种技术今天仍在使用——大多数编译器都是用它们所编译的语言编写的。

总结一下,过去某个可怕的时期,所有事情都必须手工完成,但由于那些做这件事的人的辛勤劳动,我们现在可以在已有的基础上进行建设。


11
“written in raw machine code”翻译成中文是什么?使用什么工具?一个文本编辑器?一根磁化的针在磁盘上写?这部分有点含糊不清… - user541686
9
原始的机器码是用笔和纸编写的。 使用铅笔和纸可能会更好,但橡皮擦的成本比划掉错误并重写要高。 必要时会翻转前面板开关以将位(字节)输入存储器。 最终,有人拿了一台记者的电传打字机并将其连接到计算机上,因此可以将字节流保存在纸带上,并在以后读取。 另一个人将数据仓库的穿孔卡片机与计算机连接起来。 另一个人用磁带做了同样的事情。 磁盘驱动器稍后出现。 - Windows programmer
@user541686 文本编辑器是一个程序,因此它不能用于编写第一个程序。而且,最初的计算机没有磁盘。如何将程序和其他信息输入到最初的计算机中取决于计算机本身,并不是这里问题的关键。 - Jim Balter

7
在微型计算机行业的早期,你必须费力地使用切换开关直接输入机器码。一个经典的例子是Altair 8800(如下图所示,可能是复制品):

enter image description here

通过这个,您可以设置地址和/或数据位的二进制开关,然后使用其中一个命令切换开关来执行以下操作之一:

  • 将当前地址设置为地址开关指示的任何内容;或
  • 将数据开关中的数据写入当前/下一个地址。

Altair在RESET之后,当前地址设置为零,由于这是CPU开始执行代码的地方,通常足够了。否则,您可以设置地址开关并切换EXAMINE以将当前地址设置为其他内容。在您输入程序不正确并需要修补它(而不是从头开始重新做)的情况下,也需要进行地址选择过程。

一旦地址是您想要的内容(EXAMINE会显示它在地址LED上以及数据LED上的当前内容),您将切换数据开关并执行DEPOSIT以将该数据放入当前地址(DEPOSIT-NEXT相同,但它首先增加当前地址再存储,对于顺序输入很有用)。

整个过程可以在像这个这样的网站上找到,基本思路是:
  • 手写程序,使用汇编语言。
  • 使用CPU手册将其手动组装成二进制代码,给出一系列地址/数据对。
  • 使用EXAMINE/DEPOSIT/DEPOSIT-NEXT过程将每个字节输入内存,非常小心地确保LED正确指示您刚刚存储的字节。

第二个要点可能是最难的部分,因为您需要调整跳转到尚未组装的指令,所以您基本上是一个缓慢的、生物多通道组装器。

例如,考虑以下(非常简单的)8080代码,从在线资源中获取:

0000    c3 00 00    jmp 0000 ; Infinite loop.

请注意,数据字节c3 00 00是十六进制的,但人们通常使用八进制,因为Altair开关分组成三个,而不是四个;这将给出303 000 000

因此,对于那个特定的程序,您应该:

  • 切换 RESET,将当前地址设为零。
  • 输入二进制数据开关 101 000 101 对应八进制数 303,即 JMP 操作码。
  • 切换 DEPOSIT 开关,将该字节存入内存位置 0,并在 LED 上显示地址和存入数据以进行检查。
  • 输入二进制数据开关 000 000 000 对应八进制数 000,表示要跳转到的第一个地址字节。
  • 切换 DEPOSIT-NEXT 开关,将当前地址增加到 1,将该字节存入该地址并再次显示地址和数据在 LED 上。
  • 再次切换 DEPOSIT-NEXT 开关,将当前地址增加到 2,将该字节存入该地址并再次显示地址和数据在 LED 上。由于它们与前一个字节相同,因此不需要更改数据开关。

此时,程序已经在内存中,您可以切换 RESET 然后切换 RUN 来查看它的运行情况。不过,如果您想要在运行时看到 LED 灯的变化,您可能会不断地切换 SINGLE-STEP


另一件需要记住的事情是,程序不必在其预期运行的平台上编写。可以使用在另一个平台上运行的交叉汇编程序(或编译器)来绕过上述繁琐的手动汇编过程。
您仍然必须将字节传输到目标平台,但也有解决方案。纸带读取器可以添加到Altair中,因此只需加载并运行一个小的加载程序,前提是交叉汇编系统能够生成这些纸带。
例如,在早期嵌入式设备发展阶段(在“嵌入式”意味着巨大的Linux盒子之前),我曾经是一个为基于6809的设备开发操作系统、驱动程序和应用程序代码的团队的一部分。实际的开发工具都托管在早期的UNIX系统上,并且这些工具被烧录到EPROM中以插入设备中。那些设备根本没有进行开发,因为它们非常适合其唯一的真正任务。

计算机时代的黎明并没有微型计算机。最初的计算机使用的是步进开关,而不是切换开关。"早期的Altair机器"是在数十年后制造出来的,与最早的程序编写时间并不相同。 - Jim Balter
链接失效了,你有另一个版本吗? - asheroto
1
@asheroto:我找不到相应的网站,所以我重新调整了答案并找到了一个类似的网站。我还带来了很多信息,以防那个网站在某个时候也消失了。 - paxdiablo

0
曾经,要使用计算机,你需要用二进制输入机器码。人们厌倦了这样做,于是他们编写了一个程序(使用机器码),可以读取汇编语言。过了一段时间,他们意识到编写汇编语言很糟糕,于是使用汇编语言制作了高级语言,如FORTRAN。
要了解完整的故事,请报名参加大学的CS或COMPE课程。

11
最后一句话是真的,但在我看来,作为一个回答相当令人失望... - user541686
或者在YouTube上免费观看一些关于这个主题的视频,这样可以节省很多时间、烦恼和金钱。我会搜索一些像“第一台计算机是如何编程的?”这样的内容,以获取一些关于这个主题的精彩视频。 - HartleySan
@user541686 不完全是这样。SO作为一个关于编程“如何”问题答案的知识库运作良好,但它并不能替代教育。 - Jim Balter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接