NES资料
[3] 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)。 [4] 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。 | +----------+----------+---------------------------+ [5] 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! [6] 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。 -----------------------结束------------------------------------------- |