最初:

人类设计的最早的电子计算机,计算过程是由电缆的插拔来实现的,后来为了方便操作人们开始使用纸带来实现对计算机的计算过程的抽象,这里纸带上所记录的内容我们就可以理解为机器语言,机器语言本身是机器的指令,可以实现对计算机的直接控制。

一段时间后,人们觉得直接写机器指令实在过于繁琐和低效。所以人们就把机器指令进行总结和分类,把机器指令分为计算、跳转、复制等等的类别,之后给每个分类冠以相应的伪码。这样一来,程序员就不再需要直接手写机器指令,整个编码过程分为如下三步:

  • 用汇编指令在纸上书写程序;
  • 人工的把写好的汇编代码翻译成相应的机器指令;
  • 再把机器指令输入到计算机中让计算机执行;

汇编语言极大地提高了程序员的开发速度,因为相比于毫无意义的机器指令,汇编语言更容易让人理解,开发起来也就更加的容易。

人类的需求是没有止境的。一段时间后,人们觉得每次都是人工的把汇编语言翻译成机器指令,这样既繁琐也容易出错,而且这种翻译本质上是十分机械的工作。所以人们开始尝试用现有的汇编语言写一个汇编器(机),可以自动的把汇编语言转化为机器语言。

(在通用计算机未实现之前,汇编器是作为单独一台机器存在的。)


时间跳回当下:

如果出现一个全新的CPU架构,定义了一种全新的指令集和汇编语言,如何才能为其构建汇编器?

答案是:用老的CPU、老的已实现的编程语言,来构建这个新的汇编器。

新的汇编器是二进制的,可以跑在新架构CPU上的程序。新汇编器编写好后,新的代码就可以被自翻译,不再依靠老平台。

这种思想可以一直往前推到计算机的发明之初,只要编写出了第一个汇编程序的最小子集,它就可以编写它的下一个版本,直到足够强大,功能完备。(自举)

自举:

维基百科:在计算机科学中,自举是一种自生成编译器的技术——也就是,某个编程语言的编译器(或汇编器)是该语言编写的。最初的核心编译器(自举编译器)是由其他编程语言生成的(可以是使用汇编语言),之后的编译器版本则是使用该语言的最小子集编写而成。自生成编译器的编译问题被称为编译器设计的先有鸡还是先有蛋问题,而自举则是这个问题的解决方法。

我们终于理解了为什么C语言的第一个编译器是由B语言编写,第二个编译器由C语言自身编写。

参考:
维基百科:自举 (编译器)
维基百科:History of programming languages
Were the first assemblers written in machine code?
第一个汇编器是怎么实现的?



小故事:

你是一个铁匠,专门给木匠提供生产工具的铁匠。
有一天你觉得你目前的工具制造工艺精度太低、效率不够高,于是你决定发明机床替代打铁和手工制作木制零件。
你手工用木头和铁制作了一个简单粗糙的手动机床。
有了这台机床之后你给木匠制作工具的效率明显提高了。但是用了一段时间之后,你发现这台机床的设计还是有一些不足,于是你决定开发第二版机床。
把设计图画好之后你开始动手了。这次你决定不用打铁削木头的方式制造第二版机床,而是用已经制作好的第一版机床制作大部分第二版机床的零件,只有小部分用第一代机床制作不出来的零件你手工制作。
第二代机床制作好之后你越用越顺手,越来越多的木匠开始用你用机床生产的工具。为了提升效率你决定开发第三代机床。
因为第二代机床的设计比第一代机床更完善,你发现生产第三代机床的所有零件都可以直接用第二代机床制作。于是你用第二代机床生产了第三代机床。
恭喜,你达成了机床自举。