相关影像
型号
主板
图示
快速入门
这款游戏机回应了掌机生态中无法满足的许多需求, 有一些创新也有一些妥协,不过这种组合可能会为新颖而独创的内容铺平道路。
中央处理器 (CPU)
和任天堂的上一代掌机一样,NDS的系统围绕一个名为CPU NTR的大芯片展开。 其中“NTR”是“Nitro”的缩写,是最初的NDS的代号。 该SoC也是一个成功的任天堂-ARM合作伙伴关系的延续,该合作始于Game Boy Advance。
现在,在我们查看新CPU之前,让我们看看ARM在90年代后期的发展,因为这影响了最终在任天堂DS中找到的技术。
ARM的新领域
在90年代中期,ARM公司从移动市场中获得了大量业务(在手机变得“智能”之前)。 然而,它在满足某个特定行业方面遇到了困难:高性能计算。 ARM7被许多移动设备广泛采用,但它未能完全满足苹果公司(其“Newton”PDA系列)和Acorn公司(其RiscPC系列)的需求,这些公司都发布了可以从更快的CPU中受益的软件。 除了缺乏64位解决方案(MIPS已经在商业化外),ARM还无法生产出速度超过40 MHz的CPU。 总体来说,瓶颈开始累积。
然而,在ARM7线的商业化过程中,ARM已经开始开发一个称为ARM8的继任者,这是进入高性能市场的第一次尝试。 巧合的是,Digital Equipment Corporation (DEC),这家以其 PDP 和 VAX 系列机器(所谓的’小型计算机’)及复杂的 VMS 操作系统而闻名的美国公司,正面临一个相反的问题:他们难以基于其高性能解决方案交付低功耗 CPU。
好吧,事情是这样的,ARM 的授权模式 为 DEC 提供了一个机会,它选择通过借用 ARM 的材料(其指令集和微架构)开发新的 CPU。
最终,DEC借助ARM的帮助,采用其基于RISC的Alpha处理器的数据路径设计,并与ARM的微架构相结合 [1]。 这种合作产生了StrongARM CPU,于1996年发布,针对高性能市场。
StrongARM是一种基于ARM的新型CPU,其特点包括[2]:
- ARMv4指令集。 如果你想了解更多,我之前在Game Boy Advance的文章中分析过。
- 高达233 MHz的时钟速度,比最强的ARM7芯片快约583%。
- 5级流水线,与原来的3级设计相比又做了提升。
- 一个新的缓存系统,实现了Harvard架构,其中16 KB用于指令,另16 KB用于数据。
- 这种设计有助于缓解内存瓶颈(这是冯·诺依曼/普林斯顿模型所遭受的问题)。
值得一提的是,这个芯片仍然能够以3.3 V的电源工作,就像以前的ARM芯片一样。 事实上,StrongARM需要2伏电压,仅消耗1瓦功率 [3]。 相比之下,英特尔的200 MHz奔腾芯片运行在3.5 V,功耗高达15.5 W [4]。
正如预期的那样,Acorn和Apple都被这个新芯片所迷住,它们分别立即使用DEC的发明来出货CPU升级和新的Newton模型。
同年,ARM还发布了他们承诺的基于ARM8的CPU,ARM810。 它相对较慢,并且在实际应用中没有提供任何优势。 因此,投入不足和时间过晚导致了没有商业兴趣。 因此,ARM继续改进ARM7系列以适应移动市场。 然而,DEC 的 CPU 潜力是如此具有破坏性,以至于 ARM Holdings 吸收了 StrongARM 的设计 [5],以生产其下一代 CPU,即 ARM9(这也是任天堂 DS 所采用的)。
转折点
借助StrongARM,ARM还巩固了其在掌上市场的地位,完全取代了作为可行替代品的MIPS和SuperH。 从那时起,ARM走上了成为最广泛采用的移动设备架构的道路[6]。
不幸的是,对于DEC来说,这个CPU将是他们在1998年被Compaq收购之前的最后一个重大成就。 然后,StrongARM 的命运将掌握在英特尔手中,他们在新的 ‘Intel XScale’ 系列下继续开发… 直到他们清算该部门以专注于 ‘低功耗’ 的 x86 CPU(即英特尔 Atom)[7]。 15年后,不仅英特尔错失了移动市场的机会,现在他们还发现自己在桌面领域与ARM展开激烈竞争。
任天堂首次亮相的SoC
除了上述改进外,CPU NTR还捆绑了一个有趣的多处理器架构,使用了两种不同的ARM CPU,ARM7TDMI和ARM946E-S。 这种设计是在ARM Holdings正式发布多处理器解决方案之前完成的。 因此,考虑到当前的技术实现,可能有人认为NDS的多处理器架构不是很正统。
虽然这不是本系列第一次分析游戏机的并行系统,但是NDS的设计确与其他游戏机有很大不同。 例如,NDS的设计有别于世嘉土星“实验性质”的主从配置,也不同于PS1或者N64的“协处理器”方案。 NDS包括两台非常独立的计算机,可以分别独立执行操作。每台计算机都有一条专用的总线。 这种设计方法被称为非对称多处理(Asymmetric multiprocessing)。CPU因此而相互依赖,并将左右这款游戏机的整体性能。
现在让我们看看这两颗CPU。
ARM7TDMI
首先说说我们较为熟悉的CPU。 ARM7TDMI 与 GBA 的 CPU相同,但现在运行速度提高到约34 MHz(原速度的两倍)。 它仍然包含所有原有功能(特别是Thumb)。
然后讲讲新变化:由于任天堂的工程师将ARM7放置在大多数I/O端口旁边,这个CPU将负责调节和协助I/O操作。 事实上,另一个处理器不能直接连接I/O。 如你所见,ARM7不是负责整个系统的“主”处理器。它负责在多个组件之间传递数据,是用来分担一部分主CPU负载的“子处理器”。
ARM946E-S
这是任天堂DS的“主”CPU,ARM946E-S。 它的运行速度为约67 MHz,所以并不是“StrongARM速度”。 然而,作为ARM9系列的一部分,该核心不仅继承了ARM7TDMI和StrongARM的所有特性,还包括一些额外的部分,你可能会感兴趣[8]:
- ARMv5TE ISA,是之前的ARMv4 ISA和Thumb的延伸,包含更多指令和更快的乘法器。
- 如果仔细观察核心的名称,字母“E”表示增强型(Enhanced)DSP。这意味着这些新指令大多与信号处理有关。
- 扩展手指有时被称为手指v2。 它增加了
BLX
和BKPT
,分别协助在ARM和Thumb模式之间的切换;并促进调试。
- 5级流水线,类似于StrongARM和ARM8系列。
- 12 KB L1缓存:核心现在具有缓存(原本在ARM7TDMI中缺少),与基于哈佛结构的StrongARM类似,8 KB用于指令,4 KB用于数据。
- 缓存采用写缓冲机制,其中RAM异步更新,因此CPU可以处理其他任务。
- 48 KB的紧耦合内存或“TCM”:类似于Scratchpad内存。 不过,这个系统区分了指令(32 KB)和数据(16 KB)。
- 一个集成的CP15协处理器。 它充当内存保护单元 (MPU),规定了哪些内存范围可以被访问、缓存和/或写入。
任天堂在ARM9周围还添加了以下组件:
- 一个除法器和平方根单元,以加速这些类型的操作,因为ARM9本身不能执行这种类型的算术。
- 一个直接内存访问控制器(Direct Memory Access Controller, DMAC):独立于CPU加速内存传输。 结合使用高速缓存,CPU和DMA有并发运行的潜力。
- 缓存和DMA可以提供很多性能,但也会造成新的问题,如数据完整性和一致性。 因此,开发者需要通过刷新写缓冲区来手动维护内存一致性,然后再触发DMA操作。
就NDS这硬件,也许不难发现孩子们喜欢这款游戏机的真正原因?
互连
到目前为止,我已经介绍了这两个CPU如何单独工作。 但是,作为一个整体运行,它们需要不断地合作。 为了实现这一点,两个CPU直接使用专用的FIFO单元[9]进行“对话”,该数据块包含两个64字节队列(最多16个元素),用于双向通信。
具体工作方式如下:发送方 CPU(发送消息的 CPU)将一个 32 位数据块放入队列中,接收方 CPU 可以从队列中拉取该块并对其执行所需的操作。
每当队列上写入一个值,它可以被任一CPU主动获取(轮询),但这需要不间断地检查新消息(开销会很大)。 或者,也可以激活一个中断单元,以便在队列中有新消息时通知接收方。
主存储器
就像GBA一样,NDS的RAM也被分散在许多不同的位置,以便根据访问速率来安排数据远近。 总之,可用通用内存如下:
- 32位总线的32 KB WRAM(Work RAM):用于存储在ARM7和ARM9之间共享的快速数据。
- 请记住,同一地址一次只能由一个CPU访问。
- 32位总线的64 KB WRAM:同样用于快速数据,但只能从ARM7访问,就像GBA一样。
- 16位总线的4 MB PSRAM:速度较慢,可以由任一CPU访问,由内存接口单元控制。
- 伪静态随机存取存储器(Pseudo SRAM, PSRAM)是DRAM的一种变种,与DRAM相比,PSRAM芯片自己就能执行刷新操作。 因此,它的行为类似于SRAM(DRAM的一种替代品,更快但更昂贵)。 这种设计让我想起了1T-SRAM。
向后兼容性
尽管架构与前代有很大不同,但NDS仍成功地保留了关键部分,从而原生支持GameBoy Advance (GBA)游戏。
但是,为了将NDS转为“内部”GBA,前者包括一组软件例程(software routines),可以将游戏机设置为AGB兼容模式(AGB Compatibility Mode)。 此过程中,实际上会终止ARM9,禁用大部分“特殊”硬件,重定向总线,将ARM7置于控制位置,并将其频率降至16.78 MHz。 最后,ARM7开始引导初始的AGB BIOS,该BIOS引导GamePak卡带(就像本来的GBA一样)。 这种模式仍然展示出一些GBA所没有的特征,例如游戏显示时有黑边框(在下一节中我们将看到新屏幕分辨率恰好更大)。 此外,由于NDS有两个屏幕,所以用户可以设置用哪个屏幕显示GBA游戏。
一旦进入GBA模式,就没有退路了,必须重置游戏机才能重新激活其余硬件。
性能阉割
单个廉价芯片中安装了这么多复杂组件,强制他们协作导致出了一些问题,这并不是什么神秘的事情。
让我们从 ARM9 开始。该 CPU 的运行速度是 ARM7 的两倍,但大部分(甚至是全部)的 I/O 都依赖于 ARM7。 因此,ARM9容易出现过多的停滞,直到ARM7回复。 不仅如此,ARM9的外部总线速度只有一半,因此存在一些瓶颈问题。
此外,主存储器总线只有16位。 无论哪个CPU需要从存储器中提取一个字(32位宽),接口都会阻塞CPU,然后占用高达3个“等待”周期,直到拼成一个完整的字。 内存访问不连续时影响最严重,每次内存访问都会阻塞。 这个问题在指令获取时也会出现。不幸的是,当时的ARM不支持顺序操作码获取(sequential opcode fetching),这也影响到了Thumb指令(因为每次获取16位数据都要以32位的块为单位进行)。 另一方面,这个一些资料所谓的“penalty”,可以充分利用缓存和TCM来缓解。
总的来说,这意味着在最坏的情况下,这款ARM9“猛烈”的66 MHz的处理能力实际上会被降低到只有约8 MHz, 如果程序的缓存和TCM的利用率极低的话。
如果需要详细报告,我建议查看Martin Korth的文档[10],尤其是“DS Memory Timings”一节。
图形
这一部分有些不太寻常,因为这款游戏机不仅有多个屏幕需要绘制,而且把传统的图块(tile)引擎与现代渲染器结合在了一起。
首先说说硬件:NDS包含两个LCD屏幕,每个屏幕的都有256x192个像素,比GBA多约20%个像素点。 它们可以显示262144种颜色(18位),刷新率约60 Hz。
架构
图形子系统可以绘制2D和3D对象。 前者由二维几何图形组成,其中填充了8x8像素的位图(称为“图块”),而后者则使用顶点绘制三维对象(多边形)。
深入了解操作这些屏幕的内部芯片,我们可以观察到这个掌机具有独特的2D和3D几何图形硬件。 2D数据由我们熟悉的引擎PPU(现在只称之为2D引擎)操作,而3D数据由一个全新的子系统处理。 值得一提的是,虽然这并不是第一款推出3D图形功能的游戏机,但它仍然是第一款使用自研图形渲染器来呈现3D图形的游戏机。
现在,这两个引擎必须连接到两个屏幕其中之一。对于只有2D图形的游戏来说,这不是问题,因为每个屏幕都有一个2D引擎。 然而,那些想展示前沿特性的游戏却只有一个3D引擎可用。 因此,每次只能在一个屏幕上使用3D功能。 但是2D和3D对象如何混合显示? 别急,我先分别解释清楚这两个引擎,之后再讨论这个问题。
使用2D图形构建帧
在我们检查各阶段之前,我建议先阅读有关GBA PPU的这篇文章,因为在这里我只会提到构建“下一代”2D游戏的变化。 另外,由于有两个引擎,这里我们把第一个引擎称为主引擎(Main),第二个则称为辅助引擎(Sub)。 虽然这并不一定意味着哪个屏幕连接了哪个引擎, 而且主引擎比辅助引擎包含更多的功能。
我将借用新超级马里奥兄弟(New Super Mario Bros)的资源(assets)来帮助解释。
图块(Tiles)
到目前为止,我们都知道基本的图块系统是如何工作的,但是NDS的图块处理有什么特别之处呢? 其实,总共有656 KB VRAM可用,这一块被分割成不同的bank:四个128 KB bank、一个64 KB bank、一个32 KB bank和三个16 KB bank。 开发者可以随便往bank里填充数据并将引擎指向所需数据的位置。 两个引擎都可以从这些bank读数据,但是它们不能同时访问同一个bank。
尽管如此,数据排布方式还是有一些限制。 例如,ARM7只能访问两个128 KB的bank, 这两个bank不能存储sprite;最后16 KB bank只能被“辅助引擎”访问; 等等等等……你懂得。
最后一句,3D引擎我们稍后再讨论,它可以访问其中某些bank以获取纹理(texture)。
背景类型
从Super Nintendo时代开始,PPU一直致力于为构建背景图层提供更多的灵活性。 14年后,有这样一款芯片,可以获取图块并应用许多仿射变换。甚至,最终可以从frame buffer构建图层。
在我们讨论2D引擎显示背景的不同模式之前,让我们先了解一下引擎可以生成哪些类型的背景:
- 字符类型(Character type)组:这些背景类型遵循传统的图块系统,通过填充图块来渲染帧。
- 静态(Static)或者说“文本”(text)背景: 普通背景, 最大支持512x512像素,256色和16个调色板。 包括了所有典型的特效(effects)(H/V翻转、H/V滚动、马赛克、alpha混合),外加一个额外的“褪色”效果。 最多可使用1024个图块。
- 仿射(Affine) 背景:一个带有仿射变换(affine transformations)的背景, 不支持水平/垂直翻转,并且只能获取256个图块(最大值的四分之一)。 图层大小为1024x1024像素。
- 仿射扩展 - Character:与仿射背景相同,但是可使用1024个图块,并且支持水平/垂直翻转。
- 位图(Bitmap)类型组:引擎不再处理图块,直接把VRAM用作frame buffer。
- 仿射扩展 - 256色:继承“仿射扩展 - character”中所有可用的特效。 区别在于本模式将它们应用于单个512x512 px位图。
- 仿射扩展 - 真彩色:与256色扩展类似,但本模式下frame buffer支持的颜色可达32768种(15比特)。
- 大屏幕(Large screen):利用所有四块128 KB大的VRAM块来渲染一个1024x512 px大的frame-buffer。
- 3D背景:将3D引擎的渲染结果显示为背景图层,这是展示3D引擎处理结果所必需的。 虽然它没有提供很多2D效果,但它有一些有趣的特性,比如水平滚动和alpha混合(与其他背景层)。 此外,它是唯一支持多达262,144种颜色(18位)的类型。
开发者不能任意选择背景类型。但是游戏机提供了一系列背景模式,为各类型设置了不同的组合。 尽管如此,你可以在这里看到Affine扩展模式有三种不同的风格(“字符”,“256色”和“直接颜色”)。 因此,在下一节中,当你看到X背景模式支持“仿射扩展”类型时,开发者可以选择他们想要的变体。
背景模式
背景类型实战演示。 “主”与“副”提供多种操作模式。 所有这些模式生成四个背景图层,然而,每个图层的功能取决于所激活的模式:
- 模式0:4个静态图层。
- 模式1:3个静态图层+1个仿射图层。
- 模式2:2个静态图层+2个仿射图层。
- 模式3:3个静态图层+1个仿射扩展图层。
- 模式4:2个静态图层+1个仿射图层+1个仿射扩展图层。
- 模式5:2个静态图层+2个仿射扩展图层。
- 这是最常用的模式,因为极其灵活。
- 模式6:1个3D背景图层+1个大屏幕。
- 由于VRAM bank的空间只够一个frame buffer,因此只有主引擎能使用该模式。
此外,在模式0到5中,’主’引擎可以将第一个静态图层用作3D背景。 3D功能将在稍后讨论。
Sprites
Sprites或者说“objects”继承了GBA PPU的功能,但新增两个重要功能。
首先,OAM(存储sprite条目(sprites entries)的区域)的大小现在有2 KB,使每屏每帧可以显示多达128个sprite。 因此,两个引擎都分配有1 KB。
其次,OAM现在可以引用VRAM中的位图,而不仅仅使用图块和调色板。 这是一种与tile系统不同的方法。 实际上,同一帧中可以同时存在这两种sprite“模式”,因为此选项是在每个单独的 sprite 上设置的。
结果
由于每个图层都是即时渲染的,最后阶段需要合并所有内容并将其发送到所选屏幕。 以前基于PPU渲染游戏机基本如此,但这是否意味着NDS也到这里结束了?
还没呢! 主引擎仍然必须从另一个引擎——最强大的引擎——获取图层。
3D 加速
如果你之前玩过NDS,那么你就知道这款游戏机可以显示一定数量的3D图形。 不同于GBA游戏,这些3D图形并非由CPU处理。 不过,CPU-NTR包括两个组件来构建3D引擎。 有趣的是,任天堂采用的设计让我想起了SGI的RCP。
回顾“背景模式”部分,你会注意到每个模式都至少有一个static背景,这是因为你可以用3D引擎生成的图形来填充该图层。 唯一需要注意的是只有主引擎可以这样做,这也是模式6仅适用于主引擎的原因之一。
几何引擎
如果读过第五代或第六代游戏机的文章,你可能会想知道…… SIMD 处理器哪去了? 这是个好问题,因为ARM9并不擅长矢量运算,而且我不认为专用除法器很够用。 这就是为什么任天堂嵌入了一个称为几何引擎的组件,负责处理顶点转换、投影、光照、裁剪、剔除和多边形排序,最后一项对正确使用透明特性至关重要。
这个引擎有一些严格的限制,特别是它能够处理的多边形数量:有额外的248 KB RAM可用于存储处理过的几何体,数量可以达到2048个三角形或1706个四边形。使用多边形条带的话(而不是单个多边形)还可以存储更多。 为了对这个数字有一些概念,我建议查看之前文章中的“交互模型”部分,你会发现这个限制令人担忧,但不要忘记掌机的屏幕分辨率也要小得多……所以算是抵消了一点。
无论如何,这个引擎是通过一个Command FIFO来控制的,其数据是由CPU或DMA填充。 该FIFO可以存储256个条目,并且它还有一个叫做PIPE的缓冲区,用于存储额外的四个命令(总共260个命令)。
渲染引擎
渲染引擎负责将向量转换为像素(光栅化),着色(纹理映射)并应用光照和其他效果。 它依靠透视校正和Gouraud 着色分别用来处理插值纹理和光照。 此外,该单元提供一些现代特性,例如fog,alpha混合,深度缓冲(Z缓冲,或是一种被称为W缓冲的变体),模板测试和抗锯齿。 虽然最后一项非常原始(将多边形的外部边缘设置透明而已),而且只能用于不透明像素。
渲染系统采用了新旧结合的方式:它没有直接渲染到frame buffer,而是采用行缓存渲染(line buffer rendering),在每一条扫描线上填充像素(类似于2D引擎),并将结果存储在一个较小的缓缓冲区中。 这是因为3D引擎必须与2D绘图器同步工作。
没有传统的frame buffer,光栅化器采用了扫描线渲染(scan-line rendering),遍历每个扫描线以处理其中的多边形边缘。 Arisotura(MelonDS模拟器的开发者)称,对于每个四边形,渲染器只能在每条扫描线上填充一个span[11]。 这可能会有隐患,因为如果四边形是凹四边形或者有交叉的边,结果会变得一团糟。
关于效果,该单元还提供shadowing和一个被称为Toon Shading的独特功能(又称Cel Shading):该单元不可编程,但也可以通过改变照明参数达到卡通效果。
结果
渲染引擎将不会将结果写回frame buffer以供显示,而是写入一个名为Color Buffer的块中,该块可以存储多达48条扫描线。 2D引擎会按照先进先出(FIFO)的方式获取每条扫描线,以填充BG0图层。
3D渲染在2D渲染之前开始,使后者在必要时能够应用图形变换到新图层。 主引擎还允许捕获生成的2D帧、3D帧或组合帧,将其与VRAM中的另一个帧混合(blend),并将结果写回VRAM,之后可以用于显示。
在控制方面,由于采用了基于双缓冲的机制,渲染引擎还允许在帧中间改变参数,该机制可以保留旧状态的副本,直到当前帧绘制完成。 因此,不会出现图像撕裂的现象。
知名游戏比较
一些最初在NDS上发布的游戏试图模仿另一台游戏机(即N64)上的游戏。 玩家可能会看到两种版本之间存在重大差异,我想简要总结一下原因:
第一个例子
以320×240像素渲染。
以256x192像素渲染。
第二个例子
以320×240像素渲染。
以256x192像素渲染。
所以,为了解释这里发生了什么,我将根据一些论坛用户的说法,整理出几条不同的解释:
- NDS版的纹理看起来更加方块** → 渲染引擎没有使用任何滤波器,因此纹理使用“最近邻”方法进行插值。
- NDS版的纹理看起来更加丰富** → 渲染引擎没有4KB TMEM的限制。相反,除了提供的压缩机制外,最多还有512KB的VRAM可用,因此可以加载更多数据。
- NDS模型的边缘出现了锯齿**→ 与N64相比,NDS模型的分辨率较低。
- NDS的纹理当从远处看起来会出现失真的情况→ 光栅化器使用的是定点坐标。 低分辨率和mip-mapping的缺乏也加剧了走形程度。
简要的概述这就是这些。如果想要了解更专业的情况,你可能需要深入研究两个引擎,甚至反汇编这两个游戏来研究所使用的功能以及他们的用法。
交互模型
我更新了wee model查看器,添加了“最近邻插值”功能。这样你就可以使用你自己的GPU来查看NDS模型了。
750个三角形。
636个三角形。
尽管我们讨论了图形子系统的诸多限制,但很多游戏确实充分利用了它的功能。
音频
大多数音频改进都集中在增强GBA所提供的那些PCM声道上。 我们之前看到过,GBA游戏最终将软件序列优先于PSG,结果非常令人印象深刻。
因此,新的音频系统总共有16个PCM声道(channels),可以将混音任务转移到硬件上。 PCM采样可以是8比特(GBA风格)、16比特(最高解析度)或ACPCM(压缩形式)。 无论如何,混音器都会产生一个立体声信号,可以通过扬声器(现在是立体声)或耳机播放。 它还可以将生成的立体声数据写入WRAM,让子处理器(ARM7)能够应用一些效果,例如混响。
尽管以上内容已经介绍了不少,但这是否意味着NDS终于可以播放编码音乐(例如 MP3)? 这是可行的(实际上,许多自制程序都实现了某种形式的音频解码),但音频解码需要大量的带宽和处理能力[12]。 所以,音频序列仍然是可行最高的选项。
有关PSG的一个(也许两个)古怪之处
NDS可以运行GBA游戏,因此它应该具有类似于前代PSG的功能(无论是通过硬件还是软件实现)。 正好,最后6个声道包含一个“PSG模式”,允许其中任何一个合成脉冲或自定义波形,并且其中只有两个声道可以产生噪声。 但是GBA游戏没有使用其中任何一个功能!
你看,混音器的输出频率是32 kHz,解析度为10比特(远低于输入样本的质量)。 此外,混音器没有执行任何形式的插值来平滑精度的损失。 这些限制对样本来说并不理想,因为它会引入噪声。 尽管这种现象的实际感知取决于你的听觉能力(我不会注意到有“噪声”,除非我把音量调高并将其与16比特版本放到一起比较),但这仍然是从使用8比特解析度软件混合采样以来的一大进步。 与之相比,PSG的声音的混叠效应更为棘手,因为降采样信号可能会引入错误的谐波,使原始的PSG音调失真。 然而,像《新超级马里奥兄弟》这样的游戏,脉冲波伴奏就用得很欢,所以我不认为PSG完全没有用处。
回到主题,GBA游戏是如何处理的呢? 答案是没有处理。任天堂为GBA模式适配了一个单独的声音系统(在同一外壳里),其中包括符合GBA的规格的私有的声道和混音器。 这样,GBA游戏就不会被新混音器限制所影响。 不幸的是,NDS游戏无法使用这个子系统,因为它与NDS的系统相隔离。换句话说,它不能输出到NDS的混音器。
交互式比较
我构建了这个交互式小部件,让你可以自己比较新的音频系统如何影响新一代游戏的配乐。 每个小部件都可以播放相同的曲子,但允许你在旧版和新版之间切换(建议戴上耳机以更好地体验差异)。 试一试!
GBA:逆转裁判(2001,仅日本发售)。
NDS:逆转裁判 复苏的逆转(2005)。
GBA:马力欧赛车Advance(2001)。
NDS:马力欧卡丁车DS(2005)。
(如果您在收听时遇到问题,请联系我,并提供您所使用的浏览器和设备信息。)
不得不说,我得稍微增加GBA原声音乐的增益才能把音量调整正常,这往往会影响信噪比(来回切换时请记住这一点)。 无论如何,我希望您能感受到声音子系统是如何演变的。
提升难度
现在请让我展示一些棘手的情况。移植游戏的原平台有一些独特的音频功能,在NDS上重现这些功能可不简单。现在请你来当评委:
SNES:超级马里奥赛车(1992)。
NDS:马里奥赛车DS(2005)。
Mega Drive:索尼克3D大爆炸(1996)。
NDS:索尼克编年史(2008)。
正如你从第一个示例中听到的(特别是在最后10秒钟内),要与SNES的S-SMP所提供的功能相竞争有些困难。
必须承认,第二个例子是我故意放上去的。我的意思是,究竟发生了什么? 就好像这个新的编排最初就是为雅达利游戏机而设计的一样。 如果你问我,我认为NDS可以处理某种形式的FM到PCM重新采样,所以这个新的极简主义编排可能只是一种创造性的方法。
I/O
长话短说,I/O严格交由ARM7处理。 事实上,除传递数据之外,你不会看到ARM7处理器有多少操作……这真是太可惜了。
卡带与内存的访问
有一个连接三个endpoint的外部内存接口:Slot-1(用于放置NDS卡带)、Slot-2(用于放置GBA卡带或配件)以及4 MB PSRAM(主内存)。 两个CPU均可访问该接口。接口中包含可以修改的寄存器,以便设定CPU优先级,在同一时间有两个总线请求时优先哪颗CPU。
现在要说重要的一点:NDS卡带没有内存映射。为了让任一CPU读取游戏数据,首先必须把内容复制到RAM中。 这是通过向卡带发送包含8位命令和32位地址引用的数据块来实现的。 之后,可以通过32位寄存器或DMA手动获取数据。 数据总线宽度只有8位,但速度最高可达5.96 MB每秒(如任天堂所述)。
用于保存备份的芯片(如 EEPROM、FLASH和FRAM)可以通过SPI总线(串行)访问。该总线使用自己的一套命令集,并连接到一个24位地址总线。
使用本来的引脚排布的话,Slot-2卡槽是内存映射的。但在DS模式下,为了适配提供扩展功能的硬件(额外的RAM、震动等),地址会被移位。 就像GBA一样,ROM总线为16位宽,RAM总线则有8位宽。
外部设备
ARM7也连接到另一个SPI节点,即触摸屏控制器的接口。该接口可以操作底部屏幕(电阻式,需要使用触控笔)和闪存存储器(固件就存储在这里,稍后再做详细介绍)。
这个触摸屏的一个有趣的特点,除了检测X/Y位置的功能,它还可以返回对角线位置(用于计算“压力值”,表示施加压力的区域)。 不幸的是,这个特性从未在官方SDK中公开。据我所知,没有游戏最终使用了这个未记录的特性,除了自制游戏。
许多人指出,《愿望之屋 天使的记忆》依靠这个特性来解决其中一个谜题,需要用户同时使用两个手指。 然而,情况并非如此。 在使用no$gba debugger实验之后,可以发现这个谜题并没有用到压力数据, 而是检查X/Y值是否交替剧烈变化。 游戏将这种效果解释为用户用两个手指按压屏幕的结果。
最后,同一堆东西里还有实时时钟 (real-time clock, RTC)。
无线网络
最后但同样重要的是,掌机包含一个运行在2.4 GHz频段的无线控制器,提供了一些创新功能:
- Internet Play:使任何游戏都能使用标准Wi-Fi连接连接到LAN网络。
- Multi-card Play:使用专有协议,最多可以联机16台掌机。
- Single-card Play:游戏可以将上传程序到另一个没装游戏卡的DS。
操作系统
我觉得基本可以认为,这一世代的每款游戏机都带有了某种交互界面。 NDS继承了以轻量级API为基础的操作系统模型,以简化I/O访问;但同时也提供了一个精简的用户界面,以供调整设置或者运行“应用程序”。
话虽如此,它的操作系统分散在多个芯片中。让我们从启动时读取的芯片开始。
入口点(Entry point)
在某一时刻,ARM7和ARM9将需要初始化硬件。NTR-CPU包括两个不同的小型ROM芯片来做这件事:
- 一个连接到ARM9总线的4 KB BIOS。
- 一个连接到ARM7总线的16 KB BIOS。
当掌机启动时,每个CPU从各自的ROM启动引导。 这是因为它们的复位向量(reset vector)指向每个芯片(作为参考,ARM9的向量位于0xFFFF0000
,而ARM7的向量位于0x00000000
)[13]。
继续推进,两个BIOS都存储两组例程:引导代码和中断调用。 考虑到前代掌机的历史,后者我们并不陌生,然而前者的复杂度却增加了:除了硬件初始化之外,ARM7的代码还将对DS卡带(如果插入了的话)进行一些检查来确保安全性。
运行引导代码后,两个CPU将同步,以便它们可以开始充当“单个机器”:事实证明,ARM9的加载完成要比ARM7快得多。于是ARM9向ARM7发送一个4比特值,在半无限循环中阻塞等待ARM7的响应。一旦ARM7响应,两者同时“越过终点线”,也就是说,它们现在同步了。
机会之窗
如果您现在拥有或者曾经拥有NDS,您可能会注意到只能在游戏机开机前插入卡带才能玩游戏。 这是因为ARM7的BIOS在启动时对卡带进行了一些检查(更多详情参见最后一节),如果所有测试都通过,ARM7的游戏可执行程序将被复制到WRAM,ARM9的程序则被复制到主内存。
如果出于某些原因未能复制可执行文件(卡带无效或在启动时未找到可执行文件),则无法启动游戏。用户将不得不重置游戏机才能玩游戏。
交互界面
无论是否插卡,系统都将加载交互界面完成引导。 这只是一个驻留在外部256 KB闪存存储器上的程序[14]。
存储界面的芯片还存储着固件、一些用户设置(语言、昵称、生日、闹钟和欢迎消息)以及一些系统设置(触摸屏校准、首次启动标志、固件版本和Wi-Fi设置)。
这个界面与其他同期的游戏机界面基本上差不多。 用户依靠它来启动游戏、更改设置、下载游戏(使用“Download Play”功能)或者玩玩Pictochat:一个开放的聊天室软件,可以与附近的NDS交流。
值得强调的是,只读数据和可写数据都驻留在同一个可重写的芯片中,因此理论上固件可以被覆盖! 幸运的是(或者出于显而易见的原因),任天堂在主板上的一个点(称为SL1
)上放置了跳线来保护芯片的上四分之一(64 KB)不被写入,拆卸电池仓后可以看到这个点。 然而,仍然可以覆盖闪存的其余部分,虽然结果也是灾难性的[15]!
可更新性
任天堂为了修复一些安全漏洞,曾多次更新这个固件(确切地说是5次)。 用户无法自行安装这些更新(回想一下SL1
的保护)。 但是,任天堂在下一批生产的产品中嵌入了更新后的固件。
游戏
这里能说的可就多了,首要原因是这个游戏机确实启发开发者和艺术家们想出了非常创新的设计。 让我们来看看……
存储介质
这个游戏机可以从三个来源运行游戏,其中只有两个可以充分地利用硬件:
- NDS 或者说“Slot-1”卡带:用于加载原生NDS游戏的主要介质, 这是用于发行NDS游戏的唯一介质。
- GBA或者说“Slot-2”卡带:该插槽使得掌机能够原生游玩GBA游戏。Slot-1游戏也可以访问此插槽,因此可以插入扩展卡带以增强NDS游戏。 这提供了诸多功能如更多RAM、更丰富的输入控制或反馈设备(例如振动包)。
- Download Play或“无线多重启动(Wireless MultiBoot)”:原多重启动(MultiBoot)功能的升级版,使另一个带有NDS游戏的掌机能够使用无线通信上传程序。 下载的内容存储在WRAM中,传输完成后启动。 由于WRAM是易失性存储器,数据在关机时将丢失。
- 获得授权的零售商可以使用此功能部署下载站,邀请用户在到店参观时下载演示版游戏。
程序的结构
你已经见过BIOS需要分别为ARM9和ARM7编写不同的代码,在游戏中基本也是同样的情况。 NDS卡的结构如下:
- Header(4 KB):包含元数据(各个可执行文件的位置、序列号等)。
- Secure Area(16 KB):出于复制保护的目的而设置。 我们将在本文的最后一部分详细介绍相关内容。
- Main content(大小可变):卡带的其余部分,仅包含可执行文件和游戏数据(图形、声音等)。 使用任天堂的SDK制作的零售游戏使用内置的文件系统将数据层次化地组织为文件和目录。
开发生态
对于有意为该掌机开发游戏的游戏工作室,任天堂分发了硬件套件和SDK,其中包括许多实用工具。
硬件
被称为IS-NITRO-EMULATOR的开发套件由一个中等大小的蓝色盒子组成,包含DS的大部分内部硬件和I/O接口[16]。 套件通过一根粗电缆连接到一个充当“控制器”和显示器的伪NDS外壳。 根据需求,开发工具包还可以增加可选功能,例如音频/视频输出、Wi-Fi(默认情况下使用以太网模拟)和调试功能。 我原以为后者已经包含在内了,但我意识到这些设备也可以被测试团队使用。
该套件可以读取DS卡带,但是需要使用较大的卡片和可更换的备份芯片。 这些卡片使用被称为IS-NITRO-WRITER的另一个单元刷写。
软件
软件套件包括用于与开发工具包交互的实用程序、C/C++工具链和硬件API。 需要提到的一点是,文档明确禁止绕过提供的API直接访问硬件:尽管游戏将在裸机上运行,但如果它执行“违禁”操作,任天堂将不会批准其分发。 违禁操作包括直接控制ARM7,超出显示分辨率或者关闭LCD屏幕。
这些措施情有可原,例如为了保证质量水平。 尽管在我看来,把ARM7拘束在I/O任务里纯属浪费……
交互的自由
新品类的游戏吸引了青少年之外的受众。
随着新型交互形式的出现,工作室就有机会优先考虑游戏体验而不是图形显示。
在电子消费品中,一款把触摸屏、麦克风、Wi-Fi和一个实时时钟封装到一起的掌机第一次出现。 一些游戏甚至提出了新的互动形式,比如指示用户侧握掌机。
网络服务
在以前的竞争对手获得成功之后,任天堂也加入了在线多人游戏俱乐部,并部署了他们的集中式基础设施。 使用“Internet Play”的游戏可以连接到任天堂的服务器(称为Nintendo Wi-Fi Connection)在线游玩游戏。
反盗版和自制游戏
即使DS卡没有受到光盘诅咒的影响,任天堂还是实施了一些保护措施以保持对游戏发行的控制。
安全机制
让我们看看各个领域的安全机制:
加密系统
NDS主要使用对称加密系统对内存接口和Slot-1卡之间的通信进行加密。 在讨论如何加密之前,我们需要先聊一聊所使用的算法以及如何生成用于加密的密钥。
卡片的“Header”区域包含一个名为Gamecode的值(游戏的唯一标识符),内存接口抓取这个块来生成KEY1并使用它来加密进一步发送到卡片的命令。 KEY1加密基于Blowfish算法。
之后,KEY1与内部时钟和卡带Header的一些其他值混合,生成一个新的密钥,称为KEY2。 KEY2与KEY1的根本区别在于,前者使用随机值所以无法被预测。 KEY2加密使用多个异或与移位操作来混淆数据。
KEY1和KEY2在不同阶段被Slot-1接口用来保护其与卡片的通信。 ARM7 BIOS还使用KEY1对NDS卡进行验证并初始化卡片接口。
DS卡的校验
正如我们上面看到的那样,BIOS包括一些例程,在启动时校验NDS游戏卡。 它做了以下工作:
- ARM7 BIOS检索卡带的芯片ID,将其保存在RAM上,然后启用KEY1和KEY2加密。
- 安全区的前2KB以随机顺序复制到RAM中。 该块的前8B存储了一个名为Secure Area ID的字符串,后面的数据包含校验和(CRC16类型)与一些其他元数据。 所有这些都使用KEY1加密,并且Secure Area ID使用了不同参数通过KEY1加密了两次。
- ARM7 BIOS解密了Secure Area ID,并检查解密后的值是否与
encryObj
匹配。 如果匹配,则意味着卡片验证通过了第一次测试。 此后,该字符串被销毁,防止算法泄露。 如果验证失败,Secure Area的2KB就会被无意义数据填满,阻止读取卡片的其他部分。 - 第二个测试流程为再次随机检索芯片ID(使用内部时钟做种)。 如果芯片ID的值与存储的第一个芯片ID相匹配,则第二次测试通过。
- 最后,Secure area的其余部分以随机顺序被取走并在RAM中重新构建。 之后就该执行固件了。
如果一切顺利,固件将在RAM中找到该卡所需的可执行文件,从而成功启动游戏。 否则,游戏选择框将显示为灰色。
Download Play 保护
通过Download Play收到的程序必须由任天堂使用RSA签名(只有任天堂知道私钥)进行签名。
该检查由固件执行。
击败
如果你是自制软件使用者,那么你可能会遇到可以运行该软件的选项。 事实是,在发现目前的破解方法之前,黑客们很难绕过任天堂复杂的反盗版系统。
Existing Slot-2
由于GBA子系统仍然在没有实施任何保护的情况下执行卡带(除了trademark tricks),现有的GBA闪存卡仍然与NDS兼容。 这使得运行GBA自制软件成为可能,如果你不介意错过DS游戏独有的所有新功能,那就可以正常运行。
一如既往,烧录卡带也能运行盗版ROM,但由于任天堂不能改变GBA的保护系统(因为它有可能使现有游戏无法使用),所以只得处理这个问题。
Enhanced Slot-2
秘密地深入研究DS BIOS和固件后,最终发现可以把NDS卡的执行重定向到GBA插槽。 尽管NDS卡还没有被破解,但这种方法允许暂时绕过Slot-1卡的安全系统,并在DS模式下执行Slot-2程序。
因此,市面上出现了一种新一代的Slot-2烧录卡。 它们内嵌了ARM9代码,一旦从Slot-1启动就会执行。 引导本身是使用发现的上述方法之一(被称为“passthrough方法”)实现的:
- 使用PassMe卡:该方法需要一个真正的Slot-1卡。 PassMe是一种处于Slot-1和真正的游戏卡之间的适配器。 它基本上篡改了从卡片发送到游戏机的header,以欺骗控制台执行Slot-2代码,并在此过程中加载Slot-2卡带。
- 使用WifiMe:该方法需要一台带有兼容Wi-Fi卡的PC。 修改驱动程序,然后可以广播自定义程序让NDS通过Download Play下载[17]。 一旦启动,它就会重定向执行到Slot-2。 这种方法利用了固件不会检查二进制文件特定区域的RSA签名这件事。
- 使用FlashMe:一劳永逸的方案。 通过连接先前的方法并桥接
SL1
终端,可以运行一个自制程序,该程序可以在闪存卡存在的情况下覆盖固件以从Slot-2引导。 这个破解还删除了从Download Play引导自制程序时的数字签名检查。
如预期的那样,任天堂推出了包含新固件的新版NDS,修补了这些漏洞。 因此,黑客更多地侧重于寻找BIOS的漏洞(很难修补)。
Native Slot-1
著名NDS模拟器“NO$GBA”的开发者Martin Korth随后成功提取了BIOS并逆向工程了Slot-1的安全机制。 这样一来,就可以用更新的工具和文档揭示NDS真实的安全机制。 正如你在本文中看到的那样,加密系统只要没有被破解就可以运行。这是对称加密系统的一个局限,非对称加密系统(例如RSA,永远不会存储私钥)就不会。
无论如何,这导致市场涌入了大量的即插即用Slot-1烧录卡。这些卡可以在任何类型的掌机上原生运行NDS程序。 随着“NoPass”卡的推出,passthrough方法也得到了改进。这些烧录卡可以在没有真正的游戏的情况下加载Slot-2烧录卡。
由于加密系统无法更改除法对现有的所有零售游戏造成影响,任天堂最终输掉了这场战斗。 现在,他们唯一剩下的选择就是走法律途径,就像为前代游戏机所做的一样。
观察
在我看来,与以前的游戏机的自制方法相比,烧录卡确实简单得引人注目。 在之前的文章中我曾经描述过,用户必须深入迷宫般繁琐的步骤才能运行自制或盗版游戏。
就NDS而言,烧录卡就像零售游戏一样销售。我觉得游戏工作室看到用户轻松就能诉诸盗版,情况确实堪忧。
另一件事是,市场上出现的闪存卡品牌的数量也令人惊讶(不计所有仿冒品)。 从技术角度看,烧录卡只不过是SD卡适配器[18]。 每张卡与其他卡的唯一区别是引导代码和SD读卡器。 一些制造商还投入更多的精力设计更好的文件浏览器(称为内核/固件),还加入了一些额外的硬件。
到这就结束了!
实际上我的第一台很久以前就被我卖掉了…… 好奇它现在在哪。
好嘞! 我觉得我已经谈到了我想说的内容的99%……
我希望我对某些部分的批评听起来不会太苛刻。 不要误会我的意思,我仍然认为这款游戏机是优秀的工程! 只不过有一些缺陷让我怀疑它是否真的是一种对性价比的妥协,还是设计缺陷的一部分。 说实话,这并不妨碍当时11岁的我想要一台NDS。 所以我觉得这对公司来说已经算是“任务完成”了!
还要感谢我的朋友们和MelonDS社区的大家抽出时间检查我的草稿并提出大量更正。 NDS给了我一个机会让我写下之前我就感兴趣的许多话题。虽然担心我一口吃了个胖子,但希望你能够喜欢这篇文章。
下篇文章见!
Rodrigo