任天堂产品系统文件.docx
《任天堂产品系统文件.docx》由会员分享,可在线阅读,更多相关《任天堂产品系统文件.docx(47页珍藏版)》请在冰豆网上搜索。
任天堂产品系统文件
Lelewaswrittenin2021
任天堂产品系统文件
任天堂产品系统文件
(译、编2002/10/10)
_____________________________________________________________________
1、系统简介
任天堂主机由6502处理器和一个特制的图形处理器组成。
CPU是6502,而不是传言中的65C02(CMOS)。
PPU的显存是和CPU的内存是分离的,可以通过对特殊端口的读/写来操作。
卡带可能包含的内容有位于处理器地址$8000-$FFFF的ROM,和位于PPU地址$0000-$1FFF的VROM。
由于NES只有2K的RAM,因此变量的可用的变量空间只有从$0000到$07FF共8个页面。
在开机之后RAM和VRAM中的内容是0,但是注意:
复位并不改变其中的内容。
在更小的卡带,比如只有16KB的ROM,它占有$C000-$FFFF,而$8000-$BFFF的空间是不用的。
那些大于32KB的卡带,它被特殊的电路分页到一定的地址空间。
一些卡带在$6000-$7FFF有SRAM,那是电池存储的位置。
卡带VROM被用来做图案表(例如Tile表,角色发生器等等)。
通常的数量是8KB,包含两个图案表。
大于8KB的VROM被特殊的电路分页到一定的地址空间。
内部的VRAM在PPU内存里定位于$2000-$3FFF,它用来存储命名表(例如屏幕缓冲)。
虽然PPU支持4个命名表,但只能支持两个的存放空间。
另外的两个是开始两个的镜像。
NES共有154条指令。
在本文本里,你将遇到如下形式的符号:
“Dn"(5位,3位,等等)。
位是按从最低位(0位)到最高位(7位)。
所有的十六进制都在前面加上一个美圆符号($)($2002,$4026,等等)是在6502处理器汇编里常用的符号,二进制前面加上一个百分号%。
2、缩写表
NES
任天堂娱乐系统
Famicom
任天堂家用计算机,即FC
FDS
任天堂磁碟机系统
CPU
中央处理器,NES使用一个定制的6502(NMOS)芯片,有些型号为6527
PPU
图像处理器,用来处理背景,精灵和其他图像特性,通常为6538
APU
声音处理器,集合在CPU内部,包含4个模拟通道和1个数字通道
MMC
ROM和VROM的扩容控制,用来控制访问超过6502限制的64K地址,同样,也可以扩容VROM
VRAM
图像RAM,PPU专用,2K字节
VROM
图像ROM,储存图像数据的地方,可以由MMC切换到VRAM里
ROM
程序ROM,实际程序储存的地方,扩容部分可以通过MMC切换到PRG-RAM里
RAM
程序RAM,和ROM同义,不同的是它是RAM
SPR-RAM
精灵RAM,RAM中的256字节,专用于储存精灵,它不属于VRAM或ROM
SRAM
电池RAM,卡带上用来保存游戏记录的EPROM-电擦写ROM
DMC
三角波调制通道,APU用来处理数字声音的,也写作PCM通道
EX-RAM
扩展VRAM,用在MMC5里,可以扩展VRAM容量
3、中央处理器
NES定制的6502内部特别加上了声音处理单元。
NTSC制式的NES使用主频,PAL制式使用主频。
CPU内存映像:
开始地址
用途
结束地址
$0000
2K字节RAM,做4次镜象(即$0000-$07FF可用)
$1FFF
$2000
寄存器
$2007
$2008
寄存器($2000-$2008的镜像,每8个字节镜像一次)
$3FFF
$4000
寄存器
$401F
$4020
扩展ROM
$5FFF
$6000
卡带的SRAM(需要有电池支持)
$7FFF
$8000
卡带的下层ROM
$BFFF
$C000
卡带的上层ROM
$FFFF
中断:
6502有3个中断IRQ/BRK、NMI和RESET,每个中断都有一个16位的向量,即指针,用来存放该中断发生时中断服务函数的地址。
中断发生时CPU都会把状态标志和返回地址压栈,然后调用中断服务程序。
IRQ/BRK中断由一下两种情况产生:
一是软件通过BRK指令产生,一是硬件通过IRQ引脚产生。
RESET在开机的时候触发,这是ROM被装入,6502跳到RESET向量指向的地址没有寄存器被修改,没有内存被清空,这些都只在开机是发生。
NMI指不可屏蔽中断,它在VBlank即屏幕刷新时发生,持续时间根据系统(NTSC/PAL)不同而不同。
NTSC是每秒60次,而PAL是每秒50次。
6502的中断延时是7个时钟周期,也就是说,进入和离开中断都需要7个时钟周期。
它产生于PPU的每一帧结束,NMI中断可以由$2000的第7位的1/0控制允许/禁止。
大部分中断应该使用RTI指令返回,但是有些游戏不用,例如《最终幻想1》。
它用一个很奇怪的方式:
手工修改堆栈指针,然后执行RTS指令。
这种方法在技术上是可行的,但是应该尽量避免。
以上中断在ROM内有以下对应的地址:
中断地址
中断
优先权
$FFFA
NMI
中
$FFFC
RESET
高
$FFFE
IRQ/BRK
低
特别说明:
NES的6502不支持10进制。
虽然CLD和SED指令都正常工作,但是ADC和SBC都不使用CPU状态标志的“D”位。
由于复位后“D”位的状态是不确定的,所以游戏通常在程序开始时使用一个CLD指令。
声音寄存器映射到CPU内部,所有波形发生的工作都在CPU内部完成。
注意那两个分开的16KROM段,它们可能是连续的,但是它们根据卡带的大小扮演不同的角色。
有的卡带只有一个16KROM,那么它就同时被装入$8000和$COOO。
所有游戏都将它们自己装入$8000,使用32KRAM,但是它们都能够通过内存映射把多于一个16KROM装入$8000。
VROM也是同样的道理。
当BRK中断发生的时候,CPU把状态标志压入堆栈,同时设置“B”标志。
而IRQ中断发生时,CPU把状态标志压入堆栈,同时清除“B”标志。
这是因为6502使用同一个向量来处理两种中断,用“B”标志来区分它们。
你可以用以下程序来区别两种中断:
C134:
PLA;拷贝CPU状态标志到AC135:
PHA;把状态标志还回给堆栈C136:
AND#$10;检查“B”标志C138:
BNEis_BRK_opcode;如果设置了,就是软件中断(BRK)在NMI里指向BRK会导致已经被压栈的“B”标志被设置。
6502的$6C指令(间接绝对跳转)有一个BUG,当低位字节是$FF时CPU将不能正确计算有效地址。
例如:
C100:
4FC1FF:
00C200:
23..D000:
6CFFC1-JMP($C1FF)本来它是应该跳到$2300的,但是在计算高位字节的时候,在页面边界处地址是不能再增加的,所以实际将跳转到$4F00。
需要注意的是,页面越界不会在变址间接寻址模式发生。
由于0页面的限制,由于0页面的限制,所有变址间接寻址的读写都应该在计算有效地址之后和#$FF进行逻辑与操作。
例如:
C000:
LDX#3;从$0002+$0003读变址地址,C002:
LDA($FF,X);不是$0102+$0103._________________________________________________________________________________
4、图形处理器
PPU时序:
NTSC制式
PAL制式
基频(Baseclock)
.0Hz
.0Hz
CPU主频(Cpuclock)
总扫描线数(Totalscanlines)
262
312
扫描线总周期(Scanlinetotalcycles)
1364
1362
水平扫描周期(H-Drawcycles)
1024
1024
水平空白周期(H-Blankcycles)
340
338
结束周期(Endcycles)
4
2
帧周期(Framecycles)
1364*262
1362*312
帧IRQ周期(FrameIRQcycles)
29830
35469
帧率(Framerate)
60
50Hz
帧时间(Frameperiod)
(ms)
(ms)
镜像是指通过硬件映射特殊的内存地址或范围的一个过程。
PPU内存映像:
开始地址
用途
结束地址
$0000
图案表0(256x2x8,可能是VROM)
$0FFF
$1000
图案表1(256x2x8,可能是VROM)
$1FFF
$2000
命名表0(32x30块)(镜像,见命名表镜像)
$23BF
$23C0
属性表0(镜像,见命名表镜像)
$23FF
$2400
命名表1(32x30块)(镜像,见命名表镜像)
$27BF
$27C0
属性表1(镜像,见命名表镜像)
$27FF
$2800
命名表2(32x30块)(镜像,见命名表镜像)
$2BBF
$2BC0
属性表2(镜像,见命名表镜像)
$2BFF
$2C00
命名表3(32x30块)(镜像,见命名表镜像)
$2FBF
$2FC0
属性表3(镜像,见命名表镜像)
$2FFF
$3000
$2000-$2EFF的镜像
$3EFF
$3F00
背景调色板#1
$3F0F
$3F10
精灵调色板#1
$3F1F
$3F20
镜像,(见调色板镜像)
$3FFF
$4000
$0000-$3FFF的镜像
$7FFF
命名表:
NES的图像通过Tile矩阵来显示,这个网格就叫命名表。
一个命名表和字符模式下的屏幕缓冲比较相象,它包含字符的代码,也就是30列的32Byte长度。
每个Tile有8x8个象素,每个命名表有32x30个Tile,也就是256x240象素。
PPU支持4个命名表,他们在$2000,$2400,$2800,$2C00。
在NTSC制式下,上面和下面的8象素通常不显示出来,只有256x224象素;在PAL制式下,屏幕有256x240象素。
需要说的是,虽然PPU支持4个命名表,任天堂主机只支持2个命名表。
另外两个被做了镜像。
命名表保存了Tile的编号,而Tile存在图案表里。
计算命名表里Tile号对应的实际地址的公式是:
(Tile号×16)+由$2000端口指定的图案表地址
命名表镜像:
NES只有2048字节($800)的VRAM给命名表使用,但是如前表所示,NES有能力寻址到4个命名表。
缺省情况下,NES卡带都带有水平和垂直镜像,允许你改变命名表指向PPU的VRAM位置。
这种方式同时影响两个命名表,你不能单独改变其中的一个。
每个卡带都控制着PPU地址线的A10和A11。
它可能将他们设置成以下4种可能的方式的1种。
下面这个图表有助于理解NES里的各种镜像,指向PPUVRAM中命名表的12位地址相当于“$2xxxx”:
名字
命名表#0
命名表#1
命名表#2
命名表#3
说明
地址线A11
地址线A10
水平
$000
$000
$400
$400
1
0
垂直
$000
$800
$000
$800
0
1
4屏幕镜像
$000
$400
$800
$C00
卡带里有2KVRAM,4个命名表物理上独立的
1
1
单屏幕
$X00
$X00
$X00
$X00
所有的命名表指相同的VRAM区域,X=0、4、8、C
0
0
VROM镜像
Mapper68#游戏映射VROM到PPUVRAM的命名表,这使得命名表是基于VROM的,你不能写它但却可以通过mapper自己来控制是否使用这种特性
图案表:
图案表储存了实际8x8象素的Tile,同时也储存了用来指向NES调色板全部16种颜色的4位元矩阵的低两位。
PPU支持两个图案表在$0000和$1000。
他们有以下格式:
VRAM地址
图案表内容
颜色效果
$0000............$0007
%00010000=$10
%00000000=$00
%01000100=$44
%00000000=$00
%=$FE
%00000000=$00
%=$82%00000000=$00
组0
...1....
......3...3..2.....2.1111111.2.....2.3.....3.
$0008............$000F
%00000000=$00
%00101000=$28
%01000100=$44
%=$82
%00000000=$00
%=$82
%=$82%00000000=$00
组1
点表示0号颜色,数字表示实际调色板颜色代号
注意在图案表里存储的是每个点的2个位。
其他两个由属性表得到。
所以,在屏幕上总体出现的颜色数是16,而每个块里只有4种颜色。
属性表:
每个命名表有它自己的属性表。
属性表的每一个字节代表了屏幕上的一组4x4的Tile,一共有8x8个字节。
有几种方法来描述属性表里一个字节的功能:
*保存32x32象素方格的高2位颜色,每16x16象素用2位;*保存16个8x8Tile的高2位颜色;*保存4个4x4Tile格子的高2位颜色。
看以下两个图表帮助理解:
1、一个16x16象素的格子:
#0-F代表了一个8x8Tile,方块[x]代表了4个8x8Tile,
方块0
#0
#1
#2
#3
方块1
#4
#5
#6
#7
方块2
#8
#9
#A
#B
方块3
#C
#D
#E
#F
2、属性表一个字节的实际格式定义如下(对应于上面的例子):
位
描述
0、1
方块0的高两位颜色(Tile#0,1,2,3)
2、3
方块1的高两位颜色(Tile#4,5,6,7)
4、5
方块2的高两位颜色(Tile#8,9,A,B)
6、7
方块3的高两位颜色(Tile#C,D,E,F)
调色板:
NES有两个调色板,背景调色板和精灵调色板。
调色板不包含实际的RGB值,它们更象一个索引表。
写到$3F00-$3FFF的D6-D7字节被忽略。
30
31
32
33
34
35
36
37
38
39
3A
3B
3C
3D
3F
20
21
22
23
24
25
26
27
28
29
2A
2B
2C
2D
2E
2F
10
11
12
13
14
15
16
17
18
19
1A
1B
1C
1D
1E
1F
00
01
02
03
04
05
06
07
08
09
0A
0B
0C
0D
0E
0F
调色板镜像:
镜像发生在背景调色板和精灵调色板之间,例如所有写到$3F00的数据会被镜像到$3F10,$3F04镜像到$3F14。
背景和精灵的最高3个调色板的0号色盘定义为透明,存在那里的颜色不会被画出来。
PPU使用放在$3F00里的颜色作为背景色,详细如下:
*$0D被写到$3F00(镜像到$3F10); *$03被写到$3F08(镜像到$3F18); *$1A被写到$3F18; *$3F08被读到累加器。
PPU使用$0D作为背景颜色,尽管$3F08有一个颜色$03(因为0号颜色在所有的调色板里都定义为透明)。
最后,累加器上有一个值$1A,这是从$3F18映像过来的。
又一次,这个$1A值没有被画出,因为所有的调色板的0号颜色被定义为透明。
整个背景和精灵调色板同时也映像到VRAM的其他区域,$3F20-$3FFF全部都是这两个调色板分别的映像。
写到$3F00-$3FFF的D6-D7字节被忽略。
背景滚动:
NES可以通过预提取命名表,图案表和属性表来使背景滚动,背景是独立于精灵而位于最下层的。
可以水平和垂直滚动。
水平滚动
垂直滚动
0 512
A
B
A
0
480
B
命名表A通过$2000的D1-D0指定,B是跟在后面的一个命名表,根据镜像不同B是动态的。
这个不能工作在水平和垂直同时滚动的游戏里。
背景会跨越多个命名表,如下所示:
命名表#2($2800)
命名表#3($2C00)
命名表#0($2000)
命名表#1($2400)
在$2005里写到水平滚动的值可以从0-256,写到垂直滚动的值从0-239,239是考虑了负值的结果,例如248代表-8。
屏幕和精灵分层:
下面是NES画图的循序:
前台
后台
CI
OBJs0-63
BG
OBJs0-63
EXT
精灵RAM BGPRI==0
精灵RAMBGPRI==1
CI代表颜色亮度,相当于$2001的D7-D5;BG是背景;EXT是扩展口的图像信号。
BGPRI代表VRAM里背景‘优先权’位,每个精灵都有的,即第二字节的D5位。
OBJ数代表了实际精灵的号码,不是Tile索引值。
前台高于任何其他层,最后被画上,后台低于任何其他层,最先被画上。
精灵和精灵RAM:
NES用一个页面(256字节)来存放动画,每个精灵4个字节,一共可以有64个动画/精灵,它们可以是8x8或8x16象素。
动画/精灵图案被存储在VRAM的图案表其中一个里面。
精灵属性,例如翻转和优先权被储存在一个特殊的256字节的精灵RAM,它不是CPU或PPU的地址的一部分。
整个精灵RAM可以通过$4014的DMA方式来写,写一个8位的数到$4014就将这个8位数所指定的内存页面整个拷贝到精灵RAM上。
也可以通过把开始地址放在$2003然后读/写于$2004(每次存取地址自动加一),它是一个一个字节存取的。
动画/精灵的属性格式是:
动画/精灵属性RAM:
|精灵#0|精灵#1|...|精灵#62|精灵#63|
字节
位
描述
0
YYYYYYYY
精灵左上角的Y坐标-1
1
IIIIIIII
Tile索引号
2
vhp000cc
v=垂直翻转(1=翻转)
h=水平翻转(1=翻转)
p=背景优先权(0=前台1=后台)
c=颜色的高2位
3
XXXXXXXX
精灵左上角的X坐标
Tile索引号就和命名表里的一样。
精灵图案可以象Tile图案对于背景图片一样抓取。
唯一的不同是在8x16的精灵时,上半部分(偶数号码)的Tile索引由精灵图案表的$0000开始,而下半部分(奇数号码)由$1000开始。
$2000寄存器对8x16精灵无效。
全部64个精灵有自己内部的优先权,0号最高(最后被画),63号最低(最先被画)。
每条扫描线只能显示8个精灵,每个精灵RAM条目都会被检查是否处于其他精灵的水平范围内。
注意,这是基于扫描线的,不是基于精灵的,也就是说,会进行256次检查,而不是256/8或256/16次。
在一个真实的NES芯片里,如果精灵被禁止($2001的D4是0)很长一段时间,精灵数据会渐渐消失。
可以理解为精灵RAM是一个DRAM,D4控制了DRAM的周期刷新信号。
碰撞标志:
碰撞标志是PPU状态寄存器($2002)的第6位(D6)。
PPU能够找出0号精灵的位置,然后设置D6,它是这样工作的:
PPU扫描出背景图案象素和精灵象素同时不是透明的第一个地方。
比如,屏幕的背景是一个非透明颜色(颜色号>0),0号精灵的坐标是(12,34),它只是在第4行的开始才有象素,那么,碰撞标志在屏幕刷新到(12,37)时才被设置。
要记住,0号颜色被定义为透明。
引起D6被设置的象素必须是已经被画出来的。
下面的例子能帮助理解,这是两个Tile,下划线表示透明的0号颜色,*号表示碰撞标志被设置。
精灵
+
背景
=
结果
‘*’会用2号颜色画出
__1111__
_111111_
112__211
112__211
_111111___1111__
_______________2______21_____211____2111___21111__211111_2111111
__1111__
_1111112
112__*11
112_2211
_1111111_2111111
这个例子说的是背景+精灵,但是对于靠近背景的精灵也通用,即精灵+精灵,通过设置背景优先权来实现。
D6不可以通过读PPU的状态来复位,只在每次VBlank之后被清零。
Hit标志可以用在水平或者垂直屏幕分割的时候,还有许多好玩的效果。
水平和垂直消隐:
所有的游戏机都有一个刷新动作,用来重新定位电子枪显示可见的数据。
最通用的显示设备是电视机,它分为每秒刷新60次的NTSC制式和50次的PAL制式。
电子枪从左到右画出象素,它每次只能画一条扫描线,画下一条之前要先回到左面并且做好准备,这之间有一段时间叫做水平消隐(HBlank)。
在画完全部256条扫描线之后它又回到屏幕左上角准备下一次画屏幕(帧),这之间的一段时间就是垂直消隐(VBlank)。
电子枪就是一个不断的走‘之’字形的过程。
VBlank标志就是$2002的D7,它表明PPU是否在VBlank期间,当VBlank标志存在时,你就可以通过$2006和$2007访问PPU内存。
一个程序可以通过读$2002来使D7复位。
在屏幕刷新期间,我们不能访问PPU,而PPU会在CPU背后修改VRAM指针,这样我们很容易在写入VRAM时出错,为了让PPU停下来,可以对$2000和$2001写00。
访问PPURAM:
在一个任天堂主机,访问PPU内存只可以在VBlank期间。
当在屏幕刷新时访问会破坏刷新地址寄存器,一般它经常用来做隐含的“分割屏幕”效果(见“在屏幕刷新的时候访问VRAM”)。
很多小些的ROM用只读存储体(VROM)用做图案表。
在这种情况下,你不可以写PPU地址,只可以读。
写PPU记忆体:
a)写高位地址字节到$2006
b)写低位地址字节到$2006
c)写数据到$2007。
每一个写操作后,地址会增加1($2000的第二位是0)或增加32($2000的第二位是1)。