基于FPGA的NES游戏机系统,附带超级玛丽,坦克大战,吃豆等11个游戏
上次上传的源代码好像因为签名档的缘故出了点问题,这次代码重新整理上传,有一定更新,并且附带了游戏文件首先先大致解释一下NES游戏机(即我们俗称的“小霸王”)的构成以,NES使用6502的CPU以及一块专门负责显示的PPU,两者均可寻址16K的内存,但实际内存没有这么多。CPU能访问的两块内存分别为程序段ROM,以及运行时所需的RAM。PPU能够访问三块内存,分别为图案表VROM,命名表SRAM,和精灵表PRAM。
然后这里贴一份别人总结的NES资料,比较容易看懂,相信对理解有帮助
----------------------------开始----------------------------------
(来自屎王NES资料)
引言(Introduction)
========================
首先感谢你阅读这份文档!
这并不是一份非常详尽的文档,但它绝对是带你进入 NES 编
程领域的最好教材!
同时,我本人也为自己能写出这样一份文档而自豪(grin)
这是一份技术文档,介绍了 NES 游戏机的硬件信息。但是在
阅读它之前,请保证你懂汇编语言(ASM),最基础的 80x86
要懂嘛……否则本文可能不适合你——如果你不信邪,可以
试试!
需要注意的是,这只是一份基础性的文档,并不涵盖太多太繁
杂的内容,我的目的只是让你对 NES 的硬件体系有个大体的
了解。如果你想更深入地了解 NES,访问下面的网站:
http://nesdev.parodius.com
这份文档的信息是基于 Y0SHi 的 nestech.txt 和 Marat
Fayzullin 的 nes.doc 的,同时也加入了一些我个人的开发
经验,不过不要指望它 100% 精确,现在没有 100% 精确的
NES 资料。
同时,我也要大大地感谢 Y0SHi 和 Marat Fayzullin 两位达
人,没有他们精辟的文章,小的绝不可能写出这份文件,虽然
达人们并不知道小的是谁。
这份文档目前的版本是 0.01 版,只对 NES 的 CPU、内存、
系统概况和 PPU 进行了初步介绍(没时间啊……),不过我
算写得比较详细了……配合已有的资料,相信对大家掌握 NES
应该会有所帮助吧……
后面的部分我有空的时候再继续写……累死了~~~~~~~~~ >_<
大家就把这文章当作连载吧……
如果你对这份文档有什么改进的建议,或者发现什么错误,或
者你想提供新的资料,请联系我!以便我在未来的版本中更新。
如果你要传播我的文档,请保证它的完整性。
关于作者(About The Author...)
===================================
我叫做 Necrosaro,中文名字叫屎王皮萨罗,人称屎王。
年方 18,身高 ≈ 1.72m,体重 > 40kg,黑头发,黄皮肤,
中国人。。。。
我爱好广泛,听音乐,玩游戏,写程序(小工具、模拟器、
小游戏……),看漫画,画漫画,看动画,吃饭,睡觉,等等。
我穷人一个,目前打算找一份工作,挣钱读书!我希望找到一份
程序员的工作,尤其向往游戏机软件的程序开发(最好能上GBA,嘿嘿嘿)。
PC 的话……也可,不过我不懂 VC,DirectX,恐怕出不上什么力。。。
我的程序水平决不亚于老外!!!
另外我希望交一些朋友,有实力的(不管哪个方面,不过最好是
和游戏开发有关的……),我欣赏有真才实干的人!!
请大家和我联系!!
[联系方法:]
我的主页:http://necrosaro.yeah.net
我的邮箱:necrosaro@ynmail.com(首选)
我的QQ:18177243
(note: 我一般晚上 8:00 ~ 10:00 在线)
- Necrosaro
PM 23:70 - 2002/7/31
NES 系统概述(NES Technical Overview)
==========================================
NES 是日本任天堂(Nintendo)公司于上个世纪 80 年代开发的
一款游戏主机,它同时也是此后 10 年里最受欢迎的游戏主机。
NES 在日本/亚洲的名称叫做 FC(famicom,或 family comput-
er),在欧洲叫做 Dandy,在美国叫做 NES(Nintendo Entert-
ainment System,任天堂娱乐系统),在中国,通常被称作红白
机或8位机。它的技术参数如下:
CPU:6502 NMOS 芯片。
直接寻址能力为 64KB,数据处理能力为 8位。
内建一块特殊的音频处理器。
RAM:NES 本体预留 8KB 的 RAM 空间,
但实际的物理 RAM 仅 2KB。
PPU:NES 特有的图形处理芯片,内建 10KB 显示内存。
支持垂直/平行镜像、垂直/平行滚屏,最大发色数
64 色。同屏最大发色数 26 色(也有说法是 25 色,
去掉了透明色)。支持 8x8 tile,最多支持 64 个
8x8 或 8x16 精灵。显示分辨率 = 256x240。
pAPU:NES 的音频处理器。因为是设计在 CPU 内部,所以
叫做 pAPU(pseudo Audio Processing Unit)。包
含 2 个方块波声道,1 个三角波声道,1 个杂音声
道以及 1 个数字声道。
Input:输入设备。主要是手柄,后来也出现了激光枪(Za-
pper)以及各种形形色色的新设备。
Mapper:内存映射设备。这并非 NES 本体所有,而是包含在
许多游戏卡内部,以扩充 NES 的性能。
SRAM:Save RAM,也叫 Battery-Backed RAM,即电池储存
RAM。固化在某些游戏卡上的芯片,关机后由电池供
电,信息不会丢失。多用来保存 RPG 类游戏的档案
资料。NES 本体为 SRAM 预留了 8KB 的地址空间(
实际多数游戏的 SRAM 大小也是 8KB)。
NES 内存布局(NES Memory Map)
==================================
NES 包含 3 种内存。
1 种是系统内存,可被 CPU 直接访问。
1 种是显示内存,存在于 PPU 内部,CPU 只能通过操作 PPU
寄存器间接访问这块内存。
1 种是 OAM 内存(精灵属性内存),同样存在于 PPU 内部,
CPU 可通过操作 PPU 寄存器或者利用 DMA 间接访问它。
NES 系统内存布局:
------------------
+----------+----------+---------------------------+
| 起始地址 | 结束地址 | 说明 |
+----------+----------+---------------------------+
|$0000 |$07FF | NES 本体所包含的 2KB RAM。|(2 KB)
+----------+----------+---------------------------+
|$0800 |$0FFF | 这 3 个区域都是 $0000 - |(2 KB)
+----------+----------+ $07FF 的镜像。换句话说,|
|$1000 |$17FF | 对它们的操作(读/写)实际 |(2 KB)
+----------+----------+ 就是对 $0000 - $07FF 的操 |
|$1800 |$1FFF | 作。比如:读取 $08AB 的内 |(2 KB)
| | | 容实际等于读取 $00AB 的内 |
| | | 容。而向 $15CC 写数据实际 |
| | | 等于向 $05CC 写数据。 |
| | | 这 3 块不是物理的 RAM, |
| | | 它们都是镜像(Mirror)!|
+----------+----------+---------------------------+
|$2000 |$2007 | PPU 寄存器。CPU 通过对这|(8 字节)
| | | 片区域的操作来实现对 PPU|
| | | 的控制。 |
+----------+----------+---------------------------+
|$2008 |$3FFF | PPU 寄存器的镜像。 |(上面 8 字节)
| | | $2008 = $2000, |(的 1024 次镜像)
| | | $2009 = $2001, |(连同上面 8 字节)
| | | .... |(共 8 KB)
| | | $200F = $2007, |
| | | $2010 = $2000, |
| | | $2011 = $2001, |
| | | .... |
+----------+----------+---------------------------+
|$4000 |$4013 | pAPU 寄存器。CPU 通过对这 |(20 字节)
| | | 片区域的操作来实现对 pAPU |
| | | 的控制。 |
+----------+----------+---------------------------+
|$4014 |$4014 | OAM DMA 寄存器。 |(1 字节)
| | | 通过操作这个字节,可将 |
| | | OAM(精灵属性内存)的内容 |
| | | 传送到指定的系统内存中。|
+----------+----------+---------------------------+
|$4015 |$4015 | pAPU 状态寄存器。 |(1 字节)
| | | 各声道的状态,etc.... |
+----------+----------+---------------------------+
|$4016 |$4017 | 输入设备状态寄存器。 |(2 字节)
| | | 游戏机的输入设备(例如手柄|
| | | 就通过这两个寄存器访问。|
+----------+----------+---------------------------+
|$4018 |$401F | 未用??(未知) |(8 字节)
+----------+----------+---------------------------+
|$4020 |$5FFF | 扩展 ROM。 |(8 KB - 32 字节)
| | | 某些有特殊处理芯片的游戏|
| | | 卡利用了这块空间。 |
+----------+----------+---------------------------+
|$6000 |$7FFF | SRAM(电池储存 RAM)。 |(4 KB)
| | | 注意这块 RAM 不存在于 NES |
| | | 本体,而是在某些游戏卡(|
| | | 如 RPG 游戏卡)内部。 |
+----------+----------+---------------------------+
|$8000 |$FFFF | 32K 程序代码 ROM。 |(32 KB)
| | | 存在于游戏卡内部的 ROM,|
| | | 内容为游戏程序代码。 |
+----------+----------+---------------------------+
NES 显示内存布局:
------------------
+----------+----------+---------------------------+
| 起始地址 | 结束地址 | 说明 |
+----------+----------+---------------------------+
|$0000 |$0FFF | Pattern 表 #0 |(4 KB)
+----------+----------+---------------------------+
|$1000 |$1FFF | Pattern 表 #1 |(4 KB)
+----------+----------+---------------------------+
|$2000 |$23BF | Name 表 #0 |(960 字节)
+----------+----------+---------------------------+
|$23C0 |$23FF | Attribute 表 #0 |(64 字节)
+----------+----------+---------------------------+
|$2400 |$27BF | Name 表 #1 |(960 字节)
+----------+----------+---------------------------+
|$27C0 |$27FF | Attribute 表 #1 |(64 字节)
+----------+----------+---------------------------+
|$2800 |$2BBF | Name 表 #2 |(960 字节)
+----------+----------+---------------------------+
|$2BC0 |$2BFF | Attribute 表 #2 |(64 字节)
+----------+----------+---------------------------+
|$2C00 |$2FBF | Name 表 #3 |(960 字节)
+----------+----------+---------------------------+
|$2FC0 |$2FFF | Attribute 表 #3 |(64 字节)
+----------+----------+---------------------------+
|$3000 |$3EFF | $2000 - $2EFF 的镜像 |(4 KB)
+----------+----------+---------------------------+
|$3F00 |$3F0F | 背景调色板 |(16 字节)
+----------+----------+---------------------------+
|$3F10 |$3F1F | 精灵调色板 |(16 字节)
+----------+----------+---------------------------+
|$3F20 |$3FFF | 调色板镜像。 |(上面的)
| | | |(背景调色板)
| | | $3F20 - $3F2F:背景调色板 |(精灵调色板)
| | | 的镜像。 |(的 7 次镜像)
| | | $3F30 - $3F3F:精灵调色板 |(共 224 字节)
| | | 的镜像。 |
| | | $3F40 - $3F4F:背景调色板 |(若连同上面本身的)
| | | 的镜像。 |(两个调色板)
| | | $3F50 - $3F5F:精灵调色板 |(共 256 字节)
| | | 的镜像。 |
| | | ...... |
+----------+----------+---------------------------+
|$4000 |$FFFF | $0000 - $3FFF 的镜像。 |
| | | |
| | | $4000 - $7FFF = |
| | | $0000 - $3FFF。 |
| | | |
| | | $8000 - $BFFF = |
| | | $0000 - $3FFF。 |
| | | |
| | | $C000 - $FFFF = |
| | | $0000 - $3FFF。 |
+----------+----------+---------------------------+
NES CPU 信息(NES CPU Description)
=======================================
NES 使用一块定制的 6502 CPU,主要在原 6502 的基础上加入
了音频处理能力。NES 的 6502 芯片 *只有* 151 个操作码,
换句话说,很多文档中介绍的所谓非公布(Undocumented)操
作码都是不正确的,那么如果 NES 执行到不支持的操作码时会
发生什么事呢?hmmm.....谁知道?:-)
NTSC 制式的 NES,其 CPU 的运行频率为 1.7897725MHz;
PAL 制式的 NES,其 CPU 的运行频率为 1.773447MHz。
NES 的 6502 不支持十进制模式(Decimal Mode),也就是说,
即使 CPU 的“D”标志被设定为 1,在执行加/减指令后结果仍
不会被调整为二进制编码的十进制(BCD)。
NES 的 6502 在对于操作码 6C(JMP Indirect)的处理上有一
个 bug:如果操作数的低字节 = $FF,这个指令就无法正常执
行,比如:
$AB00: $12
$ABFF: $34
$AC00: $56
指令:JMP ($ABFF)
理论上,这条指令被执行后,程序流程将跳转到 $5634。
但是 NES 的 6502 在执行这条指令时,无法正常地读取操作
数,它在读取高字节时,页面不会进行处理。换句话说,如果
高低字节所在的页面不同(比如上面:1 个在 $AB,1 个在
$AC),它将在低字节所在的页面($AB)读取高字节,那么,
本来应该到 $AC00 读取高字节的,结果就变成了 $AB00,最
后的跳转地址就成了 $1234 而不是 $5634。
NES 有 3 个中断:NMI,Reset,BRK/IRQ。
NMI 发生在屏幕刷新期间。
当 PPU 完成一帧画面的显示后,产生该中断。
注意这个中断可通过修改 PPU 控制寄存器屏蔽掉。
(纳闷~那为什么叫做 NMI:Non-Maskable-Interrupt >_<)
Reset 发生在接通电源或按下游戏机 RESET 按钮时。
Reset 的中断向量实际上就是游戏程序的入口。
BRK/IRQ 是程序中断。在执行 BRK 指令后产生该中断。
另外 pAPU 和一些具有特殊功能的游戏卡也能产生该中断。
它们的 16 位中断向量储存在(低字节在前,高字节在后):
NMI: $FFFA,$FFFB
Reset:$FFFC,$FFFD
BRK/IRQ:$FFFE,$FFFF
其中,Reset 的中断优先级最高,NMI 其次,BRK/IRQ 最低。
产生 BRK/IRQ 中断的情况有两种:执行 BRK 指令;硬件调用。
那么,中断处理程序如何判断是谁调用中断呢?
如果是执行 BRK 产生该中断,那么压入堆栈的状态寄存器值,
其 B 标志 = 1;如果是硬件调用而产生该中断,那么压入堆
栈的状态寄存器值,其 B 标志 = 0。
因此可通过下面的代码进行判断:
C134: PLA ; 将堆栈中状态寄存器的值
; 读入累加器。
C135: PHA ; 还原堆栈指针。
C136: AND #$10 ; 检查状态寄存器的第4位
(B 标志位)。
C138: BNE is_BRK_opcode ; 如果 = 1,表示是由 BRK
; 指令所产生的。
什么?操作码资料?well...well.....自己学吧,我并不打算
在这份文档中教你怎么写 6502 汇编程序……anyway, 如果你
懂 PC 汇编,这个应该难不倒你:so, learn it yourself!
NES PPU 信息(NES PPU Description)
=======================================
PPU(屁屁油),也就是 Picture Processing Unit,NES 的
图形处理芯片。这是 NES 中最重要的设备之一,同时也是个
挺复杂的东西。
PPU 包含一块 10KB 左右的 RAM,叫做 VRAM(Video RAM),
即显示内存。以及一块 256 字节的 OAM(精灵属性内存)。
这两块 RAM 基本上描述了显示在屏幕上的一切图像信息。
CPU 不能直接访问 PPU 内部的 RAM,只能通过 PPU 映射在系
统内存特别位置的寄存器间接访问它们。
同时,CPU 要想控制 PPU,也只能通过这些寄存器实现。
在 PPU 中,有 3 个很重要的表,描述了当前显示的图像:
Pattern 表、Name 表、以及 Attribute 表。
在说明这些表的用途前,我们先来看看 PPU 储存图形元素的
方式。
在 PC 中,通常最基本的图形元素是“像素”(pixel),说
白了就是屏幕上的一个小点。而在 NES 的 PPU 中,最基本的
图形元素是“Tile”。
Tile 是什么?
它是一个由 8x8 像素组成的方块。当然每个 Tile 也就描述
了一块 8x8 的图像。而整个屏幕又由 32x30 个 Tile 组成。
由此也可计算出 NES 的屏幕分辨率 = 256x240 像素。
NES 一共支持 512 个 Tile,它们的图像(点阵)信息储存在
Pattern 表中。而 Name 表用来描述显示在屏幕上的图像,在
这个表中储存的实际上是 Tile 号。PPU 从 Name 表中读取
Tile 号,然后根据 Tile 号到 Pattern 表中获取图像的点阵
信息,再根据这些点阵信息综合 Attribute 表在屏幕上画图。
(很复杂?well...可能是我说得不太清楚,read on!)
简单来说,把整个屏幕比作一面墙,而 Tile 就是组成这面墙
的大小相等的砖块。
为什么要使用 Tile?
由于电子游戏画面中,通常会出现很多重复的部分,如果一一
描述它们的点阵信息,实际上是一种空间上的浪费,而如果用
Tile,就可以有效地避免这个问题。
(不管你看没看懂,read on! ;-))
-=[ Pattern 表 ]=-
NES 的 PPU 一共有 2 个 Pattern 表:
Pattern 表 #0:位于显示内存 $0000 - $0FFF,共 $1000 字节。(4 KB)
Pattern 表 #1:位于显示内存 $1000 - $1FFF,共 $1000 字节。(4 KB)
Pattern 表中,储存着 Tile 的点阵信息(如果你是
romhacker,那么我告诉你:Pattern 表中,储存着字库)。
每个 Tile 占用 16 字节,它的格式如下:
前 8 个字节:
每个字节由 8 个二进制位组成,每个位描述一个像素颜色值的
第 0 位。一个字节(8个位)恰好描述一行像素颜色值的第 0
位。8个字节描述一个 Tile 所有像素的颜色值第 0 位。
后 8 个字节:
每个字节由 8 个二进制位组成,每个位描述一个像素颜色值的
第 1 位。一个字节(8个位)恰好描述一行像素颜色值的第 1
位。8个字节描述一个 Tile 所有像素的颜色值第 1 位。
由此可见,每个 Tile 所表现的色彩范围是 2 位。
实际上,这只是最终显示在屏幕上图像色彩的 *低 2 位*。
(BTW:高 2 位在 Attribute 表中,后面讲)。
具体的储存方式,我们用一个例子来说明:
地址 Pattern表内容
------- ---------------
字节 1: %00010000 = $10 --+
字节 2: %00000000 = $00 |
字节 3: %01000100 = $44 |
字节 4: %00000000 = $00 +-- 第 0 位
字节 5: %11111110 = $FE |
字节 6: %00000000 = $00 |
字节 7: %10000010 = $82 |
字节 8: %00000000 = $00 --+
字节 9: %00000000 = $00 --+
字节10: %00101000 = $28 |
字节11: %01000100 = $44 |
字节12: %10000010 = $82 +-- 第 1 位
字节13: %00000000 = $00 |
字节14: %10000010 = $82 |
字节15: %10000010 = $82 |
字节16: %00000000 = $00 --+
实际图像
--------
...1.... 注:为便于观察,这里用 . 代表 0。
..2.2... 数字表示图像相应位置的颜色值。
.3...3..
2.....2.
1111111.
2.....2.
3.....3.
........
可见,这个 Tile 所描述的图像是一个“A”字。每个像素
的颜色值如上图所示。
它储存在 Pattern 表中的 16 字节点阵信息按顺序依次是:
$10,$00,$44,$00,$FE,$00,$82,$00,
$00,$28,$44,$82,$00,$82,$82,$00。
在 Pattern 表中,每个 Tile 占 16 字节,由于每个 Pattern
表的大小是 $1000 字节,所以,每个 Pattern 表可储存
$1000 / 16 = 256 个 Tile 的点阵信息。两个 Pattern 表
一共可储存 512 个 Tile 的点阵信息。
-=[ Name 表 ]=-
Name 表描述的是实际显示在屏幕上的图像。但和 PC 的显存不
同的是,PC 显存中保存的是屏幕上每个像素的颜色信息,而
Name 表中保存的是 Tile 号。(如果你是 romhacker,那么我
告诉你:Name 表中,储存着脚本)
PPU 一共支持 4 个 Name 表,但 PPU 本体的显存空间实际上只
允许存在 2 个 Name 表。多数情况下,另外 2 个是前 2 个的
镜像(这个比较复杂,后面讲)。
每个 Name 表将屏幕定义为一块 32x30 个 Tile 的区域。其中
用一个字节描述一个 Tile 号,所以,每个 Name 表的大小就是
32x30 = $3C0 字节。
由于用一个字节描述一个 Tile 号,所以 Tile 号的取值范围可
以是 0 - 255 共 256 个。PPU 在画图时,首先读取 Tile 号,
然后按照 Tile 号到指定的 Pattern 表中读取点阵信息。
注意每个 Pattern 表包含 256 个 Tile,位于 $0000 - $000F
的是 0 号 Tile,$0010 - $001F 为 1 号 Tile,$0020 -
$002F 为 2 号 Tile,以此类推……
由于 Pattern 表一共有 2 个,所以具体到哪个 Pattern 表中
读取点阵信息,这取决于 PPU 寄存器的设置,后面将有介绍。
-=[ Attribute 表 ]=-
前面曾提到,Attribute 表保存着屏幕图像颜色信息的高 2 位。
是的,综合 Name 表和 Pattern 表所输出的图像,其颜色是 2
位的。而真正显示在屏幕上的颜色,还应该综合 Attribute 表
中所描述的高 2 位。
Attribute 表也有 4 个,同样由于显存空间,允许存在的仅有
2 个。说白了,它和 Name 表一一对应。
每个 Attribute 表的大小是 $40 字节。
Attribute 表中,每个字节(姑且称为 Attribute 字节)描述
了屏幕上 4x4 个 Tile (姑且把这个 4x4 的 Tile 区域称为
“描述区”)的高 2 位,具体定义如下:
Attribute 字节位 定义
----------------------------------------------------
0 - 1 描述区中左上角 2x2 个 Tile 的高 2 位。
2 - 3 描述区中右上角 2x2 个 Tile 的高 2 位。
4 - 5 描述区中左下角 2x2 个 Tile 的高 2 位。
6 - 7 描述区中右上角 2x2 个 Tile 的高 2 位。
举个例子,Attribute 表中的第一个字节描述的就是屏幕上最
左上角 4x4 Tile(相当于一个 32x32 像素的方块区)的颜色
信息的高 2 位。
屏幕上一共有 8x8 个“描述区”(分辨率相当于 256x256,不
过由于 NES 的分辨率只有 256x240,所以最下方的 16 行像素
相当于是浪费了)。
-=[ Name 表和 Attribute 表镜像 ]=-
前面说到,PPU 一共支持 4 个 Name/Attribute 表(位于显存
$2000 - $2FFF 共 $1000 字节),但显存空间实际只够容下 2
个 Name/Attribute 表(只有 $800 字节的实际空间)。这 4
个 Name/Attribute 表每 2 个共享一块空间($400 字节)。
那么到底哪 2 个共享哪一块空间呢?分两种情况,“垂直镜像”
和“平行镜像”。但也有例外的情况,比如“单屏镜像”和
“四屏布局”。
具体是怎样的呢?总的来说,有 4 种情况:
1、垂直镜像(Vertical Mirror)
2、平行镜像(Horizontal Mirror)
3、单屏镜像(Single Screen)
4、四屏布局(4-Screen layout)
1 - 垂直镜像
------------
在这种情况下,
Name/Attribute 表 #0 和 Name/Attribute 表 #2 使用
PPU 内部前 $400 字节的空间。
Name/Attribute 表 #1 和 Name/Attribute 表 #3 使用
PPU 内部后 $400 字节的空间。
2 - 平行镜像
------------
在这种情况下,
Name/Attribute 表 #0 和 Name/Attribute 表 #1 使用
PPU 内部前 $400 字节的空间。
Name/Attribute 表 #2 和 Name/Attribute 表 #3 使用
PPU 内部后 $400 字节的空间。
3 - 单屏镜像
------------
在这种情况下,4 个 Name/Attribute 表都共享同一个空间,
具体是哪个呢?这要视情况而定。
4 - 四屏布局
------------
在这种情况下,4 个 Name/Attribute 表每个都拥有物理的
空间,也就是说每个都是实际存在的。你也许会问:前面不
是说显存中没有空间容下另外 2 个 Name/Attribute 表吗?
当然,所以,这另外 2 个 Name/Attribute 表的空间一般
来自特殊的游戏卡带内部(当然由于这个原因,这种游戏卡
的售价比一般的游戏卡都要贵一些啰 ^o^ )。
Name/Attribute 表 #0:使用 PPU 内部空间前 $400 字节。
Name/Attribute 表 #1:使用 PPU 内部空间后 $400 字节。
Name/Attribute 表 #2:使用游戏卡提供的空间。
Name/Attribute 表 #3:使用游戏卡提供的空间。
前面说到的 3 个表所描述的图像,实际上仅仅是 NES 的背
景层,NES 一共有 2 个层,除了背景层,还有一个是“精灵
层”(Sprite Layer)。
So WHAT is a sprite? 精灵是什么东西?
所谓精灵,就是屏幕上自由活动的图块。例如:游戏中玩家所
操纵的角色。
NES 的 PPU 拥有一块 256 字节的精灵属性内存(OAM,Object
Attributes Memory,也叫 SRAM,Sprite RAM)。
NES 的 PPU 一共可处理 64 个 8x8 或 8x16 大小的精灵。
这 64 个精灵的属性(坐标,标志,Tile 号)被均匀地储存
在 256 字节的 OAM 中,每个精灵占用 256 / 64 = 4 个字节。
每个精灵的 4 字节属性内容解释如下:
+--------+-------------------------------------------+
| 字节 # | 说明 |
+--------+-------------------------------------------+
| 字节 1 | 精灵的 Y 坐标 - 1。 |
| | |
| | 这个字节 = 精灵所在屏幕位置的 Y 坐标 - 1|
| | 注意坐标是以像素为基准而不是 Tile。 |
+--------+-------------------------------------------+
| 字节 2 | 精灵的 Tile 号。 |
| | |
| | 精灵的图像也是用 Tile 表示的。这个字节表|
| | 示该精灵使用哪个 Tile,注意 Tile 所对应的 |
| | 实际图像储存在 Pattern 表中。 |
+--------+-------------------------------------------+
| 字节 3 | 精灵的标志。 |
| | |
| | 第 0-1 位:精灵色彩值的高 2 位。 |
| | 注意背景是通过 Attribute 表储|
| | 存高 2 位的。 |
| | |
| | 第 2-4 位:未用。 |
| | |
| | 第 5 位:精灵优先级。如果 = 0,则将精灵显 |
| | 示在背景层前面,如果 = 1,则将精 |
| | 灵显示在背景层后面。 |
| | |
| | 第 6 位:平行翻转。如果 = 0,则精灵按正常 |
| | 显示。如果 = 1,则将精灵的 Tile|
| | 平行翻转后显示。就好像一块透明玻 |
| | 璃,你到背面看写在正面的字一样。 |
| | |
| | 第 7 位:垂直翻转。如果 = 0,则精灵按正常 |
| | 显示。如果 = 1,则将精灵的 Tile|
| | 垂直翻转后显示。也就是说,第 1 |
| | 行像素显示在第 8 行,第 8 行显示 |
| | 在第 1 行,第 2 行像素显示在第 7 |
| | 行…… |
+--------+-------------------------------------------+
| 字节 4 | 精灵的 X 坐标。 |
+--------+-------------------------------------------+
注意在 OAM 中,每 4 个字节就是一个精灵的属性,$00 - $03
是 0 号精灵,$04 - $07 是 1 号精灵,$08 - $0B 是 2 号精
灵……每个精灵的 4 字节属性均是按照上面的格式储存的。
同背景层一样,精灵的图像信息也是以 Tile 的形式来体现的。
同背景层一样,精灵也通过 PPU 的寄存器来决定从哪个 Pattern
表中读取 Tile 的点阵信息。
NES 的 PPU 支持两种尺寸的精灵,8x8 和 8x16。
8x8 精灵非常简单,它只包含一个 Tile,精灵属性中明确说明
了这个 Tile 的实际图像到底从何而来。
而 8x16 精灵就有所不同了,8x16 的精灵,由上下两部分组成,
每个部分均是一个 Tile,换句话说,8x16 的精灵一共包含两
个 Tile,一个在上一个在下。可是,精灵属性中只有一个 Tile
字节,那么另一个 Tile 号到底是多少呢?
PPU 做出这样的规定:对于 8x16 的精灵,将 Tile 号第 0 位
作为标志位,表示 Tile 来自哪个 Pattern 表,如果 = 0,则
来自 Pattern 表 #0,如果 = 1,则来自 Pattern 表 #1。换
句话说,PPU 寄存器则无法决定 8x16 精灵的 Tile 来自哪个
Pattern 表。而精灵属性中的 Tile 号,其 1 - 7 位则表示精
灵两个 Tile 的 Tile 号的 1 - 7 位,对于第 1 位,上方
Tile = 0,下方 Tile = 1。
举个例子,比如一个 8x16 精灵,其 Tile 号 = $B9,那么:
$B9 = %10111001
| ||
| |+-- 第 0 位 = 1,Tile 来自 Pattern 表 #1。
| |
| +--- 上方 Tile = %10111000 = $B8。
|
+--------- 下方 Tile = %10111001 = $B9。
PPU 规定:如果某个像素的低 2 位 = 0,则这个像素透明。
这是怎么回事呢?前面已经说过,每个 Tile 包含一块 8x8
像素区域的颜色值低 2 位,换句话说,每个 Tile 包含 8x8
个像素的低 2 位。那么,如果其中某一个像素的低 2 位值
= 0,那么 PPU 在绘制这个 Tile 时,这个像素就不会被画
在屏幕上。
举个例子,比方说上面出现的 A:
...1....
..2.2...
.3...3..
2.....2.
1111111.
2.....2.
3.....3.
........
图中用“.”表示的像素,就是透明像素,因为其颜色值的低
2 位 = 0。
-----------------------结束-------------------------------------------
回到正题,这次整理代码时把NES作为一个元件剖离了出来,使得移植比较方便,而不仅仅限于DE系列的开发板
entity nestop is
port (
cpuclk : in std_logic; ---usually 1/6 of ppuclk
ppuclk : in std_logic; --- expect 25Mhz
memclk : in std_logic; --- expect 50Mhz
reset : in std_logic;
hs : out std_logic;
vs : out std_logic;
r : out std_logic_vector(3 downto 0);
g : out std_logic_vector(3 downto 0);
b : out std_logic_vector(3 downto 0);
joy1 : in std_logic_vector(7 downto 0);
joy2 : in std_logic_vector(7 downto 0);
sram_mapper : in std_logic_vector(3 downto 0);
isdouble : in std_logic ---256x240 or 512x480 ?
);
end nestop;
其中joy1和joy2参照下面的图片,可以直接使用FC手柄,也可以通过其他方式实现
http://cache.ourdev.cn/bbs_upload489681/files_9/ourdev_241140.jpg
cpuclk的话通过ppuclk分频得到,在一个时序严格的NES游戏机里面他们有下面的时序关系
NTSC制式 PAL制式
基频(Base clock) 21477270.0Hz 21281364.0Hz
CPU主频(Cpu clock) 1789772.5Hz 1773447.0Hz
总扫描线数(Total scanlines) 262 312
扫描线总周期(Scanline total cycles) 1364(15.75KHz) 1362(15.625KHz)
水平扫描周期(H-Draw cycles) 1024 1024
水平空白周期(H-Blank cycles) 340 338
结束周期(End cycles) 4 2
帧周期(Frame cycles) 1364*262 1362*312
帧IRQ周期(FrameIRQ cycles) 29830 35469
帧率(Frame rate) 60(59.94Hz) 50Hz
帧时间(Frame period) 1000.0/60.0(ms) 1000.0/50.0(ms)
不过由于设计的困难,目前还没有实现严格的时序,因此这里的cpuclk可能得随着游戏的不同在3分频,4分频,。。。7分频,8分频等之间变动,可以把分频大小用开关引出,对于不同的游戏调整即可。
压缩包里面包含的游戏有:
坦克大战
超级玛丽
水管玛丽
大金刚3
吃豆
弹射球
猫捉老鼠
三只小猪
中国象棋
以及两个专门用于NES测试的游戏
游戏可以自己从NES ROM中提取,目前支持的卡带大小为24K和40K的,24K游戏提取的时候请将ROM取重复一遍即可(什么,你问我魂斗罗能不能玩??我也想玩啊,但遗憾的是它是个128K的游戏,目前还不支持)
http://cache.amobbs.com/bbs_upload782111/files_28/ourdev_551213.JPG
(原文件名:1.JPG)
http://cache.amobbs.com/bbs_upload782111/files_28/ourdev_551214.JPG
(原文件名:2.JPG)
点击此处下载 ourdev_551215.rar(文件大小:1.35M) (原文件名:nestop.rar) 顶楼主;谢谢分享。
正好,手上有个DE2。 这是不是要嵌入软核? 感兴趣! 呵呵,顺便把关于NES的更详细的资料一块上传了吧,方便大家更详细的了解其原理。这些东西比较冷门,不是很好找。
小霸王游戏机.rarourdev_551216.rar(文件大小:1.29M) (原文件名:小霸王游戏机.rar) 回复【2楼】Yan.hong.yu
这是不是要嵌入软核?
-----------------------------------------------------------------------
里面用到的6502CPu和NES专用的PPU就可以说是软核
这个不是模拟的,是直接硬件实现 学习了,ppu显示方式。 很好的资料啊 标记一下,谢谢楼主 学习 mark 强帖 mark 不懂,但资料确实很好。 mark 哈哈,正有兴趣研究下。谢谢楼主啊。 楼主强 收藏 mark 收藏! 收藏 很好的资料!谢谢! 回复【4楼】chenxiao07
呵呵,顺便把关于nes的更详细的资料一块上传了吧,方便大家更详细的了解其原理。这些东西比较冷门,不是很好找。
小霸王游戏机.rar (原文件名:小霸王游戏机.rar)
-----------------------------------------------------------------------
一直在关注你这个项目,从你上回发布我也默默的在搞这个,不过我的目标是使用LCD作为显示输出,而整体资源能限定在EP2C5或EP2C8中
因为我手里只有EP2C8的板子,没有DE2那么多的资源那
现在LCD功能已经做好,在你的程序稍作修改的基础上也能运行起来了,但是还有不少问题
我的下一目标是做一个硬件总线操作和时序上尽量与6528接近的PPU,不使用双端口的RAM,ROM实现ram,vram,vrom
在查找精确的PPU时序说明的时候,发现了一篇好文档,看了之后有醍醐灌顶的感觉啊,绝对不可多得
尤其是PPU部分,绝对是最精确的描述,不敢独享啊
点击此处下载 ourdev_551332.pdf(文件大小:615K) (原文件名:NES Specifications.pdf) mark 楼主你很牛皮 mark mark 儿时的梦想啊。 不错不错。。。 MARK 回复【23楼】sun_gan
回复【4楼】chenxiao07
呵呵,顺便把关于nes的更详细的资料一块上传了吧,方便大家更详细的了解其原理。这些东西比较冷门,不是很好找。
小霸王游戏机.rar (原文件名:小霸王游戏机.rar)
-----------------------------------------------------------------------
一直在关注你这个项目,从你上回发布我也默默的在搞这个,不过我的目标是使用lcd作为显示输出,而整体资源能限定在ep2c5或ep2c8中
因为我手里只有ep2c8的板子,没有de2那么多的资源那
现在lcd功能已经做好,在你的程序稍作修改的基础上也能运行起来了,但是还有不少问题
我的下一目标是做一个硬件总线操作和时序上尽量与6528接近的ppu,不使用双端口的ram,rom实现ram......
-----------------------------------------------------------------------
你太厉害了~~我觉得我写的代码糟糕得自己都不想去碰它了,没想到你居然还成功移植了。。。。
呵呵,话说回来,要弄到ep2c8上问题应该不大,LE够用,只要把rom以及vrom换成外部sdram就可以了,不过vrom需要改成单端口
但2c5就够呛了,据说那上面跑个nios都难。
至于时序的问题你那篇文章相当详细啊,可惜写之前没看过,现在又懒得去改了。呵呵,如果你做出来了,那么不妨也开源分享,那会是一件非常赞的事~~~ ppu 部分是楼主写的吗?想了解一下颜色是怎么得出来的,你那个r/g/b的表是怎么回事? Port 2001h/Bit7-5 allow to adjust the palette, eg. with setting 001b the whole picture becomes more green.
000b Normal
001b Green
010b Brown
100b Blue
另外,上面这三位好像没有实现,是不是没有游戏用这三位? mark、 我这里有块3C40的板子估计是够了 回复【31楼】chenxiao07
-----------------------------------------------------------------------
你太厉害了~~我觉得我写的代码糟糕得自己都不想去碰它了,没想到你居然还成功移植了。。。。
呵呵,话说回来,要弄到ep2c8上问题应该不大,LE够用,只要把rom以及vrom换成外部sdram就可以了,不过vrom需要改成单端口
但2c5就够呛了,据说那上面跑个nios都难。
至于时序的问题你那篇文章相当详细啊,可惜写之前没看过,现在又懒得去改了。呵呵,如果你做出来了,那么不妨也开源分享,那会是一件非常赞的事~~~
-----------------------------------------------------------------------
你太谦虚了,正因为有你的代码在,我才得以用我的想法一部分一部分的替换改造,否则我的想法根本无从着手啊
我把原来VGA用的LineBuf给去掉之后,基本上资源占用可以到6000~7000LE的水平
至于rom,vrom么肯定是不够了,我弄了个小蜜蜂的游戏,它需要8K的程序rom和8K的vrom,但是ep2c8的M4K块已经不够了
所以我只留了8k的程序rom和vrom的背景页,卡通页用的是背景页的镜像才勉强装下,一个M4K都不剩了,当然执行时一部分的卡通是花的
后面如果调试通过后我是准备用外部的sdram的,但是这就涉及到还要搞一个游戏rom动态载入了,唉,莫非还要加个nios?
现在ppu部分正在努力的搞,搞好一定分享 回复【33楼】dickhou
port 2001h/bit7-5 allow to adjust the palette, eg. with setting 001b the whole picture becomes more green.
000b normal
001b green
010b brown
100b blue
另外,上面这三位好像没有实现,是不是没有游戏用这三位?
-----------------------------------------------------------------------
应该不是没有游戏用到这几位吧,不过这个并不是什么很要紧的功能,完全可以以后再实现吧 学习,楼主强人 port 2001h/bit7-5 allow to adjust the palette, eg. with setting 001b the whole picture becomes more green.
000b normal
001b green
010b brown
100b blue
另外,上面这三位好像没有实现,是不是没有游戏用这三位?
-----------------------------------------------------------------------
应该不是没有游戏用到这几位吧,不过这个并不是什么很要紧的功能,完全可以以后再实现吧
我想了解一下具体应该怎么实现?例如值为001,使图像更绿一些,难道是整个颜色上加上一定比例的绿色? 应该只是控制底背景色,也就是颜色编码为0时的底背景色
彩色模式和黑白模式时的操作有些差别,具体可以参照下面文档的23页
点击此处下载 ourdev_551497.pdf(文件大小:210K) (原文件名:任天堂产品系统文件.pdf)
你也可以下一个开源的nes模拟器,看看他的源代码中是怎样实现的 真牛比 楼主真是强悍! 占位置! 佩服~~ 学习了 关注中! 顶顶 顶顶 好东西哟~!!!!!!!!!!!1 顶啊,值得学习 好资料,mark 好东西..记号! mark 记录 mark 我移植的NES,基于qq2440,求助
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4104963&bbs_page_no=1&bbs_id=1032 mark mark!~ DE1能实现吗 值得学习 mark 有没可能实现 玩记录卡ROM?? http://cache.amobbs.com/bbs_upload782111/files_30/ourdev_563651V2C0GX.JPG
(原文件名:5401.JPG) ..经典,小时候经常玩的游戏 2层楼上,这是你做的板子?用什么器件实现的? 回复【66楼】gahang
-----------------------------------------------------------------------
http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=4104963&bbs_page_no=1&bbs_id=1032
QQ2440 收藏 MARK 赞一个,楼主很强大!! mark mark 学习了哦 很感兴趣,mark 强大的楼主! 强大的楼主啊 mark mark mark 太猛了,是不是也必须要这么猛的硬件才能跑起来。 mark mark mark 整个任天堂系统中就属PPU和APU这两快最难懂 厉害啊 厉害啊 强人啊! 强人,顶………… mark 标记下 好强啊!!! MARK 强人 mark mark 学习了 mark! mark!!! mark 厉害 mark