【原创】NES第四波:如何做一个nes静止画面?(程序运行基本流程)

如何做一个nes静止画面?
    在这一讲中,我讲最基本的东西,是以后每一步都需要的,但不会重复讲的了。
    前言
    nes文档是由文件头、程序和图形数据组成的。
    nes程序是由6502机器码(8位)一个个拼接组成。6502汇编则是与6502机器码对应的,下面说的nes程序就是说由6502汇编编写成的程序。换言之,也是在说6502汇编代码。
 
    一、nes程序的基本内容
    nes的程序可以分4部分内容。
    1 初始化和机器预热。
    2 主程序循环。
    3 NMI中断。(独立)
    4 IRQ中断。(独立)
    其中“主程序循环”是接在“初始化和机器预热”后面运行的。而两个中断是独立的。它们与主程序之间用(公用)变量沟通。汇编里面一般都是用公用变量的。如没有特别说明,都是指公用变量。
 
    二、什么是初始化和机器预热,具体什么流程?
    初始化是对cpu做一些初定、对内存做统一清理、也对一些主程序和中断里面用到的变量赋值。
    这些变量即使预设是零值也要写上。因为上电和重新复位也是要运行这部分。
    真机的上电,内部中的所有地址上的值都是随机的。
    真机的复位,内部中的所有地址上的值都保持复位前的值,不变。复位不会对内存有任何影响的,它只不过是改变了cpu中的p寄存器。
    模拟器的上电(即打开某个nes),内部地址上的值,我就说不上了。一个个模拟器作者各有不同想法,我就不知道。不过,肯定不会闲着去生成随机数,但是他们会申请一块空间,就不知会是什么情况。
    模拟器的复位,我想应该与真机的情况一样。
 
    为什么我要说上一段这些话?
    一些合集利用复位切换游戏,那只要不对某个变量初始化赋值,反而+1,并比较超限制值则复零,这样只要读此变量就能对应的切换游戏。
    一些正常使用的变量,如果上电或复位肯定是希望按预期运行,那么初始化赋值是必须的。
 
    机器预热是什么回事?
    真机的PPU的启动比CPU慢,所以程序要等待第一个vblank的出现,才能肯定PPU开始工作了,我们才能够写入数据,在下次vblank后显示出来。(关于vblank,我专门写一篇来解释。)
    预热开始就要关屏。并一直保持一边刷关屏一边读vblank。
    预热后,进入PPU的初始化。
    PPU的初始化是设置PPU。然后要对灵精做一些处理,隐藏在背景后面,防止花屏。因为上电时灵精的数据也是随机的。接下来写入背景,用不到的页面也要清空。
    显示静止画面就只有上述的最后一部分,刷静止画面是很常用的,我们写成一个子过程就最好了。
 
    小结:
    对所有变量的赋值是很快的,可以放到PPU设置之前,这样与游戏有关的部分结合在一起,对代码可读性有好处。
    某些变量如果想在复位后保持不变,可以修改内存清理代码。一般将这些保持变量定义在内存的最后几个地址($07FF)。这样改代码也方便。调整和修改程序时影响也不大。因为程序一般不会将内存用尽,那么最后几个地址都不会影响什么的。
    注意,cpu初始化全过程不要用子过程,因为子过程用到栈,而cpu初始化则要清理栈。所以不要作死。在清理栈之后,就可以使用子过程了。
    初始化和机器预热流程如下:
    1 cpu初始化(包括屏蔽十进制指令、清理栈、清理内存)。
    2 机器预热(包括关屏、等待vblank、隐藏灵精、等待vblank结束)。
    3 所有变量赋初始值。
    4 PPU设置和画开始画面。
    5 开屏显示画面。
    上述流程,1和2是通用流程,不管什么游戏都不变的。3和4是与游戏内容相关的。
 
    三、背景都有什么内容,怎样写入(或加载)?
    基本的nes电路是包含2页背景页。我们只要写入其中一页就能够显示了。每个背景页都包含一个命名表和一个属性表。命名表指定图形,属性表指定配色。
    我的这一讲是教“怎么做”,关于原理和细节要看《任...系统文件》。
 
    1 首先,图形是怎么得到的?
    nes的图形文件,我们叫chr。编辑chr就要用到yy-chr或者ch-tlp。
    我用独立的一篇来讲一讲YY-CHR的用法。
    chr图形的单位是Tile,每个Tile是8*8像素。
    我们要注意,chr是不会记录颜色的,它只会记录色盘中的编号。那么色盘是哪些颜色,图形就会显示哪些颜色了。这很灵活,还能换颜色,但是设计的时候要记录(或保存)下设计的颜色。
 
    2 然后,文字怎么显示的?
    文字(包括数字、英文、中文或者什么国家的外文,还有标点符号)也是要做成chr文件,我们称之为字模。再用yy-chr,将字模与图形粘贴成一个4K的chr文件。
    (注意,灵精用图形的方法做成另一个4K的chr文件。)
    这里说的图形和字模,指的是用在背景上的。灵精一般不用于显示文件,因为灵精一行不能超过8个,所以特别需要才用。
    本讲只针对背景上的图形和字模。
    我还是独立用一篇来讲一讲“菜菜字模工具”的用法。
    字模是图形的一个子类,那么结构和颜色记录方面也跟图形是一样的。
    我利用配色的技巧和属性表的配合,可以实现16*16汉字的压缩和显示。这些都在那一篇里说明。也会在接下来画面一篇里面说明的。
 
    3 接下来,画面怎么画?
    图片转成nes画面?这基本是不可能的。只有对nes了如指掌的人、同时对编程也是高手的人才做得到,我自认也办不到。
    先要按手头上的图形对画面做拼凑设计。我们要知道画面的尺寸。我们以格子为单位。每个格子可以放一个chr(8*8)图形。那么画面的尺寸是32*30个格子,是横放的。但是有效显示的30*28个格子。换言之,顶行和底行,最左列和最右列是不显示的。这个很重要。有同学尝试显示一个图形,就定在第一行,那么运行nes的结果就是空白。用方格本设计一个草图,就依着它来画吧。
    设计好草图,就用我的“背景编辑工具”,得到一个命名表,一个属性表和一个调色板。
 
    4 最后画面怎么加载呢?
    命名表、属性表和调色板都是PPU的数据,是需要保存在VRAM中的(具体地,命名表、属性表保存到两个背景页之一,即VRAM中;调色板要保存到PPU里面去)。
    显示那个背景页,要看PPU中的$2005上的设置。下面说的内容,通通可以在《任...系统文件》上读到,我就简明地说一次。
    注意,下文加注“PPU地址”说明是PPU地址线的地址段,没注的说明是CPU地址线的地址段,以示区别。
    加载画面的步骤:
    准备:在前一步,设置PPU($2000的D3D4位)的时候,要指定背景和灵精各用哪一页图案表。不防设为背景用图案表0号页,灵精用图案表1号页。到了用批处理脚本打包NES文档的时候,背景用的chr就放在前,灵精用的chr就放在后。这样才能保证图案对位。
    (1)关于文件头设置
    在文件头设定水平镜像/垂直镜像,这是关于背景页的第二页在哪里。
    当设为水平时,左右两个背景页是同步的。第二页在第一页的下面。
    当设为垂直时,上下两个背景页是同步的。第二页在第一页的右面。
    PPU是假定自己有4页背景,但nes只给了它2页,余下的2页只能错误地成为镜像。
    现在我们只显示一页背景,不用知道太多,只管在文件头便设定一个镜像,反正是用不着的,可以通过模拟器观察镜像情况。
    (2)关于PPU设置
    现在我们加向第一页加载,那么目标就是PPU地址$2000所在的背景页。向$2000的D0D1写入00。表明以PPU地址$2000为第一背景页。
    加载是按字节顺次写入的,所以向$2000的D2写入0。
    (3)关于写入数据
    既然背景页地址在PPU地址$2000,则向$2006写入$20,再向$2006写入$00,就定向到PPU地址$2000了。再向$2007连续写入命名表,然后继续向$2007写入属性表。最后$2006写入$3F,再向$2006写入$00,就定向到PPU地址$3F00了,这是背景调色板的地址。再向$2007连续写入一个调色板。
    (4)设置显示窗口
    我们就在第一背景页上加载和显示,那么显示窗口要定在(0,0)。即当加载完之后向$2005写入0,两次。以后再介绍怎么移动显示窗口。

    加载这就完成了,最后就是开屏显示。如果是动态刷新背景,就不能像上述那样连续加载,要在中断(或vblank)里面分次分批完成。以后的教程再讲。
 
    另外要加载第二页要怎样呢,这里重点是搞定“不同镜像下,第二页的地址在哪,显示位置在哪,怎样移动显示窗口。关于这些,我自己都要做N个试验才能说清楚,书里面真是写得迷糊。
    另外4页联屏是什么情况。我也没有时间确定,但是这都不难。
 
    本范例用不到主程序循环和两个中断。所以简化处理它们。
    相关代码我会慢慢补充。也可以暂时看看我之前发的范例代码。可以看出一二的。所有说了要独立写的篇章,将在后面慢慢补充。有部分看完面的内容也有的,只是重新会更好。

posted on 2020-09-12 09:41  大魔司教教主  阅读(266)  评论(0编辑  收藏  举报

导航