The CPU
NES的CPU是MOS 6502(1.79MHz). 6502设计于1975年的八位微处理器.
这个芯片被广泛应用到许多机器上.事实上,它的改进版65c02今天还在生产中
6502的寄存器非常有限(A,X&Y),并且它们都是有着特殊用途的寄存器.然而它的指令
有多种寻址方式."zero page" 模式可以引用到内存的前256个字$0000-$00FF
.
这些操作码在执行时只需要程序空间中更少的字节和更少的CPU周期.开发者可以将这
256个字看做"寄存器".
6502没有乘除指令,也没有浮点数,它有一个BCD(二进制编码的十进制) 模式,
但被NES版本的芯片上禁用了--或许是因为专利原因.
6502的栈容量是256字节且没有溢出检测.
6502有151个操作码(256个可能性).剩下的105个值都是非法/未定义的.他们中的大多数
都会导致系统崩溃,但其中的一些可能会因为巧合而产生有用的结果,正因如此,许多都
基于功能指定了名称.
6502有一个已知的硬件bug,JMP <addr>
,当<addr>
为$xxFF
时,间接跳转会出现问题
当读取指定地址的两个字节时,它不会将FF->00
的进位传递给高位. 例如$10FF
会被读成
$1000
而不是$1100
.
内存映射
6502有16位的地址空间,所以可以映射到64KB的内存,但是NES只有2KB的RAM$0000~$0800
.
剩下的地址空间用于访问PPU,APU,游戏卡带,输入设备等.
一些地址线未得到映射,所以大的地址实际上被映射到其他地址上.例如$1000~$1800
被映射到
$0000~$0800
. 向$1000
写入与$0000
写入是等价的
PPU(图像处理单元)
PPU为NES产生输出的视频.与CPU不同,PPU芯片是为NES特殊制作的,运行频率是CPU的三倍.
渲染时PPU的每个周期产生一个像素.
PPU可以渲染一个背景层和最多64个精灵. 精灵可以是8*8、8*16
像素. 背景既可以在X轴滚动
也可以在Y轴滚动.支持精细的滚动(1次1个像素).这在当时可以算是一件大事.
背景和精灵都是由8*8
的块构成的.在卡带ROM中的样式表定义了这些块.这些样式只指定了两位
的颜色值,另外两位来自于属性表.它指定了这些块在背景中的位置.总之,它对于现在的标准有些
混乱.并不仅仅是bitmap.
背景是由32*30 = 960
个这些8*8
的块.滚动是通过渲染超过这些32*30
的背景来实现的,
每个方向都有一个偏移.如果在XY轴同时滚动,最多有四个备选的背景,然而NES只支持两种,
所以水平或垂直镜像使用了不同的镜像模式.
PPU包含256字节的OAM(对象属性存储),存储所有64个精灵的属性.这些属性包括精灵的XY轴的坐标,
块的编号和一系列指定了精灵颜色的两位的标志,表示了精灵是出现在背景的前面还是后面,是否允许
纵向或横向的翻转.NES支持从CPU进行DMA直接读取整个256字节到OAM.这种直接存储的方式大概四倍
快于手动拷贝字节.
尽管PPU支持64个精灵,但一个扫描线最多只能显示8个.应该设置一个溢出标志从而使程序能够处理
太多精灵在一行的情况. 这就是为什么当游戏中有很多东西时精灵会闪烁.同样的,硬件bug可能会导致
溢出标志无法正常工作
许多游戏会在帧中间进行修改,使PPU能够在屏幕的不同部分执行不同操作,通常用来分隔滚动或渲染分数条,
这需要精确的时序和明确每条指令需要的CPU周期.这些东西都使模拟NES变得困难.
PPU有图元形式的碰撞检测--如果第一个(下标为0)个精灵与背景发生碰撞,需要设置标志位表示
"0号精灵碰撞",每帧只会产生一次这样的碰撞.
NES有一个内置的54色调色板--只有这些颜色能够显示,而不是RGB;调色板中的颜色基本上是
电视上的特点色度和亮度信号
APU(音频处理单元)
APU支持两个方波通道,一个三角波通道和一个噪声通道和一个增量调制通道
游戏程序会写入特定的寄存器(地址在内存中)配置这些通道来播放声音
方波通道支持频率和时长控制,频率扫描和音量包络.
噪声通道使用线性反馈移位寄存器生成伪随机噪声.
增量调制通道可以播放内存中的音频. SMB3音乐具有使用DMC的金属鼓声,
TMNT3具有像"cowabunga"的使用DMC的声音.
Mappers(映射器)
从卡带保留的地址空间将游戏限制在32KB的程序内存和8KB的字符内存(模型表).
这是非常有限的内存,于是程序员需要有创造性和应用映射器.
映射器是卡带上的硬件,它可以将新的程序/角色内存交切换到可以寻址的内存空间上.
程序可以通过写入指向映射器硬件的特殊地址来控制这个切转.
不同的游戏卡带有不同的实现切换的方式,于是要有许多不同的映射器.模拟器不仅要
模拟NES硬件,也要模拟卡带映射器.所幸90%的NES游戏都使用6种最常见的映射器
ROM (游戏文件)
一个.nes ROM 文件包含卡带中的程序存储库和字符存储库,它有一个头标识使用游戏那个
映射器和视频镜像模式,以及卡带上是否有电池供电的RAM
总结
学习NES非常有趣,我对程序员能在如此机能限制下写出的游戏感到惊讶(PS:+1),让我都想写一个
8-bit风格的游戏了.
我用GO和OpenGL实现了模拟器,GLFW来显示视频,PortAudio播放音频.代码已经开源在github上.