易码技术论坛

 找回密码
 加入易码
搜索
查看: 1020330|回复: 11

[归档] 任天堂产品系统文件

[复制链接]
发表于 2007-7-19 14:58:24 | 显示全部楼层
居然是6502……
有没有合适的开发工具,或者调试工具,以及模拟器?
 楼主| 发表于 2007-7-19 15:06:14 | 显示全部楼层
有,很早前网上找到一个 NesDevKit 试过后比较失望。
支持的汇编器是2个nesasm,ca65.有点虚。
 楼主| 发表于 2007-7-19 15:11:25 | 显示全部楼层
不过初学者最好用NESASM 后期可以用更强大的6502Simulator/ca65等等
发表于 2007-7-19 19:02:01 | 显示全部楼层
楼主的是纯文本文件,我这里有一个CHM原版的,多了一些图片、表格,理解更方便些。

[ 本帖最后由 dragon_ 于 2007-12-27 00:41 编辑 ]

任天堂产品系统文件.rar

85.39 KB, 下载次数: 543

发表于 2007-7-19 22:40:09 | 显示全部楼层
星星之火那里也有资料
发表于 2007-9-7 13:04:36 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
发表于 2007-9-7 14:04:08 | 显示全部楼层
还真是用汇编写的
因为直接用机器码写很累人
发表于 2007-9-7 14:50:45 | 显示全部楼层
真佩服用纯汇编写游戏的人...
发表于 2007-9-7 14:59:32 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
发表于 2007-9-7 18:38:34 | 显示全部楼层
是机器人
 楼主| 发表于 2007-7-19 14:51:30 | 显示全部楼层 |阅读模式
  任天堂产品系统文件
目录
1、系统简介
2、缩写表
3、中央处理器
4、图形处理器
5、声音处理器
6、手柄、控制器和扩展口
7、内存映像硬件
8、I/O端口
9、NES文件格式
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使用1.7897725MHz主频,PAL制式使用1.773447MHz主频。
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内部完成。
注意那两个分开的16K ROM段,它们可能是连续的,但是它们根据卡带的大小扮演不同的角色。有的卡带只有一个16K ROM,那么它就同时被装入$8000和$COOO。
所有游戏都将它们自己装入$8000,使用32K RAM,但是它们都能够通过内存映射把多于一个16K ROM装入$8000。VROM也是同样的道理。
当BRK中断发生的时候,CPU把状态标志压入堆栈,同时设置“B”标志。而IRQ中断发生时,CPU把状态标志压入堆栈,同时清除“B”标志。这是因为6502使用同一个向量来处理两种中断,用“B”标志来区分它们。你可以用以下程序来区别两种中断:
??? C134: PLA ;???????????????? 拷贝CPU状态标志到A
??? C135: PHA ;???????????????? 把状态标志还回给堆栈
??? C136: AND #$10 ;??????????? 检查“B”标志
??? C138: BNE is_BRK_opcode;??? 如果设置了,就是软件中断(BRK)
在NMI里指向BRK会导致已经被压栈的“B”标志被设置。
6502的$6C指令(间接绝对跳转)有一个BUG,当低位字节是$FF时CPU将不能正确计算有效地址。例如:
??? C100: 4F
??? C1FF: 00
??? C200: 23
??? ..
??? D000: 6C FF C1 - JMP ($C1FF)
本来它是应该跳到$2300的,但是在计算高位字节的时候,在页面边界处地址是不能再增加的,所以实际将跳转到$4F00。
需要注意的是,页面越界不会在变址间接寻址模式发生。由于0页面的限制,由于0页面的限制,所有变址间接寻址的读写都应该在计算有效地址之后和#$FF进行逻辑与操作。例如:
??? C000: LDX #3 ;???????? 从 $0002+$0003 读变址地址,
??? C002: LDA ($FF,X) ;??? 不是 $0102+$0103.
_________________________________________________________________________________
4、图形处理器
PPU时序:

   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)   

镜像是指通过硬件映射特殊的内存地址或范围的一个过程。
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里的各种镜像,指向PPU VRAM中命名表的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   卡带里有2K VRAM,4个命名表物理上独立的   1   1     
单屏幕   $X00   $X00   $X00   $X00   所有的命名表指相同的VRAM区域,X=0、4、8、C   0   0     
VROM镜像   Mapper 68#游戏映射VROM到PPU VRAM的命名表,这使得命名表是基于VROM的,你不能写它但却可以通过mapper自己来控制是否使用这种特性           
图案表:
图案表储存了实际8x8象素的Tile,同时也储存了用来指向NES调色板全部16种颜色的4位元矩阵的低两位。PPU支持两个图案表在$0000和$1000。他们有以下格式:

VRAM地址   图案表内容   颜色效果     
$0000
..
..
..
..
..
..
$0007   %00010000 = $10
%00000000 = $00
%01000100 = $44
%00000000 = $00
%11111110 = $FE
%00000000 = $00
%10000010 = $82
%00000000 = $00   组 0   ...1....
..2.2...
.3...3..
2.....2.
1111111.
2.....2.
3.....3.     
$0008
..
..
..
..
..
..
$000F   %00000000 = $00
%00101000 = $28
%01000100 = $44
%10000010 = $82?
%00000000 = $00
%10000010 = $82?
%10000010 = $82
%00000000 = $00   组 1   点表示0号颜色,数字表示实际调色板颜色代号   
注意在图案表里存储的是每个点的2个位。其他两个由属性表得到。所以,在屏幕上总体出现的颜色数是16,而每个块里只有4种颜色。
属性表:
每个命名表有它自己的属性表。属性表的每一个字节代表了屏幕上的一组4x4的Tile,一共有8x8个字节。有几种方法来描述属性表里一个字节的功能:
??? *保存32x32象素方格的高2位颜色,每16x16象素用2位;
??? *保存16个8x8 Tile的高2位颜色;
??? *保存4个4x4 Tile格子的高2位颜色。
看以下两个图表帮助理解:
1、一个16x16 象素的格子:#0-F 代表了一个8x8 Tile,方块 [x] 代表了4个8x8 Tile,

方块 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   3E   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   OBJs 0-63   BG   OBJs 0-63   EXT     
    ?精灵RAM?
BGPRI==0       ?精灵RAM
BGPRI==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索引号     
      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号颜色,*号表示碰撞标志被设置。

精灵      ?背景      结果   ‘’会用号颜色画出     
?__1111__
_111111_
11222211
112__211
112__211
11222211
_111111_
__1111__      ________
_______2
______21
_____211
____2111
___21111
__211111
_2111111      __1111__
_1111112
11222211
112__*11
112_2211
11222211
_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。
访问PPU RAM:
在一个任天堂主机,访问PPU内存只可以在VBlank期间。当在屏幕刷新时访问会破坏刷新地址寄存器,一般它经常用来做隐含的“分割屏幕”效果(见“在屏幕刷新的时候访问VRAM”)。很多小些的ROM用只读存储体(VROM)用做图案表。在这种情况下,你不可以写PPU地址,只可以读。
写PPU记忆体:
??? a) 写高位地址字节到$2006
??? b) 写低位地址字节到$2006
??? c) 写数据到$2007。每一个写操作后,地址会增加1($2000的第二位是0)或增加32($2000的第二位是1)。
读PPU记忆体:
??? a) 写高位地址字节到$2006
??? b) 写低位地址字节到$2006
??? c) 从$2007读数据。从$2007读到的第一个字节是无效的(见下例)。然后每读一次地址就增加一($2000的第二位是0)或增加
?????? 32($2000的第二位是1)。
例子:VRAM的$2000里有$AA $BB $CC $DD。VRAM的增量是1。执行效果如下:
??? LDA #$20
??? STA $2006
??? LDA #$00
??? STA $2006 ; VRAM 地址设为 $2000
??? LDA $2007 ; A=??? VRAM 缓冲=$AA
??? LDA $2007 ; A=$AA VRAM 缓冲=$BB
??? LDA $2007 ; A=$BB VRAM 缓冲=$CC
??? LDA #$20
??? STA $2006
??? LDA #$00
??? STA $2006 ; VRAM 地址设为 $2000
??? LDA $2007 ; A=$CC VRAM 缓冲=$AA
??? LDA $2007 ; A=$AA VRAM 缓冲=$BB
这个只适用于$0000-$FEFF,访问调色板数据没有这种现象。
在屏幕刷新的时候访问VRAM:
前面说过,在屏幕刷新的时候存取VRAM的地址和数据是不合法的。许多程序存取这些寄存器来制造不同的滚动效果。比如,一些游戏从屏幕底部开始滚动,那么它可能向$2006写第一行的状态来复位屏幕滚动。更好的诡计是PPU在屏幕刷新时用VRAM的地址寄存器来储存当前地址。通过向$2006修改地址以及让PPU从一个不同的地方接着刷新。关于$2007如何影响屏幕刷新仍不了解。
??? 当不知道向$2006中写什么数据时,看下面的图表。
??? 写到$2006的地址

位   描述     
0-4   Tiles的水平偏移量(例如1 = 8象素);水平位置=水平偏移量×8     
5-9   Tiles的垂直偏移量(例如1 = 8象素);垂直位置=垂直偏移量×8     
A、B   命名表的号码($2000,$2400,$2800,$2C00);     
C、D   附加的垂直偏移量,单位:象素(0..3);扫描线=垂直偏移量+附加的垂直偏移量     
E、F   00   
 
5、声音处理器
NES的集成CPU里包含了一个4声道的声音处理器(2A03),为播放音乐提供准模拟的声音。4个声道分别是:2个方波,一个三角波和一个噪声发生通道,每个声道的特性都不一样。
声道:
方波声道能够产生54.6Hz到12.4KHz的方波,它的主要特性是频率扫略功能和调整输出的占空周期;三角波声道可产生27.3Hz到55.9KHz分辨率是4位(16级)的三角波,它的主要特性是输出模拟三角波和线性计数器(可用来定时关闭声道);噪声通道能产生29.3Hz到44.7KHz的随机噪声,它的主要特性是产生伪随机数,在声道输出随机频率。
帧计数器:
2A03有一个内含的帧计数器,它能够产生60Hz(1/1帧率)、120Hz(1/2帧率)和240Hz(1/4帧率)的信号,提供给其他一些声音硬件使用。1/4帧率的信号是由CPU频率乘以2即3579545Hz再除以14915得到的。
声音硬件延迟:
在2A03复位之后,到各个声音硬件开始接收时钟信号之间有2048个时钟周期的延迟。当这2K个时钟周期之后,声音开始正常工作,输出使能有效。这种现象只发生在系统复位之后的2048个时钟周期内。这个资料的准确性有待验证。
控制寄存器:
2A03的声音控制寄存器在CPU内分配的特殊地址如下:

寄存器地址   描述     
$4000-$4003   方波1     
$4004-$4007   方波2     
$4008-$400B   三角波     
$400C-$400F   噪声     
$4015   声道使能、音长计数状态   
只有$4015是可读可写的,其他都是只能够写的,如果你读这些寄存器最可能得到的是$40(因为NES数据线的重负荷)。读这些只写寄存器对寄存器本身和声道都没有任何影响。每个声音通道都有4个寄存器,具体描述如下:

寄存器\位   0   1   2   3   4   5   6   7     
$4000/$4004/$400C   音量衰减率   无衰减   音长计数中止/衰减循环使能   占空周期类型     
$4001/$4005   右移量   1减少/0增加波长   扫描更新率   扫描使能     
$4002/$4006/$400A   波长低八位     
$4003/$4007/$400B/$400F   波长高三位   音长计数值     
$4008   线性计数值   音长计数中止/线性计数开始     
$4009/$400D   未使用     
$400E   播放采样率   未使用   随机数类型发生     
$4015:音长状态(读)   方1   方2   三角   噪声   DMC   未使用   DMC的IRQ状态     
$4015:通道使能(写)   方1   方2   三角   噪声   DMC   未使用   
声道结构体系:
总表:

设备   三角波   噪声   方波     
三角阶梯发生器   √             
线性计数器   √             
可编程定时器   √   √   √     
音长计数器   √   √   √     
4位DAC   √   √   √     
音量衰减单元       √   √     
扫频单元           √     
占空周期发生器           √     
波长转换器       √         
随机数发生器       √       
三角阶梯发生器:
这是一个5位的单向的计数器,只存在于三角波通道,计数器输出的低4位分别通到一个XNOR(异或非)门。4个XNOR门被同一个选通信号控制,当选通是0时,计数器输出的4个低位在门电路的输出端反相;当选通是1时,不反相。选通信号连到计数器的最高位,这样就可以在XNOR门的输出端产生一个计数序列,就造出了一个比较理想的三角阶梯信号发生器。XNOR门的输出反馈到一个4位的DAC输入端。当三角波的音长计数器或线性计数器是0时,这个5位的计算器会暂停,同时锁定,计数器不会被重设置到某一个特定的状态。在系统复位的时候,计数器装入0。计数器的时钟输入直接连接到三角波通道内一个11位的可编程定时器的输出端,这样,这个5位的三角波阶梯信号发生器的输出频率就是通道内可编程定时器频率的1/32。
线性计数器:($4008)
这是一个7位的递减计数器,只存在于三角波通道,计数器减到0时输出。计数器的时钟是240Hz(1/4帧率),用帧来计算长度就是0.25N,其中N是预先装入的7位的值。计数器会保持计数状态直到输出0,这时线性计数器和三角阶梯发生器的时钟信号被禁止,导致两个计数器被锁定,线性计数器的输出保持是0,声道因此而出现静音。线性计算器有两种工作模式:装入和计数。在装入模式,所有的输入(写到$4008)的值都会在传送到输出,也就没有计数;在计数模式,计数器会从当前值向下减,在$4008的数会被忽略。下表表示了如何通过$4008的最高位设置计数器:

旧值   新值   模式     
0   X   计数     
1   0   在写周期没有变化,计数     
1   1   没有变化   
可编程定时器($4002/3、$4006/7、$400A/B、$400F):
这是一个11位可预设的递减计数器,每个声音通道都有,但是注意在噪声通道不是直接可用的,详见“波长转换器”。这个计数器在计数完成的时候自动重载,因此它在减到0前会计算N+1个时钟周期,其中N是预装入的11位值。这个计数器直接使用6502的频率(1.79MHz)作为时钟,输出频率是1.79MHz/(N+1)。当到达计数终点的时候会产生一个1时钟周期的脉冲,而输出频率会被反馈到对应于声音通道的特殊设备。对于方波,这个计数终点脉冲会被送到占空周期发生器;对于三角波,会被送到三角阶梯发生器;对于噪声,会被送到随机数发生器。
音长计数器($4003、$4007、$400B、$400F):
这是一个7位的递减计数器,时钟是60Hz,每个声音通道都有。当计数器减到0,计数停止,相应的通道出现静音。可以通过设置音长计数时钟的禁止位来暂停计数,写1则中止计数,0则允许计数。在不同的寄存器里,这个位的位置不同,见寄存器表。计数值通过$4003、$4007、$400B和$400F的3-7位来写入。输入的5位值通过下面2个表转换到计数器的7位值:

第3位=0     
-位的值   帧      
   第7位=0   第7位=1     
0   05   06     
1   0A   0C     
2   14   18     
3   28   30     
4   50   60     
5   1E   24     
6   07   08     
7   0E   10   
  
第3位=1     
4-7位   帧     
0   7F     
1   01     
2   02     
3   03     
4   04     
5   05     
6   06     
7   07     
8   08     
9   09     
A   0A     
B   0B     
C   0C     
D   0C     
E   0E     
F   0F   
音长计数器的状态可以通过$4015来读取,0表示计数器处于0状态(停止在0),1表示处于非0状态。通过写0到$4015可以令音长计数器永远处于0状态,使得声道被禁止;写1可以取消禁止状态,但它不会改变计数器里面的值。在系统复位时这个寄存器全部5个使用的位都被设置为0。
4位DAC:
这是一个标准的4位DAC(数模转换)单元,有16级输出电压,4个声道共用。在2A03里,方波1和方波2混合通过引脚1输入,三角波和噪音通过引脚2输入。这些模拟量输出需要一个负的电流源,以使得电压的级输出变成平滑的声音信号。因为NES只外置了一个100欧姆的下拉电阻,使得输出波形的幅值很小,但是也获得最小的不平滑度。这个不平滑度小到难以发现,就算用示波器也不明显。
音量衰减单元($4000、$4004、$400C):
音量衰减器只存在于方波和噪声通道。当衰减禁止位(第4位)被设置为1,当前的音量值(0-3位)被直接送到DAC。在下列情况中,音量值会被忽略,而直接送0到DAC。
声道硬件要停止声音,例如音长计数器或扫频单元;
处于占空周期或随机数发生器输出频率信号的负值部分,也即在声音使能的时候送到DAC的或者是音量值或者是0(无声)。
当衰减禁止位被清零,0-3位当前的值就是衰减率。衰减率是指音量从最大值向下线性衰减的速度,衰减速度是240/(N+1),N的值从$0-$F。当衰减到0之后,根据寄存器第5位的状态将发生两种情况:

第5位   行为     
0   音量停在0,声道静音     
1   音量循环不断从最大值减到0   
只有通过对寄存器进行写操作才能重新设置硬件的衰减状态,否则就一直按当前状态进行衰减甚至循环,即使寄存器的第4位被设置成禁止衰减也没有用。

扫频单元:
这个东西只存在在方波通道中。当寄存器的第7位被设为1,扫频被激活,波长会实时地增加或减少(对应频率降低或提高)。波长的值通过$4002/3和$4006/7被不断的读入和更新,写到那里的值会立刻反应到声音里。第4-6位代表了频率扫略速度,或者说$4002/3和$4006/7里波长值被改变的频率。刷新率是120Hz/(N+1),N是写入的值,从0-7。第3位控制扫频的模式,1表示波长减少,0表示波长增加。第0-2位是右移量,用来控制波长增量的右移量,见下表:

第3位   表达式,N是右移量从0-7     
0   波长=波长+(波长>>N)     
1   波长=波长-(波长>>N)   
当满足以下3个条件,波长寄存器每个周期都会被更新:
第7位被设置,即扫频使能;
右移量不等于0;
音量计数器的值非0。
以下两种情况会暂停扫描时钟,使声道静音,而不管扫描刷新率是多少或扫描使能位是否设置:
11位的波长值小于$008;
扫频处于增加模式,而进位标志=1。硬件在波长寄存器被更新之前总要检查进位标志的。
占空周期发生器($4000、$4004):
占空周期发生器只存在于方波通道,它使用可编程定时器的输出频率作为时钟,使用4位计数器来产生4种类型的占空周期,因此它的输出频率是可编程定时器的1/16,输出信号反馈到音量衰减器。占空类型见下表:

6、7位   一个时钟周期里的占空周期(正/负)     
00   2/14     
01   4/12     
10   8/8     
11   12/4   
波长转换器($400E):
这个东西只存在于噪声通道,用来将一个给定的4位值转换成11位的波长,然后送到可编程定时器。下表是转换关系:

0-3位   音度   音阶   CPU时钟周期(11位波长+1)     
0   15   A   002     
1   14   A   004     
2   13   A   008     
3   12   A   010     
4   11   A   020     
5   11   D   030     
6   10   A   040     
7   10   F   050     
8   10   C   065     
9   9   A   07F     
A   9   D   0BE     
B   8   A   0FE     
C   8   D   17D     
D   7   A   1FC     
E   6   A   3F9     
F   5   A   7F2   
随机数发生器($400E):
噪声通道有一个1位的伪随机数发生器,它是由一个15位的移位寄存器和一个异或门组成,它能够提供‘长’和‘短’两种类型的伪随机数序列:当寄存器第7位是0时,产生32767位的长序列(32K模式);是1时,产生93位的短序列(93位模式)。93位模式通常在声道中产生较高频率的声音回放。下表说明了在移位寄存器的那些位里进行异或:

模式   EDCBA9876543210
<-------------<     
32K   **     
93位   *???? *   
当前的异或结果在下一个移位周期之前送到移位寄存器的0位,1个位的随机数从E引脚输出,反相然后送到音量衰减器。移位寄存器每接收到两个可编程定时器的时钟脉冲就移位一次,所以它的频率是可编程定时器的一半,也就是低一个音度。系统复位的时候移位寄存器被装入值1。
6、手柄、控制器和扩展口
NES支持很多种输入设备,主要有手柄,光枪、4适配器和摇杆。两个手柄分别通过$4016和$4017访问,用选通脉冲对手柄复位,即对$4016或$4017先写1后写0,这将在游戏手柄的电路里发生个脉冲,然后可以在$4016或$4017读取了。每次读取将返回一个单一的按钮的状态,在第0位(1表示按下,0表没有)。第1位表示了游戏棒是否和端口连接。如果连接就置0,否则是1。第6和7位的$4016/$4017好像有相同的重要性,不知是什么,有些游戏期望当一个按钮按下的时候在$4016/$4017得到$41。复位的时候返回0。

读取 #   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24     
按键   A键   B键   选择   开始   上   下   左   右   忽略   信号   0   0   0   0   
信号用来检测是4端口的接入状态:
??? %0000:没有连接设备
??? %0001:只有$4016手柄
??? %0010:只有$4017手柄
光枪:
只是简单地使用了$4016或$4017的D0、D3和D4位,完全有可能同时接两支光枪。
4适配器:
将手柄扩展到4个,支持4手柄的游戏有:Tengen的《Gauntlet II》和Nintendo的《RC Pro Am 2》。全部4个控制器使用$4016或$4017的D0位。对于$4016端口,读1-8字节是手柄1,9-16字节是手柄3,$4017则对应手柄2和4。

读取   1   2   3   4   5   6   7   8   9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24     
按键   1P A   1P B   1P选择   1P开始   1P上   1P下   1P左   1P右   2P A   2P B   2P选择   2P开始   2P上   2P下   2P左   2P右   信号   0   0   0   0   
摇杆:
有的游戏使用摇杆,例如Taito的《Arkanoid》。摇杆的位置通过$4017的D1来读取,第一个读的是MSB,第8个读的是LSB,有效值的范围是98-242,98代表了,摇杆完全是被逆时针转的。读到的数据要取反,例如读到%01101011,取反是%10010100,即146。摇杆还带有一个按钮,通过$4016的D1来读取,1代表按钮被按下。
扩展口:
NES都有一个扩展口,用来接手柄或双手柄。手柄初始化需要写1和0,如果初始化没有完成,或者循序错了,扩展口就可以用来通讯,例如:
??? LDA #%00000001
??? STA $4016
??? STA $4017????? ; 开始扩展口的读模式
??? LDA #%00000011 ; 写%110到扩展口
??? STA $4016
确实有些卡带用这种方式来通讯。
任天堂主机内部输入/输出端口映射到$2000-$2007和$4000-$4017。一些端口的用处是未明的或未知的。任何有关信息将受到感激。
7、内存映射硬件
这里只提供一些硬件资料,软件方面请参考扩容资料。
Mapper其实就是硬件上的地址映射。用了切换ROM和VROM,更有的用于动态切换VROM。动态切换VROM通常有2种办法:一是用Latches,所谓Latch就是切换器,当PPU访问命名表遇到特定的数据时,自动切换去指定的VROM,例如圣火徽章外传就用了这种方法,它的特定数据是FE和FD,就是对话框的两个边^_^;二是用IRQ中断,算好PPU的时序和CPU的指令周期后设置IRQ计数器,等待到特定的时候就触发IRQ中断,调用中断服务切换VROM,例如三国志2-霸王之大陆就是这样,有的IRQ计数器是每个CPU周期增加1,有的是每条扫描线增加1,视硬件电路而定。
图例:
R:连接到ROM部分;
S:由ROM,MMC和NES共用;
N:连接到卡带接口;
W:连接到电池RAM;
Pxxx:对应于CPU的;
Vxxx:对应于PPU的;
M2:时钟。

+5V   M2   PA13   PA14   P-CE   PD7   PD0   R/W   VA10   VA12   VA11   V10     
24   23   22 N   21 N   20 N   19 S   18 S   17   16 N   15 N   14 S   13 S     
)   MMC1     
1 R   2 R   3 R   4 R   5 R   6 W   7 R   8 R   9 R   10 R   11 R/W   12     
PA14   PA15   PA16   PA17   P-CE   B-CE   VA12   VA13   VA14   VA15   VA16/B-CE   GND   
RD387-001:

+5V   +5V   GND   VA10   VA15   VA12   VA14   VA12   VA13   VA16   VA8   VA5   VA9   VA4   VA11   VA3   VA7   VA2   VA10   VA1   V-CE     
XX   40   39   38 N   37 R   36 R   35 R   34 N   33 R   32 R   31 S   30 S   29 S   28 S   27 S   26 S   25 S   24 S   23 S   22 S   21 S     
)   MMC2?     
XX   01   02 N   03 N   04 R   05 R   06 S   07 R   08 R   09 R   10 S   11 S   12 S   13 S   14 S   15 N   16 N   17 S   18 S   19 S   20     
GND   M2   PA14   PA13   PA15   PA14   PA12   PA13   PA16   P-CE   PD4   PD3   PD0   PD1   PD2   R/W   P-CE   V-RD   VA0   VA6   GND   
 

        PD6   PA0   PD7   B-CE   M2   GND   +5V   P-CE   PA18   PA13   PA17             
        33 S   32 S   31 S   30 W   29   28   27   26 R   25 R   24 N   23 R             
PD0   34 S      22 R   PA14     
PD5   35 S      21 R   PA16     
PD1   36 S      20 N   PA14     
PD4   37 S      19 R   PA13     
PD2   38 S      18 R   PA15     
PD3   39 S      17 N   R/W     
GND   40      16   ?     
B-CE   41 W      15   GND     
B-WE   42 W      14 N   P-CE     
PA11   43 R      13   IRQ     
PA16   44 R      12 R   VA17     
        1   2 R   3 N   4 N   5 N   6   7 R   8 R   9 R   10 N   11 R             
        ?   VA10   VA12   VA11   VA10   GND   VA13   VA14   VA12   VA10   VA15           
MMC5:

引脚   功能   引脚   功能   引脚   功能   引脚   功能   引脚   功能     
1   RC0   21 R   VA16   41 S   PD5   61 R   PA14   81   NC     
2   RC1   22 R   VA17   42 S   PD6   62 R   PA15   82   NC     
3   RC2   23 R   VA18   43 S   PD7   63 R   PA16   83 W   B-CE     
4   +5V   24 R   VA19   44   +5V   64 R   PA17   84 S   VD0     
5 S   VA0   25 N   VA10   45 S   PA0   65 R   PA18   85 S   VD1     
6 S   VA1   26 N   VA11   46 S   PA1   66 R   PA19   86 S   VD2     
7 S   VA2   27 N   VA12   47 S   PA2   67 N   PA13   87 S   VD3     
8 S   VA3   28 N   VA13   48 S   PA3   68 N   PA14   88 S   VD4     
9 S   VA4   29   NC   49 S   PA4   69 W   A13   89 S   VD5     
10 S   VA5   30   NC   50 S   PA5   70 W   A14   90 S   VD6     
11 S   VA6   31 N   V-CE   51 S   PA6   71 W   CE-0   91 S   VD7     
12 S   VA7   32 N   VA10   52 S   PA7   72 W   CE-1   92   NC     
13 S   VA8   33 N   V-WR   53 S   PA8   73   NC   93   NC     
14 S   VA9   34 S   V-RD   54 S   PA9   74 R   P-CE   94 R   VA0     
15 R   VA10   35 N   IRQ   55 S   PA10   75   NC   95 R   VA1     
16 R   VA11   36 S   PD0   56 W   VCC   76 W   B-WE   96 R   VA2     
17 R   VA12   37 S   PD1   57   +BAT   77   NC   97   CL3     
18 R   VA13   38 S   PD2   58 S   PA11   78   NC   98   SL3     
19 R   VA14   39 S   PD3   59 S   PA12   79 N   M2   99   GND     
20 R   VA15   40 S   PD4   60 R   PA13   80   GND   100   RC3   
MMC6:

引脚   功能   引脚   功能   引脚   功能   引脚   功能   引脚   功能   引脚   功能   引脚   功能     
1 N   PA13   11   GND   21 R   VA13   31   IRQ   41 S   PD5   51 S   PA5   61 R   PA14     
2   M2   12   +5V   22 R   VA12   32 N   P-CE   42 S   PA0   52 S   PA6   62 R   PA18     
3   GND   13   HLD   23 R   VA14   33   R/W   43 S   PD6   53 S   PA9   63 N   PA14     
4   GND   14 N   VA10   24   GND   34 S   PD2   44 S   PA1   54 S   PA7   64 R   PA17     
5   GND   15 N   VA11   25   +BAT   35 S   PD3?   45 S   PD7   55 S   PA8             
6   NC   16   NC   26 R   VA18   36 S   PD1   46 S   PA2   56   +BAT             
7   GND   17 R   VA10   27 N   VA10   37   GND   47 R   P-CE   57   GND             
8   +5V   18 R   VA16   28 S   V-RD   38   +BAT   48 S   PA3   58 S   PA12             
9   +5V   19 R   VA11   29 S   V-CE   39 S   PD4   49 S   PA4   59 R   PA13             
10   +BAT   20 N   VA12   30 R   VA17   40 S   PD0   50 R   PA16   60 R   PA15           
NES-CN-ROM-256-[02,05]:

+5V   NC   VA14   VA13   VA12   VA11   GND   R/W     
16   15   14 R   13 R   12   11   10   09     
)   74LS161     
01   02   03   04   05   06   07   08     
+5V   P-CE   PD0   PD1   PD4   PD5   GND   GND   
NES-AOROM-03 NES-BN-ROM-01:

+5V   NC   PA15   PA16   PA17   VA10   GND   R/W     
16   15   14   13   12   11   10   09     
)   74LS161     
01   02   03   04   05   06   07   08     
+5V   P-CE   PD0   PD1   PD2   PD4   GND   GND   
NES-CN-ROM-256-[02,05]:

+5V   NC   LS32-13   LS32-2   LS32-9   NC   GND   R/W     
16   15   14 R   13 R   12   11   10   09     
)   74LS161     
01   02   03   04   05   06   07   08     
+5V   P-CE   PD0   PD1   PD2   GND   GND   GND   
  
+5V   LS161-14   A14   A14   A14   LS161-12   A16     
14   13   12   11 R   10   09   08 R     
)   LS32     
01   02   03 R   04   05   06   07     
A14   LS161-13   A15   GND   GND   NC   GND   

NES-ANROM-03:

+5V   NC   PA15   PA16   NC   VA10   GND   R/W     
16   15   14?   13?   12   11   10   09     
)   74LS161     
01   02   03   04   05   06   07   08     
+5V   P-CE   PD0   PD1   +5V   PD4   GND   GND   
  
+5V   -8-9   -1   P-CE   CE   -13   -13     
14   13   12   11 R   10 R   09   08     
)   LS02     
01   02   03 R   04   05   06   07     
-12   R/W   R/W   NC   GND   GND   GND   
CPU:

+5V   Load   Q1   Q2   INP0   INP1   NMI   R/W   IRQ   M2   GND   CLK   D0   D1   D2   D3   D4   D5   D6   D7     
40   39   38   37   36   35   34   33   32   31   30   29   28   27   26   25   24   23   22   21     
)   6502/6527     
01   02   03   04   05   06   07   08   09   10   11   12   13   14   15   16   17   18   19   20     
Audio1   Audio2   RESET   A0   A1   A2   A3   A4   A5   A6   A7   A8   A9   A10   A11   A12   A13   A14   A15   GND   
PPU:

+5V   ALE   AD0   AD1   AD2   AD3   AD4   AD5   AD6   AD7   A8   A9   A10   A11   A12   A13   RD   WR   RST   VIDEO OUT     
40   39   38   37   36   35   34   33   32   31   30   29   28   27   26   25   24   23   22   21     
)   6528/6538     
01 P   02 P   03 P   04 P   05 P   06 P   07 P   08 P   09 P   10 P   11 P   12 P   13 P   14 P   15 P   16 P   17 P   18 P   19 P   20     
R/W   D0   D1   D2   D3   D4   D5   D6   D7   A2   A1   A0   CE   GND   GND   GND   GND   CLK   INT   GND   
 
8、I/O端口
端口是预分配好的可访问地址,程序通过端口和PPU或APU交换信息。每个端口都是16位的

端口   读写/位   功能描述     
$2000   读写   PPU控制寄存器 1     
    0-1   命名表地址:
%10 ($2800)
%11 ($2C00)

%00 ($2000)
%01 ($2400)

请记住,因为镜像,只有2个真实的命名表,而不是4个;
当滚动超出当前命名表时PPU会自动切换到另一个命名表     
   2   垂直写, 1=VRAM地址以32递增:
命名表, VW=0
命名表, VW=1

——> 写?
| 写
|
V

   
   3   精灵图案表地址, 1=$1000, 0=$0000     
   4   屏幕图案表地址, 1=$1000, 0=$0000     
   5   精灵尺寸, 1=8x16, 0=8x8     
   6   PPU 主/从模式, 没有在NES里使用     
   7   Vblank使能, 1=在Vblank时发生中断     
$2001   读写   PPU控制寄存器2     
    0   显示模式,0=彩色,1=单色     
   1   背景掩码,0=不显示屏幕的左8列     
   2   精灵掩码,0=不在左8列显示精灵     
   3   屏幕使能,1=显示图像,0=黑屏     
   4   精灵使能,1=显示精灵,0=隐藏精灵     
   5-7   当D0=0:背景颜色,% 000=黑,%001=蓝,%010=绿,%100=红;不用混合使用,因为可能会损坏PPU硬件
当D0=1:颜色亮度,% 000没效果,%00 1强化蓝色,%010强化绿色,%100强化红色,不要混合使用     
$2002?   读   PPU状态寄存器:读取后$2005和$2006被复位,下一个写到$2005的数据是水平的,写到$2006的数据是高位     
    0-3   未知(???)     
   4   VRAM写标志:0=写有效,1=写忽略     
   5   扫描线精灵计数:0=当前扫描线精灵个数小于等于8个,1=大于8个     
   6   命中标志:1=精灵刷新碰到了#0精灵。在屏幕刷新状态,这个标志被复位为0     
   7   Vblank标志:1= PPU在Vblank状态。当Vblank结束或CPU读$2002时,该标志被复位为0     
$2003   写   精灵RAM地址:用来设置通过$2004访问的256字节精灵RAM地址。每次访问$2004后该地址增加1     
$2004   读写   精灵RAM数据:用来读/写精灵内存。地址通过$2003来设置,每次访问后地址增加1     
$2005   写   屏幕滚动偏移:第一个写的值会进入垂直滚动寄存器(若>239,被忽略)。第二个值出现在水平滚动寄存器     
$2006   写   VRAM地址:设置$2007访问的VRAM地址。第一个写地址的高6位。第二个写低8位。每次访问$2007后地址增加     
$2007   读写   VRAM数据:用来访问VRAM数据,通过$2006设置的地址在每次访问之后会增加1或32     
$4000   写   APU方波#1控制端口     
$4001   写   APU方波#1音量控制端口     
$4002   写   APU方波#1细音调(FT)端口     
$4003   写   APU方波#1粗音调(CT)端口     
$4004   写   APU方波#2控制端口     
$4005   写   APU方波#2音量控制端口     
$4006   写   APU方波#2细音调(FT)端口     
$4007   写   APU方波#2粗音调(CT)端口     
$4008   写   APU三角波控制端口#1     
$4009   ?   APU三角波控制端口#2     
$400A   写   APU三角波频率端口#1     
$400B   写   APU三角波频率端口#2     
$400C   写   APU噪声控制端口#1     
$400D       未使用(???)     
$400E   写   APU噪声频率端口#1     
$400F   写   APU噪声频率端口#2     
$4010   写   APU增量调制控制端口     
$4011   写   APU增量调制D/A端口     
$4012   写   APU增量调制地址端口     
$4013   写   APU增量调制数据长度     
$4014   写   DMA访问精灵RAM:通过写一个值xx到这个端口,引起CPU内存地址为$xx00-$xxFF的区域传送到精灵内存     
$4015   读写   声音通道切换     
    0   方波#1通道:1=声音使能,     
   1   方波#2通道:1=声音使能,     
   2   三角波通道:1=声音使能,     
   3   噪声通道:1=声音使能,     
   4   增量调制:1=声音使能,     
   5、7   未使用 (???)     
   6(只读)   垂直时钟信号IRQ可用:0=1帧存在,因为没有IRQ;1=1帧被IRQ中断     
$4016   读写   手柄1+选通     
    0   读:手柄1数据;写:选通,1=复位,0=清除;扩展端口:0=写,1=读     
   1   手柄1 存在, 0=连接     
   3   光枪精灵检测:1=瞄准     
   4   光枪扳机:0=按下,1=松开     
   2、5-7   未知,6、7设为%10(???)     
$4017   读写   手柄2+ 选通     
    0   读:手柄2数据;写:选通,1=复位,0=清除;扩展端口:0=??,1=读     
   1   手柄2 存在, 0=连接     
   2、5   未使用(???)     
   3   光枪精灵检测:1=瞄准     
   4   光枪扳机:0=按下,1=松开     
   6   垂直时钟信号(内部),0=存在,$4016的D6受影响;1=不存在$4016的D6不可触及     
   7   垂直时钟信号(外部),0=存在,1=不存在   
9、NES文件格式
.NES文件为模拟用来储存NES卡带的映像。下面是一个.NES文件的结构。

偏移   字节数   内容     
0-3   4   字符串“NES^Z”用来识别.NES文件     
4   1   16kB ROM的数目     
5   1   8kB VROM的数目     
6   1   D0:1=垂直镜像,0=水平镜像     
        D1:1=有电池记忆,SRAM地址$6000-$7FFF     
        D2:1=在$7000-$71FF有一个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-   16KxM   ROM段升序排列,如果存在trainer,它的512字节摆在ROM段之前     
-EOF   8KxN   VROM段, 升序排列   
10、任天堂磁碟机系统
任天堂家庭机磁碟系统(FDS)是它的扩展单位,它只是由任天堂公司生产以及只在亚洲国家销售。它有一个软盘驱动器可以插2.5寸软盘,可以以32KB的RAM(代替了ROM)来读进程序。8KB的VRAM(代替了VROM),其他的硬件描述于下。
内存映像:
FDS的地址空间的描述:

$0000   2kB InternalRAM, mirrored 4 times      
$2000   Input/Output     
$5000   Expansion Modules     
$6000   32kB Program RAM     
$E000   8kB FDS BIOS ROM   
FDS还增加了一些I/O端口于$4000-$40FF来控制磁盘系统,声音系统,IRQ记数器。

端口   读写/位   功能描述     
$4020   W   Lower Byte of IRQ Counter     
$4021   W   Upper Byte of IRQ Counter     
$4022   W   Enable/Disable IRQs     
    2   \ = Stop IRQ counter and reset its interrupt request
/ = Load IRQ counter with a value from $4020-$4021 and start it     
$4024   W   Data Write Register:To write data to the disk, output it into this register     
$4025   W   Control Register     
    0   Drive Motor, 0 = on, 1 = off     
   1   \ = Set drive head to the start of the first track     
   2   Disk Write, 0 = enabled, 1 = disabled     
   3   Screen Mirroring, 0 = vertical, 1 = horizontal     
   4-6   Unknown (???)     
   7   Disk IRQs, 0 = disabled, 1 = enabled     
$4026   W   ExPort Output (???)     
$4030   R   Disk Status Register 0      
    4   Unknown (???)     
   6   Unknown (???)     
$4031   R   Data Read Register:To read data from the disk, input it from this register     
$4032   R   Disk Status Register 1     
    0   Disk Presence, 0 = inserted, 1 = not inserted     
   1   Disk Ready, 0 = ready, 1 = not ready     
   2   Write Protection, 0 = unprotected, 1 = protected     
$4033   R   ExPort Input     
    7   Battery Status, 0 = ok, 1 = low   
可屏蔽中断计数器:
FDS提供了一个16位的IRQ记数器连接到CPU时钟发生器。计数器开始于你写在$4021的一个预设在$4020-$4021的值。它在每个CPU时钟周期后减一。当计数器为零时,它从预设的值开始继续计数。这时就发生了一个IRQ。当垂直空白时,屏幕刷新时也会发生。$4022的第2个位用来控制IRQ计数器。当它是0,那么计数停止,要到达的IRQ被复位(如果有的话)。设置了1后,记数可以被恢复。
磁碟格式:
每个磁盘有两面,A 和 B,每面可以有65000字节的数据。要反转一个当前面,必须先拿出来,反转,重新插回去。数据在每个面上被储存于一系列的不同类型的块[1,2,3,4,3,4,...,3,4]。

Block   Byte   Description     
1. Side Header Block(56 bytes)   0   $01     
    1-14   "*NINTENDO-HVC*"     
    15   Maker ID     
    16-19   Side Name     
    20   Version Number     
    21   Side Number, $00 = Side A, $01 = Side B     
    ? 22-24   Additional Disk Data     
    25   $08     
    26-56   Reserved Space (not used by BIOS)     
2. File Number Block (2 bytes)   0   $02     
    1   Number of Files on this side     
3. File Header Block (16 bytes)   0   $03     
    1-2   File Number (not used by BIOS?)     
    3-10   File Name (not used by BIOS?)     
    11-12   Starting Address in memory     
    13-14   File Size     
    15   File Type00= Program Data,$01 = Character Data,$02 = Screen Data     
4. File Data Block (variable length)   0   $04     
    1-...   Data (see File Header Block for size)   
所以两字节地方都以最重要的字节开始。面的名称可能和相同游戏软盘的不同面不同,但它们是一样的。当文件被读取,开始的地址在RAM和VRAM都是实在的地址。
磁碟访问:
下面的图表显示了FDS如何从软盘中读/写。
READ:

$4025   A   B   C   D       E   A)Initialization     
7bit   _   __   _   -       --_   B) Motor on     
6bit   _   __   -   -       --_   C) Read start mark     
5bit   -   --   -   -       ---   D) Enable IRQs     
4bit   _   __   _   _       _-_   E) Read end mark     
                                  
2bit   -   --   -   -       ---   1. Read data, ($4030)=[xx0xxxxx]     
1bit   -   -_   _   _       ___   2. Read data, ($4030)=[xxx0xxxx]     
0bit   _   --   -   -       --_         
Note                       1? 2       
WRITE:

$4025   A   B   C   D       E   A)Initialization     
7bit   _   __   __   -       --_   B) Motor on     
6bit   _   __   _-   -       --_   C) Write start mark     
5bit   -   --   --   -       ---   D) Enable IRQs     
4bit   _   __   __   _       _-_   E) Write end mark     
                                  
2bit   -   --   __   _       __-   1. Delay, write [00000000]     
1bit   -   -_   __   _       ___   2. Write [10000000]     
0bit   _   --   --   -       --_   3. Write data, ($4030)=[xx0xxxxx]     
Note           1   2       34   4. Delay   
对于读写是如何完成的并不清楚。虽然知道FDS每当一个字节从软盘里读出的时候就发生一个IRQ。FDS的IRQ处理器(是FDS的BIOS一部分)在$4031读取这个字节,然后指针指向下个字节。
磁碟错误:
下面是软盘错误的信息:

ERR   Meaning   注释     
01   No disk   ?没有软盘     
02   No power   没有电源     
03   Broken prong on the disk   ??     
04   Wrong maker ID   ??     
05   Wrong game name   不正确的游戏名字     
06   Wrong game version   不正确的游戏版本     
07   Wrong side number (flip the disk)   不正确的盘面(反转之)     
08   Wrong disk #1   一号软盘错     
09   Wrong disk #2   二号软盘错     
10   Wrong disk #3?   三号软盘错     
11             
12             
13             
14             
15             
16             
17             
18             
19             
20   Allows it to recognize screen data differs (???)   ??     
21   Wrong Side Header Block ("*NINTENDO-HVC*")   不正确的盘面柱头块     
22   Wrong Side Header Block ID ($01)   不正确的盘面柱头块号     
23   Wrong File Number Block ID ($02)   不正确的文件块号     
24   Wrong File Header Block ID ($03)   不正确的文件头块号     
25   Wrong File Data Block ID ($04)   不正确的文件数据块号     
26   Error writing data to the disk   写文件错     
27   Block ends prematurely   块过早结束     
28   The disk unit and the same period can&#39;t take it(???)   ??     
29   The disk unit and the same period can&#39;t take it (???)   ??     
30   Disk full   ?软盘满     
31   Data number of a disk doesn&#39;t match up (???)   ??
发表于 2007-11-9 21:36:19 | 显示全部楼层
是宅男……
您需要登录后才可以回帖 登录 | 加入易码

本版积分规则

Archiver|手机版|小黑屋|EMAX Studio

GMT+8, 2024-3-29 14:29 , Processed in 0.026597 second(s), 21 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表