在 Left Field Productions 使用 Linux

作者:David Ashley

Left Field Productions, Inc. 是一家位于加利福尼亚州西湖村的游戏开发商,专注于游戏机市场,特别是 Nintendo N64 和 Gameboy 系统。他们的网页是 http://www.left.com/。我于 1998 年 9 月加入 Left Field 从事 Gameboy 编程。从那时起,Linux 已在全公司网络中使用,提供各种服务,所有服务都在一台配置低端的 Pentium 90,配备 16MB RAM 和单个 6.4GB 硬盘驱动器上运行。这台机器被大家称为“Linux 盒子”。

公司使用 10BaseT 进行所有联网。Linux 盒子还提供通往互联网的主要网关。它运行着 named 名称服务器和 Apache Web 服务器,用于公司内部网页。Linux 盒子具有双重机制连接到互联网:一种通过 ISDN,另一种通过传统的 56K 调制解调器。ISDN 是首选方法,但在偶尔中断期间,有必要降级到调制解调器。

Linux 盒子提供公共 Samba 服务,为艺术家和程序员之间的文件备份和交换提供存储区域。fetchpop 程序用于从我们的 ISP 拉取邮件,并将其转发到 Linux 盒子上的个人帐户。当 fetchpop 出现一些问题时,主要是由于邮件检索期间缺少超时功能,我用 fetchmail 替换了它,我们至今仍在使用。员工首选的桌面邮件客户端定期连接到 Linux 盒子以检索他们的邮件,并将任何外发邮件发送到互联网。Sendmail 也提供外发邮件服务。

Linux 盒子的正常运行时间已超过 60 天。少数几次重启是由于计划内的电力服务中断以及添加新硬盘驱动器等升级。据我所知,从未发生过系统崩溃。我认为有时其他一些懂 Linux 的员工会不必要地重启它,因为在修改配置文件后,重启系统可能比专门重启单个任务更方便。拥有大量 DOS/Windows 经验的员工有一种“如有疑问,重启”的理念。

当我刚开始在 Left Field 工作时,我们使用免费的汇编器/链接器组合来生成 Gameboy ROM 映像的代码,并使用商业绘画和美术程序进行艺术创作。用于转换和压缩图形文件的工具都是内部开发的。我们对所有事情都使用基于 Win32 的机器。在我的 Pentium II 400 机器上,完整的重建(即汇编所有源文件,然后链接)需要 30 秒到 2 分钟,具体取决于项目。我们当时正在开发一款名为 科比·布莱恩特 3 对 3 的篮球游戏和一款名为 美女与野兽:棋盘游戏冒险 的迪士尼游戏。

一旦生成 ROM 映像,我们就可以将其下载到 Nintendo 调试器系统进行测试,或者更频繁地,我们会在 Gameboy 模拟器上运行它。该模拟器非常可用,但它非常有个性,并且充其量只是喜怒无常。我发现如果我退出模拟器并重新加载几次,它总是会使系统崩溃。此外,它对于测试游戏的音效方面是不可用的——它的声音模拟质量令人痛苦。该模拟器实际上是一个与扩展器一起工作的 DOS 程序,并且对 Windows 95 窗口环境一无所知。我认为遗留问题是其不稳定性的最可能原因。

在完成前两个项目后,我发现自己在下一个项目正式开始之前有一些空闲时间。长期以来,我一直想编写我们自己的模拟器,因为我对我们正在使用的模拟器不满意——频繁崩溃且无法获得源代码以便我们自己修复它非常令人沮丧。因此,在加入 Left Field 几个月后,我开始编写 Gameboy CPU 的模拟器的一些初步工作。我所有的编程都是用 C 语言完成的。我安装了公共领域的 DJGPP 编译器,并在 DOS 下使用它。事实上,通过使用 DJGPP 中的 MAKE 实用程序,替换我们一直在使用的 Watcom MAKE,我能够显着加快构建时间。

不幸的是,项目的需求导致我在甚至无法测试模拟器之前就停止了这项工作。然后,大约一年后,我又有一些空闲时间,并且能够回到代码。这一次,我选择远离 Win32/DOS,转而使用 Linux 下的 gcc——这一举动使编程变得无限愉快。令人惊讶的是,模拟代码中只有几个错误,在很短的时间内,我就让 CPU“工作”了。它似乎表现正确。下一步是模拟 Gameboy 的视频硬件。对于显示输出,我选择使用 SDL 库,这是一个多平台游戏库。支持的平台之一是 Win32,因此那里的好处是,我编写的任何代码都可以被仍在运行 Windows 的其他员工使用。我当时在 X Window 系统环境下使用 SDL。经过一些扎实的工作,我让视频模拟运行得非常好,看到 ROM 映像真正工作真是令人高兴。

最后,我必须添加声音模拟代码。这被证明是最简单的任务,在很短的时间后,模拟器产生了非常准确和可接受的声音,再次使用了 SDL 库。通过使用交叉编译器进行简单的重新编译,可以构建一个 .EXE 可执行文件。模拟器在 Win32 下也能工作。Windows 95 下与声音相关的一些怪癖需要解决。Windows 证明无法以我一直在 Linux 下无问题使用的 64Hz 速率为音频中断提供服务。我不得不妥协并将速率降低到 32Hz,以便 Windows 能够跟上。我从未确定问题是出在 SDL 的 Win32 代码中还是 Windows 本身中。

我们一直在使用的汇编器/链接器提供了 Linux 版本,但我对它不满意。Linux 源代码不如 DOS 版本新——这两个版本基于不同的源代码树,并且很明显 DOS 版本具有优先级。我的选择是使用较旧的 Linux 版本或将 DOS 代码移植到 Linux。我选择了放弃汇编器并编写自己的汇编器。使用源自我自己 ACC 类 C 编译器的核心代码,我设法创建了一个语法与我们一直在使用的汇编器足够相似的汇编器。我借此机会在方便的时候更改语法。我知道汇编器将如何使用,并且某些功能并不重要,因此我从未实现它们。

最终,我自己的汇编器报告的每分钟汇编行数比旧汇编器快 30 倍以上。对于小型源文件,汇编每个文件实际上是瞬间完成的。我需要的下一个部分是链接器。同样,我从 ACC 代码开始,并对其进行了修改以适应。链接速度也比以前快得多。

现在,为了测试汇编器/链接器组合,我采用了我们的游戏源代码树,并在源文件中进行了必要的语法修改。我使用了 美女与野兽 代码,并花了大约四个小时浏览所有文件以获得没有链接器错误的东西。当然,生成的 ROM 映像无法工作,但在花了一天时间在系统的所有部分查找错误后,我得到了一个 ROM 映像,它实际上在模拟器上看起来像真正的游戏——非常令人鼓舞。

在这样的项目中,调试问题可能很棘手。当没有一个部分真正经过测试时,错误可能在任何地方;因此,我发现自己经常在错误的地方寻找错误。有时,我很惊讶地发现汇编器实际上做对了事情,而模拟器才是罪魁祸首——反之亦然。

在用模拟器测试 ROM 映像时,我明显感觉到我需要在模拟器中构建一些调试功能。甚至在开始编写汇编器之前,我就已经在模拟器中添加了反汇编功能。这样做对于查找模拟器中的错误非常有帮助。在我让汇编器工作后,我添加了一些不错的功能,例如符号调试、断点、表达式求值、内存查看和指令执行历史记录。对于文本显示和输入,我添加了回滚缓冲区、名称完成和 tcsh 风格的行编辑。

错误变得越来越少见,并且越来越容易找到。很明显,新系统完全可以用于开发,并且内部工具套件提供了非常强大的优势,这是我们永远无法通过使用外部软件获得的。例如,可以轻松添加任何所需的功能,因为源代码是我们的。为了可移植性,我用标准 C 编写了所有内容。我一直牢记的一件事是,我可能随时都必须从 Linux 撤退并切换回 DOS,并且我希望这些工具也能在那里工作。汇编器和链接器使用 DJGPP 完美编译。

我很高兴地注意到构建时间几乎减少到零。以前需要 30 秒的完整重建现在在同一台机器上只需要 3 秒。必须指出的是,这些时间反映了两个不同的操作系统以及两对不同的汇编器/链接器,因此无法对加速是如何发生的进行实际分解。我从未费心进行详细分析;我很高兴能够使用新系统。

另一方面,我的模拟器是用纯 C 编写的,对 CPU 的要求明显高于我们一直在使用的 DOS 模拟器。作者可能在其中 liberally 散布了手工编码的 x86 代码。我还假设这是该模拟器导致大多数崩溃的根源。

为了完成 Linux Gameboy 开发环境,我必须移植用于转换图形文件和处理一般数据文件的各种工具。有些是我自己创建的,这些工具几乎无需修改即可移植,因为我使用了 DJGPP 作为 C 编译器。唯一需要的更改与 DOS 自定义将 CR/LF(回车/换行)作为行尾有关,而不是 UNIX 风格的仅 LF。DJGPP 头文件定义了标志 O_BINARY,该标志在打开文件时必须使用,以指定不应转换 CR/LF。在 Linux 下,O_BINARY 未定义,因此会导致编译器错误。解决方案是将以下三行

#ifndef O_BINARY
#define O_BINARY 0
#endif

放在源文件的早期,以便源代码可以在 DJGPP 和 gcc/Linux 下无需更改即可工作。

主要的图形处理工具是由公司里的其他人编写的,我必须进行更广泛的更改才能使它们在 gcc/Linux 下编译。与之前一样,存在 O_BINARY 问题,但此外,某些程序在程序本身中进行了通配符扩展。DOS shell 不进行通配符扩展,因此 DOS 程序必须自行提供该服务。不幸的是,用于执行此操作的函数是非标准 C,因此无法移植。最后,我 hack 掉了代码的那些部分,并依靠标准 UNIX shell 通配符扩展来完成这项工作。此外,头文件“windows.h”经常被包含,并且必须删除该依赖项以及代码中需要它的结构和系统调用。

出现的另一个问题是 DOS/Win32 标准做法,即文件名不区分大小写。在 UNIX 下,大小写很重要,因此在源文件中弹出错误,其中请求了诸如“Elmer.h”之类的文件,而目录中的文件实际上是 elmer.h(甚至更糟,ELMER.H)。此外,在整个代码中,程序将创建一个带有扩展名的输出文件,并且扩展名将为大写,因此我将生成名称如“cpaused.CHR”的文件。对我来说,这是刺耳且没有吸引力的,因此我修改了所有文件以生成小写扩展名。当引用错误的文件名并且出现错误消息时,这需要另一轮更改。

最后,我设法在 Linux 上镜像了我们在 DOS/Win32 下拥有的每个工具。在 Linux 下开发代码比在 DOS/Win32 下开发代码愉快得多,主要是因为它更快更稳定。在任何工作中,使用自己的工具也很有成就感。总的来说,从开始到结束,将所有内容移植到 Linux 以及编写新的模拟器大约花费了三到四个星期。

在下一个项目开始成形后不久,一位艺术家询问他是否可以自己进行构建,因为他想修改动画帧并查看它们在实际使用中的效果。作为快速解决方案,我没有费心在他的机器上设置所有工具,而是将源代码树复制到我的机器已通过 Samba 公开的目录路径。我机器上的 /d 目录在网络上显示为 //dave/d。我设置了一个简单的脚本来检查 ROM 映像文件是否存在。如果文件不存在,它将进行重建。因此,为了获得新的 ROM 映像,艺术家将复制修改后的数据文件,然后删除 ROM 映像,并等待片刻,让新的构建神奇地出现。凭借 Linux 出色的磁盘缓存,我通常不会意识到何时发生了这种情况。

最后一个问题让我有些担忧。我目前是唯一一个真正使用 Linux 作为开发平台的人。如果另一位程序员想要为项目做出贡献会发生什么?我不想将我对 Linux 的偏好强加给任何人,所以我必须能够处理这个问题。已经,我们所有的工具都有等效的 Linux/DOS 版本,所以这不是问题。问题将来自多个程序员将代码更改合并到同一个源代码树。在 Win32 下,我们一直在使用 Microsoft Visual Source Safe 来实现这一点。虽然我怀疑必须有一个 Linux 客户端来与 Source Safe 通信,但我从未看过。相反,我研究了如何使用 CVS(并发版本系统),这是广泛用于各种开源项目的传统源代码版本控制系统。在试验 CVS 并了解了足够多的怪癖后,我发现它的性能至少与 Source Safe 一样好。它通过以 UNIX 格式将源文件存储在存储库中,并在处理 Win32/DOS 客户端时根据需要添加/删除 CR,方便地处理了行尾问题。我在命令行上使用 CVS,我更喜欢命令行而不是鼠标点击;CVS 在更新修改后的文件时似乎更快。

即使我是当前项目中唯一的程序员,我仍在将我的代码更改检入到网络 CVS 服务器,只是为了养成这样做的习惯,并提供额外的备份和修改历史记录级别。您可能已经猜到,CVS 服务器正在 Linux 盒子上运行。

相关网址

电子邮件:dash@xdr.com

David Ashley (dash@xdr.com) 从事计算机和电子行业已超过 24 年。他为 Linux 编写了多款免费游戏,包括 Scavenger、XBomber 和 Sdlshanghai。他在加利福尼亚州西湖村的 Left Field Productions, Inc. 工作以支付账单,为 Nintendo 游戏机进行游戏编程。最近成为父亲后,他发现自己沉迷于 Linux 编程的时间越来越少。他告诉我们:“本文中表达的所有观点均为我个人观点,不代表 Left Field Productions, Inc. 的观点或官方政策。”

加载 Disqus 评论