NES 硬件组成
NES 有以下硬件
CPU
想必它的作用不用介绍了。它的型号是 2A03,8bit,工作频率 1.7897725 MHz。它有 16bit 地址总线,所以它的寻址范围为 0x0000 - 0xFFFF,即 64KB。CPU 支持三种中断:RESET(复位),NMI(不可屏蔽中断),IRQ(可屏蔽中断)APU(Audio Processing Unit)
APU 集成在了 2A03 里面,用于声音的产生和输出,所以它没有专门的芯片。它有 5 个通道,可以产生方波,三角波,噪声,PCM(你问为什么 4 种波形占了 5 个通道?因为有 2 个通道可以用于产生方波)。-
PPU(Picture Processing Unit)
PPU 型号为 2C02,用于产生图像。NES 中图像分两种- Background(背景)
顾名思义,用于背景的显示,比如游戏的天空,草地,建筑 - Sprite(精灵)
用于前景的显示,例如游戏里的人物,子弹等
最终上述两种图像组合后,输出到屏幕
- Background(背景)
-
Cartridge(卡带)/ Mapper
这是我们最熟悉的硬件了,就是这个:
卡带中包含了 NES 程序和图像的信息,所以不同游戏就会生产不同的卡带,CPU 和 PPU 都能直接访问卡带读取内容,至于读取的时候返回什么样的数据,这就是 Mapper 需要做的事情。有的 Mapper 扩大了程序的容量,有的 Mapper 植入了额外的芯片提升音频等等。。。 -
RAM(内存)
NES 主机中一共有 2 个 2KB RAM,一个给 CPU 用,另一个给 PPU 用
NES 卡带中可能不带任何 RAM,也可能带 8KB 的额外用于 CPU 的 RAM(也就是我们常见的带了纽扣电池的卡带),甚至还有的卡带还带了 2KB 的额外用于 PPU 用的 VRAM- 主机中 2KB RAM
用于游戏运行数据存储 - 主机中 2KB VRAM(Video RAM)
看名字就知道它干嘛了,该 RAM 用于显示数据的存储,CPU 将要显示的数据写入 VRAM,然后 PPU 会读取这里面的数据解码后输出到屏幕 - 卡带上 8KB RAM
用于游戏额外的数据存储,最重要的是它能够保存游戏存档。这也说明了为什么我们拔了卡带上的电池后存档就没了 - 卡带上 2KB VRAM
PPU 工作在 4-Screen 的时候才会用到,现在不需要了解它
- 主机中 2KB RAM
NES 总线
这里有一张原理图:
图中左上为 CPU,左下为 PPU,右上为卡槽
能够观察到图中有两条粗的蓝色的线,一个在上方,一个在下方
- 上方的蓝线
这是 CPU 的地址总线,它有 16bit,寻址范围 0x0000 - 0xFFFF - 下方的蓝线
这是 PPU 的地址总线,它有 14bit,寻址范围 0x0000 - 0x3FFF
总结下来,我们知道了 NES 有 CPU 和 PPU 两类总线,他们分别有自己的寻址空间。由于卡带上既有图像数据又有程序数据,所以卡带同时接入了两根总线
NES 内存映射
了解了 NES 有两条总线之后,我们需要关注 NES 的内存映射,也就是总线上哪些地址对应了哪些数据
1. CPU 内存映射
-
0x0000 - 0x0800 ( RAM )
这是主机中 2KB RAM 的数据,分成了 3 块-
0x0000 - 0x00FF ( Zero page )
前 256 字节划分为 Zero page,这块内存相比其他区域不同点在于能让 CPU 以更快的速度访问,所以需要频繁读写的数据会优先放入此区域 -
0x0100 - 0x01FF ( Stack )
这一块区域用于栈数据的存储,SP(栈指针) 从 0x1FF 处向下增长 -
0x0200 - 0x07FF ( 剩余 RAM )
这是 2KB 被 Zero page 和 Sack 瓜分后剩余的区域
-
0x0000 - 0x00FF ( Zero page )
0x0800 - 0x2000 ( Mirrors )
你可能会感觉到奇怪这个 Mirror 到底是干什么的。实际上它是 0x0000 - 0x07FF 数据的镜像,总共重复 3 次
例如:0x0001, 0x0801, 0x1001, 0x1801 都指向了同样的数据,用程序来解释的话,就是:
address &= 0x07FF
对应到硬件上的话,就是 bit11 - 13 的线不接
至于为什么任天堂要这样设计?我猜可能是考虑到成本原因,2KB RAM 够用了,不需要更大的 RAM,但是地址空间得用完啊,所以才有了 Mirror 效果0x2000 - 0x401F ( IO Registers )
这里包含了部分外设的数据,包括 PPU,APU,输入设备的寄存器。比如 CPU 如果想读写 VRAM 的数据,就得靠 PPU 寄存器作为中介0x4020 - 0x5FFF ( Expansion ROM )
Nesdev 的论坛上有篇解释这块区域的帖子,简单来讲,该区域用于一些 Mapper 扩展用,大部分情况用不到0x6000 - 0x7FFF ( SRAM )
这就是之前说过的带电池的 RAM 了,该区域位于卡带上0x8000 - 0xFFFF ( Program ROM )
这里对应了程序的数据,一般 CPU 就在这块区域中执行指令,该区域位于卡带上
2. PPU 内存映射
这个图简单看看就好了,后面介绍 PPU 的时候再详细看看
0x0000 - 0x1FFF ( Pattern Tables )
这里存放了 8KB 的图像数据,该区域位于卡带上,由 Mapper 管理着。它的作用是用来 PPU 渲染图像的时候作为参考。有的游戏里面这块区域是 RAM,由 CPU 写入图像数据0x2000 - 0x2FFF ( Name Tables )
这里一共 4KB 数据,其中 2KB 为主机 VRAM,另外 2KB 根据游戏配置为前 2KB 的 Mirror 或者卡带上的 VRAM。这里面存放着 Pattern Table 的偏移量,以此控制屏幕显示的内容,具体在后面的 PPU 章节讨论0x3000 - 0x3EFF ( Mirrors )
同 CPU 的 Mirror 一样0x3F00 - 0x3F1F ( Palettes )
这里是 NES 调色板数据,用于控制图像上每个像素的颜色,具体在后面的 PPU 章节讨论0x3F20 - 0x3FFF ( Mirrors )
同 CPU 的 Mirror 一样
NES 游戏运行大致流程
了解了各个硬件作用和内存映射后,下面可以来探讨 NES 游戏运行时的流程了,其实只是很简单的循环:
首先系统上电或者 RESET 按钮按下后,会触发 RESET 中断,CPU 从 0xFFFA 和 0xFFFB 存储的地址处(2byte)开始取指令运行(具体在 CPU 章节讨论),之后 CPU 会一直运行 0x8000 - 0xFFF9 区间的指令。在每一帧渲染之前,CPU 会读取输入设备,然后通过 PPU 寄存器往 PPU 总线上的 VRAM 写数据,同时往 APU 写数据,最终反馈到了屏幕和声音上