最近浏览了github,找到了比较有意思的一些开源项目,也想着使用C++写一个红白机的模拟器。说干就干吧,在这里记录一下相应的准备工作。
一些基本概念
首先是一些术语的含义,如下表所示。
其次,一些需要注意的地方:
- CPU和PPU分别拥有16位(64KB)的地址空间;
- RAM和ROM位于同一个16位地址空间内,CPU和PPU通过指定地址读取相应数据,(和计算机中的虚拟内存层次存储结构不太一样);
- RAM的地址空间为13位(0x0000~0x1FFF, 8KB),但实际上只有11位(0x0000~0x07FF, 2KB)是真实用到的,所以会有CPU地址空间分布中的"RAM镜像的概念",其实就是把原来13位中的高2位抹去,归根结底就是为了省钱,同时把地址空间给覆盖满;
- PPU中的镜像:PPU的地址空间中,任何超过0x4000的地址,也都被抹去高位,使得实际上寻址范围在0x0000~0x3FFFF,可见,镜像在NES中是广泛应用的;
CPU地址空间分布
nes的CPU地址空间分布如下表所示,这对于后续编写CPU读取数据的函数,是至关重要的。
起始地址 | 字节大小 | 具体类型 |
---|---|---|
0x0000 | 0x800 | RAM |
0x0800 | 0x800 | RAM映像 |
0x1000 | 0x800 | RAM映像 |
0x1800 | 0x800 | RAM映像 |
0x2000 | 0x8 | Register |
0x2008 | 0x1FF8 | PPU Register |
0x4000 | 0x20 | Register |
0x4020 | 0x1FDF | 扩展ROM |
0x6000 | 0x2000 | SRAM(用电池供电的额外RAM,卡带提供) |
0x8000 | 0x4000 | PRG-ROM |
0xC000 | 0x4000 | PRG-ROM |
PPU的渲染模式
nes图形种有一个palette的概念,具体是什么不用细讲,我们只需要知道,每个像素点需要4bits的信息来告知是哪一个palette即可,用以显示对应的颜色。PPU使用Name Table和Pattern Table以及Attribute Table来指示颜色信息,如图所示(写的很乱...估计除了自己,也没人看得懂了)。简单来说,游戏的每一帧有个Tile,所谓Tile,就是个像素点构成的一个小方块。然后,程序通过几个Table来指定小方块的颜色(也就是降低精度来节省空间了)。
Tile有960个,而单个NameTable刚好也是960Bytes构成,和Tile一一对应。Pattren Tables总共有8KBytes的空间,分为,分别给Background和Sprite使用。4KBytes以16Bytes为单位,划分为256个单位。而Nametable中的每个Bytes刚好作为Pattern Tables中单位的索引。单位用以指示Palette偏移的低2位,高两位由Attribute Table指示。后面也懒得写了。。。如果到这里能读懂的话,图也就能看懂了,看图吧。。。
NES文件格式
查阅了相关的资料,nes文件的文件格式如下,供后面参考。
字节范围 | 字节数 | 具体内容 |
---|---|---|
0~3 | 4 | 字符串"NES^Z" (^Z表示EOF) |
4 | 1 | 16KB ROM的数目 |
5 | 1 | 8KB VROM的数量 |
6 | 1 | D0:1=垂直镜像,0=水平镜像,即游戏的板式为横斑还是竖版(D0表示该字节的LSB,下面类推) |
D1:1=有电池记忆,SRAM地址0x6000-0x7FFF | ||
D2:1=在0x7000-0x71FF有一个512字节的Trainer | ||
D3:1=4屏幕VRAM布局 | ||
D4-D7:ROM Mapper的低4位 | ||
7 | 1 | D0-D3:保留,必须是0(准备作为副Mapper号) |
D4-D7:ROM Mapper的高4位 | ||
8~F | 8 | 保留,必须是0 |
16~ | 16KB M | ROM段升序排列,若存在trainer,则其512字节摆在ROM之前 |
~EOF | 8KB N | VROM段, 升序排列 |
工作流程
- 读取nes文件,将对应的ROM,VROM等数据读取至内存中(可以直接用vector来存储),以及mapper的建立;
- 实际运行游戏之前,先要进行reset,将CPU和PPU中的寄存器、标志位等部件恢复至默认值;
未完待续...