本文继续 PPU 的话题来讲述滚屏,从我们小时候玩游戏的经验知道 NES 是支持像素级滚屏的,这在当时那个年代是个创举,这也是为什么 FC/NES 那么火热的原因之一
那 PPU 是如何支持像素级的滚屏?这就要先来看看 PPU 的一些硬件部分。
首先来看看映射到 CPU 地址空间的一些寄存器,也是 CPU 与 PPU 通信的端口。从本文开始十六进制数我还是用 0x 表示,用 $ 有太多的格式问题,前文我每个 $ 前面加上了 \ 转义,然后使用 mdnice 导格式始终有问题,所以干脆直接就使用 0x 算了,大家也还熟悉些,只是如果要对 nes 汇编开发的话十六进制数得使用 $,不过估计几乎也没人干这事。不多说了,来看 PPU 的寄存器:
控制寄存器(0x2000):
基本上前文都说过,详细的情况后面也还会说到。
mask 都知道啥意思,要屏蔽什么东西,所以这个寄存器就是来控制哪些渲染哪些不渲染:
状态寄存器,主要记录 3 个状态:
bit5:精灵是否溢出,精灵溢出是只当前扫描行有没有超过 8 个精灵,超过则该位置 1,表溢出
bit6:sprite 0 hit,当 sprite 0 的不透明像素与背景不透明像素重叠时该位置 1,这个主要用于屏幕分割,就是制造那大片级的效果
bit7:是否处于 V_Blank,是的话置 1
看到像是 addr 和 data 寄存器,大概就知道是是用 addr 来选取一个地址,然后从 data 寄存器对该地址上的内容进行读写。的确如此,这两个端口就是用来操作 OAM 这片空间的。这里要注意因为地址总线有 16 位,而数据只有 8 位,所以每次对地址相关信息读写时要连续操作 2 次。
However,一般不这样使用,因为每次传输数据经过 CPU,太慢,通常是 OAMADDR、OAMDMA(0x4014) 两个端口配合使用。DMA 大家应该很熟悉,这里一样的道理,只要将 CPU 地址空间中的精灵信息首地址(通常是 0x200)的高低 8 位 分别填入 ADDR 和 DMA 中,DMA 就会自动将 CPU 地址空间中的精灵信息加载到 OAM,不用每次经过 CPU 中转速度大大加快。
而且大多数时间都不应该更改 OAM 里面的内容,通常情况下 OAM 里面的内容只应在 V_Blank 期间更改,因为其他时间段都处于渲染阶段,比如说当前帧渲染刚开始时坐标值表示精灵在地上,当前帧渲染要结束时修改坐标值使得精灵跑天上去了,这明显不合理是吧。
滚屏寄存器,只写,连续写两次来决定哪一个像素位于屏幕左上角。举个例子直观了解,这是马里奥的两个 NameTable:
上图一个小格就是 8 个像素,如果我向 Scroll 先后写入 24,16,则会从下图所示位置开始渲染:
PPUADDR 寄存器地址 0x2006,PPUDATA 寄存器地址 0x2007,同样的还是 addr/data 的方式读写内存。只不过这里的内存是 PPU 地址空间的内存,也就是说可以通过这两个寄存器访存 PPU RAM,PatternTable,Pallete,其他的没啥说的,基本一样。
这一部分讲述 PPU 内部不可见的寄存器,前面那几个 内存映射的寄存器 我们是可以操作访问的,但是下面这几个寄存器是不能直接访问的,来看:
Currrent VRAM address,15 bit,即 v 里面存放的是当前访问要 VRAM 的地址
Temporary VRAM address,15 bit,临时存放要访问的 VRAM 地址,或者存放滚屏地址,关于这后面详细解释。
fine X Scroll,3bit 存放滚屏时 x 轴方向的细致地址,关于滚屏后面详细说明。
toggle,1bit,一个开关,因为地址有 16bit,数据总线只有 8bit,所以写地址需要连续写两次,因此需要一个 toggle 来记录是第一次写还是第二次写。
滚屏前面在 Scroll 寄存器的地方说过一点,这里稍微详细地解释一下,也是解释内存映射寄存器和其内部的寄存器的关系。
前面我们说过向 Scroll 寄存器连续写两次(X 地址和 Y 地址)就可以设定哪一个 NameTable 的哪一个像素位于屏幕的左上角。虽然 NameTable 实际上是存放着一屏 tile 的索引,但是我们从逻辑上可以看作就是一屏 tile。
其中设定哪一个 NameTable 是通过写 0x2000 PPUCTRL 寄存器的低 2bit
而 X 地址可以分为 coarse X 和 fine X,简单的翻一下就是粗糙的 X 地址和细致的 X 地址(有啥好的翻译??),Y 地址同样也是如此,可以分为 coarse Y 和 fine Y,什么意思呢,直接来看图:
还是很好理解吧,coarse 表示某个 tile 的坐标,fine 表示这个 tile 内某个像素的精确位置
而这与 t v x t 啥关系呢?
如果 t,v 表示滚屏地址的话,它们有如下的结构:
图示很清晰不再解释,只是这里少了 fine X scroll,fine X 单独存放在 x 寄存器里面。
向 0x2000 写的数据低 2 bit 写进 t 的相应位置,表示使用哪个 NameTable
当 w = 0 即第一次向 Scroll 寄存器写时,X 地址的高 5 位写进 t 的低 5 位,数据低 3 位写进 x,写后将 w 置 1 表示下一次写将是第二次写。
当 w = 1 即第二次向 Scroll 寄存器写时,Y 地址直接写进 t 的相应位置,写后将 w 清 0.
上述操作就可以设置 某个 NameTable 的某像素位于屏幕左上角,一般情况是在 V_Blank 期间也就是 CPU 处理 NMI 的时候设置,每次使其加 1,就可以实现横向滚屏。
从编程人员的角度来说,这就是滚屏,再来总结一番:向 0x2000 低 2 位写入 NameTable,连续向 0x2005 写两次 X、Y 选取某个像素位于左上角,每次 V_Blank 期间设置一次就可以实现滚屏。
这只是一般情况下的简单滚屏方式,有一些高级玩法屏幕分割技术后面再说,另外这也只是从编程人员的角度理解,硬件怎么做的渲染部分详述。
前面说过 NES 很多抠门的地方,不过都是软件部分,这里来说说硬件部分抠门的部分。
向 0x2005 写入数据实际上就是写入 t,向 0x2006 写入地址实际上也是写入 t,只不过最后再从 t 复制到 v。地址 16 位同样需要写 2 次,所以需要一个 toggle 来记录到底是第几次写,而这个 toggle 也是共用上面提到的 w。
也就是说可以认为向 0x2005 和 0x2006 写入数据时,实际上共用两个寄存器 t 和 w,下面详细说说:
向 0x2006 第一次写入高地址时,只有数据的低 6 位有效,t 的最高位是清 0 的,另外 w 置 1。
向 0x2006 第二次写入低地址,数据的 8 位全都有效,将其写到 t 的低 8 位,写完立即将 t 复制一份 到 v,这就是写 0x2005 和 写 0x2006 的区别。写完 0x2005 后不会从 t 复制到 v,而写 0x2006 需要。另外写完之后都是需要将 w 清 0 的。
另外不论是读还是写 VRAM,都会使得 v 中的值自动加 1 或 32,这由 PPUCTRL 寄存器 bit2 控制,加 1 表示横向下一个 tile,加 32 表示纵向下一个 tile。
这部分的最后好好捋捋两个地址,一是向 0x2005 写入的滚屏地址,二是向 0x2006 写入的普通地址。
普通地址就没什么说的,它是 PPU 地址空间的地址,但是 PPU 地址空间有 64KB,但是有用的只有 8KB,所以其实 14 位就足够了,因此第一次写 0x2006 高位字节时只有 低 6 位有效。
而向写 0x2005 写的滚屏地址,严格意义上来说不能算是地址,t 与 x 加起来算是某个像素的位置。
明显的看这个图,怎么都不想一个地址的格式,一个地址也不可能这么分割。但是,t 的低 12 位,也就是 NNYYYYYXXXXX 确实可以看作一个地址。
12bit 可以索引 4KB,刚好是 4 个 NameTable & AttributeTable 的大小,而地址划分的格式刚好就是 NNYYYYYXXXXX,NN 选取 NameTable,YYYYY 表示 tile 的 Y 坐标,XXXXX 表示 tile X 坐标。
当然这里的 12 位地址不是绝对地址,而是相对于 0x2000 的相对地址。
渲染就分两部分,背景渲染和精灵渲染,以像素为单位渲染。PPU 的 “每个时钟周期” 获取背景的颜色信息和精灵的颜色信息,两者优先级竞争决定输出哪个。
很粗浅的解释,要弄清楚还是得来了解 PPU 内部的一些硬件:
下面来详细说明这些硬件在渲染期间的作用:
前面说过,渲染的方式是一个像素一个像素的渲染,且走的是 Z 字型。对于一般的 NTSC 系统来说有 262 条 Scanline,其中有 240 条 Scanline 可见,每条 Scanline 持续 341 个时钟周期,这期间就是不停的取数据然后输出渲染。这里我们先不说明每条 Scanline,每个时钟周期干什么,先来了解背景总体的渲染过程。
渲染一个背景像素需要 4bit 的颜色信息,渲染过程其实就是取得这 4bit 颜色信息。如何取得呢?
PPU 会从 v 中获取该像素所在的 tile 索引的地址信息,将这个 tile 取过来分高低位存放到 pattern_shifter 寄存器当中。然后取该 tile 的 attribute 信息分高低位存放到 attribute_shifter 寄存器当中,如此一个像素的 4 bit 颜色信息就齐了。
在每条 Scanline 的前 256 个周期,每个周期 shifter 寄存器左移 1 位,每 8 个周期就加载下一个 tile 信息到 shifter 寄存器,之后根据 fine_x 选出当前要渲染的像素,举个例子说明:
上图将 0x2005 设置滚屏地址,shifter 寄存器联系起来了,像是 attribute_shifter 也是类似的操作,图上有说明,应该能看懂什么意思的,我就不详细解释了,另外这些细节 wiki 上其实并没有明说,这是我根据模拟器的源码推出来的,这方面似乎应该也没什么详细的手册资料吧,如果有错还请指出。
可能有朋友有疑问,为什么 v 中存放着该像素所在的 tile 地址信息,这个问题其实与为什么向 0x2005 连续写两次就可以选取某个 NameTable 的某个像素位于屏幕左上角相似。
当我们向 0x2005 写两次,其实就是将某个 NameTable 的某个像素地址写入了 t,在渲染期间 t 会被复制到 v(这里我们再后文会讲述),所以写 0x2005 后第一次用 v 中的地址信息取得的 tile 就是我们所设定的,那么就使其位于屏幕左上角。之后每次使用 v 中的地址读取 tile 索引的地址信息都会自动加 1 指向下一个 tile,如此循环往复渲染 960 个 tile,一帧背景。
背景的渲染总过程就先说到这儿,一句话总结,根据 v 中记录的 tile 地址从 PatternTable 中取得 2bit 颜色信息到 pattern_shifter 寄存器,然后从 AttributeTable 中又取得 2bit 颜色信息到 attribute_shifter 寄存器,最后根据 fine_x 从 shifter 寄存器中选取要渲染的像素颜色信息
对于精灵来说,有这些相关硬件
存放 tile 图案信息到 pattern_shifter 和 attribute 信息到锁存器道理同背景,只是换了个名字锁存器其他的基本没啥不同,也不需要了解那么深入,有兴趣的可以在我后台回复 NES 获取 PPU 的手册。
这里主要说说计数器有什么作用,渲染是一行一行的渲染,每行像素的 x 坐标值范围为 [0, 255],存放在计数器中的 X 坐标每个周期是会减 1 的,所以说,当某个计数器减到 0 时说明渲染到该精灵了。
而对于精灵渲染总体过程与背景大致相同,主要是取得一个像素的 4bit 颜色信息,只是 shifter 寄存器只有等到计数器为 0 的时候才会活动(每个周期左移)。
取数据到 shifter 需要地址,这个地址就不是在 v 里面了,而是在精灵条目 OAM 中(正渲染的时候是在 Primary OAM 当中),从这里面取得 tile 索引的地址之后就去获取 tile 图案信息存放到 pattern_shifter 寄存器当中,然后获取 attribute 信息就简单了,直接从 OAM 当中获取。
好了现在我们精灵的 4bit 颜色信息和背景的 4bit 颜色信息都有了,然后就竞争到底输出哪个,当然只有背景和精灵重合的时候会有竞争,方式如下:
如果只有背景,输出背景
如果背景像素和精灵像素重合:
数字表示使用的 Pallete 中的哪个颜色,0 号颜色不管背景还是精灵都是相同的,对于背景来说可以看作是通用的背景色,对于精灵来说就是透明色。而 priority 是精灵条目中的一个属性位。
好了本文就先说这么多,本文主要讲述了内存映射的几个寄存器和内部的几个寄存器,另外简析了滚屏和渲染,后文讲述渲染每个周期的细节,以及一些关于滚屏的高级玩法。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/LOxRIy_kJFKAVUX-0aSeZw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。