
本文选自 Horizon 2026年9月4日技术简报。
一次移植,为什么值得软件工程师认真研究
1993 年,一支身处巴格达的开发团队用 Motorola 68000 汇编语言为 Amiga 编写了益智平台游戏《Babylonian Twins》。三十三年后,原作者借助大语言模型,把这套几乎没有现代工程文档、与特定硬件紧密耦合的代码迁移到了 Godot 引擎。表面上看,这只是一次带有怀旧色彩的游戏复刻;从工程角度看,它更像一场小型实验:当模型能够阅读汇编、调用旧工具链、对比二进制并生成现代语言实现时,遗留软件迁移的成本结构会发生怎样的变化?
这个案例最值得注意的数字不是“一个晚上完成移植”,而是原程序约有数万行 MC68000 汇编,目标平台与现代 PC 在处理器、图形、音频、输入和运行时模型上均无直接兼容关系。作者没有把模型当成逐行翻译器,而是让它完成理解、验证、重构和调试的闭环。移植初期,模型先调用 vasm 重新汇编原代码,再把生成物与当年发布的二进制逐字节比较。绝大多数内容一致,却留下约 108 字节差异。差异最终被解释为老式 AsmOne 工作流留下的运行时内存状态:开发者当年在内存中汇编、执行,再将内存快照保存为发布文件,因此发布二进制并不完全等于一次干净构建的输出。
这 108 字节揭示了遗留迁移中一个经常被忽视的问题:旧软件的“真实规格”并不只存在于源代码里。编译器行为、链接布局、未初始化内存、硬件寄存器时序、构建机器状态,甚至程序员当年的操作习惯,都可能进入最终产品。现代项目强调可复现构建,老项目往往没有这种条件。若只让模型把指令翻译成 GDScript、C# 或 C++,得到的程序也许能够启动,却未必保留原作的碰撞手感、滚屏节奏和动画时序。
汇编移植的难点不在语法
MC68000 是典型的 CISC 处理器,拥有 8 个数据寄存器 D0—D7、8 个地址寄存器 A0—A7,指令对字节、字和长字的处理十分直接。Amiga 又把大量多媒体能力交给专用芯片:Copper 负责按扫描线执行显示列表,Blitter 负责位块传输,Paula 处理音频和部分 I/O。游戏代码因此经常把业务逻辑、内存布局和硬件时序写在一起。例如,一段看似只是向固定地址写值的指令,实际可能在改变位平面指针、调色板或者 DMA 状态。
现代 Godot 使用场景树、节点、资源和逐帧回调组织程序。原代码中的“每次垂直消隐更新一次对象”可以映射到 _physics_process(),但两者并不天然等价。PAL Amiga 常以 50 Hz 刷新,现代显示器常见 60、120 或 144 Hz。如果直接按渲染帧率推进角色,速度会随设备变化;如果统一乘以 delta,又可能改变依赖整数像素和固定步长的跳跃轨迹。可靠迁移需要先恢复旧系统的时间模型,再决定如何映射到现代固定物理步长。
类似问题也存在于坐标和碰撞。老游戏常通过瓦片索引、位掩码或查表判断碰撞,角色位置可能由整数与定点小数组成;现代引擎通常使用浮点向量、碰撞体和连续运动 API。把旧碰撞全部替换为引擎默认物理系统,代码会更“现代”,手感却容易变化。更稳妥的办法是先在现代运行时中复现旧规则,把原始碰撞表、速度表和状态机保留下来,确认关卡行为一致后,再逐步引入引擎能力。
因此,大模型在这类任务里的核心价值不是背诵 68000 指令,而是帮助工程师建立语义映射:哪些例程是渲染,哪些负责对象更新;哪些全局变量其实是状态机;某段自修改代码是否只是为了节约周期;一张没有字段名的内存表,究竟存放精灵、地图还是音频参数。模型擅长从调用关系、访问模式和常量中提出假设,人类则要用可执行证据排除错误假设。
二进制一致性是起点,不是终点
作者采用“重新汇编并与原版逐字节比较”的方法,价值非常高。它先确认输入材料没有缺页、宏定义没有漂移、汇编器语义基本一致。对于可获得源代码的旧程序,可将验证分为四层。
第一层是构建一致性。固定汇编器版本、宏、链接顺序和资源打包方式,尽可能生成相同哈希的产物。若无法完全一致,就建立差异清单,区分时间戳、地址重定位、未初始化区域与真正的代码变化。
第二层是轨迹一致性。在模拟器中记录关键帧的寄存器、内存区域、对象坐标和随机数状态,在现代版本中记录同义状态。两边接受同一输入序列后,比较第 1、100、1000 帧的状态,而不是只看截图是否相似。
第三层是行为一致性。建立关卡通过、敌人触发、碰撞边界、死亡与重生、存档等测试。游戏可以使用输入回放;工业软件则可使用历史项目、黄金算例与基准数据集。
第四层才是体验一致性。音画同步、操作延迟、像素移动节奏很难完全由单元测试覆盖,需要熟悉原系统的人试玩。作者的三十三年记忆、旧笔记和 Git 仓库在这里构成了模型无法替代的“口述规格”。
这套分层验证也适用于工业软件。大量 CAD/CAE、数控、测控和生产计划软件仍包含 Fortran、C、汇编或厂商脚本。迁移时不能只检验新程序“能跑”,还要比较求解残差、几何公差、浮点舍入、控制周期以及异常工况。对物理计算而言,逐字节一致未必现实,但必须定义数值误差界、守恒条件和工程验收指标。
怎样组织一条可审计的 AI 迁移流水线
一个可复用的流程可以分成六步。首先冻结原始材料,包括源码、工具链、二进制、磁盘镜像、手册和运行录像,并计算哈希。其次建立可运行基线,优先在模拟器或容器中复现旧构建。第三步让模型生成代码地图,而非立刻改写:列出入口、模块、数据结构、硬件访问点和未解疑问。第四步按垂直切片迁移,例如先恢复一个关卡的地图加载、渲染、输入和碰撞,而不是同时翻译全部文件。
第五步建立自动差分测试。模型每完成一次修改,都应运行原版与新版的相同输入,输出机器可比较的状态。第六步才是现代化重构,把验证过的语义封装为节点、组件和资源,并保留旧实现作为对照。迁移与重构若同时进行,一旦行为偏离,很难判断是理解错误、数值差异还是架构调整造成的。
大模型的输出还必须进入版本控制和审查链。每个提交应说明对应的原始例程、验证证据、未解决假设和人工确认者。对生成代码进行静态分析、许可证扫描、依赖检查和模糊测试。模型可以调用编译器与测试工具,却不应被赋予“测试通过即正确”的默认信用。尤其在未定义行为、边界条件和硬件副作用上,语言模型会自然地补全一个看似合理的解释,而旧程序恰恰可能依赖不合理的历史细节。
从游戏考古走向工业软件现代化
这次移植说明,AI 辅助迁移最适合的对象并非毫无文档、无法运行的代码废墟,而是拥有至少一种可验证参照的系统:原二进制能运行、历史数据可回放、输出可比较,或者仍有领域专家在场。参照越清晰,模型越能从“猜测代码含义”转向“提出假设并接受证伪”。
它也提示企业重新评价遗留资产。过去,读懂几十年前的专用代码需要同时具备旧硬件、汇编和业务知识的人才,这类人才十分稀缺。大模型能够降低代码阅读和工具调用门槛,使专家把精力放在语义确认与验收上。但迁移项目仍需要建立软件物证库、可复现环境、黄金测试和责任边界。缺少这些基础,模型只会更快地产生一个外表现代、行为不可证明的新系统。
真正可规模化的机会,是把“代码智能体”与仿真、测试、版本管理和知识库连成闭环。模型阅读遗留代码,工具执行旧程序,差分系统提供证据,领域专家处理无法自动判定的语义。三十三年前的一款 Amiga 游戏给出的启示很朴素:AI 可以大幅缩短理解和实现时间,但软件历史不会因为生成速度提高而消失。对历史行为保持敬畏,才是迁移成功的前提。
参考资料
- Babylonian Twins,Porting a 1993 Amiga Game to Godot。
- vasm Cross Assembler 官方资料。
- Godot Engine 文档:场景树、固定物理处理与资源系统。
- Motorola M68000 Family Programmer’s Reference Manual。