第 7 章

红白机底层的代码拓荒

第7章 红白机底层的代码拓荒

岩田聪翻开任天堂送来的Family Computer开发手册时,注意到的第一个数字不是内存容量,而是价格。手册附带的商务条款里写着,每盘卡带的制造成本由任天堂统一定价,第三方开发商承担全部生产费用,任天堂收取权利金。这笔费用在当时的电子游戏行业不算新鲜——街机厂商对基板代工也有类似抽成——但红白机的商业模式与街机有一个结构性差异:街机游戏靠投币回收成本,单次游戏价格低廉,玩家重复消费;家用机卡带是一次性购买,定价必须让消费者在柜台前觉得物有所值。这意味着卡带的零售价不能太高,而制造成本又被任天堂锁死,第三方开发商的利润空间完全取决于两个变量:开发效率和销量。

岩田聪在那个春天的下午计算了HAL研究所第一个红白机项目的盈亏平衡点。他用的不是财务报表,是一张白纸和一支铅笔。卡带制造成本、任天堂的权利金比率、预期的首批发货量、HAL内部的人工和房租开支——数字一行行列下来,得出的结论是,这个项目必须控制在三个月内完成,程序员不超过两人,否则就算卖到任天堂预估的平均销量,公司也赚不到钱。这个计算的前提还包括一个假设:代码一次通过任天堂的技术审核,不需要返工。他把铅笔搁在纸上,目光回到开发手册的硬件规格那一页。两KB工作内存,两KB视频内存,6502处理器,主频一点七九兆赫。这些数字下面,是红白机卡带接口的引脚定义和内存地址映射表。映射表用十六进制标注了每一段地址空间的用途:零页内存用于零页寻址,堆栈区固定在一百到一百九十九号地址,剩余空间供程序和数据共享。对任何一个熟悉6502架构的程序员来说,这些信息不新鲜。新鲜的是那些空白处——手册没有提供的部分。开发工具、调试器的接口规范、错误代码的含义、内存溢出时的标准处理流程,这些在今天的开发文档中必不可少的内容,统统付之阙如。

他把目光从手册上抬起来,望向公寓窗外。千代田区的街道在这个季节总是灰蒙蒙的,空气中带着冬末的寒意。HAL研究所的办公室就在这栋公寓楼的地下室和一层,六名大学生兼职程序员分散在几台电脑前,康懋达VIC-20和MSX的键盘声此起彼伏。公司创始人池田光博正在隔壁房间打电话,与任天堂的发行部门协调卡带生产的排期。这间公寓里的每个人都知道,接下红白机项目意味着什么。自1983年起,HAL研究所开始与任天堂合作,参与开发任天堂各平台游戏。从1980年公司成立算起,HAL已经为康懋达VIC-20和MSX开发了多款游戏,积累了技术经验和行业口碑,但始终没有突破“家用电脑游戏外包商”的身份边界。红白机不同。它是任天堂的封闭平台,开发者一旦进入这个体系,就意味着与任天堂建立长期的商业绑定关系——同时也意味着接受任天堂的游戏规则。

岩田聪在笔记本上画了一个方框。二百五十六乘二百四十像素,屏幕显示区域。他在方框旁边标注了几个数字:程序可用内存约一点五KB——因为剩下的零点五KB要被堆栈、零页变量和输入输出缓冲区占用——图块存储区来自于卡带ROM和视频内存的联合映射,音频数据与音效代码共享剩余空间。这不是一套技术参数,这是一张约束地图。每一个数字都是一道墙,代码必须在这些墙之间流动。岩田聪后来在2005年游戏开发者大会的演讲中描述过这种状态,他说当限制严苛到一定程度时,编程就变成了一种对话——你提出一个想法,硬件说不行,你就得换个说法再问一次,直到它点头。这个比喻在当时的演讲现场引起了在场开发者的会心一笑,因为他们中的许多人都经历过类似的时刻。

他当时不知道的是,这张约束地图即将成为他职业生涯中最重要的一件工具。不是因为它给了多少空间,而是因为它揭示了一个原则:在红白机的世界里,代码优化的标准不是“跑得通”,而是“跑得比别人好”。任天堂的技术审核不会只看程序是否正常运行,还会看卡带设计是否充分利用了硬件潜力,是否给玩家提供了流畅的体验。那些在内存管理和画面渲染上做得更好的开发商,会获得更多的项目机会和更大的发行配额。而那些只能满足基本要求的开发商,会在竞争中逐渐被边缘化。岩田聪对此的理解比大多数同行更早、更透彻,因为他从这台机器上看到的不是技术瓶颈,而是一个可以被拆解的工程问题。

1983年夏天,他开始了对6502处理器和红白机硬件的系统性拆解——不是用螺丝刀,是用汇编语言。HAL研究所自己搭建的开发环境简陋到令人难以置信。一台MSX个人电脑作为代码编辑和编译主机,通过手工焊接的排线连接到一块拆除了外壳的红白机主板。主板上焊着几根飞线,接到一个插拔式的EPROM插座。岩田聪写的代码在MSX上汇编成机器指令后,通过软盘转移到另一台用于烧录EPROM的电脑上,烧好的芯片插进改造过的卡带壳,再插到红白机主板上通电测试。如果屏幕正常显示画面,说明代码跑通了;如果屏幕花屏、黑屏或死机,就得拔下EPROM芯片,放到紫外线擦除器下照射二十分钟清除数据,重新开始。这个循环周期——编码、汇编、烧录、测试、擦除——需要三十到四十分钟,其中的大部分时间花在烧录和擦除上。现代软件开发中,程序员改动一行代码后几秒钟就能看到运行结果。而在1983年的HAL,每一次测试之间的等待时间,足够岩田聪在笔记本上写完一整段优化方案的伪代码,再仔细检查一遍逻辑。

他没有抱怨这个效率。相反,他在后来的演讲中提过,这种缓慢的反馈循环迫使他养成了一种习惯:在写代码之前,先把整个逻辑在脑子里精确地跑一遍——每一条指令占用多少个CPU周期、每一次内存读写访问哪个地址、每一个中断在扫描线的哪个位置触发。他对6502指令集的掌握达到了一种程度,以至于可以脱离电脑,手写出一个完整函数在每条指令执行后处理器各寄存器的状态变化。这种能力在同时代的嵌入式系统开发者中并不罕见,但岩田聪的独特之处在于,他将这种对硬件底层时序的精确理解,直接应用于解决游戏设计层面的具体问题。

《弹珠台》的碰撞检测算法就是在这种工作方式下诞生的。弹珠台的核心玩法要求模拟一颗金属球在布满挡板、弹射器和得分区域的台面上滚动和碰撞。标准的物理模拟做法是为球分配两个坐标变量——X轴和Y轴——然后在每一帧画面中根据球的速度更新坐标,检测是否与场景中的障碍物发生重叠。问题是,每一个坐标变量至少需要一个字节来存储。如果球的运动速度需要精确到亚像素级别以实现平滑滚动,那么X和Y各需要一个半到两个字节,总共将近四个字节。放在两KB的内存里,四个字节对于单个变量来说不算什么,但弹珠台的场景中有多个动态物体——球、挡板、奖励道具——每个物体一套坐标,累计起来就成了一笔不小的内存开销。

岩田聪的解决方案体现了他对6502处理器寻址方式的深入理解。他注意到6502有一组丰富的零页寻址指令——访问内存地址范围从零到二百五十五只需要两个CPU周期,比访问其他地址快一倍,而且指令字节数更少。于是他设计了一套基于零页内存的压缩坐标系统:将弹珠台的整个台面划分为多个固定大小的区域,每个区域内用一个字节存储球的位置偏移量,区域的编号隐含在代码逻辑中而不是单独存储。读取球的完整坐标时,通过当前执行的是哪个区域的逻辑来确定高位地址,通过零页寻址读取偏移量作为低位地址。这样做的好处是,球的坐标始终只占用一个字节的内存,而访问速度比标准的绝对寻址更快。节省的内存被用在了音效上。《弹珠台》的街机原版以清脆的金属碰撞声和铃铛声为特色,岩田聪非常在意这些音效在家庭电视机扬声器上的表现。他为红白机的音频处理单元编写了一套简化的波形合成程序,用节省下来的内存存储额外的音效数据,使得球的每一次碰撞和得分都有对应的声响反馈。这个细节在当时的红白机游戏中并不多见——早期作品往往因为内存紧张而牺牲音效品质,只用简单的方波或三角波应付。《弹珠台》的音效表现引起了任天堂审核人员的注意,在审核报告的主观评价栏里,音效被标注为“良好”。

《弹珠台》于1983年通过了任天堂的技术审核,成为HAL研究所第一款正式发行的红白机游戏。审核的具体流程没有留下完整公开记录,但根据同期任天堂第三方授权管理文件可以拼凑出大致框架:提交审核的游戏卡带原型首先接受电气特性测试,确认电压和时序符合标准;然后进行内存边界检测,确保程序不会在特定操作下溢出到系统保留区;最后是一轮人工试玩,测试者填写评估表,从游戏稳定性、操作响应、画面表现和娱乐性四个维度打分。通过全部三轮才能获得发行许可。《弹珠台》的通过,标志着HAL研究所正式进入任天堂的第三方开发商名录。但这只是一个开始。真正的考验在接下来的项目——任天堂给出的下一个题目,将决定HAL在这张名单上的位置。

1983年7月15日,任天堂正式推出“Family Computer”(红白机)。这款首次尝试的卡带式家用游戏机平台非常成功,两个月内售出超过50万部。不过FC上市不久,被发现由于芯片问题容易死机,为了挽救任天堂的声誉,时任社长山内溥决定回收主机,因此损失了15亿日元。不过这次事件让任天堂树立了卓越的品牌形象,次年FC重新上市后于一年内卖出了165万台。就在这台灰色机器席卷市场的同时,岩田聪和HAL研究所接到的项目,正是要在这台创造了奇迹但也刚刚经历了质量危机的硬件上,证明自己的价值。

1984年初,任天堂向HAL提出了一个新任务:为红白机开发一款高尔夫球游戏。高尔夫球是山内溥本人非常重视的运动类型。他曾在内部会议上指出,红白机的目标用户是家庭,而家庭娱乐需要覆盖不同年龄层和兴趣偏好。儿童会买动作游戏和平台跳跃游戏,但要让父亲和祖父也拿起手柄,需要的是体育题材。棒球、网球、高尔夫球——这些运动在日本拥有深厚的观众基础,做成电子游戏后天然具有跨代吸引力。山内的这个判断后来被市场证明是正确的:红白机上的体育游戏销量稳定,重复购买率高,而且因为规则固定、玩法成熟,续作开发成本较低。但开发成本低不等于开发难度低。

《高尔夫》在技术上给岩田聪出了一道比《弹珠台》更难解的题。弹珠台是一个封闭空间内的物理模拟,所有计算都发生在有限的区域内,变量的数量和变化的幅度可控。高尔夫则不同:球的飞行轨迹是一条跨越数百像素的抛物线,受初速度、击球角度、风力甚至地形起伏的影响。玩家的每一次击球都相当于在屏幕上投射一条新的轨迹线,而这条线必须看起来自然流畅,不能让球在空中跳帧或忽快忽慢。岩田聪面对的根本问题是图像渲染,而不是物理计算。物理公式本身并不占用太多代码空间——抛物线方程加上几个修正系数,几十个字节就能写好。难的是球场画面的呈现。高尔夫球场由草地、沙坑、水塘、果岭和树木组成,玩家的视角从发球台到果岭之间移动,每一次击球后背景都要对应变化。如果按照标准做法,为每一种视角下的背景单独存储一张完整的图块排列表,内存立刻就会用光。只能动态生成,实时修改。

红白机的背景渲染机制是这样的:屏幕被划分为一个三十二乘三十的图块网格,每个图块的大小是八乘八像素。背景画面由这些图块拼合而成,视频内存中有一块属性表,专门存储每个图块使用的调色板编号和翻转状态,大小九百六十字节,对应整个屏幕所有图块。任天堂官方的开发规范建议程序员预先设计好每屏画面的图块排列,把属性表作为静态数据存进卡带ROM里,加载时一次性写入视频内存。这是最安全、最省CPU资源的做法。但岩田聪需要的不是安全,是变化。他希望玩家的击球位置变化时,草地的纹理、沙坑的边缘、水塘的反光都跟着变化,营造出一种视角移动的感觉。静态属性表做不到这一点——除非预先把每一种可能视角的属性表都存进ROM,但ROM容量虽然比工作内存大得多,也不是无限的,大量重复存储本身就是浪费。

岩田聪找到的突破口是红白机的垂直空白中断。在每一帧画面绘制完毕、屏幕扫描线从右下角回到左上角的短暂间隙里,CPU可以安全地访问视频内存而不会干扰画面输出。这个间隙的长度大约相当于二十二条扫描线的绘制时间,期间可以修改大约一百六十个字节的视频内存数据而不会导致画面抖动或雪花。岩田聪精确计算了需要修改的属性字节数量——不是全部九百六十字节,而是屏幕特定区域的几十个字节——然后编写了一段在垂直空白中断期间执行的程序,根据球的落点动态更新相应位置的属性值。这个技巧让《高尔夫》的画面产生了一种在当时看来颇为惊人的效果:随着球的飞行距离变化,地面的纹理、障碍物的颜色深浅甚至远方的树影都会有节律地改变,仿佛玩家的视角真的在球场上空移动。这不是真正的三维渲染——红白机没有任何三维图形处理能力——但它创造了一种空间连贯感,让玩家在心理上接受了“这是一片连续的高尔夫球场”的错觉。

这个技术突破在红白机开发者圈子里引起了关注。任天堂本部的开发团队在内部技术通报中提到了HAL的动态属性表修改方案,评价是“在硬件限制内实现的最大化画面表现”。这份通报没有直接点名岩田聪,但HAL研究所的名字从此在任天堂的内部资料中被多次标注为“技术能力优秀”。《高尔夫》于1984年在日本发售,之后登陆北美市场,全球累计销量超过二百万份。这个数字在1984年的红白机软件市场上属于第一梯队。更重要的是,《高尔夫》让任天堂的发行部门认识到HAL研究所具有接手复杂项目的开发能力——不是简单的街机移植或规则简单的休闲游戏,而是需要系统化设计和技术创新的中型制作。

信任的累积是一件具体的事情。1984年秋季,任天堂向HAL研究所开放了一个新的授权层级:卡带扩展芯片的技术规范。红白机的一个独特设计理念在于,主机本身的硬件规格是固定的,但卡带内部可以根据游戏需求加装额外的处理芯片。这些芯片可以扩展图形处理能力、提升音效品质、增加存档功能,甚至实现完全超越主机原生性能的复杂效果。第三方开发商如果能获得扩展芯片的设计规范,就意味着获得了在主机硬件限制之外开辟新天地的能力。但这个授权不会随便给。扩展芯片的规范涉及任天堂的核心商业机密——卡带的引脚定义、内存映射规则、时序同步要求——如果泄露出去,可能导致未经授权的兼容卡带或盗版产品出现。任天堂对扩展芯片的授权管理极其严格,只有长期合作并证明技术可靠性和商业信誉的开发商才能获得。到1984年底,拿到这个级别授权的第三方公司,除了HAL之外,主要是南梦宫和哈德森这两家规模和资历都远超HAL的老牌厂商。HAL能跻身这个名单,靠的不是公司规模或资金实力,而是岩田聪在《弹珠台》和《高尔夫》两个项目中建立的技术信誉。说得更精确一些,是靠他在项目开发过程中提交给任天堂的那些技术文档——内存使用方案、卡带电路设计、测试报告——每一份都精确、清晰、经得起审核。任天堂的工程师们逐渐形成了一个印象:HAL交过来的东西,技术上是扎实的,不需要反复质疑和修正。

在一个由审核、排队和返工构成的体系里,这种信任直接转化为商业效率。在任天堂的体制下,一个项目的发行排期是严格锁定的。错过一个窗口——比如新年商战、暑假档期或年终促销季——往往意味着销量的大幅损失。HAL通过省去反复沟通和修改的时间,能够在更短的周期内完成从接单到交付的全过程。1984年末到1985年初,HAL同时在进行的红白机项目达到三个,在同期同等规模的第三方中已经算得上高产。但高产本身也埋下了隐患。

《气球大战》是1984年底启动的项目,玩法要求玩家操控角色在空中漂浮并与对手碰撞。这款游戏的技术难点在于同时处理多个活动角色的物理模拟——每个角色都有独立的坐标、速度和碰撞判定区域,全部加起来的内存需求超过了标准的两KB限制。岩田聪的解决方案是在卡带里加装一块额外的RAM芯片,容量两KB,专门用于存储物理运算的中间数据。这个方案增加了卡带的制造成本——每盘大约增加二百日元,换算成零售价要涨几百日元——任天堂的发行部门最初对成本上升有所顾虑,但最终批准了。批准的理由不只是技术可行,更关键的是,这个方案来自HAL。任天堂的审批逻辑不是“谁都能这么做”。如果一家新晋的第三方开发商提出同样的加装RAM方案,很可能会被打回,要求他们在现有硬件规格内解决问题。区别在于信任。任天堂的工程师相信岩田聪已经穷尽了软件优化的可能性,才选择硬件扩展这条路,而不是一开始就图省事绕过标准限制。这种信任是一步一步建立起来的,从《弹珠台》的压缩坐标系统到《高尔夫》的动态属性表,每一次交付都在为下一次铺路。

《气球大战》于1985年发售,游戏获得了不错的市场反响。但更重要的是,项目交付之后,岩田聪在笔记本上写下了一段思考。他注意到,当硬件限制不再是绝对的瓶颈——当客户允许通过增加成本来突破限制——问题本身发生了变化。软件优化的质量不再单纯由技术指标来衡量,而是由“是否值得增加成本”这个商业判断来衡量。工程师必须在提交方案时自己先算清楚这笔账,而不是等着客户来替你算。这段笔记标志着岩田聪认知上的一次关键升级。在此之前,他的思维模式是纯工程导向的:找到硬件限制,用最优的代码在限制内实现功能。但从《气球大战》开始,他意识到商业决策本身也是一种限制——成本限制、时间限制、市场接受度的限制——而这些限制与硬件限制遵循类似的逻辑:它们都是给定条件,程序员要做的是在给定条件下寻找最优解。

这种认知在接下来的工作中逐渐沉淀为一种方法论。HAL研究所内部开始流传他总结的几条开发原则:在动笔写代码之前,先确定约束条件是什么——硬件、商务、排期,全部列出;为每一个功能分配一个成本预算——不是金钱成本,而是内存字节数、CPU周期数、卡带ROM空间和开发工时;超出预算的功能必须砍掉,除非能找到在别的预算项目上节省等量资源的方法。在1985年的日本游戏产业里,将硬约束前置到设计阶段而非事后补救,是一种罕见的工作习惯。

1985年是红白机在日本市场的爆发年。任天堂的年度财报显示,红白机硬件的累计销量在这一年突破了六百万台,软件出货量超过一千万份。第三方开发商的申请蜂拥而至,HAL研究所也在这股浪潮中急速膨胀——员工人数从最初的七人增长到二十多人,办公室从千代田区神田和泉町的公寓搬到了更大一些的商用空间,项目数量从每年两三个增加到同时推进四五个。但公司的核心生产力结构并没有随着规模的扩大而改变。新招进来的程序员虽然素质不错,但他们缺少在地下室时期那种极致约束下写代码的经历。他们对红白机的硬件限制有书本上的了解,但没有那种在每一行代码背后精确感知时序状态的肌肉记忆。面对复杂项目的技术难题,他们仍然需要岩田聪来指出优化方向,在关键模块上还是需要他亲自动手。

岩田聪的时间表开始失控。他每天在办公室待到深夜,桌上同时摊着三个项目的代码打印件和技术文档。早上处理《气球大战》的发售前调试,下午审核一个新项目的卡带电路设计,晚上修改另一个项目的图像压缩算法。同事后来回忆,那段时间他桌上的咖啡杯永远是满的,笔记本上的字迹越来越潦草,但思路仍然清晰——他可以在几个项目之间来回切换,前一分钟还在讨论碰撞检测的细节,后一分钟就能准确地指出另一个项目里某段代码的内存泄漏风险。这种能力令人敬佩,但它不是一种可持续的组织模式。

问题在1985年秋天暴露了出来。当时HAL同时在开发三个红白机项目:一个与任天堂联合开发的体育游戏续作,一个原创的动作游戏,和一个为某发行商代工的移植项目。三个项目的交付时间在任天堂的排期表上紧紧相邻,中间几乎没有缓冲。岩田聪连续几个星期每天只睡三四个小时,在三个项目的技术方案之间周旋。移植项目的代码审核本来应该是他的最后一道关卡——在提交任天堂审核之前,由他把代码从头到尾过一遍,确保没有隐藏的问题——但疲劳让他漏掉了一个内存泄漏缺陷。那是一个在特定条件下才会触发的错误,测试人员没有覆盖到这个边界情况,岩田聪的眼力也没能在疲惫中捕捉到。这个缺陷在任天堂的技术审核中被检测出来。审核工程师的测试流程里有一套标准化的压力测试操作,会在极端的输入组合和帧数条件下检查程序的稳定性。内存泄漏在这种测试下暴露无遗。审核部门给出的结论是:退回修改,重新提交。移植项目的发行排期因此推迟了两个月,发行商那边非常不满,HAL损失了一笔代工收入和信誉。

这是一个具体的事件,但它引发的反思远超事件本身。在移植项目被打回之后的那次内部会议上,岩田聪没有追究任何人的责任。他把问题归结为自己设下的审核流程存在结构性缺陷:当公司的核心代码质量控制完全依赖一个人的经验和精力时,这个人一旦被多项目压力挤满,质量控制就会失效。这个结论他不是在会议上临时总结的——他在笔记本上画了一个方框,框里写着“技术审核流程”,框外用箭头指向几个并列的小框,小框里分别写着项目A、项目B、项目C。箭头从一个框出发,分散到三个框,然后汇聚到一起,又回到审核框。他意识到,这个结构本身是脆弱且不可扩展的。所有的路径都要经过同一个节点,节点的容量就是系统的瓶颈。问题不是代码的问题,是结构的问题。

在1985年剩下的几个月里,岩田聪在笔记本上画了不止一个组织结构的草图。他把HAL的编程团队分成几个项目组,在每个组里指定一个技术负责人,试着把自己的一些判断标准——那些在开发手册之外的判断——写成可以传授的规则。这并不容易。有些判断涉及对6502处理器时序的直觉理解,他可以在文档里写清楚垂直空白中断期间修改属性表的安全字节数是多少,但很难用文字传达那种“你感觉到这里还可以再挤一挤”的直觉。但他不得不开始做这件事,因为他已经清楚,依赖一个人为所有项目兜底,这条路不能再走了。

这个认知转变与之前的技术突破有一个本质的不同。破解红白机的内存限制是一道有明确答案的工程题——给定输入,找到最优解。但建立一套可以让团队共享的技术标准和决策规则,是一道没有标准答案的管理题。不是靠对硬件的理解就能解决,而需要对人的理解,对协作方式的理解,对如何让一群人做出一个人能做到的事情这个古老问题的理解。岩田聪未必在1985年底就找到了这个问题的答案,但他已经清楚地看到了问题本身。这是他职业生涯的一个重要拐点:一个在技术上可以解决一切的程序员,开始意识到有些事情必须通过别人来完成。他不是最初那个在公寓地下室里一手包办全部代码的二十二岁大学生了。他现在是HAL研究所的技术核心,管理着一个正在扩大的团队,面对着一个正在爆发的市场,承担着他一个人无法独自承担的责任。

1986年初,HAL研究所的项目数量继续增加。岩田聪的办公桌上堆满了技术文档、卡带样板和任天堂的审核通知。笔记本上的组织架构图已经被修改了五六遍,每一个版本都比上一个更复杂一些。他的同事观察到他开始有意识地在会议上把技术决策权交给项目组的负责人,而不是像以前那样每件小事都由自己拍板。这个过程并不顺利——有时候拿回来的代码不如自己写的好,有时候项目组的方案出现偏差需要花更多时间重新调整——但他的方向是明确的。这种从个人能力向组织能力的转化,将成为HAL研究所下一个阶段故事的中心。在那个新的章节里,他会发现自己写的不再是汇编语言的指令序列,而是一套规则。这套规则的语法与6502的指令集毫无相似之处,但它遵循着同样的底层原理:给定约束,寻找最优。约束是人的能力、时间、精力和意愿;优化目标是把这些有限的资源,翻译成可以被一个团队持续输出的产品品质。这份翻译工作比任何一行汇编代码都更难,因为他没办法像测试EPROM芯片一样测试自己的方案——方案的后果只有在时间中才能显现,而他不能把时间擦掉重新来过。