夜雨聆风学习资料网

ARTICLE · 1223320

阿波罗11号登月源码竟然开源了!仅靠4KB内存,把人类送上月球!

阿波罗11号登月源码竟然开源了!仅靠4KB内存,把人类送上月球!

阿波罗 11 号登月飞船的完整代码,最近又小火了一把!

其实它早就在 GitHub 上开源了,每隔几年都会被翻出来刷一次屏。

最硬核的是,这套登月代码的运行内存小得可怜,仅有 4 KB。

就连存放整套程序的只读存储器,也只有约 72 KB,还不如一张手机截图大。

在如此有限的条件下,1969 年的工程师居然用汇编一行行写出了一整套飞控系统!

真是螺蛳壳里做道场,太强了...

仓库名叫 chrislgarry/Apollo-11,截至 2026 年,已经有 7.5 万个 Star。

仓库链接:https://github.com/chrislgarry/Apollo-11

于是,我把这个仓库又又又从头到尾翻了一遍,发现了很多有意思的细节。

一、基础结构

仓库里有两套核心程序:负责指令舱的 Comanche055(Colossus 2A),和负责“鹰号”登月舱的 Luminary099(Luminary 1A)。

程序各约 6.5 万行(含注释),各有 90 个左右的文件,全部平铺在一个目录里,没有子文件夹。

它本质上是从头到尾一次性汇编,不像现代项目那样拆成独立模块。

  • 底层是操作系统:EXECUTIVE(任务调度)、WAITLIST(定时任务)、中断处理、重启恢复。

  • 中间层是公共能力:INTERPRETER(虚拟机)、各种数学子程序、PINBALL_GAME_BUTTONS_AND_LIGHTS(键盘和显示)。

  • 上层是具体任务,文件直接按程序编号命名,比如 P20-P25、P40-P47、P51-P53。这些编号和宇航员在键盘上输入的程序号是一一对应的,P63 就是登月下降。

看起来平平无奇,实则每一层的设计都非常精妙,我们逐个拆解这套代码的关键设计,你就会理解 4KB内存是怎么把人送上月球的。

二、极限内存和储存

4KB运行内存,72 KB 储存登月,怎么做到的?

首先了解一下他们用的计算机,是阿波罗导航计算机。

这台计算机重约 32 千克,功耗仅 55 瓦,主频约 2.048 MHz,是最早采用集成电路的计算机之一。

今天的电脑和手机普遍数据按 8 位一个字节来存。

而 AGC 一个字只有 15 位数据,外加 1 位用来查错的奇偶校验位,连“字节”这个概念都没有。

也没有浮点数,程序员只能自己记住每个数的小数点在哪。

阿波罗机载飞行软件团队负责人玛格丽特·汉密尔顿

所以,在这样一台机器上写登月程序,其实是非常困难的。

我翻了专门分配内存的 ERASABLE_ASSIGNMENTS.agc 和调度程序 EXECUTIVE.agc,发现 60 年代的程序员为了把这点内存用到极致,想了两个办法:

第一,同一块内存按阶段轮流用。

翻到这个文件的开头可以发现,他们给大量内存位置标了“身份”。

PRM 表示整个任务期间专用;TMP 表示可以临时借用,不同时运行的程序能共用同一块。

光是登月舱程序,就标注了 1500 个变量。

而且每个变量还有不同注释,写明它可以和谁共用,不能和谁共用。

比如有的写着“可以覆盖,但要小心,只在动力下降时使用”,有的写着“不能覆盖 RONE 和 VONE”。

第二,每个任务只给 12 个字的空间。

登月过程中,计算机要同时做好几个任务,比如导航计算、发动机控制、姿态调整。

但它不会临时给任务分配内存,而是提前切好 8 个“任务槽”。

每个槽 12 个字,相当于一个极简的进程上下文。

当前任务固定在 0 号槽,切换任务只需交换两个槽的 12 个字。

要做向量、矩阵这类复杂运算的任务,还能再领一块 44 个字的“草稿区”。

但一共只有 5 块,用不着的任务一个字都不多给。

算下来,整套多任务系统只占了大约 340 个字,用六分之一的内存,就能让这台计算机实现多任务调度。

而 72KB的储存则更硬核,是用铜线织出来的。

那时候没有闪存芯片,大家的选择不多,只能选择磁芯绳存储器。

这是一种纯手工制作的存储器。甚至没有通电写入这个功能,数据就是它的物理结构本身。

代码定稿后,会被送到工厂。

由女工们坐在放大镜前,拿着细细的铜线,对着一排排微小的磁环穿针引线,铜线从磁环中间穿过代表 1,从旁边绕过代表 0,这样储存一整套登月程序。

女工穿线

铜绳一旦织好,就再也改不了,更没法在线打补丁。所以,代码结构必须万分严谨,不能有冗余。

翻开 FIXED_FIXED_CONSTANT_POOL.agc,能看到代码中刻意去重的地方。

有不少常数直接引用别处已有的位置,比如说数字 4、数字 11,都可以借用键盘显示程序里现成的存储单元。

更有意思的是,常数的相邻关系也被利用了,负零放在零前面,两个字连起来,又能作为一个双精度常数使用。

而最大的一笔减法,是在这台机器里被塞进了一台“虚拟机”。

二、波兰虚拟机

翻开 INTERPRETER.agc 文件,会发现一个更疯狂的设计。

前面说到的阿波罗导航计算机,它天生只认识 8 条基本指令,甚至无法直接进行向量和矩阵运算,连乘除法都要借助额外的前缀指令才能完成。

但是,我们必须要让它算出三维空间里的登月轨道,怎么办?

在这样的硬件条件之上,60年代的程序员硬生生用汇编写出了一套解释器。

按源码粗略估算,它只占了约 2000 个字的程序存储,却能直接处理 28 位的双精度数,以及由 6 个字组成的三维向量。

只要在汇编里写一行 TC INTPRET,这台 15 位的机器就会切换进虚拟机模式,点乘(DOT)、叉乘(VXV)、单位化(UNIT)、矩阵乘向量(MXV),都只要一条指令。

为了省空间,这台虚拟机也处处精打细算。

一是一个字塞两条指令。

虚拟机的操作码只有 7 位,最多能编 128 种运算。

汇编器会把相邻的两条打包进同一个 15 位的字里,指令部分的空间直接省下一半。

二是用正负号省掉地址。

虚拟机采用不需要括号的波兰表示法,操作码字存储时被取反,是负数;内存地址则是正数。

需要取操作数时,它会先偷看下一个字。

是正数,就去那个地址取数;是负数,说明程序员没写地址,下一个字已经是新指令了。

它就直接从栈里弹出上一步的结果,接着算。

三、软重启机制

还有一部分值得说,就是藏在 RESTART_TABLES.agc 里的软重启机制。

1969 年 7 月 20 日,“鹰号”登月舱下降途中,发生了惊心动魄的一幕。

离月面还有约 1 万米时,计算机突然报警。

阿波罗登月器导航计算机专家杰克:加尔曼在发射前模拟导致着陆中止后,编写了一份备忘单,列出了警报代码。史蒂夫·贝尔斯在阿波罗11号登陆时将这份副本贴在他的控制台上。

警报一共响了 5 次,大部分是 1202,中间还夹着一次 1201。

OCT1201#NOVACAREAS.OCT1202#NOCORESETSAVAILABLE.

1202 是“任务槽用完了”,1201 是“草稿区用完了”。

前面说的那 8 个任务槽、5 块草稿区,在登月最关键的时刻被挤满了。

换成很多计算机,这时候大概已经死机了。

但阿波罗计算机很快就恢复了工作,导航和发动机控制几乎没受影响,这全部归功于程序设计的软重启机制。

它的思路很简单:关键任务每走到一个重要步骤,都会调用一次 PHASCHNG,记下“我是第几组任务、走到了第几步”。

这些断点都保存在内存里,重启也不会丢。

一旦撑不住,计算机就进入报警程序 BAILOUT,记下报警代码,再跳到软重启入口 ENEMA。

把堵住的任务一口气清空,再按断点记录,把导航、发动机控制这些关键任务重新拉起来。

飞船就这样在一次次重启中,继续稳稳地往下降。

四、彩蛋

翻到注释里,你会发现这群 60 年代的程序员其实非常好玩。

登月舱的主点火程序,文件名叫 BURN_BABY_BURN--MASTER_IGNITION_ROUTINE.agc,直译过来就是“燃烧吧宝贝燃烧,主点火程序”。

这句话来自 60 年代的灵魂乐 DJ Magnificent Montague,他每次播到最火的新歌,都会大喊一句“Burn, baby! BURN!”,写点火程序的 Don Eyles 和 Peter Adler 就顺手借来当了名字。

点火参数表上方还写着一句 NOLI SE TANGERE,化用《圣经》里的“不要碰我”,意思是这张表谁也别乱动。

登月程序 THE_LUNAR_LANDING.agc 里的注释更逗。

计算机提示宇航员手动转动雷达天线时,注释写着“PLEASE CRANK THE SILLY THING AROUND”(请把这破玩意儿转过来)。

宇航员按了确认,它还要再查一遍,注释写着“SEE IF HE’S LYING”(看看他是不是在骗人)。

确认无误后,程序跳转去点火,注释则写着“OFF TO SEE THE WIZARD...”(去见魔法师了),化用的是电影《绿野仙踪》里的经典歌名。

有意思的是,全篇搜不到一个 TODO 或 FIXME,可能因为在登月这件事上,没人敢把问题留到以后。

最后说说这份代码是怎么来的。

它不是 NASA 从哪块硬盘里导出来的,而是一摞存放在 MIT 博物馆里的纸质打印稿。

翻开任意一个文件的开头,都能看到这样一段说明:

代码由飞行历史学者 Paul Fjeld 在博物馆逐页拍照,由博物馆的 Deborah Douglas 协助安排,再由 Virtual AGC 项目的 Ron Burkey 带着志愿者,对着照片一个字符一个字符敲回电脑,最后用汇编器 yaYUL 重新编译验证。

登月舱程序的打印稿上,还印着当年的汇编时间:1969 年 7 月 14 日 16 点 27 分。

也就是说,这份代码定稿时,离阿波罗 11 号发射只剩两天。

五、结尾

chrislgarry/Apollo-11 这个仓库,每隔几年就会被新一代程序员重新翻出来膜拜。

为什么在拥有千亿参数大模型、几十 GB 内存手机的今天,我们看着五十多年前的八进制汇编,依然会起一身鸡皮疙瘩?

因为极度的匮乏,逼出了极致的优雅。

今天的我们太富有了。内存不够?加内存条。

代码跑得慢?升级显卡。

有 Bug?先上线,等用户报错了再打补丁。

我们在海量算力的温床里,写出了越来越臃肿、越来越脆弱的软件。

而打开阿波罗 11 号的源码,你会看到另一个世界。

在那个只有 4 KB 内存、连改一行代码都要剪断铜线的年代,一群平均年龄二十多岁的年轻人,在针尖上跳出了一支零失误的芭蕾。

在这堆发黄的汇编指令背后,是人类在算力荒原上,仅凭智慧、纪律与勇气,向星辰大海递出的第一封信。

相关学习资料