1 00:00:14,493 --> 00:00:17,814 下一个演讲 我个人十分期待 2 00:00:17,829 --> 00:00:23,189 我也知道你们很多人为此在这里等候 3 00:00:24,259 --> 00:00:26,209 我也非常激动 4 00:00:26,862 --> 00:00:31,593 我自己有台GameBoy 我也很荣幸能拥有一台 5 00:00:31,593 --> 00:00:36,481 我一直放着 有时也拿出来玩玩 6 00:00:36,491 --> 00:00:39,041 当然一般情况下还是玩模拟器多 7 00:00:39,770 --> 00:00:43,861 而且我只在模拟器上玩我拥有实体卡带的游戏 8 00:00:45,817 --> 00:00:52,533 本来就应该是这样的 对吧 9 00:00:54,049 --> 00:00:54,457 嗯是的 10 00:00:55,770 --> 00:00:57,254 非常期待 11 00:00:57,684 --> 00:01:01,518 顺便 我个人在Gameboy平台上最喜欢的游戏 12 00:01:02,257 --> 00:01:07,038 第一个 是Megaman 2 13 00:01:07,660 --> 00:01:14,095 随后是Wario Land, Mystic Quest 如果你不同意的话 14 00:01:14,095 --> 00:01:16,565 那说明你还不够了解我 15 00:01:18,676 --> 00:01:24,636 好了讲讲演讲吧 16 00:01:24,936 --> 00:01:26,556 非常期待 17 00:01:27,307 --> 00:01:31,785 主讲是Michael Steil 18 00:01:32,638 --> 00:01:36,430 他在25C3给过一个非常棒的演讲 19 00:01:36,670 --> 00:01:40,515 “The Ultimate Commodore 64 Talk” 现在 20 00:01:40,711 --> 00:01:43,704 为大家带来的是 “The Ultimate Gameboy Talk” 21 00:01:43,876 --> 00:01:44,929 很期待 22 00:01:45,153 --> 00:01:48,980 Michael Steil 白天他在工作研究操作系统相关技术 23 00:01:49,166 --> 00:01:51,726 晚上回到家就研究这些古董系统 24 00:01:51,916 --> 00:01:55,150 在之前 它是个游戏机破解工作者 25 00:01:55,150 --> 00:01:57,748 请为他热烈的鼓掌 26 00:01:57,870 --> 00:01:59,740 把讲台交给Michel 27 00:02:07,640 --> 00:02:09,190 大家好 28 00:02:09,997 --> 00:02:11,422 那 我的名字是Michael Steil 29 00:02:11,537 --> 00:02:13,484 这次讲座是“The Ultimate Gameboy Talk” (一场讲座,关于Gameboy的一切) 30 00:02:21,978 --> 00:02:23,806 这场讲座的主要想法就是 31 00:02:23,927 --> 00:02:28,176 在60分钟内 尽可能多得谈谈Gameboy的各种硬件细节 32 00:02:28,365 --> 00:02:31,531 60分钟,关于Gameboy的一切 33 00:02:31,742 --> 00:02:36,059 我准备了大约200张幻灯片 800张不同的表 34 00:02:36,110 --> 00:02:38,693 所以信息密度可能比一般稍微高了一点 35 00:02:38,992 --> 00:02:41,046 所以赶紧开始吧 36 00:02:41,151 --> 00:02:42,760 这次的GameBoy演讲 37 00:02:42,914 --> 00:02:45,766 算是一个大系列之一 38 00:02:45,914 --> 00:02:47,808 我在几年前讲了Commodore 64 39 00:02:48,012 --> 00:02:50,859 后来有人就接上讲了Atari 2600 40 00:02:51,132 --> 00:02:53,801 讲了Galaksija 还有Amiga 500 41 00:02:53,930 --> 00:02:55,090 所以现在又轮到我了 42 00:02:56,848 --> 00:02:58,563 我选择了GameBoy 43 00:02:58,705 --> 00:03:00,609 为什么GameBoy那么有趣 44 00:03:00,781 --> 00:03:03,517 因为他们销售了很多很多的GameBoy 45 00:03:03,710 --> 00:03:06,451 GameBoy和GBC加起来就有1亿1800万台 46 00:03:06,610 --> 00:03:11,454 如果再考虑上兼容GameBoy的GBA机型 不包括GBM 47 00:03:11,523 --> 00:03:13,906 加起来这几乎是2亿台机器 48 00:03:14,108 --> 00:03:17,837 大约有1600款游戏 49 00:03:19,307 --> 00:03:23,619 从1989年开始生产 8-Bit型号一直生产到2003年 50 00:03:23,738 --> 00:03:26,689 一样,如果考虑兼容的GBA型号 51 00:03:26,837 --> 00:03:31,256 8-Bit兼容机一直生产到2009年 52 00:03:31,334 --> 00:03:32,992 也就是20年 相当厉害 53 00:03:34,361 --> 00:03:36,343 我们再来看看当时的竞争对手 54 00:03:36,475 --> 00:03:38,348 就在当时 就在GameBoy发布后 55 00:03:38,614 --> 00:03:42,669 Atari发布了Lynx SEGA发布了Game Gear NEC发布了Turbo Express 56 00:03:42,776 --> 00:03:45,674 这些竞品有一个共同点 它们都有彩屏 57 00:03:45,782 --> 00:03:47,365 很棒的彩屏 58 00:03:47,442 --> 00:03:51,794 但是还有一个共同点 就是渣续航 大约3-5小时 59 00:03:51,941 --> 00:03:54,275 然而GameBoy可以做到15-30小时 60 00:03:54,467 --> 00:03:57,290 然而妥协就是,只有一个这样的屏幕 61 00:03:57,391 --> 00:04:00,440 而且一旦开始卷动就很难看清楚东西 62 00:04:01,669 --> 00:04:04,851 当然,并不是所有的GameBoy都是这个情况 63 00:04:06,199 --> 00:04:09,407 最初的GameBoy 也是生产最长时间的型号 64 00:04:09,550 --> 00:04:14,092 最初代号是DMG 代表的是Dot-Matrix Game(点阵游戏) 65 00:04:14,392 --> 00:04:20,509 1996年他们发布了GBP 带有效果好得多的屏幕 体积也小了很多 代号MGB 66 00:04:20,639 --> 00:04:23,645 GBL只在日本发售 带有屏幕背光 67 00:04:23,753 --> 00:04:29,464 最后是GBC 2倍的CPU速度 2倍的内存 2倍的显存 以及支持彩色 68 00:04:31,491 --> 00:04:36,407 接着是GBA系列 架构完全不同 基于ARM CPU 69 00:04:36,407 --> 00:04:40,800 但是仍然和GB、GBC游戏100%兼容 70 00:04:40,947 --> 00:04:45,036 至于GBA SP,存在两种型号 71 00:04:45,193 --> 00:04:48,531 如果你要买的话,记得买AGS101 72 00:04:48,611 --> 00:04:51,007 也就是带有背光而不是前光的型号 73 00:04:52,077 --> 00:04:55,165 而且可以看见任天堂总是有一点超前 74 00:04:55,165 --> 00:04:59,418 不单单是做了翻盖设计 而且还需要一个转接头才能接一般的耳机 75 00:05:06,069 --> 00:05:09,403 如果你想在电视上玩GameBoy 76 00:05:09,549 --> 00:05:13,736 有两种选择 要么是Super Nintendo插上Super Game Boy 77 00:05:13,837 --> 00:05:19,996 有两个版本 SGB2是日本专有型号 有专门的计时系统 不会比正常的快3% 78 00:05:20,120 --> 00:05:23,947 以及Game Boy Player 搭配NGC使用 79 00:05:24,007 --> 00:05:27,141 这三个都是内置了完整的GameBoy硬件 80 00:05:27,261 --> 00:05:31,126 基本就是一个GameBoy 只是把图形输出到主机而不是自己的屏幕 81 00:05:32,435 --> 00:05:34,132 所以GameBoy具体的外观 82 00:05:34,271 --> 00:05:38,775 一个2.6英寸的屏幕 按键 单声道喇叭 耳机是立体的 83 00:05:38,988 --> 00:05:42,266 有一个联机口 通过串行总线连接两台GameBoy 84 00:05:42,381 --> 00:05:43,749 对比度和音量调节旋钮 85 00:05:43,927 --> 00:05:46,904 背面 这里是插游戏卡带的地方 86 00:05:47,056 --> 00:05:48,791 这里则是安装电池的地方 87 00:05:49,233 --> 00:05:51,321 一般的游戏卡带就长这个样子 88 00:05:51,462 --> 00:05:53,148 一般的游戏卡带里面一般也就是一片ROM芯片 89 00:05:53,148 --> 00:05:54,482 没有什么特殊的 (具体后面会讲) 90 00:05:55,589 --> 00:05:57,115 参数 91 00:05:57,275 --> 00:05:58,895 GameBoy的参数是怎么样的 92 00:05:58,968 --> 00:06:02,263 拿来和一些其它可能根本不具备可比性的机型比一比吧 93 00:06:02,348 --> 00:06:04,678 CPU是1MHz的8Bit处理器 94 00:06:04,678 --> 00:06:10,319 有人可能会讲这里错了 应该是个4MHz的CPU 但是我一会会解释为什么这里称之为1MHz 95 00:06:11,738 --> 00:06:15,160 8KB的内存 对于这类的游戏机其实算是充裕了 96 00:06:15,305 --> 00:06:17,395 显存(VRAM)也是8KB 稍微有点紧张 97 00:06:17,578 --> 00:06:24,413 分辨率160*144 非常糟糕 但是对于这个屏幕尺寸 其实效果也还可以 98 00:06:24,622 --> 00:06:29,251 同屏最大4种颜色 就是4级灰度 99 00:06:29,417 --> 00:06:32,181 每行最多10个精灵 100 00:06:32,374 --> 00:06:35,605 所以如果和这里其它的一些系统比较的话 101 00:06:35,805 --> 00:06:43,414 很明显 GameBoy比Atari2600要先进得多 但是却又比不上SNES 102 00:06:43,563 --> 00:06:48,339 更接近于一般的NES(美版红白机)或者是Commodore 64 103 00:06:48,528 --> 00:06:53,671 有趣的是 NES和C64是在80年代早期发布的 104 00:06:53,869 --> 00:06:55,312 GameBoy是89年 105 00:06:55,498 --> 00:07:01,010 而且如我前面所说 兼容机一直生产到了2009年 106 00:07:01,159 --> 00:07:02,937 这也就非常有趣了 107 00:07:03,070 --> 00:07:08,969 它是8-Bit机 但是是最晚一批大量使用的8-Bit游戏机 108 00:07:10,765 --> 00:07:12,021 看看内部 109 00:07:12,164 --> 00:07:14,946 右边的板没什么花头 110 00:07:15,054 --> 00:07:19,075 从正面看 LCD就是连接在这块板上 以及扬声器和按键 111 00:07:20,790 --> 00:07:23,458 背后这块板就有趣多了 112 00:07:23,637 --> 00:07:27,913 可以看见3块芯片 这个是DMG 也就是最初的型号 113 00:07:28,077 --> 00:07:29,870 2片一样的RAM芯片 114 00:07:29,870 --> 00:07:32,549 一片工作内存(WRAM) 一片视频内存(VRAM) 115 00:07:32,748 --> 00:07:35,962 中间这个叫DMG-CPU 116 00:07:36,117 --> 00:07:38,742 然而其实更应该被称为SoC(System on a Chip) 117 00:07:38,958 --> 00:07:44,824 正常这类的系统都应该有很多芯片 118 00:07:44,824 --> 00:07:48,330 但是这里全部都集成进了一个芯片 也就这个DMG芯片 119 00:07:48,473 --> 00:07:51,777 来对比一下其它的GameBoy机型 120 00:07:51,777 --> 00:07:54,235 比如SGB 这些芯片看起来很接近 121 00:07:54,235 --> 00:07:57,066 其实基本上就是一样的 122 00:07:58,276 --> 00:08:05,201 GBP 稍微优化了一下 只带有一片RAM芯片 123 00:08:05,284 --> 00:08:11,057 GBL看起来基本没有区别 只是MGB系列的另外一个小型号 124 00:08:11,057 --> 00:08:12,454 带有背光而已 125 00:08:12,624 --> 00:08:15,792 接着是SGB2 基于GBP设计 126 00:08:17,107 --> 00:08:19,481 这是GBC内部的样子 127 00:08:19,647 --> 00:08:21,326 都有一个巨大的集成片 128 00:08:22,467 --> 00:08:24,512 这个呢 这个比较特殊 129 00:08:24,667 --> 00:08:26,840 可能从标识上不容易认 130 00:08:27,022 --> 00:08:28,796 这个是GB Boy 131 00:08:29,022 --> 00:08:31,889 总是有公司山寨GameBoy 132 00:08:32,058 --> 00:08:35,390 这个型号 是一个相当完美的克隆 133 00:08:35,505 --> 00:08:41,141 基本上就是 完全抄了一个原版的芯片 134 00:08:42,420 --> 00:08:44,953 来自中国的山寨GameBoy 135 00:08:45,065 --> 00:08:48,295 而且至今都可以在eBay上花三四十刀买到 136 00:08:49,084 --> 00:08:53,137 然而很遗憾晶振快了30% 游戏都玩不了了 137 00:08:56,186 --> 00:08:58,406 回来讲DMG的板子 138 00:08:58,406 --> 00:09:00,835 DMG CPU还是相当有趣的 139 00:09:00,864 --> 00:09:02,394 他接管整个系统的一切 140 00:09:02,695 --> 00:09:04,632 DMG CPU包括了什么 141 00:09:04,818 --> 00:09:09,705 CPU 中断控制器 定时器 内存 启动ROM 142 00:09:10,063 --> 00:09:16,113 还有所有的外设以及IO控制器 摇杆 用于联机的串行控制器 143 00:09:16,113 --> 00:09:17,788 声音控制器 还有 图形控制器 144 00:09:17,788 --> 00:09:19,263 也就是PPU (Pixel Processing Unit) 145 00:09:19,949 --> 00:09:21,430 来讲讲CPU吧 146 00:09:21,674 --> 00:09:22,862 历史角度来讲 147 00:09:23,088 --> 00:09:24,760 最初的GameBoy,1989年 148 00:09:24,961 --> 00:09:29,926 发布时间是在NES的和SNES的之间 149 00:09:31,228 --> 00:09:33,038 NES搭载的是6502处理器 150 00:09:33,245 --> 00:09:36,408 SNES搭载的是65816处理器 151 00:09:36,408 --> 00:09:38,391 也就是相同CPU的16位版本 152 00:09:39,144 --> 00:09:42,823 所以显然GameBoy搭载了SHARP LR35902 (强行显然2333) 153 00:09:44,134 --> 00:09:47,588 SHARP LR35902是什么 154 00:09:48,167 --> 00:09:49,347 这个和6502没啥关系 155 00:09:49,569 --> 00:09:53,758 更接近于i8080和Z80 156 00:09:53,758 --> 00:09:54,398 但是也不是其中任何一种 157 00:09:55,249 --> 00:09:56,961 两种cpu都相当有趣 158 00:09:57,166 --> 00:09:59,975 比如8080用于Altair 159 00:10:00,208 --> 00:10:03,414 也就是Bill Gates最初写程序并销售的平台 160 00:10:03,634 --> 00:10:09,994 Z80则是用于所有没有采用6502的8bit家用机 161 00:10:10,177 --> 00:10:11,482 相当成功的架构 162 00:10:12,692 --> 00:10:16,441 所以如果你想象 这是i8080的功能 163 00:10:16,630 --> 00:10:18,833 这是Z80的功能 164 00:10:18,833 --> 00:10:21,479 也就是一个超集 而且完全向下兼容 165 00:10:24,006 --> 00:10:28,013 然后这个 是GameBoy CPU 166 00:10:30,114 --> 00:10:33,886 核心架构是相同的 也就8080的那些 167 00:10:34,095 --> 00:10:39,298 比如寄存器组 指令编码 这些都是一样的 168 00:10:39,941 --> 00:10:41,499 当然也有一些不支持的功能 169 00:10:42,182 --> 00:10:44,635 但是又支持一部分Z80的功能 170 00:10:44,834 --> 00:10:47,214 然而大部分的Z80功能都不支持 171 00:10:47,432 --> 00:10:49,396 接着又在这之上加了一点自己的功能 172 00:10:50,465 --> 00:10:51,664 来看一遍吧 173 00:10:51,933 --> 00:10:55,576 先来讲讲8080的架构 174 00:10:55,827 --> 00:10:57,535 它拥有这些寄存器 175 00:10:57,535 --> 00:11:02,670 一个累加器 可以用于进行算数运算 其它寄存器不行 176 00:11:02,808 --> 00:11:04,054 一个状态寄存器 177 00:11:04,100 --> 00:11:08,525 在GB内 只有两个有用的标志位 零 和 进位 178 00:11:08,725 --> 00:11:11,006 剩下两个是用于十进制调整的 179 00:11:11,193 --> 00:11:14,568 然后是BCDEHL 6个8位寄存器 180 00:11:14,803 --> 00:11:19,041 但是你可以把B和C合起来 变成BC 181 00:11:19,237 --> 00:11:21,008 DE HL同理 182 00:11:21,148 --> 00:11:25,385 所以你实际上拥有可以作为指针的16位寄存器 183 00:11:25,930 --> 00:11:30,130 所以加起来一共有4个16位计算器 184 00:11:30,806 --> 00:11:34,531 7个8位寄存器 加上一个特殊的指针寄存器 185 00:11:34,531 --> 00:11:40,921 指向hl寄存器指向的内存地址的数据 186 00:11:41,187 --> 00:11:45,208 可以放到任何可以操作寄存器的指令中使用 187 00:11:47,542 --> 00:11:49,071 然后来看指令 188 00:11:49,337 --> 00:11:55,482 读取/存储指令 同一个指令可以直接或者间接寻址 189 00:11:55,706 --> 00:11:59,675 堆栈是16位的 只能操作16位寄存器 190 00:11:59,938 --> 00:12:02,543 这些是算数和逻辑指令 191 00:12:02,723 --> 00:12:04,809 如之前所说 这些指令只能配合累加器使用 192 00:12:04,809 --> 00:12:07,621 inc和dec(自加自减)指令是例外 可以配合任何寄存器使用 193 00:12:07,786 --> 00:12:10,889 而且也可以配合16位寄存器使用 194 00:12:11,124 --> 00:12:17,685 移位指令 还有控制指令 跳转 调用 返回 条件 间接操作 等等 195 00:12:17,846 --> 00:12:23,789 还有一些杂类的指令 清除设置进位标志 空指令 停机 停用启用中断 196 00:12:24,399 --> 00:12:26,726 中断模型 197 00:12:27,634 --> 00:12:31,992 大部分当时的机器都有一个中断向量来控制中断 198 00:12:32,123 --> 00:12:34,914 然而这个上面 并不是向量 也不止一个 199 00:12:35,113 --> 00:12:37,168 他并不是跳转到一个中断向量 200 00:12:37,349 --> 00:12:40,299 而是跳转到内存中开始部分一个固定的地址 201 00:12:40,470 --> 00:12:45,322 不同的中断对应不同的地址 比如0x40 0x48这种 202 00:12:46,159 --> 00:12:48,603 还有一些软件中断 203 00:12:48,782 --> 00:12:51,435 你可以使用特殊指令跳转到这些地方 204 00:12:51,645 --> 00:12:54,737 RST 0是一个特殊指令 因为就相当于RESET 205 00:12:55,038 --> 00:12:58,708 当你启动8080 CPU 他就从0地址开始执行 206 00:13:00,358 --> 00:13:03,877 来谈谈一些不被支持的8080功能 207 00:13:04,043 --> 00:13:07,799 这个是GB的标志位 这个是8080的标志位 208 00:13:07,952 --> 00:13:14,597 它有两个额外的标志 一个是符号位 还算有点用 一个奇偶校验 就不太有用 209 00:13:14,732 --> 00:13:16,494 所以这些指令都是不支持的 210 00:13:16,638 --> 00:13:20,781 还有一些其它指令 因为种种原因 他们决定不包括这些指令 211 00:13:20,910 --> 00:13:26,798 端口IO 你可能在8086上听说过 有独立的端口空间 212 00:13:26,798 --> 00:13:29,265 在8080上也是支持的 但是GameBoy上不支持 213 00:13:29,265 --> 00:13:31,486 因为GB使用了MMIO(内存映射IO) 214 00:13:32,870 --> 00:13:41,212 Z80支持大量额外的移位和位移位 设置位 清楚位指令 215 00:13:41,226 --> 00:13:42,219 这些都是支持的 216 00:13:42,420 --> 00:13:47,203 相对跳转 也就是更优化的跳转指令 217 00:13:47,428 --> 00:13:49,143 从中断返回 218 00:13:49,143 --> 00:13:50,327 这些都是支持的 219 00:13:50,529 --> 00:13:54,005 而不支持的则是一些Z80有趣的功能 220 00:13:54,242 --> 00:13:59,254 比如备份寄存器组 额外的寄存器 还有很多很多功能 221 00:13:59,254 --> 00:14:03,809 还有适合复制内存的自加循环指令 222 00:14:03,981 --> 00:14:07,688 不过他们也有一些自己的指令来代替这些 223 00:14:07,967 --> 00:14:12,800 比如它具有访问预减后加指令 224 00:14:12,800 --> 00:14:19,584 比如你要访问hl指针 可以使用预减指令或者后加指令 225 00:14:21,009 --> 00:14:22,689 还有一个叫做 第0页 (Zero Page) 的概念 226 00:14:22,852 --> 00:14:25,874 这个叫法有点晕 因为并不是真正的第0页 227 00:14:26,171 --> 00:14:27,792 而是在内存的顶部 228 00:14:27,948 --> 00:14:31,602 如果你了解6502就知道 这个是从6502借来的概念 229 00:14:31,914 --> 00:14:40,228 含义只是对于在内存最上端的数据可以优化编码 230 00:14:40,458 --> 00:14:45,755 比如原先这样载入需要3个字节 执行需要4个时钟机器周期 231 00:14:45,755 --> 00:14:49,291 现在可以写成这样 也就是2个字节 执行需要3个机器周期 232 00:14:49,889 --> 00:14:53,149 显然得在最高的位置放一些有用的东西 233 00:14:53,149 --> 00:14:54,646 特别是对计时敏感的东西 234 00:14:54,884 --> 00:14:56,497 还有一些额外的堆栈指令 235 00:14:56,713 --> 00:15:00,088 存储SP的指令 一个用于交换一个字节高低4位的指令 236 00:15:00,088 --> 00:15:04,912 这样8个指令 以及一个额外的电源指令 237 00:15:05,866 --> 00:15:07,529 操作码表看起来是这样的 238 00:15:07,748 --> 00:15:10,069 表明了颜色所以看起来更加直观一点 239 00:15:12,955 --> 00:15:15,963 有几个操作码是无效的 或者说没有被用到 240 00:15:15,963 --> 00:15:18,135 如果执行的话都会造成死机 应该算是个好设计 241 00:15:19,384 --> 00:15:22,392 这个比较特殊 操作码CB 242 00:15:22,604 --> 00:15:25,525 是个操作码前缀 以此为前缀又有256个操作码 243 00:15:25,744 --> 00:15:34,854 这也就是从Z80借来的滚动、移位指令 以及自己新增加的指令的空间 244 00:15:35,265 --> 00:15:38,481 再来看另外一个指令 只是作为一个例子 245 00:15:38,733 --> 00:15:42,863 这是从一个固定地址载入到累加器 246 00:15:42,863 --> 00:15:47,388 表示需要3个字节 在4MHz下需要16个时钟 247 00:15:47,388 --> 00:15:52,785 虽然存在争议 就是应该是1MHz还是4MHz (实际4MHz 但是把4个周期当1个看) 248 00:15:52,941 --> 00:15:56,409 因为所有的时钟周期数都是可以被4整除的 249 00:15:57,168 --> 00:16:01,438 因为整个系统都是和内存挂钩的 250 00:16:01,498 --> 00:16:06,272 CPU只能以RAM能够提供数据的速度来计算 251 00:16:07,218 --> 00:16:11,620 所以你可以说这是一个1MHz的CPU 252 00:16:11,820 --> 00:16:14,238 然后这个指令需要4个周期 253 00:16:14,416 --> 00:16:15,708 这个数字就小得多 254 00:16:15,881 --> 00:16:18,102 而且这个数字也才可以和类似的系统进行比较 255 00:16:18,304 --> 00:16:24,939 比如说和同样拥有1MHz内存速度的6502机器比较 256 00:16:24,939 --> 00:16:28,070 所以说 理论上 CPU确实频率是4MHz 257 00:16:28,332 --> 00:16:30,063 RAM速度是1MHz 258 00:16:30,270 --> 00:16:34,074 PPU以4MHz的速度绘图 259 00:16:34,294 --> 00:16:36,718 连接到速度为2MHz的VRAM 260 00:16:36,928 --> 00:16:38,582 所以这里就稍微有点复杂 261 00:16:38,791 --> 00:16:40,720 但是大部分情况下 262 00:16:40,888 --> 00:16:45,677 大部分的数据可以以1MHz为基准表示 263 00:16:45,909 --> 00:16:49,639 但是要精确的话 并不是1MHz 264 00:16:49,858 --> 00:16:54,044 而是1MiHz 1024*1024Hz 265 00:16:54,267 --> 00:16:59,172 所以他们没有使用十进制 而是用了二进制 266 00:16:59,337 --> 00:17:00,411 很有趣 267 00:17:00,620 --> 00:17:05,308 所以以后我提到周期 我说的是1MHz的机器周期 268 00:17:07,729 --> 00:17:13,749 所以这是一个拥有16位地址空间的8-Bit CPU 269 00:17:13,749 --> 00:17:19,092 因为拥有16Bit的指针 所以一共也就是64KB的可见地址 270 00:17:19,302 --> 00:17:24,875 32KB的是ROM 直接来自卡带 映射到卡带 271 00:17:25,103 --> 00:17:28,991 然后在上方有一个Boot ROM 一会提 272 00:17:29,195 --> 00:17:36,088 VRAM也映射在这里 可选的外部RAM 以及内部RAM 273 00:17:36,364 --> 00:17:40,180 没有使用的部分 要么没有东西 要么就是镜像已有的 274 00:17:40,521 --> 00:17:41,690 把最高地址的这些放大 275 00:17:41,690 --> 00:17:47,946 可以看到另外一页的OAM内存 就像是一种特殊用途的VRAM 276 00:17:48,164 --> 00:17:50,579 独立于VRAM 一会会具体讲 277 00:17:50,822 --> 00:17:55,076 最后一页 也就是第0页 包括了IO部分 278 00:17:55,308 --> 00:17:57,617 所有的寄存器 声音的视频的 279 00:17:57,875 --> 00:18:05,215 还有127字节的HRAM 独立于其它的RAM 280 00:18:05,454 --> 00:18:07,890 所以这代表游戏只能有32KB 281 00:18:08,105 --> 00:18:10,661 有些游戏确实只需要32KB 282 00:18:10,826 --> 00:18:13,556 比如说俄罗斯方块 就刚好挤进32KB 里面就一片芯片 283 00:18:13,837 --> 00:18:16,302 生产也很方便 284 00:18:16,506 --> 00:18:18,102 然后其它的卡带 285 00:18:18,412 --> 00:18:22,985 容量没有理论上限 但是实际上最大是到2MB 286 00:18:23,246 --> 00:18:25,708 这个是128KB 287 00:18:25,929 --> 00:18:29,737 实现的办法是 增加一个MBC (内存分页控制器) 288 00:18:29,963 --> 00:18:32,601 一个特殊的可以切换分页的芯片 289 00:18:32,823 --> 00:18:35,338 这个在各种系统上都是很常见的做法 290 00:18:35,563 --> 00:18:40,907 这些控制器可以很不一样 但是实际上 大部分的工作方式是这样的 291 00:18:41,134 --> 00:18:45,931 最低16KB空间 永远映射到Bank0 292 00:18:46,050 --> 00:18:51,204 而16KB-32KB这部分可以映射到Bank1 Bank2等等 293 00:18:52,961 --> 00:18:57,109 切换分页的方法就是往一个地址空间内写入一些特定数值 294 00:18:57,365 --> 00:19:01,319 这些就会被MBC读取到 然后切换分页 295 00:19:01,572 --> 00:19:03,613 这点对于外部RAM也是一样的 296 00:19:03,836 --> 00:19:08,919 游戏卡带如果含有外置内存 比如用于保存游戏 297 00:19:09,166 --> 00:19:11,841 比如装有备份电池的内存 这也是保存游戏的唯一办法 298 00:19:12,075 --> 00:19:15,581 可以把外部内存映射在这里 一样的模型 299 00:19:15,850 --> 00:19:18,063 所以这个BootROM是什么 300 00:19:18,331 --> 00:19:22,440 CPU从内存地址0开始执行 301 00:19:22,649 --> 00:19:26,820 BootROM的作用就是显示这些东西 然后响一声 302 00:19:27,292 --> 00:19:29,252 BootROM是内置在GameBoy里面的 303 00:19:29,508 --> 00:19:32,900 花了一段时间这些代码才被提取出来 还是挺麻烦的 304 00:19:33,070 --> 00:19:34,250 (不是我完成的) 305 00:19:35,040 --> 00:19:37,271 这个就是完整的BootROM 306 00:19:37,271 --> 00:19:38,321 首先初始化RAM 307 00:19:38,321 --> 00:19:39,692 初始化声音 308 00:19:39,692 --> 00:19:42,429 设置和解码显示在屏幕上的logo 309 00:19:42,649 --> 00:19:44,762 卷动logo 播放声音 310 00:19:44,988 --> 00:19:48,647 然后就是有趣的部分 它对比logo 311 00:19:48,848 --> 00:19:51,912 游戏卡带内需要有一份logo副本 312 00:19:52,114 --> 00:19:55,531 如果两者不一样 就不会继续开机 313 00:19:55,743 --> 00:20:00,327 这样任天堂就能控制什么游戏是给这个平台发行的 314 00:20:00,531 --> 00:20:02,175 游戏必须要内置这个logo 315 00:20:02,288 --> 00:20:08,145 如果未经允许加入 那不单单是违反版权保护 而且还是违反商标保护 316 00:20:09,584 --> 00:20:13,142 最后是计算校验和 算是再一次确认 317 00:20:13,142 --> 00:20:15,550 否则你得拆出来吹吹卡带 吹吹机子 重新插一遍 318 00:20:15,550 --> 00:20:19,249 接着它关闭BootROM 继续执行 319 00:20:19,369 --> 00:20:26,868 这个logo 实际上是从卡带里面读取并且显示出来的 320 00:20:27,094 --> 00:20:30,025 如果不插卡带开机会看见这个 321 00:20:30,741 --> 00:20:34,276 但是这并不代表游戏开发商可以往里面装入任何logo 322 00:20:34,276 --> 00:20:36,110 因为GameBoy会对比这个logo 323 00:20:36,136 --> 00:20:38,084 如果logo错误就不会继续启动了 324 00:20:39,328 --> 00:20:41,859 因为没有清理代码 325 00:20:42,029 --> 00:20:44,112 没有重启系统或者其它事情 326 00:20:44,112 --> 00:20:47,155 所以有些游戏就直接开始调戏这个logo 327 00:20:47,393 --> 00:20:48,323 直接在此之上继续 328 00:20:48,553 --> 00:20:50,776 demo也显然很喜欢调戏这个 329 00:20:50,987 --> 00:20:54,442 任天堂logo已经在屏幕上了 就对它做点啥吧(阴险 330 00:20:59,343 --> 00:21:00,033 很不错 331 00:21:01,771 --> 00:21:06,171 BootROM一直运行 直到最后一条指令关闭BootROM 332 00:21:06,383 --> 00:21:11,717 这是即使是最低地址显示的也是游戏卡带的内容了 333 00:21:11,926 --> 00:21:13,542 它就继续从这里执行 334 00:21:13,739 --> 00:21:16,507 运行来自游戏中的下一条指令 335 00:21:16,660 --> 00:21:18,548 通常这里是一个跳转指令 336 00:21:18,755 --> 00:21:22,131 因为这其实是所谓的ROM头 按照要求就应该是这样的 337 00:21:23,489 --> 00:21:27,571 头部必须内置任天堂Logo 头部校验和 这是很重要的 338 00:21:27,571 --> 00:21:29,576 剩下的元数据就不重要了 339 00:21:29,576 --> 00:21:32,158 对于当时的游戏开发者来说什么很重要 340 00:21:32,158 --> 00:21:34,452 但是没有软件会真正检查这些内容 341 00:21:34,452 --> 00:21:38,040 在这之后 就可以放真正的游戏数据了 342 00:21:39,259 --> 00:21:42,939 还没讲到过的东西就是IO部分和HRAM了 343 00:21:42,939 --> 00:21:51,788 也就是在最高页 所谓的第0页 可以比较高效得操作 344 00:21:51,973 --> 00:21:55,682 最高的127字节是额外的内存 345 00:21:55,864 --> 00:22:00,100 剩下的 就是这些不同的设备 346 00:22:00,293 --> 00:22:02,735 这些是所有系统内的寄存器 347 00:22:02,907 --> 00:22:06,572 中断控制器 声音控制器 摇杆输入 串口传输 定时器 还有PPU 348 00:22:06,743 --> 00:22:09,543 这些就是要讲的所有东西 349 00:22:09,915 --> 00:22:11,318 摇杆输入 350 00:22:11,530 --> 00:22:15,356 非常简单 这是所有GameBoy拥有的输入 351 00:22:15,513 --> 00:22:16,992 4个按钮 4个方向 352 00:22:17,203 --> 00:22:21,424 于是你会想 弄8个GPIO 这样就能实现了 353 00:22:21,424 --> 00:22:23,674 但是也可以用6个GPIO 354 00:22:24,226 --> 00:22:26,390 2列4行 355 00:22:26,533 --> 00:22:32,675 你选择想要测试的行 然后知道这一行按下了什么按键 356 00:22:32,725 --> 00:22:34,620 这样就能用6个而不是8个 357 00:22:35,141 --> 00:22:36,719 按键就是这些 很简单 358 00:22:37,448 --> 00:22:38,837 串行数据传输 359 00:22:39,134 --> 00:22:42,286 你可以用一条联机线把两台GameBoy连在一起 360 00:22:42,459 --> 00:22:49,700 联机线其实就是 1条单向数据线 1条另外一个方向的数据线 1条时钟线 361 00:22:49,922 --> 00:22:54,410 两台GameBoy得确定谁发送时钟 谁接收时钟 362 00:22:54,626 --> 00:22:56,593 这些就是控制这些东西的位 363 00:22:56,761 --> 00:22:59,443 一个设置输出时钟 永远是8kHz 364 00:22:59,655 --> 00:23:04,342 接收时钟范围很宽 可以从0一直到0.5MHz都能收到 365 00:23:04,342 --> 00:23:09,831 一旦传输开始 就会一位位移出字节 而且永远是全双工的 366 00:23:11,676 --> 00:23:12,732 定时器 367 00:23:12,983 --> 00:23:15,694 任何系统都得有个时钟 这个系统只有一个时钟 368 00:23:17,349 --> 00:23:21,083 TMA寄存器用来存放计数初值 369 00:23:21,311 --> 00:23:24,697 然后可以选择四种速度之一 370 00:23:24,891 --> 00:23:27,744 接着打开定时器 向上计时 371 00:23:27,966 --> 00:23:35,026 直到溢出 就会载入初值 然后可选的产生一个中断 372 00:23:35,239 --> 00:23:36,993 讲到中断 373 00:23:37,163 --> 00:23:40,125 中断控制器支持5种不同的中断 374 00:23:40,289 --> 00:23:45,213 V-Blank(场消隐) LCD状态 也就是和PPU相关 一会讲 375 00:23:45,419 --> 00:23:47,328 已经讲过的定时器 可以产生中断 376 00:23:47,328 --> 00:23:50,020 串口 可以在接受到数据的时候产生中断 377 00:23:50,020 --> 00:23:52,003 摇杆 可以在有按键按下的时候产生中断 378 00:23:52,457 --> 00:23:54,479 这个是中断启用寄存器 379 00:23:54,659 --> 00:23:56,299 一个中断标志寄存器 380 00:23:56,539 --> 00:23:59,100 可以看见有哪些中断还没有被处理 381 00:23:59,293 --> 00:24:02,672 这些是中断发生时会跳转的地址 382 00:24:02,875 --> 00:24:05,406 你不需要手动确定发生的是哪个中断 383 00:24:05,576 --> 00:24:07,138 因为直接跳转的就是不同的地址 384 00:24:07,314 --> 00:24:09,946 所以 声音控制器 385 00:24:10,255 --> 00:24:14,512 声音控制器的寄存器数量是最多的 386 00:24:14,675 --> 00:24:19,523 一共有四个声部 每个声部有自己的一组独立的寄存器 387 00:24:19,700 --> 00:24:21,505 这样看更为合理 388 00:24:21,697 --> 00:24:24,468 4个声部 每个声部5个寄存器 389 00:24:26,451 --> 00:24:29,890 每个寄存器有特定的含义 390 00:24:31,608 --> 00:24:33,370 但是含义只是大致的 391 00:24:33,370 --> 00:24:35,917 因为四个声道并不完全一样 392 00:24:36,125 --> 00:24:38,573 有两个很接近的 产生方波 393 00:24:38,798 --> 00:24:42,321 剩下两个一个是PCM采样 一个是杂波 394 00:24:42,646 --> 00:24:46,497 看位定义的话 有一定相似性 但是还是不一样 395 00:24:46,608 --> 00:24:49,569 具体通道有具体的定义 396 00:24:50,250 --> 00:24:53,107 先看看共通的一些参数 397 00:24:53,291 --> 00:24:56,382 每个声部都有一个开关位 也就是控制这个通道的开关 398 00:24:56,611 --> 00:24:59,147 你可以在某个时候关掉这个通道 399 00:24:59,147 --> 00:25:02,520 但是还有一个长度开关 以及长度设置寄存器 400 00:25:02,734 --> 00:25:05,681 所以比如你可以这么设置 在0.25秒后自动关闭 401 00:25:06,801 --> 00:25:09,794 然后先来看PCM采样声部的寄存器 也是最简单的一个 402 00:25:11,291 --> 00:25:14,790 这个声部的作用就是 可以播放任何的波形 403 00:25:14,972 --> 00:25:18,656 它有额外的16字节寄存器 404 00:25:18,806 --> 00:25:21,488 里面有32个位置 每个4位 405 00:25:21,685 --> 00:25:28,475 你可以在里面放任何你想要的波形 406 00:25:28,688 --> 00:25:29,780 这里是几个例子 407 00:25:29,946 --> 00:25:31,577 比如你可以产生一个锯齿波 408 00:25:36,060 --> 00:25:37,197 算是相当基本的波形 409 00:25:37,428 --> 00:25:38,972 也可以有正弦波 410 00:25:44,014 --> 00:25:46,107 或者是任何自定义的东西 411 00:25:50,880 --> 00:25:52,134 相当灵活 412 00:25:52,797 --> 00:25:54,335 然后有一个频率控制 413 00:25:54,541 --> 00:25:58,818 也就是控制采样的播放速度 414 00:25:59,076 --> 00:26:00,550 2位的音量 415 00:26:00,793 --> 00:26:03,721 音量可以在100% 50% 25%和静音之间控制 416 00:26:05,196 --> 00:26:10,074 另外两个 是很相似的两个方波声部 417 00:26:10,267 --> 00:26:13,160 这些位都是一样的,功能也一样 418 00:26:13,415 --> 00:26:18,077 你不能指定波形 波形是固定的 只能是方波 419 00:26:18,087 --> 00:26:19,590 就是只能是高或者低 420 00:26:20,533 --> 00:26:22,260 但是高低比例(占空比)可以不同 421 00:26:22,260 --> 00:26:25,780 这两个位就是控制占空比的 422 00:26:27,930 --> 00:26:30,668 可以设置12.5%高 剩下的低 423 00:26:33,387 --> 00:26:34,235 25% 424 00:26:36,604 --> 00:26:37,349 50% 425 00:26:37,349 --> 00:26:40,065 75%听起来应该和25%一样,只是反相了 426 00:26:43,317 --> 00:26:46,507 这个是两个声部都支持的功能 427 00:26:46,758 --> 00:26:49,493 另外有一个功能叫音量扫频 (Volume Sweep,翻译不准确,暂时想不到更好的) 428 00:26:49,507 --> 00:26:52,299 就是音量可以增高或者降低 429 00:26:52,461 --> 00:26:56,264 降低就是模拟乐器的情况 430 00:26:58,633 --> 00:27:01,104 或者也能增高 效果比较有趣 431 00:27:04,527 --> 00:27:10,458 然后 只有第一个声部有扫频功能 432 00:27:10,663 --> 00:27:12,419 可以升高 433 00:27:15,245 --> 00:27:16,193 也可以降低 434 00:27:18,689 --> 00:27:21,157 所以可以注意到主要是用于音效的 435 00:27:21,331 --> 00:27:22,617 更多的音效例子 436 00:27:25,440 --> 00:27:26,928 这就是可以实现的效果 437 00:27:27,616 --> 00:27:30,428 然后还有第四个杂波通道 438 00:27:30,608 --> 00:27:35,580 就是一个只能产生伪随机数的移位寄存器 439 00:27:36,029 --> 00:27:41,021 根据你设置的15bit模式或者7bit模式 440 00:27:41,237 --> 00:27:45,394 他能产生两种不同的波形 441 00:27:48,194 --> 00:27:49,348 这是15bit模式 442 00:27:51,571 --> 00:27:52,693 这是7bit模式 443 00:27:54,135 --> 00:27:55,584 这些是所有的寄存器 444 00:27:56,061 --> 00:28:00,043 4个声部的寄存器 3个通用寄存器 445 00:28:00,393 --> 00:28:03,556 这个可以设置左声道和右声道的音量 446 00:28:03,734 --> 00:28:07,525 有趣的是卡带可以有自己的音频控制器 447 00:28:07,737 --> 00:28:10,853 可以产生接到这个上面的音频信号 448 00:28:11,023 --> 00:28:12,207 但是没有游戏做过这种事情 449 00:28:12,814 --> 00:28:20,852 然后还有一个寄存器 可以设置一个通道是接到左声道还是右声道 或者都接、都不接 450 00:28:21,043 --> 00:28:23,064 然后有一个电源开关 451 00:28:23,195 --> 00:28:26,561 关掉音频部分可以减少约30%的电力消耗 452 00:28:28,576 --> 00:28:33,482 然后 GameBoy的声音系统不单单被用于游戏 453 00:28:33,763 --> 00:28:38,540 人们现在还在GB上用Little Sound DJ一类的软件编曲 454 00:28:38,739 --> 00:28:40,539 我展示一个简单的例子 455 00:29:17,130 --> 00:29:18,143 音频就到此为止 456 00:29:18,706 --> 00:29:21,188 我们来谈谈Pixel Processing Unit (像素处理单元/PPU) 457 00:29:21,607 --> 00:29:25,451 PPU就是产生图形的部分 拥有12个寄存器 458 00:29:25,588 --> 00:29:26,510 并不是很多 459 00:29:27,343 --> 00:29:28,954 首先来看看规格 460 00:29:29,294 --> 00:29:32,432 在之前讲过,160*144像素,不是很多 461 00:29:32,629 --> 00:29:36,219 4级灰度 其实更接近于4级不同的绿色 462 00:29:36,400 --> 00:29:39,077 当然后续的GameBoy要好得多 463 00:29:39,272 --> 00:29:41,464 所有屏幕上的东西都是以8*8像素的方格为单位的 464 00:29:41,657 --> 00:29:44,445 屏幕上有一定数量的方格 还有一些精灵 465 00:29:44,609 --> 00:29:46,138 这些东西都得装进8KB的显存 466 00:29:46,340 --> 00:29:49,401 具体解释下这个8*8像素方格 467 00:29:49,640 --> 00:29:51,441 我们看一下俄罗斯方块的游戏画面 468 00:29:51,639 --> 00:29:54,354 所有东西都是基于这些方块的 469 00:29:54,564 --> 00:30:00,709 塞尔达也是一样 画上网格线就能看见重复的图案 而且都是和网格线对齐的 470 00:30:00,897 --> 00:30:06,241 超级马里奥大陆也是很明显 因为没有很多不同的方格 471 00:30:06,513 --> 00:30:09,399 甚至是像Donkey Kong Land这样的游戏 472 00:30:09,639 --> 00:30:12,185 在画上网格后还是能看出来的 473 00:30:12,439 --> 00:30:14,157 可以看见重复对齐的图案 474 00:30:14,360 --> 00:30:16,587 尽管他们很好地隐藏了这一点 475 00:30:17,841 --> 00:30:21,024 有些游戏干脆就利用这个特性来设计 476 00:30:21,024 --> 00:30:23,806 在Turrican里面干脆就用方块做了个动画 477 00:30:24,536 --> 00:30:26,737 我们来看看方格是什么样的 478 00:30:27,072 --> 00:30:30,312 一个方格尺寸是8*8 可以有4种颜色 479 00:30:32,150 --> 00:30:35,199 4种颜色的编码就是00 01 10和11 480 00:30:36,035 --> 00:30:40,242 把这个叠加到像素上看看编码是如何完成的 481 00:30:40,496 --> 00:30:42,901 看第一行 直接当成一个二进制数读 482 00:30:43,066 --> 00:30:46,921 于是就是 02 FF 一行像素需要2个字节 483 00:30:47,140 --> 00:30:50,707 整个下来一个方块就是16字节 484 00:30:51,981 --> 00:30:56,458 你可能注意到了这个颜色的排序比较奇怪 485 00:30:56,683 --> 00:30:58,978 这是因为调色板是可以自定的 486 00:30:59,249 --> 00:31:00,869 我可以选择任何想要的调色板 487 00:31:00,869 --> 00:31:04,210 系统内对于这些背景方块有个2位到2位的映射 488 00:31:05,517 --> 00:31:09,854 原生的表示方法是 00表示白 11表示黑 489 00:31:10,235 --> 00:31:12,978 我可以选择任意的这种的调色板 490 00:31:13,153 --> 00:31:16,175 如果有需要 我也可以选择一样的颜色 491 00:31:17,754 --> 00:31:19,615 系统内一共支持256个方块 492 00:31:19,856 --> 00:31:22,036 看这个设置 你认出是什么游戏了吗 493 00:31:22,240 --> 00:31:24,041 这是俄罗斯方块 494 00:31:24,410 --> 00:31:26,585 如果你不认识底部那些跳舞的人 495 00:31:26,766 --> 00:31:28,438 说明你没有通关过俄罗斯方块 496 00:31:31,437 --> 00:31:32,441 这是塞尔达 497 00:31:33,742 --> 00:31:37,260 这是超级马里奥大陆 只使用了128个方块 498 00:31:39,032 --> 00:31:40,859 有人认识这个吗? 499 00:31:42,954 --> 00:31:44,987 这个只是一个拼图 500 00:31:46,167 --> 00:31:47,121 是网球 501 00:31:50,327 --> 00:31:55,315 20*18个方块填满了整个屏幕 502 00:31:55,509 --> 00:31:56,829 但是其实事情不完全是这样 503 00:31:57,016 --> 00:32:01,617 在视频内存中 有32*32方块 504 00:32:01,797 --> 00:32:03,018 这才是完整的背景映射 505 00:32:03,222 --> 00:32:05,640 屏幕上能看见的只是一个区域(Viewport) 506 00:32:07,246 --> 00:32:12,144 256*256个像素还是相当方便的数字 507 00:32:12,670 --> 00:32:16,683 卷动很方便,只要移动这个区域就可以了 508 00:32:17,603 --> 00:32:23,023 使用这个模拟器就能很清楚得看到背景中发生了什么事情 509 00:32:23,256 --> 00:32:24,941 以及可视区域是如何移动的 510 00:32:25,115 --> 00:32:28,806 其实就像是一个摄像机,在32*32的映射中移动 511 00:32:29,793 --> 00:32:33,591 但是这种方法只适用于最大地图只有32*32的游戏 512 00:32:33,727 --> 00:32:35,767 但是那种需要无限卷动的游戏怎么办 513 00:32:35,965 --> 00:32:37,614 比如超级马里奥大陆 514 00:32:37,832 --> 00:32:40,371 最多可以有这么多的列 515 00:32:40,535 --> 00:32:42,398 可以把区域移动到这里 516 00:32:42,533 --> 00:32:45,073 但是到最后怎么办 517 00:32:45,269 --> 00:32:47,510 它会直接卷到左边 518 00:32:47,762 --> 00:32:52,106 如果我们能在显示出来之前重绘这些画面 519 00:32:52,281 --> 00:32:53,275 就可以拥有无限的世界大小 520 00:32:54,038 --> 00:32:56,435 在模拟器里就看得很清楚了 521 00:32:56,638 --> 00:33:01,521 在屏幕显示不到的地方会不断刷新新的地图 522 00:33:01,708 --> 00:33:03,559 对于二维也是一个道理 523 00:33:03,559 --> 00:33:05,059 这是Donkey Kong Land 524 00:33:05,059 --> 00:33:06,521 这个看起来很奇怪 525 00:33:06,521 --> 00:33:11,234 只在需要显示的地方绘制新的地图 526 00:33:14,066 --> 00:33:16,645 这是我们已经谈论过的一个图层 527 00:33:18,674 --> 00:33:19,701 背景图层 528 00:33:19,843 --> 00:33:23,084 除了背景外 还能放一层图像 529 00:33:23,253 --> 00:33:24,286 窗口 530 00:33:24,458 --> 00:33:25,286 可以直接覆盖 531 00:33:25,286 --> 00:33:26,706 也可以放在任意地方 532 00:33:26,706 --> 00:33:28,392 有个XY坐标 533 00:33:28,392 --> 00:33:31,142 他会从起始坐标开始向右下方绘制 534 00:33:31,363 --> 00:33:33,109 然而没有透明支持 535 00:33:33,287 --> 00:33:36,006 通常的用法就是放到右边 536 00:33:36,193 --> 00:33:37,698 或者放到下面 537 00:33:37,835 --> 00:33:40,611 因为这个是不受卷动影响的 538 00:33:41,943 --> 00:33:46,690 显然这个功能对于屏幕下方的分数显示来说是很必要的 539 00:33:46,732 --> 00:33:49,097 对于游戏来说非常简单好用的功能 540 00:33:49,898 --> 00:33:51,806 当然也能放在右边 541 00:33:52,033 --> 00:33:55,572 这些是GBC游戏 但是不影响 在GB上也能玩 542 00:33:57,305 --> 00:34:00,371 然后在背景和窗口层之上还有一个图层 543 00:34:00,371 --> 00:34:01,421 就是精灵层 544 00:34:01,619 --> 00:34:06,075 精灵就是放在屏幕上的不用受限于8*8方格的物体 545 00:34:06,075 --> 00:34:07,550 可以自由摆放 546 00:34:07,739 --> 00:34:09,801 现在屏幕上有3个精灵 547 00:34:09,909 --> 00:34:11,533 任天堂把它们叫做 对象,OBJ 548 00:34:12,171 --> 00:34:14,789 但是我还是继续叫他们精灵 因为就是精灵 549 00:34:15,025 --> 00:34:16,843 就直接拿这个蘑菇举例吧 550 00:34:17,033 --> 00:34:20,170 每个精灵都有自己的属性 551 00:34:20,344 --> 00:34:23,609 系统内有个 OAM 也就是对象属性映射表 552 00:34:23,853 --> 00:34:25,100 这个就是一个OAM记录 553 00:34:25,308 --> 00:34:26,695 含有以下值 554 00:34:26,872 --> 00:34:28,119 第一个是X位置 555 00:34:28,294 --> 00:34:30,854 把它放到屏幕的左边 556 00:34:31,030 --> 00:34:34,435 你可能会以为应该横向坐标为0 557 00:34:34,674 --> 00:34:35,683 但是不是 是8 558 00:34:35,861 --> 00:34:36,561 为什么呢 559 00:34:36,672 --> 00:34:37,694 因为如果你把它放在4 560 00:34:37,845 --> 00:34:38,579 他就在这 561 00:34:38,579 --> 00:34:39,484 如果设置成0 562 00:34:39,484 --> 00:34:41,192 那就看不见了 因为宽度是8 563 00:34:41,280 --> 00:34:42,441 需要有一种方法能让它进入屏幕 564 00:34:42,956 --> 00:34:45,756 对于屏幕顶端的情况也是一样的 565 00:34:45,931 --> 00:34:49,451 但是你看现在的Y坐标是16 566 00:34:49,451 --> 00:34:58,386 因为一个精灵最高可以有16像素高 567 00:34:58,605 --> 00:35:01,685 把它放到一个自然的位置 568 00:35:01,921 --> 00:35:05,932 下一个问题就是应该长什么样 569 00:35:06,159 --> 00:35:08,393 首先看见是一个8*8的方格 570 00:35:08,393 --> 00:35:09,358 和之前的方格是一样的编码 571 00:35:09,358 --> 00:35:11,262 只是这个还带有透明色 572 00:35:11,262 --> 00:35:13,636 所以00 表示透明 573 00:35:13,834 --> 00:35:16,706 因为是一样的编码 所以也是一样的方格 574 00:35:16,870 --> 00:35:20,666 而且系统内也有256个方格位可供使用 575 00:35:20,853 --> 00:35:23,173 你看现在是方块编号0x90 576 00:35:24,323 --> 00:35:26,614 剩下一个是 X翻转 选项 577 00:35:27,094 --> 00:35:32,310 所以你没有必要为 向左走 和 向右走 的蘑菇单独存两个方块 578 00:35:32,497 --> 00:35:35,417 水平翻转一下 就有向右走的了 579 00:35:35,547 --> 00:35:37,649 垂直翻转一下 就有死蘑菇了 580 00:35:37,820 --> 00:35:41,739 同时垂直和水平翻转 就有向右走的死蘑菇啦~ 581 00:35:42,734 --> 00:35:44,879 调回来 582 00:35:45,077 --> 00:35:47,009 下一个是调色板 583 00:35:47,169 --> 00:35:52,196 因为其中一种的颜色表示的是透明 584 00:35:52,398 --> 00:35:55,289 所以只剩下3种颜色可以选择 585 00:35:55,492 --> 00:36:00,633 他们不想强制固定这三种颜色 586 00:36:00,801 --> 00:36:05,221 所以你可以在四种颜色里面挑3种想要的 587 00:36:05,379 --> 00:36:07,863 而且不同的精灵不需要使用同样的3种颜色 588 00:36:08,059 --> 00:36:10,439 因为系统内可以有两个调色板 589 00:36:10,664 --> 00:36:14,757 所以才有这个位 可以选择是调色板0还是调色板1 590 00:36:14,879 --> 00:36:18,455 调色板1和0分别效果就是这样 591 00:36:19,642 --> 00:36:21,032 剩下一个是优先级 592 00:36:21,347 --> 00:36:25,030 就是精灵碰到背景时如何绘图 593 00:36:26,000 --> 00:36:28,124 如果优先级是1就有趣了 594 00:36:28,281 --> 00:36:30,311 它会画在这些白色像素之上 595 00:36:30,441 --> 00:36:34,345 但是在所有不是白色的像素后面 596 00:36:34,407 --> 00:36:36,106 当然白色并不是一个什么特殊颜色 597 00:36:36,196 --> 00:36:41,700 只是指背景调色板里的0号颜色 598 00:36:41,815 --> 00:36:46,412 所以改调色板 就能让蘑菇显示在任意颜色之上 599 00:36:46,556 --> 00:36:50,189 优先级0的话就会绘制在任何东西之上 600 00:36:50,189 --> 00:36:51,874 当然不包括透明像素 601 00:36:53,394 --> 00:36:55,337 然后就是精灵之间的优先级问题 602 00:36:55,498 --> 00:36:57,055 这个没有办法自己设置 603 00:36:57,055 --> 00:36:57,930 这个是固定的 604 00:36:57,985 --> 00:37:01,273 现在蘑菇在正方形之上 605 00:37:01,406 --> 00:37:04,687 因为它的横向坐标比正方形小 606 00:37:04,830 --> 00:37:06,765 一旦达到一样的坐标 607 00:37:06,795 --> 00:37:09,267 精灵编号小的显示在上 608 00:37:09,267 --> 00:37:14,032 因为精灵在内存中也是一个数组 存前面的画上面 609 00:37:14,032 --> 00:37:20,774 然而一旦正方形的X坐标小于蘑菇的X坐标 就会画在上面 610 00:37:20,904 --> 00:37:24,494 一部分游戏中穿透其它精灵的时候 就能看到这种现象 611 00:37:25,814 --> 00:37:30,017 系统支持最多40个精灵 屏幕上最多也是能放40个 612 00:37:30,439 --> 00:37:31,660 但是还有一个限制 613 00:37:32,008 --> 00:37:33,784 每一行最多只能有10个 614 00:37:33,909 --> 00:37:35,356 比如这里有11个 615 00:37:35,513 --> 00:37:37,375 10个是按照一行像素计算的 616 00:37:37,463 --> 00:37:39,059 所以这些像素就不会被显示出来 617 00:37:39,933 --> 00:37:42,366 或者是下面两行 618 00:37:42,403 --> 00:37:45,313 这个不是讲从左到右的第十个 619 00:37:45,488 --> 00:37:50,728 而是按照精灵的编号来决定的 620 00:37:51,815 --> 00:37:54,252 所以这是一个完整的OAM记录 621 00:37:54,431 --> 00:37:55,909 一共4个字节 622 00:37:56,060 --> 00:37:58,082 这是内存中的一个OAM记录 623 00:37:58,238 --> 00:38:01,483 FE00就是存储这些记录的起始地址 624 00:38:01,625 --> 00:38:03,785 一共有40个记录 里面能存40个精灵 625 00:38:03,785 --> 00:38:05,397 整个这个叫做OAM内存 626 00:38:05,397 --> 00:38:08,709 就是一个存在这个位置的专用内存 627 00:38:08,709 --> 00:38:10,420 并不是显存的一部分 628 00:38:11,655 --> 00:38:13,432 还有一件事情 629 00:38:13,648 --> 00:38:15,385 就是即使是小马里奥 630 00:38:15,541 --> 00:38:17,914 也超过了8*8 631 00:38:18,082 --> 00:38:20,568 你可以用4个精灵来实现 632 00:38:20,727 --> 00:38:21,829 这也是游戏里的做法 633 00:38:21,956 --> 00:38:23,110 但是还有一个模式 634 00:38:23,110 --> 00:38:25,479 可以拥有16像素高的精灵 635 00:38:25,479 --> 00:38:27,004 但是这个设定是全局的 636 00:38:27,004 --> 00:38:30,373 整个游戏都得使用16像素高的精灵 637 00:38:31,043 --> 00:38:33,085 所以已经见过3层了 638 00:38:33,228 --> 00:38:37,660 还有一件事情是 屏幕是可以被彻底关闭的 639 00:38:37,777 --> 00:38:39,549 这样就有第五种颜色了(强行) 640 00:38:39,697 --> 00:38:42,394 稍微比白色位再淡一点 641 00:38:42,545 --> 00:38:46,075 没啥用 因为得把屏幕关掉 642 00:38:46,184 --> 00:38:49,162 一旦打开屏幕但是不画任何东西 643 00:38:49,346 --> 00:38:50,544 就是白色 644 00:38:50,544 --> 00:38:53,456 然后打开背景层 比如说要显示个淡灰色 645 00:38:53,456 --> 00:38:55,686 然后就完全代替了这个颜色 646 00:38:55,686 --> 00:38:56,925 再画个窗口 647 00:38:57,158 --> 00:38:58,536 注意没有透明 648 00:38:58,661 --> 00:39:01,369 还能在上面画精灵 649 00:39:01,498 --> 00:39:05,097 但是注意精灵和窗口的颜色就无法区分了 650 00:39:05,248 --> 00:39:07,071 所以说窗口上没有裁剪一类的事情 651 00:39:08,556 --> 00:39:11,894 所以内存映射是怎么样的 652 00:39:12,060 --> 00:39:13,767 4KB的精灵方块 653 00:39:13,767 --> 00:39:16,157 4KB的背景方块 654 00:39:16,330 --> 00:39:19,115 1KB的背景映射表 655 00:39:19,215 --> 00:39:20,735 32*32 656 00:39:20,845 --> 00:39:22,955 1KB的窗口映射表 657 00:39:22,989 --> 00:39:24,380 这不是最高效的表示方法 658 00:39:24,380 --> 00:39:27,278 但是他们这么做了 因为方便 659 00:39:27,419 --> 00:39:30,229 但是我们只有8KB的显存 660 00:39:30,354 --> 00:39:34,234 放个精灵方块和背景方块 就用完了 661 00:39:34,658 --> 00:39:36,143 换种方法 662 00:39:36,143 --> 00:39:39,746 先放上精灵方块 然后在最后放上背景和窗口映射 663 00:39:39,746 --> 00:39:41,383 背景方块怎么办 664 00:39:41,383 --> 00:39:42,560 就让它们重合 665 00:39:43,561 --> 00:39:46,575 有不同的配置 3个位 666 00:39:46,615 --> 00:39:48,374 可以让它们完全重合 667 00:39:48,374 --> 00:39:49,480 也可以只重合一部分 668 00:39:49,645 --> 00:39:52,402 这些也可以交换或者重合 669 00:39:53,031 --> 00:39:54,567 重合表示什么意思 670 00:39:54,607 --> 00:40:00,790 这个情况就是精灵方块和背景方块完全重合 671 00:40:00,790 --> 00:40:03,352 可以共享完全相同的方块 672 00:40:03,352 --> 00:40:05,026 但是也可以这么放 673 00:40:05,026 --> 00:40:11,543 前128个就是精灵专用 后128个就是背景专用 674 00:40:11,543 --> 00:40:14,574 中间128个共享 675 00:40:14,574 --> 00:40:16,468 比如这个超级马里奥大陆 676 00:40:16,662 --> 00:40:20,113 前2/3都用于精灵了 677 00:40:20,337 --> 00:40:22,021 剩下的1/3被用于背景 678 00:40:24,471 --> 00:40:26,669 下一个部分是横向计时 679 00:40:30,253 --> 00:40:32,866 在基于CRT的机器当中 680 00:40:33,006 --> 00:40:38,269 这就是CRT显示图像的慢动作效果 681 00:40:38,839 --> 00:40:41,674 从头到底 从左到右 682 00:40:43,048 --> 00:40:44,503 对于GameBoy也是一样的 683 00:40:44,694 --> 00:40:47,222 它以60Hz的速度不停地扫描 684 00:40:47,395 --> 00:40:48,883 从上到下 一行一行 685 00:40:48,883 --> 00:40:49,668 从左到右 686 00:40:49,668 --> 00:40:52,471 这不是因为使用了一些旧的器件 687 00:40:52,663 --> 00:40:57,010 他们完全重新实现了这一部分的内容 688 00:40:57,169 --> 00:40:59,820 但是液晶本身就需要以60Hz的速度刷新 689 00:40:59,994 --> 00:41:01,131 所以就这样了 690 00:41:01,358 --> 00:41:05,937 如果要实现一些特效的话是很重要的 691 00:41:06,148 --> 00:41:06,942 比如这个游戏中 692 00:41:07,108 --> 00:41:09,754 屏幕不同的区域行为是不一样的 693 00:41:09,920 --> 00:41:14,110 比如我们先只看这个滚动的城市图 694 00:41:14,253 --> 00:41:18,538 直接全屏滚动肯定是不成问题的 695 00:41:18,656 --> 00:41:20,456 但是我们只想让它在屏幕上的一部分滚动 696 00:41:20,625 --> 00:41:22,923 就可以通过这些额外的寄存器完成 697 00:41:23,061 --> 00:41:28,466 如果你之前接触过8位机的编程 比如C64什么的 那么应该会理解 698 00:41:28,659 --> 00:41:31,262 你可以知道当前正在画哪一行 699 00:41:31,442 --> 00:41:33,987 或者马上会画哪一行 700 00:41:33,987 --> 00:41:37,382 除了一直等着 还可以设定一个中断 701 00:41:37,597 --> 00:41:39,736 一旦到达某一行就会执行特定代码 702 00:41:39,909 --> 00:41:43,746 我们先把SCX寄存器设定到0 703 00:41:43,897 --> 00:41:45,815 然后在第8行触发 704 00:41:45,984 --> 00:41:48,288 所以它就会以卷动为0的参数显示出这些 705 00:41:48,471 --> 00:41:56,823 然后现在我把横向滚动 设置成比如23 然后下一帧设置成24 这样就能滚动了 706 00:41:57,534 --> 00:42:02,573 把对比寄存器设置到42 就会一直继续绘图到42行 707 00:42:02,738 --> 00:42:09,815 设置成其它的 再修改LYC 画完这个路面 一会具体讲路面的事情 708 00:42:11,240 --> 00:42:14,239 最后再设置回0 因为仪表盘不需要卷动 709 00:42:14,423 --> 00:42:16,693 下一帧也是同样这么处理 710 00:42:16,819 --> 00:42:22,673 这个例子中就不需要修改X卷动寄存器了 711 00:42:22,918 --> 00:42:26,585 这个例子中 右上角的马里奥其实是一个窗口 712 00:42:27,621 --> 00:42:33,450 我们在之前讲过 窗口会从左到右从上到下绘图 713 00:42:33,450 --> 00:42:35,581 本身并不能这样只停在右上角 714 00:42:35,581 --> 00:42:36,510 但是可以做到 715 00:42:37,473 --> 00:42:39,750 在第0行触发中断 启用窗口 716 00:42:39,950 --> 00:42:42,457 在第40行的时候再触发中断 关闭窗口 717 00:42:42,658 --> 00:42:48,656 于是在40行的时候 PPU的情况就是:“窗口?啥子窗口?没听过这个说法” 718 00:42:48,843 --> 00:42:50,915 于是就不会继续画 719 00:42:51,092 --> 00:42:53,470 这个方法在不同的游戏中都有应用 720 00:42:53,660 --> 00:42:57,853 一些是关于窗口,一些则是和卷动有关 721 00:42:59,274 --> 00:43:02,454 但是如果你不是在一个特定行触发 722 00:43:02,618 --> 00:43:05,168 而是每一行都触发的话 就能实现一些比如这样的效果 723 00:43:05,368 --> 00:43:06,854 左边是屏幕上的效果 724 00:43:06,854 --> 00:43:11,069 右边是实际上存储在显存中的数据 725 00:43:12,055 --> 00:43:13,972 如果你在每一行都改变卷动寄存器 726 00:43:14,502 --> 00:43:16,893 比如按照这个曲线调整 727 00:43:16,893 --> 00:43:19,140 那么就能实现对图像的变形 728 00:43:19,140 --> 00:43:20,672 然后每一帧都改变这个曲线 729 00:43:20,895 --> 00:43:28,810 程序要做的就是 在每一行都向SCX写入一个值 730 00:43:29,080 --> 00:43:32,830 当然如果图片每行都显示那就每行都得修改 731 00:43:32,996 --> 00:43:37,560 而这个赛车游戏的效果其实差不多也是一样的道理 732 00:43:37,709 --> 00:43:39,294 这是视频内存中的情况 733 00:43:39,505 --> 00:43:40,740 只是一条直的路 734 00:43:40,933 --> 00:43:44,652 但是在绘图的时候被扭曲 735 00:43:44,859 --> 00:43:46,159 放大这块 736 00:43:46,159 --> 00:43:48,457 这是原始数据 这是效果 737 00:43:48,597 --> 00:43:50,923 忽略这里的精灵 738 00:43:51,075 --> 00:43:53,829 这是为了扭曲需要的曲线 739 00:43:53,985 --> 00:43:56,999 这是需要的偏移 也就是SCX 740 00:43:56,999 --> 00:44:00,903 如果你每行都调整SCX 就能扭曲成这样 741 00:44:00,986 --> 00:44:02,522 另外一件事情就是 742 00:44:02,648 --> 00:44:08,304 路中间的线变成了虚线 路外部也有一些横线的图案 743 00:44:08,445 --> 00:44:11,675 这是通过每几行就调整调色板来实现的 744 00:44:14,318 --> 00:44:19,141 如果看这个游戏的话 甚至可以实现弹跳的效果 745 00:44:19,282 --> 00:44:24,039 这是通过在每行同时调整纵向和横向卷动寄存器实现的 746 00:44:24,206 --> 00:44:28,269 所以就可以重复一行的内容或者跳过某些行 747 00:44:28,465 --> 00:44:30,808 只要计算好 就可以实现这种效果 748 00:44:30,996 --> 00:44:36,018 只要在行中修改垂直卷动寄存器 749 00:44:36,195 --> 00:44:38,518 就能实现二维的扭曲效果 750 00:44:38,742 --> 00:44:43,518 但是我们还得再仔细讲一讲 横向计时 751 00:44:46,706 --> 00:44:49,011 在一行绘制的时候 发生了什么 752 00:44:49,975 --> 00:44:53,125 这是PPU的像素传输模式 753 00:44:53,296 --> 00:44:55,172 通常需要43个时钟的时间 754 00:44:55,401 --> 00:44:57,822 一共144行 755 00:44:58,037 --> 00:45:00,579 但是你不能直接认为 756 00:45:00,788 --> 00:45:04,958 在第一行结束后 它会立即绘制下一行的第一个像素 757 00:45:05,099 --> 00:45:06,584 现实不是这样的 758 00:45:06,759 --> 00:45:10,907 因为在每一行的开始还有一个OAM搜索阶段 需要20时钟 759 00:45:11,117 --> 00:45:12,278 我一会讲 760 00:45:12,424 --> 00:45:16,370 每行的结尾还有一个51时钟的行消隐 761 00:45:16,642 --> 00:45:19,532 在行消隐阶段,PPU就闲着,什么事情都不做。 762 00:45:19,720 --> 00:45:23,640 还有一个场消隐阶段 就是在帧之间的空闲 763 00:45:23,834 --> 00:45:25,404 来计算一下 764 00:45:25,602 --> 00:45:28,984 一行有114时钟 频率是1MHz 765 00:45:29,021 --> 00:45:32,619 154行 所以需要这么多个时钟周期完成1帧 766 00:45:32,619 --> 00:45:35,163 如果以用基准频率除以这个 767 00:45:35,344 --> 00:45:37,763 得到的就是59.7Hz的刷新率 768 00:45:41,486 --> 00:45:45,174 PPU当前所在的状态是可以直接读出的 769 00:45:45,334 --> 00:45:49,351 CPU可以知道 而且也可以基于这个设定中断 770 00:45:49,351 --> 00:45:51,842 但是CPU为什么需要知道呢 771 00:45:52,492 --> 00:45:55,114 我们来看看不同模式下都发生了什么 772 00:45:55,313 --> 00:45:58,607 首先 每行开始20周期的OAM搜索是做什么的 773 00:45:58,778 --> 00:46:04,111 每一行 PPU都需要确定这一行中哪些精灵是可见的 774 00:46:04,111 --> 00:46:06,319 所以系统内一共有40个精灵 775 00:46:06,531 --> 00:46:12,422 然后它得找出 这一行中可见的精灵 776 00:46:12,549 --> 00:46:16,319 然后放进一个最多可以保存10个精灵的 可见精灵数组 777 00:46:16,541 --> 00:46:21,146 逻辑是 x坐标不能为0 因为那样就不可见了 778 00:46:21,345 --> 00:46:27,219 然后当前所绘制的行必须要在精灵的第一行和最后一行之间 779 00:46:27,404 --> 00:46:29,315 符合要求就加入可见精灵数组 780 00:46:29,516 --> 00:46:32,012 然后这需要20周期的时间 781 00:46:32,266 --> 00:46:35,531 顺便在原版的GameBoy中这里有个有趣的bug 782 00:46:35,724 --> 00:46:43,990 如果你在OAM搜索期间 进行了任何数字在FE00到FEFF之间的16位运算 783 00:46:44,179 --> 00:46:45,511 也就是到OAM内存的指针 784 00:46:45,654 --> 00:46:47,113 即使你没有访问内存 785 00:46:47,942 --> 00:46:52,023 他也会破坏这一块的内存 786 00:46:56,036 --> 00:46:58,587 还有什么需要注意的 787 00:46:59,894 --> 00:47:01,665 CPU连接到内存 788 00:47:01,907 --> 00:47:04,234 PPU连接到显存 789 00:47:04,443 --> 00:47:07,573 OAM内存是特殊的 PPU也直接连接到OAM内存 790 00:47:07,727 --> 00:47:12,387 CPU也可以连接到显存 这样就能写入到显存 791 00:47:12,387 --> 00:47:13,918 但是并不是这样完成的 792 00:47:14,106 --> 00:47:15,958 那样需要双倍速的显存 793 00:47:16,127 --> 00:47:20,127 你看C64是那样做的 但是在GameBoy上就必须经过PPU 794 00:47:20,544 --> 00:47:23,308 PPU内部有个开关 PPU可以决定CPU能不能访问 795 00:47:23,308 --> 00:47:24,843 如果PPU不让CPU访问 796 00:47:24,843 --> 00:47:27,597 那么CPU进行写入 什么都不会发生 797 00:47:27,597 --> 00:47:29,163 CPU进行读取 读到的全部是FF 798 00:47:29,270 --> 00:47:31,001 至少不会发生什么严重的问题 799 00:47:31,001 --> 00:47:32,286 但是也没有什么意义 800 00:47:32,286 --> 00:47:36,791 所以说CPU必须确保PPU在合适的模式下 这样才能顺利访问 801 00:47:38,338 --> 00:47:41,602 在像素传输阶段 不能访问显存 802 00:47:41,832 --> 00:47:48,169 但是在OAM搜索 行消隐和场消隐阶段都是可以访问的 803 00:47:48,346 --> 00:47:50,409 如果你想要访问OAM内存 804 00:47:50,635 --> 00:47:54,281 OAM搜索或者像素传输都不可以 805 00:47:54,392 --> 00:47:57,096 像素传输时PPU要绘图 需要精灵数据 806 00:47:57,279 --> 00:47:59,655 只能在这些时候才能访问 807 00:47:59,818 --> 00:48:02,782 所以在屏幕绘图时就得十分小心 808 00:48:03,822 --> 00:48:06,877 所以这些时间对于CPU来说都是不好的 809 00:48:07,079 --> 00:48:08,777 不应该做任何重要的事情 810 00:48:08,967 --> 00:48:12,010 比如说CPU想要把新的一行移动进背景映射 811 00:48:12,010 --> 00:48:13,572 最好是在场消隐阶段完成 812 00:48:13,754 --> 00:48:16,016 有最多的不被打断的时间 813 00:48:16,511 --> 00:48:21,348 而所有的游戏逻辑和AI可以在屏幕绘图的时候完成 814 00:48:21,487 --> 00:48:26,737 但是这个时候不能立即写入新的精灵位置 815 00:48:26,784 --> 00:48:28,176 因为OAM也是不能访问的 816 00:48:28,830 --> 00:48:30,789 所以游戏一般的做法都是 817 00:48:30,988 --> 00:48:36,306 把新的精灵信息全部写入一个OAM内存的副本 818 00:48:36,376 --> 00:48:39,781 然后在场消隐阶段复制进OAM 819 00:48:40,041 --> 00:48:44,042 就是从任何这里其中一个 存放OAM副本的地方 820 00:48:44,042 --> 00:48:47,290 复制进OAM 这个图不是按比例画的 821 00:48:48,733 --> 00:48:50,854 而且这个过程不需要自己完成 822 00:48:50,854 --> 00:48:52,572 有一个DMA功能 823 00:48:52,708 --> 00:48:55,534 写入想要复制的块 824 00:48:55,737 --> 00:48:58,629 然后等待160个时钟 825 00:48:58,824 --> 00:49:02,550 在复制的过程中 CPU还是在运行的 826 00:49:02,756 --> 00:49:06,988 但是不能访问任何的源地址空间 827 00:49:07,158 --> 00:49:08,200 所以只能等着 828 00:49:08,390 --> 00:49:11,261 但是因为这个代码也得有地方存放 829 00:49:11,431 --> 00:49:14,107 所以可能的地方只有是HRAM 830 00:49:14,318 --> 00:49:15,542 也算是个不错的用途 831 00:49:17,235 --> 00:49:18,951 然后是像素流水线 832 00:49:19,120 --> 00:49:21,939 我们继续深入来研究像素流水线 833 00:49:21,939 --> 00:49:28,662 这个还算是非常新的研究 之前是没有公开的 834 00:49:28,915 --> 00:49:35,030 像素FIFO(先入先出缓存)是GameBoy绘图的核心概念 835 00:49:35,575 --> 00:49:37,210 我们直接跳到中间过程 836 00:49:37,210 --> 00:49:38,645 屏幕上已经有一些像素了 837 00:49:38,645 --> 00:49:42,296 比如这5个像素已经被送给LCD了 838 00:49:42,806 --> 00:49:45,481 然后像素FIFO 里面有一些像素 839 00:49:45,635 --> 00:49:49,959 然后每一个4MHz的步骤 它都会移出一个像素 840 00:49:50,153 --> 00:49:51,456 送给LCD 841 00:49:51,696 --> 00:49:53,794 移出一个像素 送给LCD 842 00:49:54,054 --> 00:49:55,862 然后再移出下一个给LCD 843 00:49:55,862 --> 00:49:59,277 你可能注意到那个绿色的指示灯变成红色了 844 00:49:59,691 --> 00:50:04,152 因为像素FIFO必须要有至少8个像素才能移出像素 845 00:50:04,364 --> 00:50:05,785 为什么 一会会讲到 846 00:50:05,931 --> 00:50:09,392 现在需要产生新的数据来填充FIFO了 847 00:50:09,581 --> 00:50:12,684 Fetch(获取)单元就是做这个的 848 00:50:12,882 --> 00:50:14,555 它会读取背景方块单元 849 00:50:14,729 --> 00:50:18,645 9802是目前在读取的映射位置 850 00:50:18,842 --> 00:50:22,056 它从背景映射中读取方块的ID 需要1个周期 851 00:50:22,219 --> 00:50:28,033 然后从方块数据内存 读取第一部分和第二部分的数据 852 00:50:28,226 --> 00:50:31,615 因为每一行的方块是16位 853 00:50:31,800 --> 00:50:34,773 这样它就能产生8个新的像素 854 00:50:34,945 --> 00:50:38,781 然后就重新开始 进入下一个位置 855 00:50:38,949 --> 00:50:42,876 就可以把这8个像素移入FIFO的上半部分 856 00:50:43,058 --> 00:50:45,666 这样就可以继续移出新的像素 857 00:50:45,848 --> 00:50:47,540 然而在执行过程中 858 00:50:47,765 --> 00:50:51,435 并不是说等待有空了 然后开始Fetch填充 移出再这样 859 00:50:51,435 --> 00:50:54,001 并不是交替进行的 而是同时进行的 860 00:50:55,522 --> 00:50:58,042 FIFO是2倍速的 861 00:50:58,224 --> 00:51:02,102 所以Fetch进行1个步骤 FIFO移出两个像素 862 00:51:02,259 --> 00:51:06,617 移出 移出 读取第一字节的数据 863 00:51:06,802 --> 00:51:10,699 移出 移出 读取第二字节的数据 864 00:51:10,853 --> 00:51:12,410 然后到这个时候 865 00:51:13,386 --> 00:51:16,310 现在还不能把数据存入FIFO 866 00:51:16,431 --> 00:51:17,839 因为FIFO还放不下 867 00:51:17,939 --> 00:51:19,920 所以Fetch先关闭 868 00:51:20,042 --> 00:51:21,884 等待2个周期 869 00:51:23,093 --> 00:51:24,803 也就是空闲一段时间 870 00:51:24,953 --> 00:51:26,521 然后放入数据 871 00:51:26,640 --> 00:51:28,911 所以如果你看内存访问的模式 872 00:51:29,059 --> 00:51:33,291 就能看见3次读取 1次空闲 3次读取 1次空闲这样 873 00:51:35,279 --> 00:51:38,942 所以 FIFO就是4MHz时钟下 每个周期移出一个像素 874 00:51:39,080 --> 00:51:40,975 除非含有超过8个像素 否则暂停 875 00:51:40,975 --> 00:51:42,501 Fetch单元 运行频率是2MHz 876 00:51:42,501 --> 00:51:44,538 需要3个周期来获取8个像素 877 00:51:44,832 --> 00:51:48,331 然后在第四周期停住 直到FIFO有空位 878 00:51:49,050 --> 00:51:51,159 横向移动的实现方法非常简单 879 00:51:51,308 --> 00:51:52,859 比如说我们移动3个像素 880 00:51:53,001 --> 00:51:54,778 所以所有东西都向左移动了3个像素 881 00:51:54,931 --> 00:51:57,398 最先的3个像素就直接无视掉了 882 00:51:57,531 --> 00:52:00,239 然后下一个像素送往LCD 883 00:52:00,378 --> 00:52:01,568 而在行的最后 884 00:52:01,568 --> 00:52:02,817 事情变得有趣了 885 00:52:02,939 --> 00:52:05,807 因为我们想在X=160的时候触发 886 00:52:05,902 --> 00:52:09,063 FIFO可能还包含一些根本不会被画出来的像素 887 00:52:09,149 --> 00:52:14,225 然后Fetch可能也已经在获取下8个像素的内容了 也不要了 888 00:52:14,346 --> 00:52:16,885 然后就全部停止 889 00:52:16,885 --> 00:52:19,217 就是干了太多活了 直接进入行消隐 890 00:52:19,217 --> 00:52:25,427 这也是一行需要43周期 而不是40周期的原因 891 00:52:26,603 --> 00:52:27,784 如果我们开启了窗口 892 00:52:27,889 --> 00:52:30,687 比如说窗口坐标是26 893 00:52:30,815 --> 00:52:34,448 我们现在就在26 然后FIFO已经缓存了一些像素了 894 00:52:34,558 --> 00:52:36,870 它会完全清空FIFO 895 00:52:37,037 --> 00:52:38,161 然后FIFO会被停止 896 00:52:38,161 --> 00:52:39,714 因为这些像素已经不需要了 897 00:52:41,443 --> 00:52:44,928 然后Fetch会被转到窗口映射 898 00:52:45,086 --> 00:52:46,964 然后Fetch被重新开始 899 00:52:46,964 --> 00:52:49,614 然后还是一样 读ID 读两个字节 900 00:52:49,614 --> 00:52:51,440 就得到了窗口的数据 901 00:52:51,596 --> 00:52:52,990 然后就可以放入像素FIFO 902 00:52:53,102 --> 00:52:56,555 只要这些开始被移出了 窗口就绘制出来了 903 00:52:56,665 --> 00:52:59,561 而精灵则是有10个比较器 904 00:52:59,720 --> 00:53:01,908 也是根据X位置触发 905 00:53:02,047 --> 00:53:05,116 比如说在这里X=26有一个精灵 906 00:53:05,258 --> 00:53:09,861 一样 像素FIFO里面有很多像素 然后Fetch执行到一半 907 00:53:09,990 --> 00:53:12,534 首先我们暂时暂停像素FIFO 908 00:53:12,717 --> 00:53:14,328 就不能继续移出新的像素 909 00:53:15,185 --> 00:53:18,392 然后暂时调整Fetch让它产生精灵数据 910 00:53:18,475 --> 00:53:20,610 重新开始Fetch 911 00:53:20,743 --> 00:53:22,145 然后就得到了精灵数据 912 00:53:22,324 --> 00:53:24,551 然而这次不是放到最后 913 00:53:24,668 --> 00:53:26,832 而是和前8个像素叠加 914 00:53:26,993 --> 00:53:28,798 混合到已有的像素上 915 00:53:28,962 --> 00:53:32,176 这也就解释了为什么FIFO中必须要有至少8个像素 916 00:53:32,344 --> 00:53:35,052 因为精灵是用这种方法来混合的 917 00:53:36,668 --> 00:53:41,373 然后和其它的系统有一个区别 918 00:53:41,558 --> 00:53:43,917 在窗口开始前 都以恒定速度移出像素 919 00:53:44,096 --> 00:53:46,093 当窗口开始的时候 920 00:53:46,203 --> 00:53:51,491 FIFO被清空 很长一段时间都不能输出数据 921 00:53:51,605 --> 00:53:54,326 直到FIFO内有了窗口数据 922 00:53:54,420 --> 00:53:57,477 也就是等到恢复了 923 00:53:57,608 --> 00:53:59,887 也就是需要至少43个周期 924 00:54:00,064 --> 00:54:03,535 如果有精灵就需要更多 925 00:54:03,695 --> 00:54:06,485 在基于液晶的系统上这么做是完全可行的 926 00:54:06,485 --> 00:54:07,798 你可以暂停发送像素 927 00:54:07,932 --> 00:54:09,656 而在基于CRT的系统上 不行 928 00:54:09,915 --> 00:54:11,524 比如说在C64上 929 00:54:11,681 --> 00:54:14,184 一行必须是正好的40时钟 930 00:54:14,263 --> 00:54:18,659 因为任何晚出来的像素看起来都会被右移 931 00:54:20,833 --> 00:54:23,468 所以这个图并不是完全准确 932 00:54:23,539 --> 00:54:26,540 像素传输应该是至少43时钟 933 00:54:26,663 --> 00:54:30,451 行消隐则是剩下的时间 934 00:54:30,690 --> 00:54:34,554 然后实际过程中 取决于屏幕上的内容 更接近于这样 935 00:54:37,485 --> 00:54:41,076 但是之前这个并不是像素FIFO真正的工作方式 936 00:54:41,076 --> 00:54:43,305 它并不存储像素颜色 937 00:54:43,305 --> 00:54:49,008 它存储的是原始的位组合 938 00:54:49,170 --> 00:54:50,153 以及来源 939 00:54:50,339 --> 00:54:53,341 比如说这里是9个背景像素 940 00:54:53,519 --> 00:54:55,023 Fetch也是一样的 941 00:54:55,146 --> 00:54:56,797 它并不产生像素颜色 942 00:54:56,972 --> 00:55:00,012 它产生的是位组合 943 00:55:00,210 --> 00:55:04,945 加上来源或者精灵调色板 944 00:55:05,096 --> 00:55:07,394 我们来把这个混合一下 945 00:55:07,594 --> 00:55:10,489 精灵的00表示在背景之上绘图 946 00:55:10,699 --> 00:55:12,415 来过一遍 947 00:55:12,607 --> 00:55:16,748 这里是精灵调色板1 数据00 表明是透明的 背景获胜 948 00:55:16,821 --> 00:55:20,358 这个情况下精灵获胜 因为应该画在背景之上 949 00:55:20,456 --> 00:55:22,696 大部分像素都是这个情况 950 00:55:22,848 --> 00:55:30,493 而最后这个像素 是精灵调色板1 数据透明 所以背景获胜 951 00:55:30,671 --> 00:55:33,986 再来和同一坐标下另外一个精灵混合一下 952 00:55:36,850 --> 00:55:42,185 这里是精灵调色板0 在背景之上 精灵获胜 953 00:55:42,338 --> 00:55:45,590 然后这个情况下 新的精灵不会覆盖旧的 954 00:55:45,754 --> 00:55:50,255 所以旧的获胜 然后就一直这样 955 00:55:50,389 --> 00:55:52,712 这也就是精灵会画在右边精灵的上面 956 00:55:52,712 --> 00:55:54,713 但是不会画在已有精灵之上 957 00:55:54,860 --> 00:55:57,934 高ID精灵输给低ID精灵的原因 958 00:55:58,101 --> 00:56:00,427 最后这里新的精灵又获胜了 959 00:56:00,589 --> 00:56:03,671 然后应用上调色板 960 00:56:03,768 --> 00:56:06,079 只有在最后像素被移出的时候才进行 961 00:56:06,195 --> 00:56:09,138 这里对应调色板 移出一个像素到液晶 962 00:56:09,291 --> 00:56:14,015 查表 转换 LCD 963 00:56:14,492 --> 00:56:16,728 另外一个 黑色 964 00:56:17,928 --> 00:56:22,053 这也是在彩色机型上的做法 965 00:56:22,209 --> 00:56:24,278 从SGB开始 966 00:56:24,448 --> 00:56:27,231 现有的游戏不能处理颜色 但是可以上色 967 00:56:27,371 --> 00:56:32,578 然后就是 已有的三种调色板现在变成了RGB调色板 968 00:56:32,693 --> 00:56:34,343 其它所有的都是一致的 969 00:56:35,844 --> 00:56:38,726 但是一旦移出像素 970 00:56:38,881 --> 00:56:41,625 它就对应RGB颜色 971 00:56:41,838 --> 00:56:43,266 把这个粉色的像素显示在屏幕上 972 00:56:43,517 --> 00:56:46,194 S1 11是这里 973 00:56:46,367 --> 00:56:48,645 黑色像素 974 00:56:48,788 --> 00:56:53,689 再来一个例子 S1 01 精灵调色板1 显示在屏幕上 975 00:56:54,674 --> 00:56:56,704 所有技术性内容到此为止 976 00:57:08,829 --> 00:57:09,582 还有5分钟的时间 977 00:57:12,031 --> 00:57:13,216 来讲讲开发 978 00:57:13,539 --> 00:57:16,200 如果你对GameBoy开发感兴趣 979 00:57:16,340 --> 00:57:17,871 有一些很不错的开发工具 980 00:57:18,021 --> 00:57:21,791 Rednex Game Boy 开发系统是一套命令行工具 981 00:57:21,961 --> 00:57:24,623 可以配合自己的编辑器和Makefile使用 982 00:57:24,801 --> 00:57:27,053 如果想要Debug 983 00:57:27,208 --> 00:57:30,109 BGB模拟器 就很不错 984 00:57:30,109 --> 00:57:35,449 为Windows设计 但是配合Wine在OSX和Linux上也没问题 985 00:57:35,720 --> 00:57:38,525 内置调试器 断点啊 单步啊 一类的 986 00:57:38,525 --> 00:57:43,272 然后还有一个显存查看器 可以查看所有发生的事情 987 00:57:43,365 --> 00:57:47,284 很适合在上面运行Demo然后观察发生了什么 988 00:57:48,287 --> 00:57:50,790 如果你想要在真机上运行 989 00:57:50,790 --> 00:57:52,902 也有像Everdrive一类的设备 990 00:57:53,032 --> 00:57:54,495 可以插SD卡 991 00:57:56,183 --> 00:57:58,690 因为还有4分钟时间 992 00:57:58,836 --> 00:58:02,864 我们来谈谈我最喜欢的GameBoy外设 993 00:58:02,864 --> 00:58:04,171 Game Boy Camera 994 00:58:04,338 --> 00:58:05,895 但是不是技术层面 995 00:58:06,009 --> 00:58:07,930 只是吹一波这个设备 996 00:58:08,099 --> 00:58:09,305 这个就是Game Boy Camera 997 00:58:09,473 --> 00:58:11,714 你把它插入GameBoy 998 00:58:11,837 --> 00:58:13,607 能拍照片 999 00:58:13,695 --> 00:58:15,150 然后可以用Game Boy Printer打印出来 1000 00:58:15,296 --> 00:58:17,140 用的热敏纸 1001 00:58:17,271 --> 00:58:18,833 如果还买得到的话 1002 00:58:20,228 --> 00:58:23,912 然后可以拍出像这样的照片 1003 00:58:24,082 --> 00:58:25,958 放大一点 1004 00:58:30,602 --> 00:58:31,990 非常棒的照片 1005 00:58:33,064 --> 00:58:37,603 每张照片 都是用这个14K像素的CCD拍出来的 1006 00:58:38,662 --> 00:58:42,142 位深2位 (译者注:硬件为3位,抖动为2位以匹配GameBoy屏幕) 1007 00:58:52,581 --> 00:58:55,969 所以下次去旅游的时候 记得带上GameBoy 1008 00:59:09,490 --> 00:59:15,961 然后还有Game Boy Camera 加上一台带并口的PC 还有已经买不到的联机线 1009 00:59:16,094 --> 00:59:24,312 所以感谢这些人 研究GameBoy 以及帮助我准备这场讲座 1010 00:59:24,826 --> 00:59:27,711 这些人也是以各种途径帮助了我 1011 00:59:29,596 --> 00:59:30,540 所以 1012 00:59:40,085 --> 00:59:43,508 在这个系列中 这已经是第五场讲座 1013 00:59:43,671 --> 00:59:44,798 下一个是什么 1014 00:59:45,065 --> 00:59:48,296 明年应该有个讲座吧 1015 00:59:48,494 --> 00:59:51,206 我建议两个讲座 1016 00:59:51,286 --> 00:59:54,301 我就先给34C3几个提名 1017 00:59:55,255 --> 00:59:58,912 Dominik Wagner来讲Acorn Archimedes 1018 01:00:03,862 --> 01:00:16,084 然后Jannis Harder来讲SNES 1019 01:00:16,384 --> 01:00:18,810 这是你们自己的选择 可以做这些讲座 1020 01:00:18,973 --> 01:00:21,520 或者也可以在自己头上倒冰水 1021 01:00:25,766 --> 01:00:27,144 感谢参加 明年见 1022 01:00:28,571 --> 01:01:07,000 subtitles created by c3subtitles.de in the year 2017. Join, and help us!