下一个演讲 我个人十分期待 我也知道你们很多人为此在这里等候 我也非常激动 我自己有台GameBoy 我也很荣幸能拥有一台 我一直放着 有时也拿出来玩玩 当然一般情况下还是玩模拟器多 而且我只在模拟器上玩我拥有实体卡带的游戏 本来就应该是这样的 对吧 嗯是的 非常期待 顺便 我个人在Gameboy平台上最喜欢的游戏 第一个 是Megaman 2 随后是Wario Land, Mystic Quest 如果你不同意的话 那说明你还不够了解我 好了讲讲演讲吧 非常期待 主讲是Michael Steil 他在25C3给过一个非常棒的演讲 “The Ultimate Commodore 64 Talk” 现在 为大家带来的是 “The Ultimate Gameboy Talk” 很期待 Michael Steil 白天他在工作研究操作系统相关技术 晚上回到家就研究这些古董系统 在之前 它是个游戏机破解工作者 请为他热烈的鼓掌 把讲台交给Michel 大家好 那 我的名字是Michael Steil 这次讲座是“The Ultimate Gameboy Talk” (一场讲座,关于Gameboy的一切) 这场讲座的主要想法就是 在60分钟内 尽可能多得谈谈Gameboy的各种硬件细节 60分钟,关于Gameboy的一切 我准备了大约200张幻灯片 800张不同的表 所以信息密度可能比一般稍微高了一点 所以赶紧开始吧 这次的GameBoy演讲 算是一个大系列之一 我在几年前讲了Commodore 64 后来有人就接上讲了Atari 2600 讲了Galaksija 还有Amiga 500 所以现在又轮到我了 我选择了GameBoy 为什么GameBoy那么有趣 因为他们销售了很多很多的GameBoy GameBoy和GBC加起来就有1亿1800万台 如果再考虑上兼容GameBoy的GBA机型 不包括GBM 加起来这几乎是2亿台机器 大约有1600款游戏 从1989年开始生产 8-Bit型号一直生产到2003年 一样,如果考虑兼容的GBA型号 8-Bit兼容机一直生产到2009年 也就是20年 相当厉害 我们再来看看当时的竞争对手 就在当时 就在GameBoy发布后 Atari发布了Lynx SEGA发布了Game Gear NEC发布了Turbo Express 这些竞品有一个共同点 它们都有彩屏 很棒的彩屏 但是还有一个共同点 就是渣续航 大约3-5小时 然而GameBoy可以做到15-30小时 然而妥协就是,只有一个这样的屏幕 而且一旦开始卷动就很难看清楚东西 当然,并不是所有的GameBoy都是这个情况 最初的GameBoy 也是生产最长时间的型号 最初代号是DMG 代表的是Dot-Matrix Game(点阵游戏) 1996年他们发布了GBP 带有效果好得多的屏幕 体积也小了很多 代号MGB GBL只在日本发售 带有屏幕背光 最后是GBC 2倍的CPU速度 2倍的内存 2倍的显存 以及支持彩色 接着是GBA系列 架构完全不同 基于ARM CPU 但是仍然和GB、GBC游戏100%兼容 至于GBA SP,存在两种型号 如果你要买的话,记得买AGS101 也就是带有背光而不是前光的型号 而且可以看见任天堂总是有一点超前 不单单是做了翻盖设计 而且还需要一个转接头才能接一般的耳机 如果你想在电视上玩GameBoy 有两种选择 要么是Super Nintendo插上Super Game Boy 有两个版本 SGB2是日本专有型号 有专门的计时系统 不会比正常的快3% 以及Game Boy Player 搭配NGC使用 这三个都是内置了完整的GameBoy硬件 基本就是一个GameBoy 只是把图形输出到主机而不是自己的屏幕 所以GameBoy具体的外观 一个2.6英寸的屏幕 按键 单声道喇叭 耳机是立体的 有一个联机口 通过串行总线连接两台GameBoy 对比度和音量调节旋钮 背面 这里是插游戏卡带的地方 这里则是安装电池的地方 一般的游戏卡带就长这个样子 一般的游戏卡带里面一般也就是一片ROM芯片 没有什么特殊的 (具体后面会讲) 参数 GameBoy的参数是怎么样的 拿来和一些其它可能根本不具备可比性的机型比一比吧 CPU是1MHz的8Bit处理器 有人可能会讲这里错了 应该是个4MHz的CPU 但是我一会会解释为什么这里称之为1MHz 8KB的内存 对于这类的游戏机其实算是充裕了 显存(VRAM)也是8KB 稍微有点紧张 分辨率160*144 非常糟糕 但是对于这个屏幕尺寸 其实效果也还可以 同屏最大4种颜色 就是4级灰度 每行最多10个精灵 所以如果和这里其它的一些系统比较的话 很明显 GameBoy比Atari2600要先进得多 但是却又比不上SNES 更接近于一般的NES(美版红白机)或者是Commodore 64 有趣的是 NES和C64是在80年代早期发布的 GameBoy是89年 而且如我前面所说 兼容机一直生产到了2009年 这也就非常有趣了 它是8-Bit机 但是是最晚一批大量使用的8-Bit游戏机 看看内部 右边的板没什么花头 从正面看 LCD就是连接在这块板上 以及扬声器和按键 背后这块板就有趣多了 可以看见3块芯片 这个是DMG 也就是最初的型号 2片一样的RAM芯片 一片工作内存(WRAM) 一片视频内存(VRAM) 中间这个叫DMG-CPU 然而其实更应该被称为SoC(System on a Chip) 正常这类的系统都应该有很多芯片 但是这里全部都集成进了一个芯片 也就这个DMG芯片 来对比一下其它的GameBoy机型 比如SGB 这些芯片看起来很接近 其实基本上就是一样的 GBP 稍微优化了一下 只带有一片RAM芯片 GBL看起来基本没有区别 只是MGB系列的另外一个小型号 带有背光而已 接着是SGB2 基于GBP设计 这是GBC内部的样子 都有一个巨大的集成片 这个呢 这个比较特殊 可能从标识上不容易认 这个是GB Boy 总是有公司山寨GameBoy 这个型号 是一个相当完美的克隆 基本上就是 完全抄了一个原版的芯片 来自中国的山寨GameBoy 而且至今都可以在eBay上花三四十刀买到 然而很遗憾晶振快了30% 游戏都玩不了了 回来讲DMG的板子 DMG CPU还是相当有趣的 他接管整个系统的一切 DMG CPU包括了什么 CPU 中断控制器 定时器 内存 启动ROM 还有所有的外设以及IO控制器 摇杆 用于联机的串行控制器 声音控制器 还有 图形控制器 也就是PPU (Pixel Processing Unit) 来讲讲CPU吧 历史角度来讲 最初的GameBoy,1989年 发布时间是在NES的和SNES的之间 NES搭载的是6502处理器 SNES搭载的是65816处理器 也就是相同CPU的16位版本 所以显然GameBoy搭载了SHARP LR35902 (强行显然2333) SHARP LR35902是什么 这个和6502没啥关系 更接近于i8080和Z80 但是也不是其中任何一种 两种cpu都相当有趣 比如8080用于Altair 也就是Bill Gates最初写程序并销售的平台 Z80则是用于所有没有采用6502的8bit家用机 相当成功的架构 所以如果你想象 这是i8080的功能 这是Z80的功能 也就是一个超集 而且完全向下兼容 然后这个 是GameBoy CPU 核心架构是相同的 也就8080的那些 比如寄存器组 指令编码 这些都是一样的 当然也有一些不支持的功能 但是又支持一部分Z80的功能 然而大部分的Z80功能都不支持 接着又在这之上加了一点自己的功能 来看一遍吧 先来讲讲8080的架构 它拥有这些寄存器 一个累加器 可以用于进行算数运算 其它寄存器不行 一个状态寄存器 在GB内 只有两个有用的标志位 零 和 进位 剩下两个是用于十进制调整的 然后是BCDEHL 6个8位寄存器 但是你可以把B和C合起来 变成BC DE HL同理 所以你实际上拥有可以作为指针的16位寄存器 所以加起来一共有4个16位计算器 7个8位寄存器 加上一个特殊的指针寄存器 指向hl寄存器指向的内存地址的数据 可以放到任何可以操作寄存器的指令中使用 然后来看指令 读取/存储指令 同一个指令可以直接或者间接寻址 堆栈是16位的 只能操作16位寄存器 这些是算数和逻辑指令 如之前所说 这些指令只能配合累加器使用 inc和dec(自加自减)指令是例外 可以配合任何寄存器使用 而且也可以配合16位寄存器使用 移位指令 还有控制指令 跳转 调用 返回 条件 间接操作 等等 还有一些杂类的指令 清除设置进位标志 空指令 停机 停用启用中断 中断模型 大部分当时的机器都有一个中断向量来控制中断 然而这个上面 并不是向量 也不止一个 他并不是跳转到一个中断向量 而是跳转到内存中开始部分一个固定的地址 不同的中断对应不同的地址 比如0x40 0x48这种 还有一些软件中断 你可以使用特殊指令跳转到这些地方 RST 0是一个特殊指令 因为就相当于RESET 当你启动8080 CPU 他就从0地址开始执行 来谈谈一些不被支持的8080功能 这个是GB的标志位 这个是8080的标志位 它有两个额外的标志 一个是符号位 还算有点用 一个奇偶校验 就不太有用 所以这些指令都是不支持的 还有一些其它指令 因为种种原因 他们决定不包括这些指令 端口IO 你可能在8086上听说过 有独立的端口空间 在8080上也是支持的 但是GameBoy上不支持 因为GB使用了MMIO(内存映射IO) Z80支持大量额外的移位和位移位 设置位 清楚位指令 这些都是支持的 相对跳转 也就是更优化的跳转指令 从中断返回 这些都是支持的 而不支持的则是一些Z80有趣的功能 比如备份寄存器组 额外的寄存器 还有很多很多功能 还有适合复制内存的自加循环指令 不过他们也有一些自己的指令来代替这些 比如它具有访问预减后加指令 比如你要访问hl指针 可以使用预减指令或者后加指令 还有一个叫做 第0页 (Zero Page) 的概念 这个叫法有点晕 因为并不是真正的第0页 而是在内存的顶部 如果你了解6502就知道 这个是从6502借来的概念 含义只是对于在内存最上端的数据可以优化编码 比如原先这样载入需要3个字节 执行需要4个时钟机器周期 现在可以写成这样 也就是2个字节 执行需要3个机器周期 显然得在最高的位置放一些有用的东西 特别是对计时敏感的东西 还有一些额外的堆栈指令 存储SP的指令 一个用于交换一个字节高低4位的指令 这样8个指令 以及一个额外的电源指令 操作码表看起来是这样的 表明了颜色所以看起来更加直观一点 有几个操作码是无效的 或者说没有被用到 如果执行的话都会造成死机 应该算是个好设计 这个比较特殊 操作码CB 是个操作码前缀 以此为前缀又有256个操作码 这也就是从Z80借来的滚动、移位指令 以及自己新增加的指令的空间 再来看另外一个指令 只是作为一个例子 这是从一个固定地址载入到累加器 表示需要3个字节 在4MHz下需要16个时钟 虽然存在争议 就是应该是1MHz还是4MHz (实际4MHz 但是把4个周期当1个看) 因为所有的时钟周期数都是可以被4整除的 因为整个系统都是和内存挂钩的 CPU只能以RAM能够提供数据的速度来计算 所以你可以说这是一个1MHz的CPU 然后这个指令需要4个周期 这个数字就小得多 而且这个数字也才可以和类似的系统进行比较 比如说和同样拥有1MHz内存速度的6502机器比较 所以说 理论上 CPU确实频率是4MHz RAM速度是1MHz PPU以4MHz的速度绘图 连接到速度为2MHz的VRAM 所以这里就稍微有点复杂 但是大部分情况下 大部分的数据可以以1MHz为基准表示 但是要精确的话 并不是1MHz 而是1MiHz 1024*1024Hz 所以他们没有使用十进制 而是用了二进制 很有趣 所以以后我提到周期 我说的是1MHz的机器周期 所以这是一个拥有16位地址空间的8-Bit CPU 因为拥有16Bit的指针 所以一共也就是64KB的可见地址 32KB的是ROM 直接来自卡带 映射到卡带 然后在上方有一个Boot ROM 一会提 VRAM也映射在这里 可选的外部RAM 以及内部RAM 没有使用的部分 要么没有东西 要么就是镜像已有的 把最高地址的这些放大 可以看到另外一页的OAM内存 就像是一种特殊用途的VRAM 独立于VRAM 一会会具体讲 最后一页 也就是第0页 包括了IO部分 所有的寄存器 声音的视频的 还有127字节的HRAM 独立于其它的RAM 所以这代表游戏只能有32KB 有些游戏确实只需要32KB 比如说俄罗斯方块 就刚好挤进32KB 里面就一片芯片 生产也很方便 然后其它的卡带 容量没有理论上限 但是实际上最大是到2MB 这个是128KB 实现的办法是 增加一个MBC (内存分页控制器) 一个特殊的可以切换分页的芯片 这个在各种系统上都是很常见的做法 这些控制器可以很不一样 但是实际上 大部分的工作方式是这样的 最低16KB空间 永远映射到Bank0 而16KB-32KB这部分可以映射到Bank1 Bank2等等 切换分页的方法就是往一个地址空间内写入一些特定数值 这些就会被MBC读取到 然后切换分页 这点对于外部RAM也是一样的 游戏卡带如果含有外置内存 比如用于保存游戏 比如装有备份电池的内存 这也是保存游戏的唯一办法 可以把外部内存映射在这里 一样的模型 所以这个BootROM是什么 CPU从内存地址0开始执行 BootROM的作用就是显示这些东西 然后响一声 BootROM是内置在GameBoy里面的 花了一段时间这些代码才被提取出来 还是挺麻烦的 (不是我完成的) 这个就是完整的BootROM 首先初始化RAM 初始化声音 设置和解码显示在屏幕上的logo 卷动logo 播放声音 然后就是有趣的部分 它对比logo 游戏卡带内需要有一份logo副本 如果两者不一样 就不会继续开机 这样任天堂就能控制什么游戏是给这个平台发行的 游戏必须要内置这个logo 如果未经允许加入 那不单单是违反版权保护 而且还是违反商标保护 最后是计算校验和 算是再一次确认 否则你得拆出来吹吹卡带 吹吹机子 重新插一遍 接着它关闭BootROM 继续执行 这个logo 实际上是从卡带里面读取并且显示出来的 如果不插卡带开机会看见这个 但是这并不代表游戏开发商可以往里面装入任何logo 因为GameBoy会对比这个logo 如果logo错误就不会继续启动了 因为没有清理代码 没有重启系统或者其它事情 所以有些游戏就直接开始调戏这个logo 直接在此之上继续 demo也显然很喜欢调戏这个 任天堂logo已经在屏幕上了 就对它做点啥吧(阴险 很不错 BootROM一直运行 直到最后一条指令关闭BootROM 这是即使是最低地址显示的也是游戏卡带的内容了 它就继续从这里执行 运行来自游戏中的下一条指令 通常这里是一个跳转指令 因为这其实是所谓的ROM头 按照要求就应该是这样的 头部必须内置任天堂Logo 头部校验和 这是很重要的 剩下的元数据就不重要了 对于当时的游戏开发者来说什么很重要 但是没有软件会真正检查这些内容 在这之后 就可以放真正的游戏数据了 还没讲到过的东西就是IO部分和HRAM了 也就是在最高页 所谓的第0页 可以比较高效得操作 最高的127字节是额外的内存 剩下的 就是这些不同的设备 这些是所有系统内的寄存器 中断控制器 声音控制器 摇杆输入 串口传输 定时器 还有PPU 这些就是要讲的所有东西 摇杆输入 非常简单 这是所有GameBoy拥有的输入 4个按钮 4个方向 于是你会想 弄8个GPIO 这样就能实现了 但是也可以用6个GPIO 2列4行 你选择想要测试的行 然后知道这一行按下了什么按键 这样就能用6个而不是8个 按键就是这些 很简单 串行数据传输 你可以用一条联机线把两台GameBoy连在一起 联机线其实就是 1条单向数据线 1条另外一个方向的数据线 1条时钟线 两台GameBoy得确定谁发送时钟 谁接收时钟 这些就是控制这些东西的位 一个设置输出时钟 永远是8kHz 接收时钟范围很宽 可以从0一直到0.5MHz都能收到 一旦传输开始 就会一位位移出字节 而且永远是全双工的 定时器 任何系统都得有个时钟 这个系统只有一个时钟 TMA寄存器用来存放计数初值 然后可以选择四种速度之一 接着打开定时器 向上计时 直到溢出 就会载入初值 然后可选的产生一个中断 讲到中断 中断控制器支持5种不同的中断 V-Blank(场消隐) LCD状态 也就是和PPU相关 一会讲 已经讲过的定时器 可以产生中断 串口 可以在接受到数据的时候产生中断 摇杆 可以在有按键按下的时候产生中断 这个是中断启用寄存器 一个中断标志寄存器 可以看见有哪些中断还没有被处理 这些是中断发生时会跳转的地址 你不需要手动确定发生的是哪个中断 因为直接跳转的就是不同的地址 所以 声音控制器 声音控制器的寄存器数量是最多的 一共有四个声部 每个声部有自己的一组独立的寄存器 这样看更为合理 4个声部 每个声部5个寄存器 每个寄存器有特定的含义 但是含义只是大致的 因为四个声道并不完全一样 有两个很接近的 产生方波 剩下两个一个是PCM采样 一个是杂波 看位定义的话 有一定相似性 但是还是不一样 具体通道有具体的定义 先看看共通的一些参数 每个声部都有一个开关位 也就是控制这个通道的开关 你可以在某个时候关掉这个通道 但是还有一个长度开关 以及长度设置寄存器 所以比如你可以这么设置 在0.25秒后自动关闭 然后先来看PCM采样声部的寄存器 也是最简单的一个 这个声部的作用就是 可以播放任何的波形 它有额外的16字节寄存器 里面有32个位置 每个4位 你可以在里面放任何你想要的波形 这里是几个例子 比如你可以产生一个锯齿波 算是相当基本的波形 也可以有正弦波 或者是任何自定义的东西 相当灵活 然后有一个频率控制 也就是控制采样的播放速度 2位的音量 音量可以在100% 50% 25%和静音之间控制 另外两个 是很相似的两个方波声部 这些位都是一样的,功能也一样 你不能指定波形 波形是固定的 只能是方波 就是只能是高或者低 但是高低比例(占空比)可以不同 这两个位就是控制占空比的 可以设置12.5%高 剩下的低 25% 50% 75%听起来应该和25%一样,只是反相了 这个是两个声部都支持的功能 另外有一个功能叫音量扫频 (Volume Sweep,翻译不准确,暂时想不到更好的) 就是音量可以增高或者降低 降低就是模拟乐器的情况 或者也能增高 效果比较有趣 然后 只有第一个声部有扫频功能 可以升高 也可以降低 所以可以注意到主要是用于音效的 更多的音效例子 这就是可以实现的效果 然后还有第四个杂波通道 就是一个只能产生伪随机数的移位寄存器 根据你设置的15bit模式或者7bit模式 他能产生两种不同的波形 这是15bit模式 这是7bit模式 这些是所有的寄存器 4个声部的寄存器 3个通用寄存器 这个可以设置左声道和右声道的音量 有趣的是卡带可以有自己的音频控制器 可以产生接到这个上面的音频信号 但是没有游戏做过这种事情 然后还有一个寄存器 可以设置一个通道是接到左声道还是右声道 或者都接、都不接 然后有一个电源开关 关掉音频部分可以减少约30%的电力消耗 然后 GameBoy的声音系统不单单被用于游戏 人们现在还在GB上用Little Sound DJ一类的软件编曲 我展示一个简单的例子 音频就到此为止 我们来谈谈Pixel Processing Unit (像素处理单元/PPU) PPU就是产生图形的部分 拥有12个寄存器 并不是很多 首先来看看规格 在之前讲过,160*144像素,不是很多 4级灰度 其实更接近于4级不同的绿色 当然后续的GameBoy要好得多 所有屏幕上的东西都是以8*8像素的方格为单位的 屏幕上有一定数量的方格 还有一些精灵 这些东西都得装进8KB的显存 具体解释下这个8*8像素方格 我们看一下俄罗斯方块的游戏画面 所有东西都是基于这些方块的 塞尔达也是一样 画上网格线就能看见重复的图案 而且都是和网格线对齐的 超级马里奥大陆也是很明显 因为没有很多不同的方格 甚至是像Donkey Kong Land这样的游戏 在画上网格后还是能看出来的 可以看见重复对齐的图案 尽管他们很好地隐藏了这一点 有些游戏干脆就利用这个特性来设计 在Turrican里面干脆就用方块做了个动画 我们来看看方格是什么样的 一个方格尺寸是8*8 可以有4种颜色 4种颜色的编码就是00 01 10和11 把这个叠加到像素上看看编码是如何完成的 看第一行 直接当成一个二进制数读 于是就是 02 FF 一行像素需要2个字节 整个下来一个方块就是16字节 你可能注意到了这个颜色的排序比较奇怪 这是因为调色板是可以自定的 我可以选择任何想要的调色板 系统内对于这些背景方块有个2位到2位的映射 原生的表示方法是 00表示白 11表示黑 我可以选择任意的这种的调色板 如果有需要 我也可以选择一样的颜色 系统内一共支持256个方块 看这个设置 你认出是什么游戏了吗 这是俄罗斯方块 如果你不认识底部那些跳舞的人 说明你没有通关过俄罗斯方块 这是塞尔达 这是超级马里奥大陆 只使用了128个方块 有人认识这个吗? 这个只是一个拼图 是网球 20*18个方块填满了整个屏幕 但是其实事情不完全是这样 在视频内存中 有32*32方块 这才是完整的背景映射 屏幕上能看见的只是一个区域(Viewport) 256*256个像素还是相当方便的数字 卷动很方便,只要移动这个区域就可以了 使用这个模拟器就能很清楚得看到背景中发生了什么事情 以及可视区域是如何移动的 其实就像是一个摄像机,在32*32的映射中移动 但是这种方法只适用于最大地图只有32*32的游戏 但是那种需要无限卷动的游戏怎么办 比如超级马里奥大陆 最多可以有这么多的列 可以把区域移动到这里 但是到最后怎么办 它会直接卷到左边 如果我们能在显示出来之前重绘这些画面 就可以拥有无限的世界大小 在模拟器里就看得很清楚了 在屏幕显示不到的地方会不断刷新新的地图 对于二维也是一个道理 这是Donkey Kong Land 这个看起来很奇怪 只在需要显示的地方绘制新的地图 这是我们已经谈论过的一个图层 背景图层 除了背景外 还能放一层图像 窗口 可以直接覆盖 也可以放在任意地方 有个XY坐标 他会从起始坐标开始向右下方绘制 然而没有透明支持 通常的用法就是放到右边 或者放到下面 因为这个是不受卷动影响的 显然这个功能对于屏幕下方的分数显示来说是很必要的 对于游戏来说非常简单好用的功能 当然也能放在右边 这些是GBC游戏 但是不影响 在GB上也能玩 然后在背景和窗口层之上还有一个图层 就是精灵层 精灵就是放在屏幕上的不用受限于8*8方格的物体 可以自由摆放 现在屏幕上有3个精灵 任天堂把它们叫做 对象,OBJ 但是我还是继续叫他们精灵 因为就是精灵 就直接拿这个蘑菇举例吧 每个精灵都有自己的属性 系统内有个 OAM 也就是对象属性映射表 这个就是一个OAM记录 含有以下值 第一个是X位置 把它放到屏幕的左边 你可能会以为应该横向坐标为0 但是不是 是8 为什么呢 因为如果你把它放在4 他就在这 如果设置成0 那就看不见了 因为宽度是8 需要有一种方法能让它进入屏幕 对于屏幕顶端的情况也是一样的 但是你看现在的Y坐标是16 因为一个精灵最高可以有16像素高 把它放到一个自然的位置 下一个问题就是应该长什么样 首先看见是一个8*8的方格 和之前的方格是一样的编码 只是这个还带有透明色 所以00 表示透明 因为是一样的编码 所以也是一样的方格 而且系统内也有256个方格位可供使用 你看现在是方块编号0x90 剩下一个是 X翻转 选项 所以你没有必要为 向左走 和 向右走 的蘑菇单独存两个方块 水平翻转一下 就有向右走的了 垂直翻转一下 就有死蘑菇了 同时垂直和水平翻转 就有向右走的死蘑菇啦~ 调回来 下一个是调色板 因为其中一种的颜色表示的是透明 所以只剩下3种颜色可以选择 他们不想强制固定这三种颜色 所以你可以在四种颜色里面挑3种想要的 而且不同的精灵不需要使用同样的3种颜色 因为系统内可以有两个调色板 所以才有这个位 可以选择是调色板0还是调色板1 调色板1和0分别效果就是这样 剩下一个是优先级 就是精灵碰到背景时如何绘图 如果优先级是1就有趣了 它会画在这些白色像素之上 但是在所有不是白色的像素后面 当然白色并不是一个什么特殊颜色 只是指背景调色板里的0号颜色 所以改调色板 就能让蘑菇显示在任意颜色之上 优先级0的话就会绘制在任何东西之上 当然不包括透明像素 然后就是精灵之间的优先级问题 这个没有办法自己设置 这个是固定的 现在蘑菇在正方形之上 因为它的横向坐标比正方形小 一旦达到一样的坐标 精灵编号小的显示在上 因为精灵在内存中也是一个数组 存前面的画上面 然而一旦正方形的X坐标小于蘑菇的X坐标 就会画在上面 一部分游戏中穿透其它精灵的时候 就能看到这种现象 系统支持最多40个精灵 屏幕上最多也是能放40个 但是还有一个限制 每一行最多只能有10个 比如这里有11个 10个是按照一行像素计算的 所以这些像素就不会被显示出来 或者是下面两行 这个不是讲从左到右的第十个 而是按照精灵的编号来决定的 所以这是一个完整的OAM记录 一共4个字节 这是内存中的一个OAM记录 FE00就是存储这些记录的起始地址 一共有40个记录 里面能存40个精灵 整个这个叫做OAM内存 就是一个存在这个位置的专用内存 并不是显存的一部分 还有一件事情 就是即使是小马里奥 也超过了8*8 你可以用4个精灵来实现 这也是游戏里的做法 但是还有一个模式 可以拥有16像素高的精灵 但是这个设定是全局的 整个游戏都得使用16像素高的精灵 所以已经见过3层了 还有一件事情是 屏幕是可以被彻底关闭的 这样就有第五种颜色了(强行) 稍微比白色位再淡一点 没啥用 因为得把屏幕关掉 一旦打开屏幕但是不画任何东西 就是白色 然后打开背景层 比如说要显示个淡灰色 然后就完全代替了这个颜色 再画个窗口 注意没有透明 还能在上面画精灵 但是注意精灵和窗口的颜色就无法区分了 所以说窗口上没有裁剪一类的事情 所以内存映射是怎么样的 4KB的精灵方块 4KB的背景方块 1KB的背景映射表 32*32 1KB的窗口映射表 这不是最高效的表示方法 但是他们这么做了 因为方便 但是我们只有8KB的显存 放个精灵方块和背景方块 就用完了 换种方法 先放上精灵方块 然后在最后放上背景和窗口映射 背景方块怎么办 就让它们重合 有不同的配置 3个位 可以让它们完全重合 也可以只重合一部分 这些也可以交换或者重合 重合表示什么意思 这个情况就是精灵方块和背景方块完全重合 可以共享完全相同的方块 但是也可以这么放 前128个就是精灵专用 后128个就是背景专用 中间128个共享 比如这个超级马里奥大陆 前2/3都用于精灵了 剩下的1/3被用于背景 下一个部分是横向计时 在基于CRT的机器当中 这就是CRT显示图像的慢动作效果 从头到底 从左到右 对于GameBoy也是一样的 它以60Hz的速度不停地扫描 从上到下 一行一行 从左到右 这不是因为使用了一些旧的器件 他们完全重新实现了这一部分的内容 但是液晶本身就需要以60Hz的速度刷新 所以就这样了 如果要实现一些特效的话是很重要的 比如这个游戏中 屏幕不同的区域行为是不一样的 比如我们先只看这个滚动的城市图 直接全屏滚动肯定是不成问题的 但是我们只想让它在屏幕上的一部分滚动 就可以通过这些额外的寄存器完成 如果你之前接触过8位机的编程 比如C64什么的 那么应该会理解 你可以知道当前正在画哪一行 或者马上会画哪一行 除了一直等着 还可以设定一个中断 一旦到达某一行就会执行特定代码 我们先把SCX寄存器设定到0 然后在第8行触发 所以它就会以卷动为0的参数显示出这些 然后现在我把横向滚动 设置成比如23 然后下一帧设置成24 这样就能滚动了 把对比寄存器设置到42 就会一直继续绘图到42行 设置成其它的 再修改LYC 画完这个路面 一会具体讲路面的事情 最后再设置回0 因为仪表盘不需要卷动 下一帧也是同样这么处理 这个例子中就不需要修改X卷动寄存器了 这个例子中 右上角的马里奥其实是一个窗口 我们在之前讲过 窗口会从左到右从上到下绘图 本身并不能这样只停在右上角 但是可以做到 在第0行触发中断 启用窗口 在第40行的时候再触发中断 关闭窗口 于是在40行的时候 PPU的情况就是:“窗口?啥子窗口?没听过这个说法” 于是就不会继续画 这个方法在不同的游戏中都有应用 一些是关于窗口,一些则是和卷动有关 但是如果你不是在一个特定行触发 而是每一行都触发的话 就能实现一些比如这样的效果 左边是屏幕上的效果 右边是实际上存储在显存中的数据 如果你在每一行都改变卷动寄存器 比如按照这个曲线调整 那么就能实现对图像的变形 然后每一帧都改变这个曲线 程序要做的就是 在每一行都向SCX写入一个值 当然如果图片每行都显示那就每行都得修改 而这个赛车游戏的效果其实差不多也是一样的道理 这是视频内存中的情况 只是一条直的路 但是在绘图的时候被扭曲 放大这块 这是原始数据 这是效果 忽略这里的精灵 这是为了扭曲需要的曲线 这是需要的偏移 也就是SCX 如果你每行都调整SCX 就能扭曲成这样 另外一件事情就是 路中间的线变成了虚线 路外部也有一些横线的图案 这是通过每几行就调整调色板来实现的 如果看这个游戏的话 甚至可以实现弹跳的效果 这是通过在每行同时调整纵向和横向卷动寄存器实现的 所以就可以重复一行的内容或者跳过某些行 只要计算好 就可以实现这种效果 只要在行中修改垂直卷动寄存器 就能实现二维的扭曲效果 但是我们还得再仔细讲一讲 横向计时 在一行绘制的时候 发生了什么 这是PPU的像素传输模式 通常需要43个时钟的时间 一共144行 但是你不能直接认为 在第一行结束后 它会立即绘制下一行的第一个像素 现实不是这样的 因为在每一行的开始还有一个OAM搜索阶段 需要20时钟 我一会讲 每行的结尾还有一个51时钟的行消隐 在行消隐阶段,PPU就闲着,什么事情都不做。 还有一个场消隐阶段 就是在帧之间的空闲 来计算一下 一行有114时钟 频率是1MHz 154行 所以需要这么多个时钟周期完成1帧 如果以用基准频率除以这个 得到的就是59.7Hz的刷新率 PPU当前所在的状态是可以直接读出的 CPU可以知道 而且也可以基于这个设定中断 但是CPU为什么需要知道呢 我们来看看不同模式下都发生了什么 首先 每行开始20周期的OAM搜索是做什么的 每一行 PPU都需要确定这一行中哪些精灵是可见的 所以系统内一共有40个精灵 然后它得找出 这一行中可见的精灵 然后放进一个最多可以保存10个精灵的 可见精灵数组 逻辑是 x坐标不能为0 因为那样就不可见了 然后当前所绘制的行必须要在精灵的第一行和最后一行之间 符合要求就加入可见精灵数组 然后这需要20周期的时间 顺便在原版的GameBoy中这里有个有趣的bug 如果你在OAM搜索期间 进行了任何数字在FE00到FEFF之间的16位运算 也就是到OAM内存的指针 即使你没有访问内存 他也会破坏这一块的内存 还有什么需要注意的 CPU连接到内存 PPU连接到显存 OAM内存是特殊的 PPU也直接连接到OAM内存 CPU也可以连接到显存 这样就能写入到显存 但是并不是这样完成的 那样需要双倍速的显存 你看C64是那样做的 但是在GameBoy上就必须经过PPU PPU内部有个开关 PPU可以决定CPU能不能访问 如果PPU不让CPU访问 那么CPU进行写入 什么都不会发生 CPU进行读取 读到的全部是FF 至少不会发生什么严重的问题 但是也没有什么意义 所以说CPU必须确保PPU在合适的模式下 这样才能顺利访问 在像素传输阶段 不能访问显存 但是在OAM搜索 行消隐和场消隐阶段都是可以访问的 如果你想要访问OAM内存 OAM搜索或者像素传输都不可以 像素传输时PPU要绘图 需要精灵数据 只能在这些时候才能访问 所以在屏幕绘图时就得十分小心 所以这些时间对于CPU来说都是不好的 不应该做任何重要的事情 比如说CPU想要把新的一行移动进背景映射 最好是在场消隐阶段完成 有最多的不被打断的时间 而所有的游戏逻辑和AI可以在屏幕绘图的时候完成 但是这个时候不能立即写入新的精灵位置 因为OAM也是不能访问的 所以游戏一般的做法都是 把新的精灵信息全部写入一个OAM内存的副本 然后在场消隐阶段复制进OAM 就是从任何这里其中一个 存放OAM副本的地方 复制进OAM 这个图不是按比例画的 而且这个过程不需要自己完成 有一个DMA功能 写入想要复制的块 然后等待160个时钟 在复制的过程中 CPU还是在运行的 但是不能访问任何的源地址空间 所以只能等着 但是因为这个代码也得有地方存放 所以可能的地方只有是HRAM 也算是个不错的用途 然后是像素流水线 我们继续深入来研究像素流水线 这个还算是非常新的研究 之前是没有公开的 像素FIFO(先入先出缓存)是GameBoy绘图的核心概念 我们直接跳到中间过程 屏幕上已经有一些像素了 比如这5个像素已经被送给LCD了 然后像素FIFO 里面有一些像素 然后每一个4MHz的步骤 它都会移出一个像素 送给LCD 移出一个像素 送给LCD 然后再移出下一个给LCD 你可能注意到那个绿色的指示灯变成红色了 因为像素FIFO必须要有至少8个像素才能移出像素 为什么 一会会讲到 现在需要产生新的数据来填充FIFO了 Fetch(获取)单元就是做这个的 它会读取背景方块单元 9802是目前在读取的映射位置 它从背景映射中读取方块的ID 需要1个周期 然后从方块数据内存 读取第一部分和第二部分的数据 因为每一行的方块是16位 这样它就能产生8个新的像素 然后就重新开始 进入下一个位置 就可以把这8个像素移入FIFO的上半部分 这样就可以继续移出新的像素 然而在执行过程中 并不是说等待有空了 然后开始Fetch填充 移出再这样 并不是交替进行的 而是同时进行的 FIFO是2倍速的 所以Fetch进行1个步骤 FIFO移出两个像素 移出 移出 读取第一字节的数据 移出 移出 读取第二字节的数据 然后到这个时候 现在还不能把数据存入FIFO 因为FIFO还放不下 所以Fetch先关闭 等待2个周期 也就是空闲一段时间 然后放入数据 所以如果你看内存访问的模式 就能看见3次读取 1次空闲 3次读取 1次空闲这样 所以 FIFO就是4MHz时钟下 每个周期移出一个像素 除非含有超过8个像素 否则暂停 Fetch单元 运行频率是2MHz 需要3个周期来获取8个像素 然后在第四周期停住 直到FIFO有空位 横向移动的实现方法非常简单 比如说我们移动3个像素 所以所有东西都向左移动了3个像素 最先的3个像素就直接无视掉了 然后下一个像素送往LCD 而在行的最后 事情变得有趣了 因为我们想在X=160的时候触发 FIFO可能还包含一些根本不会被画出来的像素 然后Fetch可能也已经在获取下8个像素的内容了 也不要了 然后就全部停止 就是干了太多活了 直接进入行消隐 这也是一行需要43周期 而不是40周期的原因 如果我们开启了窗口 比如说窗口坐标是26 我们现在就在26 然后FIFO已经缓存了一些像素了 它会完全清空FIFO 然后FIFO会被停止 因为这些像素已经不需要了 然后Fetch会被转到窗口映射 然后Fetch被重新开始 然后还是一样 读ID 读两个字节 就得到了窗口的数据 然后就可以放入像素FIFO 只要这些开始被移出了 窗口就绘制出来了 而精灵则是有10个比较器 也是根据X位置触发 比如说在这里X=26有一个精灵 一样 像素FIFO里面有很多像素 然后Fetch执行到一半 首先我们暂时暂停像素FIFO 就不能继续移出新的像素 然后暂时调整Fetch让它产生精灵数据 重新开始Fetch 然后就得到了精灵数据 然而这次不是放到最后 而是和前8个像素叠加 混合到已有的像素上 这也就解释了为什么FIFO中必须要有至少8个像素 因为精灵是用这种方法来混合的 然后和其它的系统有一个区别 在窗口开始前 都以恒定速度移出像素 当窗口开始的时候 FIFO被清空 很长一段时间都不能输出数据 直到FIFO内有了窗口数据 也就是等到恢复了 也就是需要至少43个周期 如果有精灵就需要更多 在基于液晶的系统上这么做是完全可行的 你可以暂停发送像素 而在基于CRT的系统上 不行 比如说在C64上 一行必须是正好的40时钟 因为任何晚出来的像素看起来都会被右移 所以这个图并不是完全准确 像素传输应该是至少43时钟 行消隐则是剩下的时间 然后实际过程中 取决于屏幕上的内容 更接近于这样 但是之前这个并不是像素FIFO真正的工作方式 它并不存储像素颜色 它存储的是原始的位组合 以及来源 比如说这里是9个背景像素 Fetch也是一样的 它并不产生像素颜色 它产生的是位组合 加上来源或者精灵调色板 我们来把这个混合一下 精灵的00表示在背景之上绘图 来过一遍 这里是精灵调色板1 数据00 表明是透明的 背景获胜 这个情况下精灵获胜 因为应该画在背景之上 大部分像素都是这个情况 而最后这个像素 是精灵调色板1 数据透明 所以背景获胜 再来和同一坐标下另外一个精灵混合一下 这里是精灵调色板0 在背景之上 精灵获胜 然后这个情况下 新的精灵不会覆盖旧的 所以旧的获胜 然后就一直这样 这也就是精灵会画在右边精灵的上面 但是不会画在已有精灵之上 高ID精灵输给低ID精灵的原因 最后这里新的精灵又获胜了 然后应用上调色板 只有在最后像素被移出的时候才进行 这里对应调色板 移出一个像素到液晶 查表 转换 LCD 另外一个 黑色 这也是在彩色机型上的做法 从SGB开始 现有的游戏不能处理颜色 但是可以上色 然后就是 已有的三种调色板现在变成了RGB调色板 其它所有的都是一致的 但是一旦移出像素 它就对应RGB颜色 把这个粉色的像素显示在屏幕上 S1 11是这里 黑色像素 再来一个例子 S1 01 精灵调色板1 显示在屏幕上 所有技术性内容到此为止 还有5分钟的时间 来讲讲开发 如果你对GameBoy开发感兴趣 有一些很不错的开发工具 Rednex Game Boy 开发系统是一套命令行工具 可以配合自己的编辑器和Makefile使用 如果想要Debug BGB模拟器 就很不错 为Windows设计 但是配合Wine在OSX和Linux上也没问题 内置调试器 断点啊 单步啊 一类的 然后还有一个显存查看器 可以查看所有发生的事情 很适合在上面运行Demo然后观察发生了什么 如果你想要在真机上运行 也有像Everdrive一类的设备 可以插SD卡 因为还有4分钟时间 我们来谈谈我最喜欢的GameBoy外设 Game Boy Camera 但是不是技术层面 只是吹一波这个设备 这个就是Game Boy Camera 你把它插入GameBoy 能拍照片 然后可以用Game Boy Printer打印出来 用的热敏纸 如果还买得到的话 然后可以拍出像这样的照片 放大一点 非常棒的照片 每张照片 都是用这个14K像素的CCD拍出来的 位深2位 (译者注:硬件为3位,抖动为2位以匹配GameBoy屏幕) 所以下次去旅游的时候 记得带上GameBoy 然后还有Game Boy Camera 加上一台带并口的PC 还有已经买不到的联机线 所以感谢这些人 研究GameBoy 以及帮助我准备这场讲座 这些人也是以各种途径帮助了我 所以 在这个系列中 这已经是第五场讲座 下一个是什么 明年应该有个讲座吧 我建议两个讲座 我就先给34C3几个提名 Dominik Wagner来讲Acorn Archimedes 然后Jannis Harder来讲SNES 这是你们自己的选择 可以做这些讲座 或者也可以在自己头上倒冰水 感谢参加 明年见 subtitles created by c3subtitles.de in the year 2017. Join, and help us!